From 599556ff3dd31aab68bb9685f1ed7fc4867803e7 Mon Sep 17 00:00:00 2001 From: Apple Date: Mon, 3 Nov 2014 23:59:51 +0000 Subject: [PATCH] ld64-241.9.tar.gz --- doc/man/man1/ld.1 | 41 ++- ld64.xcodeproj/project.pbxproj | 20 +- src/abstraction/MachOFileAbstraction.hpp | 12 + src/ld/HeaderAndLoadCommands.hpp | 5 + src/ld/InputFiles.cpp | 71 ++++- src/ld/InputFiles.h | 2 +- src/ld/LinkEditClassic.hpp | 65 ++++- src/ld/Options.cpp | 264 ++++++++++++++++-- src/ld/Options.h | 41 ++- src/ld/OutputFile.cpp | 117 ++++++-- src/ld/Resolver.cpp | 102 ++++++- src/ld/Resolver.h | 5 +- src/ld/SymbolTable.cpp | 80 ++++++ src/ld/ld.cpp | 218 +++++++++++---- src/ld/ld.hpp | 22 +- .../parsers/libunwind/DwarfInstructions.hpp | 2 + src/ld/parsers/libunwind/DwarfParser.hpp | 2 +- src/ld/parsers/libunwind/Registers.hpp | 32 +++ src/ld/parsers/lto_file.cpp | 124 +++++--- src/ld/parsers/lto_file.h | 1 + src/ld/parsers/macho_dylib_file.cpp | 29 +- src/ld/parsers/macho_relocatable_file.cpp | 260 ++++++++++++++--- src/ld/passes/branch_island.cpp | 31 +- src/ld/passes/compact_unwind.cpp | 25 +- src/ld/passes/objc.cpp | 32 ++- src/ld/passes/order.cpp | 72 +++-- src/other/ObjectDump.cpp | 28 +- src/other/unwinddump.cpp | 16 +- unit-tests/include/common.makefile | 4 +- unit-tests/test-cases/alias-basic/Makefile | 47 ++++ unit-tests/test-cases/alias-basic/aliases.s | 26 ++ unit-tests/test-cases/alias-basic/main.c | 16 ++ unit-tests/test-cases/alias-objects/Makefile | 14 +- unit-tests/test-cases/alias-objects/aliases.s | 13 +- unit-tests/test-cases/alt-entry/Makefile | 40 +++ unit-tests/test-cases/alt-entry/foo.c | 7 + unit-tests/test-cases/alt-entry/main.c | 7 + unit-tests/test-cases/lto-r/Makefile | 53 ++++ unit-tests/test-cases/lto-r/bar.c | 1 + unit-tests/test-cases/lto-r/foo.c | 38 +++ unit-tests/test-cases/lto-r/main.c | 15 + .../test-cases/lto-rename_section/Makefile | 52 ++++ unit-tests/test-cases/lto-rename_section/a.c | 4 + unit-tests/test-cases/lto-rename_section/b.c | 1 + .../test-cases/lto-rename_section/main.c | 11 + .../test-cases/lto-rename_segment/Makefile | 52 ++++ unit-tests/test-cases/lto-rename_segment/a.c | 4 + unit-tests/test-cases/lto-rename_segment/b.c | 1 + .../test-cases/lto-rename_segment/main.c | 18 ++ .../lto-symbol-section-move/Makefile | 60 ++++ .../test-cases/lto-symbol-section-move/foo.c | 38 +++ .../test-cases/lto-symbol-section-move/main.c | 37 +++ .../lto-symbol-section-move/other.c | 9 + .../lto-symbol-section-move/ram1.symbols | 5 + .../lto-symbol-section-move/rom1.symbols | 6 + .../test-cases/preload-section_order/Makefile | 54 ++++ .../test-cases/preload-section_order/extra.s | 15 + .../test-cases/preload-section_order/main.c | 5 + .../preload-section_order/main1.expected | 7 + .../preload-section_order/main2.expected | 7 + .../test-cases/preload-section_order/more.s | 19 ++ .../test-cases/preload-segment_order/Makefile | 50 ++++ .../test-cases/preload-segment_order/a.c | 13 + .../test-cases/preload-segment_order/b.c | 9 + .../preload-segment_order/main-segs.expected | 4 + .../test-cases/preload-segment_order/main.c | 11 + unit-tests/test-cases/section-labels/main.c | 6 +- .../test-cases/symbol-section-move/Makefile | 60 ++++ .../test-cases/symbol-section-move/main.c | 54 ++++ .../test-cases/symbol-section-move/other.c | 9 + .../symbol-section-move/ram1.symbols | 3 + .../symbol-section-move/rom1.symbols | 6 + .../test-cases/weak_import-undefined/Makefile | 2 +- 73 files changed, 2312 insertions(+), 320 deletions(-) create mode 100644 unit-tests/test-cases/alias-basic/Makefile create mode 100644 unit-tests/test-cases/alias-basic/aliases.s create mode 100644 unit-tests/test-cases/alias-basic/main.c create mode 100644 unit-tests/test-cases/alt-entry/Makefile create mode 100644 unit-tests/test-cases/alt-entry/foo.c create mode 100644 unit-tests/test-cases/alt-entry/main.c create mode 100644 unit-tests/test-cases/lto-r/Makefile create mode 100644 unit-tests/test-cases/lto-r/bar.c create mode 100644 unit-tests/test-cases/lto-r/foo.c create mode 100644 unit-tests/test-cases/lto-r/main.c create mode 100644 unit-tests/test-cases/lto-rename_section/Makefile create mode 100644 unit-tests/test-cases/lto-rename_section/a.c create mode 100644 unit-tests/test-cases/lto-rename_section/b.c create mode 100644 unit-tests/test-cases/lto-rename_section/main.c create mode 100644 unit-tests/test-cases/lto-rename_segment/Makefile create mode 100644 unit-tests/test-cases/lto-rename_segment/a.c create mode 100644 unit-tests/test-cases/lto-rename_segment/b.c create mode 100644 unit-tests/test-cases/lto-rename_segment/main.c create mode 100644 unit-tests/test-cases/lto-symbol-section-move/Makefile create mode 100644 unit-tests/test-cases/lto-symbol-section-move/foo.c create mode 100644 unit-tests/test-cases/lto-symbol-section-move/main.c create mode 100644 unit-tests/test-cases/lto-symbol-section-move/other.c create mode 100644 unit-tests/test-cases/lto-symbol-section-move/ram1.symbols create mode 100644 unit-tests/test-cases/lto-symbol-section-move/rom1.symbols create mode 100644 unit-tests/test-cases/preload-section_order/Makefile create mode 100644 unit-tests/test-cases/preload-section_order/extra.s create mode 100644 unit-tests/test-cases/preload-section_order/main.c create mode 100644 unit-tests/test-cases/preload-section_order/main1.expected create mode 100644 unit-tests/test-cases/preload-section_order/main2.expected create mode 100644 unit-tests/test-cases/preload-section_order/more.s create mode 100644 unit-tests/test-cases/preload-segment_order/Makefile create mode 100644 unit-tests/test-cases/preload-segment_order/a.c create mode 100644 unit-tests/test-cases/preload-segment_order/b.c create mode 100644 unit-tests/test-cases/preload-segment_order/main-segs.expected create mode 100644 unit-tests/test-cases/preload-segment_order/main.c create mode 100644 unit-tests/test-cases/symbol-section-move/Makefile create mode 100644 unit-tests/test-cases/symbol-section-move/main.c create mode 100644 unit-tests/test-cases/symbol-section-move/other.c create mode 100644 unit-tests/test-cases/symbol-section-move/ram1.symbols create mode 100644 unit-tests/test-cases/symbol-section-move/rom1.symbols diff --git a/doc/man/man1/ld.1 b/doc/man/man1/ld.1 index 964d099..df4b5a4 100644 --- a/doc/man/man1/ld.1 +++ b/doc/man/man1/ld.1 @@ -384,8 +384,6 @@ Don't turn private external (aka visibility=hidden) symbols into static symbols, but rather leave them as private external in the resulting object file. .It Fl d Force definition of common symbols. That is, transform tentative definitions into real definitions. -.It Fl rename_section Ar fromSegment fromSection toSegment toSection -Renames section fromSegment/fromSection to toSegment/toSection. .El .Ss Options that control symbol resolution .Bl -tag @@ -521,10 +519,49 @@ of wildcards. .Bl -tag .It Fl v Prints the version of the linker. +.It Fl move_to_rw_segment Ar segment_name Ar filename +Moves data symbols to another segment. The command line option specifies the +target segment name and a path to a file containing a list of symbols to move. +Comments can be added to the symbol file by starting a line with a #. +If there are multiple instances of a symbol name (for instance a "static int foo=5;" in multiple files) +the symbol name in the symbol list file can be prefixed with the object file name +(e.g. "init.o:_foo") to move a specific instance. +.It Fl move_to_ro_section Ar segment_name Ar section_name Ar filename +Moves code symbols to another segment. The command line option specifies the +target segment name and a path to a file containing a list of symbols to move. +Comments can be added to the symbol file by starting a line with a #. +If there are multiple instances of a symbol name (for instance a "static int foo() {}" in multiple files) +the symbol name in the symbol list file can be prefixed with the object file name +(e.g. "init.o:_foo") to move a specific instance. +.It Fl rename_section Ar orgSegment orgSection newSegment newSection +Renames section orgSegment/orgSection to newSegment/newSection. +.It Fl rename_segment Ar orgSegment newSegment +Renames all sections with orgSegment segment name to have newSegment segment name. +.It Fl trace_symbol_layout +For using in debugging -rename_section, -rename_segment, -move_to_ro_segment, and -move_to_rw_segment. +This option prints out a line show where and why each symbol was moved. +Note: These options do not chain. For each symbol, the linker first checks +-move_to_ro_segment and -move_to_rw_segment. If the symbol is not moved, +it checks for an applicable -rename_section. Only if the symbol still has +not been moved, does the linker look for an applicable -rename_segment option. +.It Fl section_order Ar segname Ar colon_separated_section_list +Only for use with -preload. Specifies the order that sections with the specified segment should be layout out. +For example: "-section_order __ROM __text:__const:__cstring". +.It Fl segment_order Ar colon_separated_segment_list +Only for use with -preload. Specifies the order segments should be layout out. +For example: "-segment_order __ROM:__ROM2:__RAM". .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 behavior and allows instructions on any page to be executed. +.It Fl application_extension +Specifies that the code is being linked for use in an application extension. The linker +will then validiate that any dynamic libraries linked against are safe for use in +application extensions. +.It Fl no_application_extension +Specifies that the code is being linked is not safe for use in an application extension. +For instance, can be used when creating a framework that should not be used in +an application extension. .It Fl fatal_warnings Causes the linker to exit with a non-zero value if any warnings were emitted. .It Fl no_eh_labels diff --git a/ld64.xcodeproj/project.pbxproj b/ld64.xcodeproj/project.pbxproj index 250fa5e..854356e 100644 --- a/ld64.xcodeproj/project.pbxproj +++ b/ld64.xcodeproj/project.pbxproj @@ -245,7 +245,12 @@ B3B672441406D44300A376BB /* Snapshot.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; name = Snapshot.h; path = src/ld/Snapshot.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; B3C7A09914295B9C005FC714 /* compile_stubs */ = {isa = PBXFileReference; lastKnownFileType = text.script.csh; path = compile_stubs; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9023C3906D5A23E001BBF46 /* ld */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ld; sourceTree = BUILT_PRODUCTS_DIR; }; - F9023C3F06D5A254001BBF46 /* ld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ld.cpp; path = src/ld/ld.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9023C3F06D5A254001BBF46 /* ld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ld.cpp; path = src/ld/ld.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F91B7B0318987D5F0099486F /* AddressSpace.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AddressSpace.hpp; sourceTree = ""; }; + F91B7B0418987D5F0099486F /* DwarfInstructions.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DwarfInstructions.hpp; sourceTree = ""; }; + F91B7B0518987D5F0099486F /* DwarfParser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DwarfParser.hpp; sourceTree = ""; }; + F91B7B0618987D5F0099486F /* InternalMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InternalMacros.h; sourceTree = ""; }; + F91B7B0718987D5F0099486F /* Registers.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Registers.hpp; sourceTree = ""; }; F92D9C2710657AAB00FF369B /* stub_x86_64_classic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_x86_64_classic.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F933D9460929277C0083EAC8 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = FileAbstraction.hpp; path = src/abstraction/FileAbstraction.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = src/abstraction/MachOFileAbstraction.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; @@ -401,6 +406,18 @@ name = Products; sourceTree = ""; }; + F91B7B0218987D5F0099486F /* libunwind */ = { + isa = PBXGroup; + children = ( + F91B7B0318987D5F0099486F /* AddressSpace.hpp */, + F91B7B0418987D5F0099486F /* DwarfInstructions.hpp */, + F91B7B0518987D5F0099486F /* DwarfParser.hpp */, + F91B7B0618987D5F0099486F /* InternalMacros.h */, + F91B7B0718987D5F0099486F /* Registers.hpp */, + ); + path = libunwind; + sourceTree = ""; + }; F9AA650B1051BD2B003E3539 /* passes */ = { isa = PBXGroup; children = ( @@ -449,6 +466,7 @@ F9AA65861051E750003E3539 /* parsers */ = { isa = PBXGroup; children = ( + F91B7B0218987D5F0099486F /* libunwind */, F9AA6784105700C2003E3539 /* opaque_section_file.cpp */, F9AA6785105700C2003E3539 /* opaque_section_file.h */, F9AA65D71051EC4A003E3539 /* archive_file.cpp */, diff --git a/src/abstraction/MachOFileAbstraction.hpp b/src/abstraction/MachOFileAbstraction.hpp index 9279734..8b58efd 100644 --- a/src/abstraction/MachOFileAbstraction.hpp +++ b/src/abstraction/MachOFileAbstraction.hpp @@ -192,6 +192,10 @@ #define N_SYMBOL_RESOLVER 0x100 #endif +#ifndef N_AST + #define N_AST 0x32 +#endif + #ifndef LC_FUNCTION_STARTS #define LC_FUNCTION_STARTS 0x26 #endif @@ -229,7 +233,13 @@ }; #endif +#ifndef MH_APP_EXTENSION_SAFE + #define MH_APP_EXTENSION_SAFE 0x02000000 +#endif +#ifndef N_ALT_ENTRY + #define N_ALT_ENTRY 0x0200 +#endif #ifndef CPU_SUBTYPE_ARM_V7F #define CPU_SUBTYPE_ARM_V7F ((cpu_subtype_t) 10) @@ -360,6 +370,7 @@ #define UNWIND_ARM64_DWARF_SECTION_OFFSET 0x00FFFFFF + #ifndef LC_SOURCE_VERSION #define LC_SOURCE_VERSION 0x2A struct source_version_command { @@ -431,6 +442,7 @@ #define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t) 8) #endif + struct ArchInfo { const char* archName; cpu_type_t cpuType; diff --git a/src/ld/HeaderAndLoadCommands.hpp b/src/ld/HeaderAndLoadCommands.hpp index acbdf4f..3629af5 100644 --- a/src/ld/HeaderAndLoadCommands.hpp +++ b/src/ld/HeaderAndLoadCommands.hpp @@ -591,6 +591,8 @@ uint32_t HeaderAndLoadCommandsAtom::flags() const bits |= MH_HAS_TLV_DESCRIPTORS; if ( _options.hasNonExecutableHeap() ) bits |= MH_NO_HEAP_EXECUTION; + if ( _options.markAppExtensionSafe() && (_options.outputKind() == Options::kDynamicLibrary) ) + bits |= MH_APP_EXTENSION_SAFE; } if ( _options.hasExecutableStack() ) bits |= MH_ALLOW_STACK_EXECUTION; @@ -750,6 +752,9 @@ uint32_t HeaderAndLoadCommandsAtom::sectionFlags(ld::Internal::FinalSection* case ld::Section::typeTempLTO: assert(0 && "typeTempLTO should not make it to final linked image"); return S_REGULAR; + case ld::Section::typeTempAlias: + assert(0 && "typeAlias should not make it to final linked image"); + return S_REGULAR; case ld::Section::typeAbsoluteSymbols: assert(0 && "typeAbsoluteSymbols should not make it to final linked image"); return S_REGULAR; diff --git a/src/ld/InputFiles.cpp b/src/ld/InputFiles.cpp index f49f2e3..595b5d1 100644 --- a/src/ld/InputFiles.cpp +++ b/src/ld/InputFiles.cpp @@ -255,8 +255,17 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib uint32_t fileOffset = OSSwapBigToHostInt32(archs[sliceToUse].offset); len = OSSwapBigToHostInt32(archs[sliceToUse].size); if ( fileOffset+len > info.fileLen ) { - throwf("truncated fat file. Slice from %u to %llu is past end of file with length %llu", + // file size was read awhile ago. If file is being written, wait a second to see if big enough now + sleep(1); + uint64_t newFileLen = info.fileLen; + struct stat statBuffer; + if ( stat(info.path, &statBuffer) == 0 ) { + newFileLen = statBuffer.st_size; + } + if ( fileOffset+len > newFileLen ) { + throwf("truncated fat file. Slice from %u to %llu is past end of file with length %llu", fileOffset, fileOffset+len, info.fileLen); + } } // if requested architecture is page aligned within fat file, then remap just that portion of file if ( (fileOffset & 0x00000FFF) == 0 ) { @@ -301,11 +310,27 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib } // see if it is a dynamic library - ld::dylib::File* dylibResult = mach_o::dylib::parse(p, len, info.path, info.modTime, _options, info.ordinal, info.options.fBundleLoader, indirectDylib); - if ( dylibResult != NULL ) { - return dylibResult; + ld::dylib::File* dylibResult; + bool dylibsNotAllowed = false; + switch ( _options.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + dylibResult = mach_o::dylib::parse(p, len, info.path, info.modTime, _options, info.ordinal, info.options.fBundleLoader, indirectDylib); + if ( dylibResult != NULL ) { + return dylibResult; + } + break; + case Options::kStaticExecutable: + case Options::kDyld: + case Options::kPreload: + case Options::kObjectFile: + case Options::kKextBundle: + dylibsNotAllowed = true; + break; } + // see if it is a static library ::archive::ParserOptions archOpts; archOpts.objOpts = objOpts; @@ -350,6 +375,13 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib } } + if ( dylibsNotAllowed ) { + cpu_type_t dummy1; + cpu_type_t dummy2; + if ( mach_o::dylib::isDylibFile(p, &dummy1, &dummy2) ) + throw "ignoring unexpected dylib file"; + } + // error handling if ( ((fat_header*)p)->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { throwf("missing required architecture %s in file %s (%u slices)", _options.architectureName(), info.path, sliceCount); @@ -538,7 +570,7 @@ bool InputFiles::libraryAlreadyLoaded(const char* path) } -void InputFiles::addLinkerOptionLibraries(ld::Internal& state) +void InputFiles::addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHandler& handler) { if ( _options.outputKind() == Options::kObjectFile ) return; @@ -554,6 +586,7 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state) ld::dylib::File* dylibReader = dynamic_cast(reader); if ( dylibReader != NULL ) { if ( ! dylibReader->installPathVersionSpecific() ) { + dylibReader->forEachAtom(handler); dylibReader->setImplicitlyLinked(); this->addDylib(dylibReader, info); } @@ -574,10 +607,13 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state) if ( ! this->libraryAlreadyLoaded(info.path) ) { info.ordinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal(); try { + // -force_load_swift_libs + info.options.fForceLoad = _options.forceLoadSwiftLibs() && (strncmp(libName, "swift", 5) == 0); ld::File* reader = this->makeFile(info, true); ld::dylib::File* dylibReader = dynamic_cast(reader); ld::archive::File* archiveReader = dynamic_cast(reader); if ( dylibReader != NULL ) { + dylibReader->forEachAtom(handler); dylibReader->setImplicitlyLinked(); this->addDylib(dylibReader, info); } @@ -585,6 +621,10 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state) _searchLibraries.push_back(LibraryInfo(archiveReader)); if ( _options.dumpDependencyInfo() ) _options.dumpDependency(Options::depArchive, archiveReader->path()); + // -force_load_swift_libs + if (info.options.fForceLoad) { + archiveReader->forEachAtom(handler); + } } else { throwf("linker option dylib at %s is not a dylib", info.path); @@ -895,6 +935,9 @@ void InputFiles::parseWorkerThread() { warning("ignoring file %s, %s", entry.path, msg); } } + else if ( strstr(msg, "ignoring unexpected") != NULL ) { + warning("%s, %s", entry.path, msg); + } else { asprintf((char**)&exception, "%s file '%s'", msg, entry.path); } @@ -1127,7 +1170,7 @@ void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler, ld::Internal } markExplicitlyLinkedDylibs(); - addLinkerOptionLibraries(state); + addLinkerOptionLibraries(state, handler); createIndirectDylibs(); createOpaqueFileSections(); @@ -1276,6 +1319,14 @@ static bool vectorContains(const std::vector& vec, ld::dylib:: return std::find(vec.begin(), vec.end(), key) != vec.end(); } +struct DylibByInstallNameSorter +{ + bool operator()(const ld::dylib::File* left, const ld::dylib::File* right) + { + return (strcmp(left->installPath(), right->installPath()) < 0); + } +}; + void InputFiles::dylibs(ld::Internal& state) { bool dylibsOK = false; @@ -1310,14 +1361,18 @@ void InputFiles::dylibs(ld::Internal& state) } // add implicitly linked dylibs if ( _options.nameSpace() == Options::kTwoLevelNameSpace ) { + std::vector implicitDylibs; for (InstallNameToDylib::const_iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); ++it) { ld::dylib::File* dylibFile = it->second; if ( dylibFile->implicitlyLinked() && dylibsOK ) { - if ( ! vectorContains(state.dylibs, dylibFile) ) { - state.dylibs.push_back(dylibFile); + if ( ! vectorContains(implicitDylibs, dylibFile) ) { + implicitDylibs.push_back(dylibFile); } } } + // make implicit dylib order be deterministic by sorting by install_name + std::sort(implicitDylibs.begin(), implicitDylibs.end(), DylibByInstallNameSorter()); + state.dylibs.insert(state.dylibs.end(), implicitDylibs.begin(), implicitDylibs.end()); } //fprintf(stderr, "all dylibs:\n"); diff --git a/src/ld/InputFiles.h b/src/ld/InputFiles.h index b1e85cb..608ce39 100644 --- a/src/ld/InputFiles.h +++ b/src/ld/InputFiles.h @@ -74,7 +74,7 @@ public: bool inferredArch() const { return _inferredArch; } - void addLinkerOptionLibraries(ld::Internal& state); + void addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHandler& handler); void createIndirectDylibs(); // for -print_statistics diff --git a/src/ld/LinkEditClassic.hpp b/src/ld/LinkEditClassic.hpp index b9b1215..3389f5c 100644 --- a/src/ld/LinkEditClassic.hpp +++ b/src/ld/LinkEditClassic.hpp @@ -224,7 +224,7 @@ private: uint32_t stringOffsetForStab(const ld::relocatable::File::Stab& stab, StringPoolAtom* pool); uint64_t valueForStab(const ld::relocatable::File::Stab& stab); uint8_t sectionIndexForStab(const ld::relocatable::File::Stab& stab); - + bool isAltEntry(const ld::Atom* atom); mutable std::vector > _globals; mutable std::vector > _locals; @@ -247,6 +247,29 @@ template int SymbolTableAtom::_s_anonNameIndex = 1; +template +bool SymbolTableAtom::isAltEntry(const ld::Atom* atom) +{ + // alt entries have a group subordinate reference to the previous atom + for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + if ( fit->kind == ld::Fixup::kindNoneGroupSubordinate ) { + if ( fit->binding == Fixup::bindingDirectlyBound ) { + const Atom* prevAtom = fit->u.target; + assert(prevAtom != NULL); + for (ld::Fixup::iterator fit2 = prevAtom->fixupsBegin(); fit2 != prevAtom->fixupsEnd(); ++fit2) { + if ( fit2->kind == ld::Fixup::kindNoneFollowOn ) { + if ( fit2->binding == Fixup::bindingDirectlyBound ) { + if ( fit2->u.target == atom ) + return true; + } + } + } + } + } + } + return false; +} + template bool SymbolTableAtom::addLocal(const ld::Atom* atom, StringPoolAtom* pool) { @@ -312,6 +335,8 @@ bool SymbolTableAtom::addLocal(const ld::Atom* atom, StringPoolAtom* pool) desc |= N_WEAK_DEF; if ( atom->isThumb() ) desc |= N_ARM_THUMB_DEF; + if ( (this->_options.outputKind() == Options::kObjectFile) && this->_state.allObjectFilesScatterable && isAltEntry(atom) ) + desc |= N_ALT_ENTRY; entry.set_n_desc(desc); // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) @@ -387,6 +412,8 @@ void SymbolTableAtom::addGlobal(const ld::Atom* atom, StringPoolAtom* pool) desc |= N_SYMBOL_RESOLVER; if ( atom->dontDeadStrip() && (this->_options.outputKind() == Options::kObjectFile) ) desc |= N_NO_DEAD_STRIP; + if ( (this->_options.outputKind() == Options::kObjectFile) && this->_state.allObjectFilesScatterable && isAltEntry(atom) ) + desc |= N_ALT_ENTRY; if ( (atom->definition() == ld::Atom::definitionRegular) && (atom->combine() == ld::Atom::combineByName) ) { desc |= N_WEAK_DEF; // support auto hidden weak symbols: .weak_def_can_be_hidden @@ -451,7 +478,13 @@ void SymbolTableAtom::addImport(const ld::Atom* atom, StringPoolAtom* pool) // set n_type if ( this->_options.outputKind() == Options::kObjectFile ) { - if ( (atom->scope() == ld::Atom::scopeLinkageUnit) + if ( atom->section().type() == ld::Section::typeTempAlias ) { + if ( atom->scope() == ld::Atom::scopeLinkageUnit ) + entry.set_n_type(N_INDR | N_EXT | N_PEXT); + else + entry.set_n_type(N_INDR | N_EXT); + } + else if ( (atom->scope() == ld::Atom::scopeLinkageUnit) && (atom->definition() == ld::Atom::definitionTentative) ) entry.set_n_type(N_UNDF | N_EXT | N_PEXT); else @@ -500,8 +533,24 @@ void SymbolTableAtom::addImport(const ld::Atom* atom, StringPoolAtom* pool) // set n_value, zero for import proxy and size for tentative definition if ( atom->definition() == ld::Atom::definitionTentative ) entry.set_n_value(atom->size()); - else + else if ( atom->section().type() != ld::Section::typeTempAlias ) entry.set_n_value(0); + else { + assert(atom->fixupsBegin() != atom->fixupsEnd()); + for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + assert(fit->kind == ld::Fixup::kindNoneFollowOn); + switch ( fit->binding ) { + case ld::Fixup::bindingByNameUnbound: + entry.set_n_value(pool->add(fit->u.name)); + break; + case ld::Fixup::bindingsIndirectlyBound: + entry.set_n_value(pool->add((_state.indirectBindingTable[fit->u.bindingIndex])->name())); + break; + default: + assert(0 && "internal error: unexpected alias binding"); + } + } + } // add to array _imports.push_back(entry); @@ -775,13 +824,13 @@ uint64_t LocalRelocationsAtom::relocBaseAddress(ld::Internal& state) // for kext bundles the reloc base address starts at __TEXT segment return _options.baseAddress(); } - // for all other kinds, the x86_64 reloc base address starts at __DATA segment + // for all other kinds, the x86_64 reloc base address starts at first writable segment (usually __DATA) for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; - if ( strcmp(sect->segmentName(), "__DATA") == 0 ) + if ( !sect->isSectionHidden() && _options.initialSegProtection(sect->segmentName()) & VM_PROT_WRITE ) return sect->address; } - throw "__DATA segment not found"; + throw "writable (__DATA) segment not found"; } template @@ -892,10 +941,10 @@ uint64_t ExternalRelocationsAtom::relocBaseAddress(ld::Internal& state) // for x86_64 the reloc base address starts at __DATA segment for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; - if ( strcmp(sect->segmentName(), "__DATA") == 0 ) + if ( !sect->isSectionHidden() && _options.initialSegProtection(sect->segmentName()) & VM_PROT_WRITE ) return sect->address; } - throw "__DATA segment not found"; + throw "writable (__DATA) segment not found"; } template diff --git a/src/ld/Options.cpp b/src/ld/Options.cpp index 057fad9..aa4f6ee 100644 --- a/src/ld/Options.cpp +++ b/src/ld/Options.cpp @@ -127,7 +127,7 @@ Options::Options(int argc, const char* argv[]) 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"), + fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fFinalName(NULL), fEntryName(NULL), fBaseAddress(0), fMaxAddress(0x7FFFFFFFFFFFFFFFLL), fBaseWritableAddress(0), fSplitSegs(false), fExportMode(kExportDefault), fLibrarySearchMode(kSearchDylibAndArchiveInEachDir), @@ -176,7 +176,8 @@ Options::Options(int argc, const char* argv[]) fAllowSimulatorToLinkWithMacOSX(false), fKeepDwarfUnwind(true), fKeepDwarfUnwindForcedOn(false), fKeepDwarfUnwindForcedOff(false), fVerboseOptimizationHints(false), fIgnoreOptimizationHints(false), - fGenerateDtraceDOF(true), fAllowBranchIslands(true), + fGenerateDtraceDOF(true), fAllowBranchIslands(true), fTraceSymbolLayout(false), + fMarkAppExtensionSafe(false), fCheckAppExtensionSafe(false), fForceLoadSwiftLibs(false), fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL), fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset), fSaveTempFiles(false), fSnapshotRequested(false), fPipelineFifo(NULL), @@ -539,6 +540,17 @@ bool Options::keepLocalSymbol(const char* symbolName) const throw "internal error"; } +const std::vector* Options::sectionOrder(const char* segName) const +{ + for (std::vector::const_iterator it=fSectionOrder.begin(); it != fSectionOrder.end(); ++it) { + if ( strcmp(it->segmentName, segName) == 0 ) + return &it->sectionOrder; + } + return NULL; +} + + + void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype) { for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) { @@ -1012,19 +1024,40 @@ void Options::SetWithWildcards::insert(const char* symbol) fRegular.insert(symbol); } -bool Options::SetWithWildcards::contains(const char* symbol) const +bool Options::SetWithWildcards::contains(const char* symbol, bool* matchBecauseOfWildcard) const { + if ( matchBecauseOfWildcard != NULL ) + *matchBecauseOfWildcard = false; // first look at hash table on non-wildcard symbols if ( fRegular.find(symbol) != fRegular.end() ) return true; // next walk list of wild card symbols looking for a match for(std::vector::const_iterator it = fWildCard.begin(); it != fWildCard.end(); ++it) { - if ( wildCardMatch(*it, symbol) ) + if ( wildCardMatch(*it, symbol) ) { + if ( matchBecauseOfWildcard != NULL ) + *matchBecauseOfWildcard = true; return true; + } } return false; } +// Support "foo.o:_bar" to mean symbol _bar in file foo.o +bool Options::SetWithWildcards::containsWithPrefix(const char* symbol, const char* file, bool& wildCardMatch) const +{ + wildCardMatch = false; + if ( contains(symbol, &wildCardMatch) ) + return true; + if ( file == NULL ) + return false; + const char* s = strrchr(file, '/'); + if ( s != NULL ) + file = s+1; + char buff[strlen(file)+strlen(symbol)+2]; + sprintf(buff, "%s:%s", file, symbol); + return contains(buff, &wildCardMatch); +} + bool Options::SetWithWildcards::containsNonWildcard(const char* symbol) const { // look at hash table on non-wildcard symbols @@ -1753,6 +1786,59 @@ void Options::addSectionRename(const char* srcSegment, const char* srcSection, c } +void Options::addSegmentRename(const char* srcSegment, const char* dstSegment) +{ + if ( strlen(srcSegment) > 16 ) + throw "-rename_segment segment name max 16 chars"; + if ( strlen(dstSegment) > 16 ) + throw "-rename_segment segment name max 16 chars"; + + SegmentRename info; + info.fromSegment = srcSegment; + info.toSegment = dstSegment; + + fSegmentRenames.push_back(info); +} + + + +void Options::addSymbolMove(const char* dstSegment, const char* symbolList, + std::vector& list, const char* optionName) +{ + if ( strlen(dstSegment) > 16 ) + throwf("%s segment name max 16 chars", optionName); + + SymbolsMove tmp; + list.push_back(tmp); + SymbolsMove& info = list.back(); + info.toSegment = dstSegment; + loadExportFile(symbolList, optionName, info.symbols); +} + +bool Options::moveRwSymbol(const char* symName, const char* filePath, const char*& seg, bool& wildCardMatch) const +{ + for (std::vector::const_iterator it=fSymbolsMovesData.begin(); it != fSymbolsMovesData.end(); ++it) { + const SymbolsMove& info = *it; + if ( info.symbols.containsWithPrefix(symName, filePath, wildCardMatch)) { + seg = info.toSegment; + return true; + } + } + return false; +} + +bool Options::moveRoSymbol(const char* symName, const char* filePath, const char*& seg, bool& wildCardMatch) const +{ + for (std::vector::const_iterator it=fSymbolsMovesCode.begin(); it != fSymbolsMovesCode.end(); ++it) { + const SymbolsMove& info = *it; + if ( info.symbols.containsWithPrefix(symName, filePath, wildCardMatch)) { + seg = info.toSegment; + return true; + } + } + return false; +} + void Options::addSectionAlignment(const char* segment, const char* section, const char* alignmentStr) { if ( strlen(segment) > 16 ) @@ -2904,6 +2990,7 @@ void Options::parse(int argc, const char* argv[]) fKextsUseStubs = true; } else if ( strcmp(argv[i], "-dependency_info") == 0 ) { + snapshotArgCount = 0; ++i; // previously handled by buildSearchPaths() } @@ -2962,9 +3049,96 @@ void Options::parse(int argc, const char* argv[]) addSectionRename(argv[i+1], argv[i+2], argv[i+3], argv[i+4]); i += 4; } + else if ( strcmp(arg, "-rename_segment") == 0 ) { + if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) ) + throw "-rename_segment missing "; + addSegmentRename(argv[i+1], argv[i+2]); + i += 2; + } + else if ( strcmp(arg, "-move_to_ro_segment") == 0 ) { + if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) ) + throw "-move_to_ro_segment missing "; + addSymbolMove(argv[i+1], argv[i+2], fSymbolsMovesCode, "-move_to_ro_segment"); + i += 2; + } + else if ( strcmp(arg, "-move_to_rw_segment") == 0 ) { + if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) ) + throw "-move_to_rw_segment missing "; + addSymbolMove(argv[i+1], argv[i+2], fSymbolsMovesData, "-move_to_rw_segment"); + i += 2; + } + else if ( strcmp(arg, "-trace_symbol_layout") == 0 ) { + fTraceSymbolLayout = true; + } else if ( strcmp(arg, "-no_branch_islands") == 0 ) { fAllowBranchIslands = false; } + else if ( strcmp(arg, "-segment_order") == 0 ) { + // ex: -segment_order __TEXT:__DATA:__JUNK + const char* optString = argv[++i]; + if ( optString == NULL ) + throw "-segment_order missing colon separated "; + if ( !fSegmentOrder.empty() ) + throw "-segment_order used more than once"; + // break up into list of tokens at colon + char* buffer = strdup(optString); + char* start = buffer; + for (char* s = buffer; ; ++s) { + if ( *s == ':' ) { + *s = '\0'; + fSegmentOrder.push_back(start); + start = s+1; + } + else if ( *s == '\0' ) { + fSegmentOrder.push_back(start); + break; + } + } + } + else if ( strcmp(arg, "-section_order") == 0 ) { + // ex: -section_order __DATA __data:__const:__nl_pointers + if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) ) + throw "-section_order missing "; + const char* segName = argv[++i]; + const char* optString = argv[++i]; + if ( sectionOrder(segName) != NULL ) + throwf("-section_order %s ... used more than once", segName); + SectionOrderList dummy; + fSectionOrder.push_back(dummy); + SectionOrderList& entry = fSectionOrder.back(); + entry.segmentName = segName; + // break up into list of tokens at colon + char* buffer = strdup(optString); + char* start = buffer; + for (char* s = buffer; ; ++s) { + if ( *s == ':' ) { + *s = '\0'; + entry.sectionOrder.push_back(start); + start = s+1; + } + else if ( *s == '\0' ) { + entry.sectionOrder.push_back(start); + break; + } + } + } + else if ( strcmp(arg, "-application_extension") == 0 ) { + fMarkAppExtensionSafe = true; + fCheckAppExtensionSafe = true; + } + else if ( strcmp(arg, "-no_application_extension") == 0 ) { + fMarkAppExtensionSafe = false; + fCheckAppExtensionSafe = false; + } + else if ( strcmp(arg, "-add_ast_path") == 0 ) { + const char* path = argv[++i]; + if ( path == NULL ) + throw "-add_ast_path missing ::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, : ld::dylib::File(strdup(pth), mTime, ord), _macVersionMin(macMin), _iOSVersionMin(iOSMin), _allowSimToMacOSXLinking(allowSimToMacOSX), _addVersionLoadCommand(addVers), _linkingFlat(linkingFlatNamespace), _implicitlyLinkPublicDylibs(hoistImplicitPublicDylibs), - _objcContraint(ld::File::objcConstraintNone), + _objcContraint(ld::File::objcConstraintNone), _swiftVersion(0), _importProxySection("__TEXT", "__import", ld::Section::typeImportProxies, true), _flatDummySection("__LINKEDIT", "__flat_dummy", ld::Section::typeLinkEdit, true), _parentUmbrella(NULL), _importAtom(NULL), _codeSignatureDR(NULL), _noRexports(false), _hasWeakExports(false), _deadStrippable(false), _hasPublicInstallName(false), - _providedAtom(false), _explictReExportFound(false), _wrongOS(false), _installPathOverride(false), _indirectDylibsProcessed(false) + _providedAtom(false), _explictReExportFound(false), _wrongOS(false), _installPathOverride(false), + _indirectDylibsProcessed(false), _appExtensionSafe(false), + _macMinVersionInDylib(ld::macVersionUnset), _iOSMinVersionInDylib(ld::iOSVersionUnset) { const macho_header

* header = (const macho_header

*)fileContent; const uint32_t cmd_count = header->ncmds(); @@ -287,6 +297,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, || (header->filetype() == MH_EXECUTE); // bundles and exectuables can be used via -bundle_loader _hasWeakExports = (header->flags() & MH_WEAK_DEFINES); _deadStrippable = (header->flags() & MH_DEAD_STRIPPABLE_DYLIB); + _appExtensionSafe = (header->flags() & MH_APP_EXTENSION_SAFE); // pass 1: get pointers, and see if this dylib uses compressed LINKEDIT format const macho_dysymtab_command

* dynamicInfo = NULL; @@ -344,6 +355,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, if ( _addVersionLoadCommand && !indirectDylib ) throw "building for iOS Simulator, but linking against dylib built for MacOSX"; } + _macMinVersionInDylib = (ld::MacVersionMin)((macho_version_min_command

*)cmd)->version(); break; case LC_VERSION_MIN_IPHONEOS: if ( _macVersionMin != ld::macVersionUnset ) { @@ -351,6 +363,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, if ( _addVersionLoadCommand && !indirectDylib ) throw "building for MacOSX, but linking against dylib built for iOS Simulator"; } + _iOSMinVersionInDylib = (ld::IOSVersionMin)((macho_version_min_command

*)cmd)->version(); break; case LC_CODE_SIGNATURE: codeSignature = (macho_linkedit_data_command

* )cmd; @@ -382,6 +395,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, _objcContraint = ld::File::objcConstraintRetainReleaseForSimulator; else _objcContraint = ld::File::objcConstraintRetainRelease; + _swiftVersion = ((flags >> 8) & 0xFF); } else if ( sect->size() > 0 ) { warning("can't parse %s/%s section in %s", objCInfoSegmentName(), objCInfoSectionName(), this->path()); @@ -1074,17 +1088,6 @@ bool isDylibFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* *subResult = CPU_SUBTYPE_ARM64_ALL; return true; } - if ( Parser::validFile(fileContent, false) ) { - *result = CPU_TYPE_POWERPC; - const macho_header >* header = (const macho_header >*)fileContent; - *subResult = header->cpusubtype(); - return true; - } - if ( Parser::validFile(fileContent, false) ) { - *result = CPU_TYPE_POWERPC64; - *subResult = CPU_SUBTYPE_POWERPC_ALL; - return true; - } return false; } diff --git a/src/ld/parsers/macho_relocatable_file.cpp b/src/ld/parsers/macho_relocatable_file.cpp index ad5720e..d3990e3 100644 --- a/src/ld/parsers/macho_relocatable_file.cpp +++ b/src/ld/parsers/macho_relocatable_file.cpp @@ -79,6 +79,7 @@ public: _dwarfDebugInfoSect(NULL), _dwarfDebugAbbrevSect(NULL), _dwarfDebugLineSect(NULL), _dwarfDebugStringSect(NULL), _objConstraint(ld::File::objcConstraintNone), + _swiftVersion(0), _cpuSubType(0), _canScatterAtoms(false) {} virtual ~File(); @@ -96,6 +97,7 @@ public: virtual bool canScatterAtoms() const { return _canScatterAtoms; } virtual const char* translationUnitSource() const; virtual LinkerOptionsList* linkerOptions() const { return &_linkerOptions; } + virtual uint8_t swiftVersion() const { return _swiftVersion; } const uint8_t* fileContent() { return _fileContent; } private: @@ -109,8 +111,10 @@ private: const uint8_t* _fileContent; Section** _sectionsArray; uint8_t* _atomsArray; + uint8_t* _aliasAtomsArray; uint32_t _sectionsArrayCount; uint32_t _atomsArrayCount; + uint32_t _aliasAtomsArrayCount; std::vector _fixups; std::vector _unwindInfos; std::vector _lineInfos; @@ -122,6 +126,7 @@ private: const macho_section

* _dwarfDebugLineSect; const macho_section

* _dwarfDebugStringSect; ld::File::ObjcConstraint _objConstraint; + uint8_t _swiftVersion; uint32_t _cpuSubType; bool _canScatterAtoms; std::vector > _linkerOptions; @@ -182,6 +187,7 @@ protected: class Atom* _beginAtoms; class Atom* _endAtoms; bool _hasAliases; + std::set*> _altEntries; }; @@ -191,7 +197,7 @@ class CFISection : public Section public: CFISection(Parser& parser, File& f, const macho_section* s) : Section(f, s) { } - uint32_t cfiCount(); + uint32_t cfiCount(Parser& parser); virtual ld::Atom::ContentType contentType() { return ld::Atom::typeCFI; } virtual uint32_t computeAtomCount(class Parser& parser, struct Parser::LabelAndCFIBreakIterator& it, const struct Parser::CFI_CU_InfoArrays&); @@ -887,6 +893,39 @@ void Atom::verifyAlignment(const macho_section

&) const } +class AliasAtom : public ld::Atom +{ +public: + AliasAtom(const char* name, bool hidden, const ld::File* file, const char* aliasOfName) : + ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + (hidden ? ld::Atom::scopeLinkageUnit : ld::Atom::scopeGlobal), + ld::Atom::typeUnclassified, ld::Atom::symbolTableIn, + false, false, true, 0), + _file(file), + _name(name), + _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindNoneFollowOn, ld::Fixup::bindingByNameUnbound, aliasOfName) { } + + virtual const ld::File* file() const { return _file; } + virtual const char* translationUnitSource() const + { return NULL; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 0; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual ld::Fixup::iterator fixupsBegin() const { return &((ld::Fixup*)&_fixup)[0]; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + static ld::Section _s_section; + + const ld::File* _file; + const char* _name; + ld::Fixup _fixup; +}; + +ld::Section AliasAtom::_s_section("__LD", "__aliases", ld::Section::typeTempAlias, true); + + template class Parser { @@ -984,6 +1023,7 @@ public: static bool isThumbFromSymbol(const macho_nlist

& sym); static bool weakImportFromSymbol(const macho_nlist

& sym); static bool resolverFromSymbol(const macho_nlist

& sym); + static bool altEntryFromSymbol(const macho_nlist

& sym); uint32_t symbolIndexFromIndirectSectionAddress(pint_t,const macho_section

*); const macho_section

* firstMachOSection() { return _sectionsStart; } const macho_section

* machOSectionFromSectionIndex(uint32_t index); @@ -1014,7 +1054,7 @@ public: bool forceDwarfConversion() { return _forceDwarfConversion; } bool verboseOptimizationHints() { return _verboseOptimizationHints; } bool neverConvertDwarf() { return _neverConvertDwarf; } - + macho_data_in_code_entry

* dataInCodeStart() { return _dataInCodeStart; } macho_data_in_code_entry

* dataInCodeEnd() { return _dataInCodeEnd; } const uint8_t* optimizationHintsStart() { return _lohStart; } @@ -1111,10 +1151,13 @@ private: void parseDebugInfo(); void parseStabs(); + void appendAliasAtoms(uint8_t* atomBuffer); static bool isConstFunStabs(const char *stabStr); bool read_comp_unit(const char ** name, const char ** comp_dir, uint64_t *stmt_list); - const char* getDwarfString(uint64_t form, const uint8_t* p); + pint_t realAddr(pint_t addr); + const char* getDwarfString(uint64_t form, const uint8_t*& p); + uint64_t getDwarfOffset(uint64_t form, const uint8_t*& di, bool dwarf64); bool skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t form, uint8_t addr_size, bool dwarf64); @@ -1130,6 +1173,7 @@ private: File* _file; const macho_nlist

* _symbols; uint32_t _symbolCount; + uint32_t _indirectSymbolCount; const char* _strings; uint32_t _stringsSize; const uint32_t* _indirectTable; @@ -1174,7 +1218,7 @@ Parser::Parser(const uint8_t* fileContent, uint64_t fileLength, const char* p bool neverConvertDwarf, bool verboseOptimizationHints) : _fileContent(fileContent), _fileLength(fileLength), _path(path), _modTime(modTime), _ordinal(ordinal), _file(NULL), - _symbols(NULL), _symbolCount(0), _strings(NULL), _stringsSize(0), + _symbols(NULL), _symbolCount(0), _indirectSymbolCount(0), _strings(NULL), _stringsSize(0), _indirectTable(NULL), _indirectTableCount(0), _undefinedStartIndex(0), _undefinedEndIndex(0), _sectionsStart(NULL), _machOSectionsCount(0), _hasUUID(false), @@ -1555,6 +1599,18 @@ bool Parser::LabelAndCFIBreakIterator::next(Parser& parser, const Section< return false; } +template <> +typename arm::P::uint_t Parser::realAddr(typename arm::P::uint_t addr) +{ + return addr & (-2); +} + +template +typename A::P::uint_t Parser::realAddr(typename A::P::uint_t addr) +{ + return addr; +} + #define STACK_ALLOC_IF_SMALL(_type, _name, _actual_count, _maxCount) \ _type* _name = NULL; \ uint32_t _name##_count = 1; \ @@ -1619,7 +1675,7 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) // stack allocate (if not too large) array of CFI_Atom_Info uint32_t countOfCFIs = 0; if ( _EHFrameSection != NULL ) - countOfCFIs = _EHFrameSection->cfiCount(); + countOfCFIs = _EHFrameSection->cfiCount(*this); STACK_ALLOC_IF_SMALL(typename CFISection::CFI_Atom_Info, cfiArray, countOfCFIs, 1024); // stack allocate (if not too large) a copy of __eh_frame to apply relocations to @@ -1655,7 +1711,7 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) if ( cfiArray[i].isCIE ) continue; if ( cfiArray[i].u.fdeInfo.function.targetAddress != CFI_INVALID_ADDRESS ) - cfiStartsArray[cfiStartsArrayCount++] = cfiArray[i].u.fdeInfo.function.targetAddress; + cfiStartsArray[cfiStartsArrayCount++] = realAddr(cfiArray[i].u.fdeInfo.function.targetAddress); if ( cfiArray[i].u.fdeInfo.lsda.targetAddress != CFI_INVALID_ADDRESS ) cfiStartsArray[cfiStartsArrayCount++] = cfiArray[i].u.fdeInfo.lsda.targetAddress; ++countOfFDEs; @@ -1792,6 +1848,16 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) } } + // process indirect symbols which become AliasAtoms + _file->_aliasAtomsArray = NULL; + _file->_aliasAtomsArrayCount = 0; + if ( _indirectSymbolCount != 0 ) { + _file->_aliasAtomsArrayCount = _indirectSymbolCount; + _file->_aliasAtomsArray = new uint8_t[_file->_aliasAtomsArrayCount*sizeof(AliasAtom)]; + this->appendAliasAtoms(_file->_aliasAtomsArray); + } + + // parse dwarf debug info to get line info this->parseDebugInfo(); @@ -1799,7 +1865,6 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) } - template <> uint8_t Parser::loadCommandSizeMask() { return 0x03; } template <> uint8_t Parser::loadCommandSizeMask() { return 0x07; } template <> uint8_t Parser::loadCommandSizeMask() { return 0x03; } @@ -1950,7 +2015,11 @@ void Parser::prescanSymbolTable() } continue; } - + else if ( ((sym.n_type() & N_TYPE) == N_INDR) && ((sym.n_type() & N_EXT) != 0) ) { + _indirectSymbolCount++; + continue; + } + // count absolute symbols if ( (sym.n_type() & N_TYPE) == N_ABS ) { const char* absName = this->nameFromSymbol(sym); @@ -1987,6 +2056,34 @@ void Parser::prescanSymbolTable() } } +template +void Parser::appendAliasAtoms(uint8_t* p) +{ + for (uint32_t i=0; i < this->_symbolCount; ++i) { + const macho_nlist

& sym = symbolFromIndex(i); + // ignore stabs + if ( (sym.n_type() & N_STAB) != 0 ) + continue; + + // only look at N_INDR symbols + if ( (sym.n_type() & N_TYPE) != N_INDR ) + continue; + + // skip non-external aliases + if ( (sym.n_type() & N_EXT) == 0 ) + continue; + + const char* symbolName = this->nameFromSymbol(sym); + const char* aliasOfName = &_strings[sym.n_value()]; + bool isHiddenVisibility = (sym.n_type() & N_PEXT); + AliasAtom* allocatedSpace = (AliasAtom*)p; + new (allocatedSpace) AliasAtom(symbolName, isHiddenVisibility, _file, aliasOfName); + p += sizeof(AliasAtom); + } +} + + + template int Parser::sectionIndexSorter(void* extra, const void* l, const void* r) { @@ -2206,6 +2303,7 @@ void Parser::makeSections() _file->_objConstraint = ld::File::objcConstraintRetainReleaseForSimulator; else _file->_objConstraint = ld::File::objcConstraintRetainRelease; + _file->_swiftVersion = ((flags >> 8) & 0xFF); if ( sect->size() > 8 ) { warning("section %s/%s has unexpectedly large size %llu in %s", sect->segname(), Section::makeSectionName(sect), sect->size(), _file->path()); @@ -3011,6 +3109,12 @@ bool Parser::resolverFromSymbol(const macho_nlist

& sym) return ( sym.n_desc() & N_SYMBOL_RESOLVER ); } +template +bool Parser::altEntryFromSymbol(const macho_nlist

& sym) +{ + return ( sym.n_desc() & N_ALT_ENTRY ); +} + /* Skip over a LEB128 value (signed or unsigned). */ static void @@ -3144,21 +3248,51 @@ bool Parser::skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t template -const char* Parser::getDwarfString(uint64_t form, const uint8_t* p) +const char* Parser::getDwarfString(uint64_t form, const uint8_t*& di) { - if ( form == DW_FORM_string ) - return (const char*)p; - else if ( form == DW_FORM_strp ) { - uint32_t offset = E::get32(*((uint32_t*)p)); - const char* dwarfStrings = (char*)_file->fileContent() + _file->_dwarfDebugStringSect->offset(); - if ( offset > _file->_dwarfDebugStringSect->size() ) { - warning("unknown dwarf DW_FORM_strp (offset=0x%08X) is too big in %s\n", offset, this->_path); - return NULL; - } - return &dwarfStrings[offset]; + uint32_t offset; + const char* dwarfStrings; + const char* result = NULL; + switch (form) { + case DW_FORM_string: + result = (const char*)di; + di += strlen(result) + 1; + break; + case DW_FORM_strp: + offset = E::get32(*((uint32_t*)di)); + dwarfStrings = (char*)_file->fileContent() + _file->_dwarfDebugStringSect->offset(); + if ( offset < _file->_dwarfDebugStringSect->size() ) + result = &dwarfStrings[offset]; + else + warning("dwarf DW_FORM_strp (offset=0x%08X) is too big in %s", offset, this->_path); + di += 4; + break; + default: + warning("unknown dwarf string encoding (form=%lld) in %s", form, this->_path); + break; } - warning("unknown dwarf string encoding (form=%lld) in %s\n", form, this->_path); - return NULL; + return result; +} + +template +uint64_t Parser::getDwarfOffset(uint64_t form, const uint8_t*& di, bool dwarf64) +{ + if ( form == DW_FORM_sec_offset ) + form = (dwarf64 ? DW_FORM_data8 : DW_FORM_data4); + uint64_t result = -1; + switch (form) { + case DW_FORM_data4: + result = A::P::E::get32(*(uint32_t*)di); + di += 4; + break; + case DW_FORM_data8: + result = A::P::E::get64(*(uint64_t*)di); + di += 8; + break; + default: + warning("unknown dwarf DW_FORM_ for DW_AT_stmt_list in %s", this->_path); + } + return result; } @@ -3400,6 +3534,7 @@ void Parser::parseStabs() case N_LSYM: case N_RSYM: case N_PSYM: + case N_AST: // not associated with an atom, just copy stab.string = symString; break; @@ -3676,20 +3811,23 @@ bool Parser::read_comp_unit(const char ** name, const char ** comp_dir, return false; else if (attr == 0) return true; - if (form == DW_FORM_indirect) form = read_uleb128 (&di, end); - if (attr == DW_AT_name) - *name = getDwarfString(form, di); - else if (attr == DW_AT_comp_dir) - *comp_dir = getDwarfString(form, di); - else if (attr == DW_AT_stmt_list && form == DW_FORM_data4) - *stmt_list = A::P::E::get32(*(uint32_t*)di); - else if (attr == DW_AT_stmt_list && form == DW_FORM_data8) - *stmt_list = A::P::E::get64(*(uint64_t*)di); - if (! skip_form (&di, end, form, address_size, dwarf64)) - return false; + switch (attr) { + case DW_AT_name: + *name = getDwarfString(form, di); + break; + case DW_AT_comp_dir: + *comp_dir = getDwarfString(form, di); + break; + case DW_AT_stmt_list: + *stmt_list = getDwarfOffset(form, di, dwarf64); + break; + default: + if (! skip_form (&di, end, form, address_size, dwarf64)) + return false; + } } } @@ -3708,7 +3846,7 @@ const char* File::translationUnitSource() const return _dwarfTranslationUnitPath; } - + template bool File::forEachAtom(ld::File::AtomHandler& handler) const @@ -3719,7 +3857,13 @@ bool File::forEachAtom(ld::File::AtomHandler& handler) const handler.doAtom(*((Atom*)p)); p += sizeof(Atom); } - return (_atomsArrayCount != 0); + p = _aliasAtomsArray; + for(int i=_aliasAtomsArrayCount; i > 0; --i) { + handler.doAtom(*((AliasAtom*)p)); + p += sizeof(AliasAtom); + } + + return (_atomsArrayCount != 0) || (_aliasAtomsArrayCount != 0); } template @@ -3922,10 +4066,14 @@ uint32_t Section::sectionNum(class Parser& parser) const } // arm does not have zero cost exceptions -template <> uint32_t CFISection::cfiCount() { return 0; } +template <> +uint32_t CFISection::cfiCount(Parser& parser) +{ + return 0; +} template -uint32_t CFISection::cfiCount() +uint32_t CFISection::cfiCount(Parser& parser) { // create ObjectAddressSpace object for use by libunwind OAS oas(*this, (uint8_t*)this->file().fileContent()+this->_machOSection->offset()); @@ -4059,6 +4207,9 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, assert(count == 0); } + + + template <> void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], @@ -4209,8 +4360,6 @@ void CFISection::addCiePersonalityFixups(class Parser& parser, const C } } - - #if SUPPORT_ARCH_arm64 template <> void CFISection::addCiePersonalityFixups(class Parser& parser, const CFI_Atom_Info* cieInfo) @@ -4236,6 +4385,7 @@ void CFISection::addCiePersonalityFixups(class Parser& parser, con } #endif + template void CFISection::addCiePersonalityFixups(class Parser& parser, const CFI_Atom_Info* cieInfo) { @@ -4481,8 +4631,7 @@ const char* CUSection::personalityName(class Parser& parser, con else { const pint_t* content = (pint_t*)(this->file().fileContent() + this->_machOSection->offset() + reloc->r_address()); pint_t personalityAddr = *content; - Section* personalitySection = parser.sectionForAddress(personalityAddr); - assert((personalitySection->type() == ld::Section::typeCode) && "personality column in __compact_unwind section is not pointer to function"); + assert((parser.sectionForAddress(personalityAddr)->type() == ld::Section::typeCode) && "personality column in __compact_unwind section is not pointer to function"); // atoms may not be constructed yet, so scan symbol table for labels const char* name = parser.scanSymbolTableForAddress(personalityAddr); return name; @@ -4537,6 +4686,7 @@ const char* CUSection::personalityName(class Parser& parser, const } #endif + template const char* CUSection::personalityName(class Parser& parser, const macho_relocation_info

* reloc) { @@ -4780,6 +4930,8 @@ uint32_t SymboledSection::appendAtoms(class Parser& parser, uint8_t* p, new (allocatedSpace) Atom(*this, parser, *label, size, isAlias); if ( isAlias ) this->_hasAliases = true; + if ( parser.altEntryFromSymbol(*label) ) + this->_altEntries.insert(allocatedSpace); } else { ld::Atom::SymbolTableInclusion inclusion = ld::Atom::symbolTableNotIn; @@ -6085,7 +6237,12 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati if ((instruction & 0xFE000000) == 0xFA000000) displacement += ((instruction & 0x01000000) >> 23); if ( reloc->r_extern() ) { - target.addend = srcAddr + displacement; + dstAddr = srcAddr + displacement; + // support large .o files + if ( srcAddr > 0x2000000 ) { + dstAddr -= ((srcAddr + 0x1FFFFFF) & 0xFC000000); + } + target.addend = dstAddr; if ( externSymbolIsThumbDef ) target.addend &= -2; // remove thumb bit } @@ -6132,7 +6289,11 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati dstAddr &= 0xFFFFFFFC; if ( reloc->r_extern() ) { - target.addend = dstAddr; + // support large .o files + if ( srcAddr > 0x1000000 ) { + dstAddr -= ((srcAddr + 0xFFFFFF) & 0xFE000000); + } + target.addend = (int64_t)(int32_t)dstAddr; } else { parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); @@ -6994,6 +7155,21 @@ void Section::makeFixups(class Parser& parser, const struct Parser::CFI } } } + if ( !this->_altEntries.empty() && !this->addFollowOnFixups() ) { + if ( _altEntries.count(_beginAtoms) != 0 ) + warning("N_ALT_ENTRY bit set on first atom in section %s/%s", sect->segname(), Section::makeSectionName(sect)); + + Atom* end = &_endAtoms[-1]; + for(Atom* p = _beginAtoms; p < end; ++p) { + Atom* nextAtom = &p[1]; + if ( _altEntries.count(nextAtom) != 0 ) { + typename Parser::SourceLocation src(p, 0); + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindNoneFollowOn, nextAtom); + typename Parser::SourceLocation src2(nextAtom, 0); + parser.addFixup(src2, ld::Fixup::k1of1, ld::Fixup::kindNoneGroupSubordinate, p); + } + } + } // track data-in-code if ( parser.hasDataInCodeLabels() && (this->type() == ld::Section::typeCode) ) { diff --git a/src/ld/passes/branch_island.cpp b/src/ld/passes/branch_island.cpp index 5f5612c..8efd10a 100644 --- a/src/ld/passes/branch_island.cpp +++ b/src/ld/passes/branch_island.cpp @@ -71,8 +71,8 @@ public: _name(nm), _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARMBranch24, target), _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindIslandTarget, finalTarget.atom) { - if (_s_log) fprintf(stderr, "%s: ARM jump instruction branch island to final target %s\n", - target->name(), finalTarget.atom->name()); + if (_s_log) fprintf(stderr, "%p: ARM-to-ARM branch island to final target %s\n", + this, finalTarget.atom->name()); } virtual const ld::File* file() const { return NULL; } @@ -101,8 +101,10 @@ public: ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), _name(nm), - _target(target), - _finalTarget(finalTarget) { } + _finalTarget(finalTarget) { + if (_s_log) fprintf(stderr, "%p: ARM-to-thumb1 branch island to final target %s\n", + this, finalTarget.atom->name()); + } virtual const ld::File* file() const { return NULL; } virtual const char* name() const { return _name; } @@ -115,8 +117,6 @@ public: int64_t displacement = _finalTarget.atom->finalAddress() + _finalTarget.offset - (this->finalAddress() + 12); if ( _finalTarget.atom->isThumb() ) displacement |= 1; - if (_s_log) fprintf(stderr, "%s: 4 ARM instruction jump to final target at 0x%08llX\n", - _target->name(), _finalTarget.atom->finalAddress()); OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc004); // ldr ip, pc + 4 OSWriteLittleInt32(&buffer[ 4], 0, 0xe08fc00c); // add ip, pc, ip OSWriteLittleInt32(&buffer[ 8], 0, 0xe12fff1c); // bx ip @@ -126,7 +126,6 @@ public: private: const char* _name; - const ld::Atom* _target; TargetAndOffset _finalTarget; }; @@ -141,8 +140,8 @@ public: _name(nm), _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressThumbBranch22, target), _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindIslandTarget, finalTarget.atom) { - if (_s_log) fprintf(stderr, "%s: Thumb jump instruction branch island to final target %s\n", - target->name(), finalTarget.atom->name()); + if (_s_log) fprintf(stderr, "%p: Thumb-to-thumb branch island to final target %s\n", + this, finalTarget.atom->name()); } virtual const ld::File* file() const { return NULL; } @@ -175,7 +174,10 @@ public: _fixup2(0, ld::Fixup::k2of2, ld::Fixup::kindStoreThumbLow16), _fixup3(4, ld::Fixup::k1of2, ld::Fixup::kindSetTargetAddress, finalTarget.atom), _fixup4(4, ld::Fixup::k2of2, ld::Fixup::kindStoreThumbHigh16), - _fixup5(0, ld::Fixup::k1of1, ld::Fixup::kindIslandTarget, finalTarget.atom) { } + _fixup5(0, ld::Fixup::k1of1, ld::Fixup::kindIslandTarget, finalTarget.atom) { + if (_s_log) fprintf(stderr, "%p: Thumb-to-thumb absolute branch island to final target %s\n", + this, finalTarget.atom->name()); + } virtual const ld::File* file() const { return NULL; } virtual const char* name() const { return _name; } @@ -208,8 +210,10 @@ public: ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), _name(nm), - _target(target), - _finalTarget(finalTarget) { } + _finalTarget(finalTarget) { + if (_s_log) fprintf(stderr, "%p: NoPIC ARM-to-Thumb branch island to final target %s\n", + this, finalTarget.atom->name()); + } virtual const ld::File* file() const { return NULL; } virtual const char* name() const { return _name; } @@ -222,8 +226,6 @@ public: uint32_t targetAddr = _finalTarget.atom->finalAddress(); if ( _finalTarget.atom->isThumb() ) targetAddr |= 1; - if (_s_log) fprintf(stderr, "%s: 2 ARM instruction jump to final target at 0x%08llX\n", - _target->name(), _finalTarget.atom->finalAddress()); OSWriteLittleInt32(&buffer[0], 0, 0xe51ff004); // ldr pc, [pc, #-4] OSWriteLittleInt32(&buffer[4], 0, targetAddr); // .long target-this } @@ -231,7 +233,6 @@ public: private: const char* _name; - const ld::Atom* _target; TargetAndOffset _finalTarget; }; diff --git a/src/ld/passes/compact_unwind.cpp b/src/ld/passes/compact_unwind.cpp index ad8a504..3ddd9b8 100644 --- a/src/ld/passes/compact_unwind.cpp +++ b/src/ld/passes/compact_unwind.cpp @@ -108,6 +108,7 @@ private: void addImageOffsetFixupPlusAddend(uint32_t offset, const ld::Atom* targ, uint32_t addend); uint8_t* _pagesForDelete; + uint8_t* _pageAlignedPages; uint8_t* _pages; uint64_t _pagesSize; uint8_t* _header; @@ -129,8 +130,8 @@ template UnwindInfoAtom::UnwindInfoAtom(const std::vector& entries, uint64_t ehFrameSize) : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, - symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)), - _pagesForDelete(NULL), _pages(NULL), _pagesSize(0), _header(NULL), _headerSize(0) + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _pagesForDelete(NULL), _pageAlignedPages(NULL), _pages(NULL), _pagesSize(0), _header(NULL), _headerSize(0) { // build new compressed list by removing entries where next function has same encoding std::vector uniqueEntries; @@ -160,11 +161,12 @@ UnwindInfoAtom::UnwindInfoAtom(const std::vector& entries, uint6 const unsigned int entriesPerRegularPage = (4096-sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry); assert(uniqueEntries.size() > 0); const unsigned int pageCount = ((uniqueEntries.size() - 1)/entriesPerRegularPage) + 2; - _pagesForDelete = (uint8_t*)calloc(pageCount,4096); + _pagesForDelete = (uint8_t*)calloc(pageCount+1,4096); if ( _pagesForDelete == NULL ) { warning("could not allocate space for compact unwind info"); return; } + _pageAlignedPages = (uint8_t*)((((uintptr_t)_pagesForDelete) + 4095) & -4096); // make last second level page smaller so that all other second level pages can be page aligned uint32_t maxLastPageSize = 4096 - (ehFrameSize % 4096); @@ -179,7 +181,7 @@ UnwindInfoAtom::UnwindInfoAtom(const std::vector& entries, uint6 uint8_t* secondLevelPagesStarts[pageCount*3]; unsigned int endIndex = uniqueEntries.size(); unsigned int secondLevelPageCount = 0; - uint8_t* pageEnd = &_pagesForDelete[pageCount*4096]; + uint8_t* pageEnd = &_pageAlignedPages[pageCount*4096]; uint32_t pageSize = maxLastPageSize; while ( endIndex > 0 ) { endIndex = makeCompressedSecondLevelPage(uniqueEntries, commonEncodings, pageSize, endIndex, pageEnd); @@ -193,9 +195,8 @@ UnwindInfoAtom::UnwindInfoAtom(const std::vector& entries, uint6 } } _pages = pageEnd; - _pagesSize = &_pagesForDelete[pageCount*4096] - pageEnd; - - + _pagesSize = &_pageAlignedPages[pageCount*4096] - pageEnd; + // calculate section layout const uint32_t commonEncodingsArraySectionOffset = sizeof(macho_unwind_info_section_header

); const uint32_t commonEncodingsArrayCount = commonEncodings.size(); @@ -212,7 +213,7 @@ UnwindInfoAtom::UnwindInfoAtom(const std::vector& entries, uint6 const uint32_t headerEndSectionOffset = lsdaIndexArraySectionOffset + lsdaIndexArraySize; // now that we know the size of the header, slide all existing fixups on the pages - const int32_t fixupSlide = headerEndSectionOffset + (_pagesForDelete - _pages); + const int32_t fixupSlide = headerEndSectionOffset + (_pageAlignedPages - _pages); for(std::vector::iterator it = _fixups.begin(); it != _fixups.end(); ++it) { it->offsetInAtom += fixupSlide; } @@ -547,11 +548,11 @@ unsigned int UnwindInfoAtom::makeRegularSecondLevelPage(const std::vectoraddRegularAddressFixup(offset, info.func); if ( encodingMeansUseDwarf(info.encoding) ) { // add fixup for dwarf offset part of page specific encoding - uint32_t encOffset = (uint8_t*)(&entryTable[i]) - _pagesForDelete; + uint32_t encOffset = (uint8_t*)(&entryTable[i]) - _pageAlignedPages; this->addRegularFDEOffsetFixup(encOffset, info.fde); } } @@ -678,11 +679,11 @@ unsigned int UnwindInfoAtom::makeCompressedSecondLevelPage(const std::vector< uint32_t entryIndex = i - endIndex + entryCount; E::set32(entiresArray[entryIndex], encodingIndex << 24); // add fixup for address part of entry - uint32_t offset = (uint8_t*)(&entiresArray[entryIndex]) - _pagesForDelete; + uint32_t offset = (uint8_t*)(&entiresArray[entryIndex]) - _pageAlignedPages; this->addCompressedAddressOffsetFixup(offset, info.func, firstFunc); if ( encodingMeansUseDwarf(info.encoding) ) { // add fixup for dwarf offset part of page specific encoding - uint32_t encOffset = (uint8_t*)(&encodingsArray[encodingIndex-commonEncodings.size()]) - _pagesForDelete; + uint32_t encOffset = (uint8_t*)(&encodingsArray[encodingIndex-commonEncodings.size()]) - _pageAlignedPages; this->addCompressedEncodingFixup(encOffset, info.fde); } } diff --git a/src/ld/passes/objc.cpp b/src/ld/passes/objc.cpp index d921a64..2bbf54a 100644 --- a/src/ld/passes/objc.cpp +++ b/src/ld/passes/objc.cpp @@ -65,7 +65,7 @@ template class ObjCImageInfoAtom : public ld::Atom { public: ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, - bool compaction, bool abi2); + bool compaction, bool abi2, uint8_t swiftVersion); virtual const ld::File* file() const { return NULL; } virtual const char* name() const { return "objc image info"; } @@ -89,7 +89,7 @@ template ld::Section ObjCImageInfoAtom::_s_sectionABI2("__DATA", template ObjCImageInfoAtom::ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, bool compaction, - bool abi2) + bool abi2, uint8_t swiftVersion) : ld::Atom(abi2 ? _s_sectionABI2 : _s_sectionABI1, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)) @@ -117,6 +117,9 @@ ObjCImageInfoAtom::ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, break; } + // provide swift language version in final binary for runtime to inspect + value |= (swiftVersion << 8); + _content.version = 0; A::P::E::set32(_content.flags, value); } @@ -362,7 +365,7 @@ void ObjCData::setPointerInContent(ld::Internal& state, const ld::Atom* conte template class Category : public ObjCData { public: - static const ld::Atom* getClass(ld::Internal& state, const ld::Atom* contentAtom); + static const ld::Atom* getClass(ld::Internal& state, const ld::Atom* contentAtom, bool& hasAddend); static const ld::Atom* getInstanceMethods(ld::Internal& state, const ld::Atom* contentAtom); static const ld::Atom* getClassMethods(ld::Internal& state, const ld::Atom* contentAtom); static const ld::Atom* getProtocols(ld::Internal& state, const ld::Atom* contentAtom); @@ -374,9 +377,9 @@ private: template -const ld::Atom* Category::getClass(ld::Internal& state, const ld::Atom* contentAtom) +const ld::Atom* Category::getClass(ld::Internal& state, const ld::Atom* contentAtom, bool& hasAddend) { - return ObjCData::getPointerInContent(state, contentAtom, sizeof(pint_t)); // category_t.cls + return ObjCData::getPointerInContent(state, contentAtom, sizeof(pint_t), &hasAddend); // category_t.cls } template @@ -838,10 +841,17 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) // ignore categories also in __objc_nlcatlist if ( nlcatListAtoms.count(categoryAtom) != 0 ) continue; - const ld::Atom* categoryOnClassAtom = Category::getClass(state, categoryAtom); + const ld::Atom* categoryOnClassAtom = Category::getClass(state, categoryAtom, hasAddend); assert(categoryOnClassAtom != NULL); + // only look at classes defined in this image if ( categoryOnClassAtom->definition() != ld::Atom::definitionProxy ) { - // only look at classes defined in this image + // for now, back off optimization on new style classes + if ( hasAddend != 0 ) + continue; + // don't apply categories to swift classes + if ( categoryOnClassAtom->hasFixupsOfKind(ld::Fixup::kindNoneGroupSubordinate) ) + continue; + CatMap::iterator pos = classToCategories.find(categoryOnClassAtom); if ( pos == classToCategories.end() ) { classToCategories[categoryOnClassAtom] = new std::vector(); @@ -1171,25 +1181,25 @@ void doPass(const Options& opts, ld::Internal& state) #if SUPPORT_ARCH_x86_64 case CPU_TYPE_X86_64: state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, - true)); + true, state.swiftVersion)); break; #endif #if SUPPORT_ARCH_i386 case CPU_TYPE_I386: state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, - opts.objCABIVersion2POverride() ? true : false)); + opts.objCABIVersion2POverride() ? true : false, state.swiftVersion)); break; #endif #if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, - true)); + true, state.swiftVersion)); break; #endif #if SUPPORT_ARCH_arm64 case CPU_TYPE_ARM64: state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, - true)); + true, state.swiftVersion)); break; #endif default: diff --git a/src/ld/passes/order.cpp b/src/ld/passes/order.cpp index 684cb79..139e761 100644 --- a/src/ld/passes/order.cpp +++ b/src/ld/passes/order.cpp @@ -78,10 +78,11 @@ private: class Comparer { public: - Comparer(const Layout& l) : _layout(l) {} + Comparer(const Layout& l, ld::Internal& s) : _layout(l), _state(s) {} bool operator()(const ld::Atom* left, const ld::Atom* right); private: const Layout& _layout; + ld::Internal& _state; }; typedef std::unordered_map NameToAtom; @@ -114,7 +115,7 @@ private: bool Layout::_s_log = false; Layout::Layout(const Options& opts, ld::Internal& state) - : _options(opts), _state(state), _comparer(*this), _haveOrderFile(opts.orderedSymbolsCount() != 0) + : _options(opts), _state(state), _comparer(*this, state), _haveOrderFile(opts.orderedSymbolsCount() != 0) { } @@ -167,11 +168,21 @@ bool Layout::Comparer::operator()(const ld::Atom* left, const ld::Atom* right) bool leftIsAlias = left->isAlias(); if ( leftIsAlias ) { for (ld::Fixup::iterator fit=left->fixupsBegin(); fit != left->fixupsEnd(); ++fit) { + const ld::Atom* target = NULL; if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { - assert(fit->binding == ld::Fixup::bindingDirectlyBound); - if ( fit->u.target == right ) + switch ( fit->binding ) { + case ld::Fixup::bindingsIndirectlyBound: + target = _state.indirectBindingTable[fit->u.bindingIndex]; + break; + case ld::Fixup::bindingDirectlyBound: + target = fit->u.target; + break; + default: + break; + } + if ( target == right ) return true; // left already before right - left = fit->u.target; // sort as if alias was its target + left = target; // sort as if alias was its target break; } } @@ -179,11 +190,21 @@ bool Layout::Comparer::operator()(const ld::Atom* left, const ld::Atom* right) bool rightIsAlias = right->isAlias(); if ( rightIsAlias ) { for (ld::Fixup::iterator fit=right->fixupsBegin(); fit != right->fixupsEnd(); ++fit) { + const ld::Atom* target = NULL; if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { - assert(fit->binding == ld::Fixup::bindingDirectlyBound); - if ( fit->u.target == left ) + switch ( fit->binding ) { + case ld::Fixup::bindingsIndirectlyBound: + target = _state.indirectBindingTable[fit->u.bindingIndex]; + break; + case ld::Fixup::bindingDirectlyBound: + target = fit->u.target; + break; + default: + break; + } + if ( target == left ) return false; // need to swap, alias is after target - right = fit->u.target; // continue with sort as if right was target + right = target; // continue with sort as if right was target break; } } @@ -565,6 +586,18 @@ void Layout::buildOrdinalOverrideMap() void Layout::doPass() { + const bool log = false; + if ( log ) { + fprintf(stderr, "Unordered atoms:\n"); + for (std::vector::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + for (std::vector::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + fprintf(stderr, "\t%p\t%s\t%s\n", atom, sect->sectionName(), atom->name()); + } + } + } + // handle .o files that cannot have their atoms rearranged this->buildFollowOnTables(); @@ -574,19 +607,22 @@ void Layout::doPass() // sort atoms in each section for (std::vector::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; - //fprintf(stderr, "sorting section %s\n", sect->sectionName()); + if ( sect->type() == ld::Section::typeTempAlias ) + continue; + if ( log ) fprintf(stderr, "sorting section %s\n", sect->sectionName()); std::sort(sect->atoms.begin(), sect->atoms.end(), _comparer); } - //fprintf(stderr, "Sorted atoms:\n"); - //for (std::vector::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) { - // ld::Internal::FinalSection* sect = *sit; - // for (std::vector::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { - // const ld::Atom* atom = *ait; - // fprintf(stderr, "\t%p\t%s\t%s\n", atom, sect->sectionName(), atom->name()); - // } - //} - + if ( log ) { + fprintf(stderr, "Sorted atoms:\n"); + for (std::vector::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + for (std::vector::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + fprintf(stderr, "\t%p\t%s\t%s\n", atom, sect->sectionName(), atom->name()); + } + } + } } diff --git a/src/other/ObjectDump.cpp b/src/other/ObjectDump.cpp index c9eb46c..e957e2b 100644 --- a/src/other/ObjectDump.cpp +++ b/src/other/ObjectDump.cpp @@ -831,6 +831,12 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindStoreARM64TLVPLoadPageOff12: printf(", then store as ARM64 12-bit page offset of TLVP"); break; + case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPage21: + printf(", then store as ARM64 21-bit pcrel ADRP of lea of TLVP"); + break; + case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPageOff12: + printf(", then store as ARM64 12-bit page offset of lea of TLVP"); + break; case ld::Fixup::kindStoreARM64PointerToGOT: printf(", then store as 64-bit pointer to GOT entry"); break; @@ -990,12 +996,6 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindStoreTargetAddressARM64PageOff12: printf("ARM64 store 12-bit page offset of %s", referenceTargetAtomName(ref)); break; - case ld::Fixup::kindStoreTargetAddressARM64TLVPage21: - printf("ARM64 store 21-bit pcrel ADRP to TLV for %s", referenceTargetAtomName(ref)); - break; - case ld::Fixup::kindStoreTargetAddressARM64TLVPageOff12: - printf("ARM64 store 12-bit page offset of TLV of %s", referenceTargetAtomName(ref)); - break; case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: printf("ARM64 store 21-bit pcrel ADRP to GOT for %s", referenceTargetAtomName(ref)); break; @@ -1003,10 +1003,22 @@ void dumper::dumpFixup(const ld::Fixup* ref) printf("ARM64 store 12-bit page offset of GOT of %s", referenceTargetAtomName(ref)); break; case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: - printf("ARM64 store 21-bit pcrel ADRP for lea of %s", referenceTargetAtomName(ref)); + printf("ARM64 store 21-bit pcrel ADRP to GOT lea for %s", referenceTargetAtomName(ref)); break; case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12: - printf("ARM64 store 12-bit page offset of lea of %s", referenceTargetAtomName(ref)); + printf("ARM64 store 12-bit page offset of GOT lea of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21: + printf("ARM64 store 21-bit pcrel ADRP to TLV for %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12: + printf("ARM64 store 12-bit page offset of TLV of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPage21: + printf("ARM64 store 21-bit pcrel ADRP to lea for TLV for %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12: + printf("ARM64 store 12-bit page offset of lea for TLV of %s", referenceTargetAtomName(ref)); break; //default: // printf("unknown fixup"); diff --git a/src/other/unwinddump.cpp b/src/other/unwinddump.cpp index 3da01c5..effd09b 100644 --- a/src/other/unwinddump.cpp +++ b/src/other/unwinddump.cpp @@ -221,7 +221,14 @@ const char* UnwindPrinter::functionName(pint_t addr, uint32_t* offset) 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) ) { - if ( fSymbols[i].n_value() == addr ) { + uint32_t value = fSymbols[i].n_value(); + if ( value == addr ) { + const char* r = &fStrings[fSymbols[i].n_strx()]; + return r; + } + if ( fSymbols[i].n_desc() & N_ARM_THUMB_DEF ) + value |= 1; + if ( value == addr ) { const char* r = &fStrings[fSymbols[i].n_strx()]; //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; @@ -730,6 +737,8 @@ void UnwindPrinter::decode(uint32_t encoding, const uint8_t* funcStart, c } #endif + + template <> const char* UnwindPrinter::personalityName(const macho_relocation_info* reloc) { @@ -759,6 +768,7 @@ const char* UnwindPrinter::personalityName(const macho_relocation_info bool UnwindPrinter::hasExernReloc(uint64_t sectionOffset, const char** personalityStr, pint_t* addr) { @@ -795,6 +805,7 @@ void UnwindPrinter::printObjectUnwindSection(bool showFunctionNames) } else { functionNameStr = this->functionName(entry->codeStart(), &offsetInFunction); + funcAddress = entry->codeStart(); } if ( offsetInFunction == 0 ) printf(" start: 0x%08llX %s\n", (uint64_t)funcAddress, functionNameStr); @@ -990,7 +1001,7 @@ static void dump(const char* path, const std::set& onlyArchs, bool s if ( UnwindPrinter::validFile(p + offset) ) UnwindPrinter::make(p + offset, size, path, showFunctionNames); else - throw "in universal file, arm64 slice does not contain arm mach-o"; + throw "in universal file, arm64 slice does not contain arm64 mach-o"; break; #endif default: @@ -1062,6 +1073,7 @@ int main(int argc, const char* argv[]) #if SUPPORT_ARCH_arm64 onlyArchs.insert(CPU_TYPE_ARM64); #endif + onlyArchs.insert(CPU_TYPE_ARM); } // process each file diff --git a/unit-tests/include/common.makefile b/unit-tests/include/common.makefile index 3f4647d..545b4a7 100644 --- a/unit-tests/include/common.makefile +++ b/unit-tests/include/common.makefile @@ -13,7 +13,7 @@ MYDIR=$(shell cd ../../bin;pwd) LD = ld OBJECTDUMP = ObjectDump MACHOCHECK = machocheck -OTOOL = /Applications/Xcode.app/Contents/Developer/Toolchains/iOS7.0.xctoolchain/usr/bin/otool +OTOOL = xcrun otool REBASE = rebase DYLDINFO = dyldinfo @@ -65,7 +65,7 @@ LD_NEW_LINKEDIT = -macosx_version_min 10.6 CXX = $(shell xcrun -find clang++) -arch ${ARCH} ${SDKExtra} CXXFLAGS = -Wall -stdlib=libc++ -IOS_SDK = $(shell xcodebuild -sdk iphoneos7.0.internal -version Path 2>/dev/null) +IOS_SDK = $(shell xcodebuild -sdk iphoneos8.0.internal -version Path 2>/dev/null) ifeq ($(ARCH),armv6) LDFLAGS := -syslibroot $(IOS_SDK) diff --git a/unit-tests/test-cases/alias-basic/Makefile b/unit-tests/test-cases/alias-basic/Makefile new file mode 100644 index 0000000..e457aeb --- /dev/null +++ b/unit-tests/test-cases/alias-basic/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2014 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Verify that code and data references can be redirected via aliases. +# + +CC=/Volumes/my/src/puzzlebox/build/Debug+Asserts/bin/clang + +run: all + +all: + # verify aliases can redirect references to code and data + ${CC} -arch ${ARCH} ${CCFLAGS} main.c -c -o main.o + ${CC} -arch ${ARCH} ${ASMFLAGS} aliases.s -c -o aliases.o + ${CC} -arch ${ARCH} main.o aliases.o -o main.exe + nm -nm main.exe | grep _barHidden | grep " external " | ${FAIL_IF_STDIN} + # verify dead stripping can remove unused and undefined alias + ${CC} -arch ${ARCH} ${ASMFLAGS} aliases.s -c -o aliases2.o -DUNUSED_ALIAS=1 + ${CC} -arch ${ARCH} main.o aliases2.o -dead_strip -o main2.exe + nm -nm main2.exe | grep _barAlt | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main2.exe + +clean: + rm -rf *.o *.dump *.exe diff --git a/unit-tests/test-cases/alias-basic/aliases.s b/unit-tests/test-cases/alias-basic/aliases.s new file mode 100644 index 0000000..90c1149 --- /dev/null +++ b/unit-tests/test-cases/alias-basic/aliases.s @@ -0,0 +1,26 @@ + + .globl _main +_main = _mymain + + .globl _bar +_bar = _mybar + + .globl _barAlt +_barAlt = _mybar + + .private_extern _barHidden +_barHidden = _mybar + + .globl _barExtra +_barExtra = _barAlt + + .globl _result +_result = _myresult + + .globl _resultHidden +_resultHidden = _myresult + +#if UNUSED_ALIAS + .globl _unusedAlias +_unusedAlias = _unusedUndefined +#endif diff --git a/unit-tests/test-cases/alias-basic/main.c b/unit-tests/test-cases/alias-basic/main.c new file mode 100644 index 0000000..d89f421 --- /dev/null +++ b/unit-tests/test-cases/alias-basic/main.c @@ -0,0 +1,16 @@ +extern void bar(); +extern int result; + +int myresult = 1; + +int mymain() +{ + bar(); + return result; +} + +void mybar() +{ + +} + diff --git a/unit-tests/test-cases/alias-objects/Makefile b/unit-tests/test-cases/alias-objects/Makefile index f4bfdc8..7b7667d 100644 --- a/unit-tests/test-cases/alias-objects/Makefile +++ b/unit-tests/test-cases/alias-objects/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2006-2014 Apple Computer, Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -31,14 +31,16 @@ include ${TESTROOT}/include/common.makefile # No differences means this test passes # +CC=/Volumes/my/src/puzzlebox/build/Debug+Asserts/bin/clang + run: all all: - ${CC} ${ASMFLAGS} aliases.s -c -o aliases.${ARCH}.o - ${LD} -arch ${ARCH} -r -keep_private_externs aliases.${ARCH}.o -o aliases-r.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases.${ARCH}.o > aliases.${ARCH}.o.dump - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases-r.${ARCH}.o > aliases-r.${ARCH}.o.dump - ${PASS_IFF} diff aliases.${ARCH}.o.dump aliases-r.${ARCH}.o.dump + ${CC} -arch ${ARCH} ${ASMFLAGS} aliases.s -c -o aliases.o + ${LD} -arch ${ARCH} -r -keep_private_externs aliases.o -o aliases-r.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases.o > aliases.o.dump + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases-r.o > aliases-r.o.dump + ${PASS_IFF} diff aliases.o.dump aliases-r.o.dump clean: rm -rf *.o *.dump diff --git a/unit-tests/test-cases/alias-objects/aliases.s b/unit-tests/test-cases/alias-objects/aliases.s index 5e92d8d..b669233 100644 --- a/unit-tests/test-cases/alias-objects/aliases.s +++ b/unit-tests/test-cases/alias-objects/aliases.s @@ -31,14 +31,23 @@ _temp: nop _foo: nop nop +/* this should make an alias "_fooalt" for "_foo" */ .globl _fooalt .globl _fooalt2 -/* this should make an alias "_fooalt" for "_foo" */ _fooalt = _foo _fooalt2 = _foo + .global _myAlias +_myAlias = _myBase + + .global _myHiddenAlias + .private_extern _myHiddenAlias +_myHiddenAlias = _myHiddenBase + + + _bar: nop nop - .subsections_via_symbols \ No newline at end of file + .subsections_via_symbols diff --git a/unit-tests/test-cases/alt-entry/Makefile b/unit-tests/test-cases/alt-entry/Makefile new file mode 100644 index 0000000..292be81 --- /dev/null +++ b/unit-tests/test-cases/alt-entry/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2014 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that -e works for dynamic executables. +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c -e _mymain -o main1 + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} main.c -e _foo libfoo.dylib -o main2 + + ${PASS_IFF_GOOD_MACHO} main1 + +clean: + rm -f main1 main2 libfoo.dylib diff --git a/unit-tests/test-cases/alt-entry/foo.c b/unit-tests/test-cases/alt-entry/foo.c new file mode 100644 index 0000000..1b1d09b --- /dev/null +++ b/unit-tests/test-cases/alt-entry/foo.c @@ -0,0 +1,7 @@ +#include + +int foo() +{ + fprintf(stdout, "hello foo\n"); + return 0; +} \ No newline at end of file diff --git a/unit-tests/test-cases/alt-entry/main.c b/unit-tests/test-cases/alt-entry/main.c new file mode 100644 index 0000000..8af852a --- /dev/null +++ b/unit-tests/test-cases/alt-entry/main.c @@ -0,0 +1,7 @@ +#include + +int mymain() +{ + fprintf(stdout, "hello mymain\n"); + return 0; +} \ No newline at end of file diff --git a/unit-tests/test-cases/lto-r/Makefile b/unit-tests/test-cases/lto-r/Makefile new file mode 100644 index 0000000..49e0b68 --- /dev/null +++ b/unit-tests/test-cases/lto-r/Makefile @@ -0,0 +1,53 @@ +## +# Copyright (c) 2014 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that -r mode preserves symbols with LTO +# + + +run: all + +all: + ${CC} ${CCFLAGS} -flto foo.c -c -o foo.o + ${CC} ${CCFLAGS} bar.c -c -o bar.o + ${LD} -arch ${ARCH} -r foo.o bar.o -o foobar.o + nm -nm foobar.o | grep _foo_hidden | grep non-external | ${FAIL_IF_EMPTY} + nm -nm foobar.o | grep _foo_static | grep non-external | ${FAIL_IF_EMPTY} + nm -nm foobar.o | grep _foo_weak_hidden | grep non-external | ${FAIL_IF_EMPTY} + nm -nm foobar.o | grep _foo_weak_global | grep "weak external " | ${FAIL_IF_EMPTY} + nm -nm foobar.o | grep _foo_global | grep " external " | ${FAIL_IF_EMPTY} + nm -nm foobar.o | grep _bar | grep " external " | ${FAIL_IF_EMPTY} + ${LD} -arch ${ARCH} -r -keep_private_externs foo.o bar.o -o foobar2.o + nm -nm foobar2.o | grep _foo_hidden | grep "private external" | ${FAIL_IF_EMPTY} + nm -nm foobar2.o | grep _foo_static | grep non-external | ${FAIL_IF_EMPTY} + nm -nm foobar2.o | grep _foo_weak_hidden | grep "weak private external" | ${FAIL_IF_EMPTY} + nm -nm foobar2.o | grep _foo_weak_global | grep "weak external " | ${FAIL_IF_EMPTY} + nm -nm foobar2.o | grep _foo_global | grep " external " | ${FAIL_IF_EMPTY} + nm -nm foobar2.o | grep _bar | grep " external " | ${FAIL_IF_EMPTY} + ${PASS_IFF} true + +clean: + rm -f foo.o bar.o foobar.o foobar2.o diff --git a/unit-tests/test-cases/lto-r/bar.c b/unit-tests/test-cases/lto-r/bar.c new file mode 100644 index 0000000..e425999 --- /dev/null +++ b/unit-tests/test-cases/lto-r/bar.c @@ -0,0 +1 @@ +void bar() {} diff --git a/unit-tests/test-cases/lto-r/foo.c b/unit-tests/test-cases/lto-r/foo.c new file mode 100644 index 0000000..2918f9b --- /dev/null +++ b/unit-tests/test-cases/lto-r/foo.c @@ -0,0 +1,38 @@ +static int var_static = 3; + +__attribute__((visibility("hidden"))) +int var_hidden = 4; + +int var_global = 5; + + +__attribute__((visibility("hidden"), weak)) +int var_weak_hidden = 4; + +__attribute__((weak)) +int var_weak_global = 5; + + + + +static int* foo_static() { return &var_static; } + +__attribute__((visibility("hidden"))) +int* foo_hidden() { return &var_hidden; } + + +int* foo_global() { return &var_global; } + + +__attribute__((visibility("hidden"),weak)) +int* foo_weak_hidden() { return &var_weak_hidden; } + + +__attribute__((weak)) +int* foo_weak_global() { return &var_weak_global; } + + +__attribute__((visibility("hidden"))) +void* keep[] = { &foo_static }; + + diff --git a/unit-tests/test-cases/lto-r/main.c b/unit-tests/test-cases/lto-r/main.c new file mode 100644 index 0000000..578d24b --- /dev/null +++ b/unit-tests/test-cases/lto-r/main.c @@ -0,0 +1,15 @@ + +#include + + +void foo(int x) +{ + printf("hello, world %d\n", x); +} + +int main() +{ + foo(10); + return 0; +} + diff --git a/unit-tests/test-cases/lto-rename_section/Makefile b/unit-tests/test-cases/lto-rename_section/Makefile new file mode 100644 index 0000000..17ba9c3 --- /dev/null +++ b/unit-tests/test-cases/lto-rename_section/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2014 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verify -rename_sectione works with LTO +# + +run: all + +all: + ${CC} ${CCFLAGS} -flto a.c -c -o a.o + ${CC} ${CCFLAGS} -flto b.c -c -o b.o + ${CC} ${CCFLAGS} -flto main.c -c -o main.o + ${CC} ${CCFLAGS} main.o a.o b.o -Wl,-preload -o main.preload \ + -e _entry -nostdlib -Wl,-segalign,0x20 -Wl,-seg1addr,0x200 \ + -Wl,-rename_section,__DATA,__data,__RAM,__vars \ + -Wl,-rename_section,__TEXT,__text,__ROM,__code \ + -Wl,-rename_section,__TEXT,__eh_frame,__ROM,__eh_frame \ + -Wl,-rename_section,__TEXT,__cstring,__ROM,__const + size -l main.preload | grep __TEXT | ${FAIL_IF_STDIN} + size -l main.preload | grep __DATA | ${FAIL_IF_STDIN} + nm -m main.preload | grep __ROM | grep __code | grep _entry | ${FAIL_IF_EMPTY} + nm -m main.preload | grep __RAM | grep __vars | grep _mystring | ${FAIL_IF_EMPTY} + size -l main.preload | grep __ROM | ${PASS_IFF_STDIN} + + + + +clean: + rm -f a.o b.o main.o main.preload diff --git a/unit-tests/test-cases/lto-rename_section/a.c b/unit-tests/test-cases/lto-rename_section/a.c new file mode 100644 index 0000000..c17b3e3 --- /dev/null +++ b/unit-tests/test-cases/lto-rename_section/a.c @@ -0,0 +1,4 @@ + +extern const char* mystring; + +const char** myp = &mystring; diff --git a/unit-tests/test-cases/lto-rename_section/b.c b/unit-tests/test-cases/lto-rename_section/b.c new file mode 100644 index 0000000..b7ad5e1 --- /dev/null +++ b/unit-tests/test-cases/lto-rename_section/b.c @@ -0,0 +1 @@ + const char* mystring = "hello"; diff --git a/unit-tests/test-cases/lto-rename_section/main.c b/unit-tests/test-cases/lto-rename_section/main.c new file mode 100644 index 0000000..8c9c61e --- /dev/null +++ b/unit-tests/test-cases/lto-rename_section/main.c @@ -0,0 +1,11 @@ + +extern const char** myp; + + +const char** entry(int i) { + if ( i ) { + *myp = "help"; + } + return myp; +} + diff --git a/unit-tests/test-cases/lto-rename_segment/Makefile b/unit-tests/test-cases/lto-rename_segment/Makefile new file mode 100644 index 0000000..2f3286c --- /dev/null +++ b/unit-tests/test-cases/lto-rename_segment/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2014 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verify -rename_segment and -rename_section works with LTO +# + +run: all + +all: + ${CC} ${CCFLAGS} -flto a.c -c -o a.o + ${CC} ${CCFLAGS} -flto b.c -c -o b.o + ${CC} ${CCFLAGS} -flto main.c -c -o main.o + ${CC} ${CCFLAGS} main.o a.o b.o -Wl,-preload -o main.preload \ + -e _entry -nostdlib -Wl,-segalign,0x20 -Wl,-seg1addr,0x200 \ + -Wl,-rename_segment,__TEXT,__ROM \ + -Wl,-rename_segment,__DATA,__RAM \ + -Wl,-rename_section,__DATA,__data_extra,__RAM2,__data \ + -Wl,-exported_symbol,_get + size -l main.preload | grep __TEXT | ${FAIL_IF_STDIN} + size -l main.preload | grep __DATA | ${FAIL_IF_STDIN} + nm -m main.preload | grep __ROM | grep __text | grep _entry | ${FAIL_IF_EMPTY} + nm -m main.preload | grep __ROM | grep __text | grep _get | ${FAIL_IF_EMPTY} + nm -m main.preload | grep __RAM | grep __data | grep _mystring | ${FAIL_IF_EMPTY} + nm -m main.preload | grep __RAM2 | grep __data | grep _param | ${FAIL_IF_EMPTY} + size -l main.preload | grep __ROM | ${PASS_IFF_STDIN} + + +clean: + rm -f a.o b.o main.o main.preload diff --git a/unit-tests/test-cases/lto-rename_segment/a.c b/unit-tests/test-cases/lto-rename_segment/a.c new file mode 100644 index 0000000..c17b3e3 --- /dev/null +++ b/unit-tests/test-cases/lto-rename_segment/a.c @@ -0,0 +1,4 @@ + +extern const char* mystring; + +const char** myp = &mystring; diff --git a/unit-tests/test-cases/lto-rename_segment/b.c b/unit-tests/test-cases/lto-rename_segment/b.c new file mode 100644 index 0000000..b7ad5e1 --- /dev/null +++ b/unit-tests/test-cases/lto-rename_segment/b.c @@ -0,0 +1 @@ + const char* mystring = "hello"; diff --git a/unit-tests/test-cases/lto-rename_segment/main.c b/unit-tests/test-cases/lto-rename_segment/main.c new file mode 100644 index 0000000..c219503 --- /dev/null +++ b/unit-tests/test-cases/lto-rename_segment/main.c @@ -0,0 +1,18 @@ + +extern const char** myp; +extern const char* mystring; + + +__attribute__((section("__DATA,__data_extra"))) +int param = 0; + + +const char** entry(int i) { + if ( i ) { + *myp = "help"; + } + param = i; + return myp; +} + +int get() { return param; } \ No newline at end of file diff --git a/unit-tests/test-cases/lto-symbol-section-move/Makefile b/unit-tests/test-cases/lto-symbol-section-move/Makefile new file mode 100644 index 0000000..018087d --- /dev/null +++ b/unit-tests/test-cases/lto-symbol-section-move/Makefile @@ -0,0 +1,60 @@ +## +# Copyright (c) 2014 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check interaction of -section_rename, -segment_rename, and -move_to_r._segment +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c -c -o main.o -flto + ${CC} ${CCFLAGS} foo.c -c -o foo.o -flto + ${CC} ${CCFLAGS} other.c -c -o other.o -flto + ${LD} -arch ${ARCH} main.o foo.o other.o -preload -o main.preload \ + -e _foo -trace_symbol_layout \ + -move_to_ro_segment __ROM1 rom1.symbols \ + -move_to_rw_segment __RAM1 ram1.symbols + nm -m main.preload | grep _mainget | grep __ROM1 | grep __text | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _getpi | grep __ROM1 | grep __text | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _bar | grep __ROM1 | grep __text | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _foo | grep __ROM1 | grep __text | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _def | grep __RAM1 | grep __data | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _ghi | grep __RAM1 | grep __data | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _com | grep __RAM1 | grep __bss | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _abc | grep __RAM1 | grep __data | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _com4 | grep __RAM1 | grep __bss | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _main | grep __TEXT | grep __text | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _version | grep __TEXT | grep __text | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _otherget | grep __TEXT | grep __text | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _mylocal | grep __TEXT | grep __text | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _x | grep __DATA | grep __data | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _all | grep __DATA | grep __data | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _x | grep __DATA | grep __data | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _com5 | grep __DATA | grep __bss | ${FAIL_IF_EMPTY} + ${PASS_IFF} true + +clean: + rm -f main.preload main.o other.o foo.o diff --git a/unit-tests/test-cases/lto-symbol-section-move/foo.c b/unit-tests/test-cases/lto-symbol-section-move/foo.c new file mode 100644 index 0000000..9360585 --- /dev/null +++ b/unit-tests/test-cases/lto-symbol-section-move/foo.c @@ -0,0 +1,38 @@ +extern void* otherget(); +extern int main(); +extern const char* version(); +extern void* mainget(); + +extern int def; +extern int ghi; +extern int com; + +double getpi() { return 3.1415926535; } + +void bar() +{ +} + + + +extern void* __dso_handle; +void* x = &__dso_handle; + +int abc = 10; + + +int com3; +int com4; +int com5; + +extern void* foo(); + +void* all[] = { &main, &version, &mainget, &getpi, &otherget, + &bar, &foo, &x, &abc, &def, &ghi, &com, &com3, &com4, &com5 }; + + +void* foo() +{ + return all; +} + diff --git a/unit-tests/test-cases/lto-symbol-section-move/main.c b/unit-tests/test-cases/lto-symbol-section-move/main.c new file mode 100644 index 0000000..ce97d92 --- /dev/null +++ b/unit-tests/test-cases/lto-symbol-section-move/main.c @@ -0,0 +1,37 @@ +extern void* otherget(); + +void mm() +{ +} + +static void s1() { + mm(); +} + +static void s2() { + mm(); +} + +int main() +{ + s1(); + s2(); + return 0; +} + +const char* version() { return "1.0"; } + +static int mylocal() +{ + return 0; +} + +void* mainget() { return mylocal; } + + +int def = 20; + +int ghi = 30; + +int com; + diff --git a/unit-tests/test-cases/lto-symbol-section-move/other.c b/unit-tests/test-cases/lto-symbol-section-move/other.c new file mode 100644 index 0000000..37d5047 --- /dev/null +++ b/unit-tests/test-cases/lto-symbol-section-move/other.c @@ -0,0 +1,9 @@ + +static int mylocal() +{ + return 1; +} + +void* otherget() { return mylocal; } + + diff --git a/unit-tests/test-cases/lto-symbol-section-move/ram1.symbols b/unit-tests/test-cases/lto-symbol-section-move/ram1.symbols new file mode 100644 index 0000000..ac26378 --- /dev/null +++ b/unit-tests/test-cases/lto-symbol-section-move/ram1.symbols @@ -0,0 +1,5 @@ +main.o:* +_abc +_com4 + + diff --git a/unit-tests/test-cases/lto-symbol-section-move/rom1.symbols b/unit-tests/test-cases/lto-symbol-section-move/rom1.symbols new file mode 100644 index 0000000..ce8e335 --- /dev/null +++ b/unit-tests/test-cases/lto-symbol-section-move/rom1.symbols @@ -0,0 +1,6 @@ +foo.o:* +_mainget + + + + diff --git a/unit-tests/test-cases/preload-section_order/Makefile b/unit-tests/test-cases/preload-section_order/Makefile new file mode 100644 index 0000000..653ccf7 --- /dev/null +++ b/unit-tests/test-cases/preload-section_order/Makefile @@ -0,0 +1,54 @@ +## +# Copyright (c) 2014 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verify -section_order works +# + +run: all + +all: + ${CC} ${CCFLAGS} extra.s -c -o extra.o + ${CC} ${CCFLAGS} more.s -c -o more.o + ${CC} ${CCFLAGS} main.c -c -o main.o + # test basic re-order of sections from different files + ${CC} ${CCFLAGS} main.o more.o extra.o -Wl,-preload -Wl,-pie -o main1.preload \ + -e _entry -nostdlib -Wl,-segalign,0x20 -Wl,-seg1addr,0x200 \ + -Wl,-section_order,__MYSEG,__my_yyy:__my_ccc:__my_aaa + ${OTOOL} -l main1.preload | grep "sectname __my_" > main1.found + ${FAIL_IF_ERROR} diff main1.found main1.expected + # test renaming and re-ordering + ${CC} ${CCFLAGS} main.o more.o extra.o -Wl,-preload -Wl,-pie -o main2.preload \ + -e _entry -nostdlib -Wl,-segalign,0x20 -Wl,-seg1addr,0x200 \ + -Wl,-rename_section,__MYSEG,__my_yyy,__MYSEG,__my_iii \ + -Wl,-rename_section,__MYSEG,__my_ccc,__MYSEG,__my_jjj \ + -Wl,-section_order,__MYSEG,__my_iii:__my_aaa:__my_jjj + ${OTOOL} -l main2.preload | grep "sectname __my_" > main2.found + ${PASS_IFF} diff main1.found main1.expected + + + +clean: + rm extra.o more.o main.o main1.preload main1.found main2.preload main2.found diff --git a/unit-tests/test-cases/preload-section_order/extra.s b/unit-tests/test-cases/preload-section_order/extra.s new file mode 100644 index 0000000..9e07959 --- /dev/null +++ b/unit-tests/test-cases/preload-section_order/extra.s @@ -0,0 +1,15 @@ + + + + .section __MYSEG,__my_xxx +_x: .long 0 + + + .section __MYSEG,__my_yyy +_y: .long 0 + + + .section __MYSEG,__my_zzz +_z: .long 0 + + diff --git a/unit-tests/test-cases/preload-section_order/main.c b/unit-tests/test-cases/preload-section_order/main.c new file mode 100644 index 0000000..a9a516f --- /dev/null +++ b/unit-tests/test-cases/preload-section_order/main.c @@ -0,0 +1,5 @@ + + +void entry() { +} + diff --git a/unit-tests/test-cases/preload-section_order/main1.expected b/unit-tests/test-cases/preload-section_order/main1.expected new file mode 100644 index 0000000..06f643b --- /dev/null +++ b/unit-tests/test-cases/preload-section_order/main1.expected @@ -0,0 +1,7 @@ + sectname __my_yyy + sectname __my_ccc + sectname __my_aaa + sectname __my_bbb + sectname __my_ddd + sectname __my_xxx + sectname __my_zzz diff --git a/unit-tests/test-cases/preload-section_order/main2.expected b/unit-tests/test-cases/preload-section_order/main2.expected new file mode 100644 index 0000000..6ac3c0e --- /dev/null +++ b/unit-tests/test-cases/preload-section_order/main2.expected @@ -0,0 +1,7 @@ + sectname __my_iii + sectname __my_aaa + sectname __my_jjj + sectname __my_bbb + sectname __my_ddd + sectname __my_xxx + sectname __my_zzz diff --git a/unit-tests/test-cases/preload-section_order/more.s b/unit-tests/test-cases/preload-section_order/more.s new file mode 100644 index 0000000..6db305b --- /dev/null +++ b/unit-tests/test-cases/preload-section_order/more.s @@ -0,0 +1,19 @@ + + + .section __MYSEG,__my_aaa +_a: .long 0 + + + .section __MYSEG,__my_bbb +_b: .long 0 + + + .section __MYSEG,__my_ccc +_c: .long 0 + + + + .section __MYSEG,__my_ddd +_d: .long 0 + + diff --git a/unit-tests/test-cases/preload-segment_order/Makefile b/unit-tests/test-cases/preload-segment_order/Makefile new file mode 100644 index 0000000..b5b4994 --- /dev/null +++ b/unit-tests/test-cases/preload-segment_order/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2014 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verify -segment_order works +# + +run: all + +all: + ${CC} ${CCFLAGS} a.c -c -o a.o -static + ${CC} ${CCFLAGS} b.c -c -o b.o -static + ${CC} ${CCFLAGS} main.c -c -o main.o -static + ${CC} ${CCFLAGS} main.o a.o b.o -Wl,-preload -Wl,-pie -o main.preload \ + -e _entry -nostdlib -Wl,-segalign,0x20 -Wl,-seg1addr,0x200 \ + -Wl,-rename_section,__TEXT,__text,__ROM,__code \ + -Wl,-rename_section,__TEXT,__eh_frame,__ROM,__eh_frame \ + -Wl,-rename_section,__TEXT,__cstring,__ROM2,__strings \ + -Wl,-rename_section,__DATA,__data,__RAM,__inited \ + -Wl,-rename_section,__DATA,__common,__ZF,__zf \ + -Wl,-segment_order,__ROM2:__ROM:__RAM:__ZF + ${OTOOL} -l main.preload | grep -A2 LC_SEGMENT | grep segname > main-segs.found + ${PASS_IFF} diff main-segs.found main-segs.expected + + + +clean: + rm a.o b.o main.o main.preload main-segs.found diff --git a/unit-tests/test-cases/preload-segment_order/a.c b/unit-tests/test-cases/preload-segment_order/a.c new file mode 100644 index 0000000..b07e94c --- /dev/null +++ b/unit-tests/test-cases/preload-segment_order/a.c @@ -0,0 +1,13 @@ + +extern const char* mystring; + +const char** myp = &mystring; + +int com; + +const char* inc() { + ++com; + return ""; +} + + diff --git a/unit-tests/test-cases/preload-segment_order/b.c b/unit-tests/test-cases/preload-segment_order/b.c new file mode 100644 index 0000000..89db6f1 --- /dev/null +++ b/unit-tests/test-cases/preload-segment_order/b.c @@ -0,0 +1,9 @@ +const char* mystring = "hello"; + +int var = 10; + +const char* incget() { + ++var; + return mystring; +} + diff --git a/unit-tests/test-cases/preload-segment_order/main-segs.expected b/unit-tests/test-cases/preload-segment_order/main-segs.expected new file mode 100644 index 0000000..c378179 --- /dev/null +++ b/unit-tests/test-cases/preload-segment_order/main-segs.expected @@ -0,0 +1,4 @@ + segname __ROM2 + segname __ROM + segname __RAM + segname __ZF diff --git a/unit-tests/test-cases/preload-segment_order/main.c b/unit-tests/test-cases/preload-segment_order/main.c new file mode 100644 index 0000000..8c9c61e --- /dev/null +++ b/unit-tests/test-cases/preload-segment_order/main.c @@ -0,0 +1,11 @@ + +extern const char** myp; + + +const char** entry(int i) { + if ( i ) { + *myp = "help"; + } + return myp; +} + diff --git a/unit-tests/test-cases/section-labels/main.c b/unit-tests/test-cases/section-labels/main.c index a8c4bfa..c7c4564 100644 --- a/unit-tests/test-cases/section-labels/main.c +++ b/unit-tests/test-cases/section-labels/main.c @@ -28,14 +28,14 @@ struct stuff { int a; int b; }; struct stuff stuff1 __attribute__ ((section ("__DATA,__my"))) = { 1, 2}; struct stuff stuff2 __attribute__ ((section ("__DATA,__my"))) = { 3 ,4 }; -extern struct stuff* stuff_start __asm("section$start$__DATA$__my"); -extern struct stuff* stuff_end __asm("section$end$__DATA$__my"); +extern struct stuff stuff_start __asm("section$start$__DATA$__my"); +extern struct stuff stuff_end __asm("section$end$__DATA$__my"); int main() { struct stuff* p; - for (p = stuff_start; p < stuff_end; ++p) { + for (p = &stuff_start; p < &stuff_end; ++p) { p->a = 0; } return 0; diff --git a/unit-tests/test-cases/symbol-section-move/Makefile b/unit-tests/test-cases/symbol-section-move/Makefile new file mode 100644 index 0000000..32a24b4 --- /dev/null +++ b/unit-tests/test-cases/symbol-section-move/Makefile @@ -0,0 +1,60 @@ +## +# Copyright (c) 2014 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check interaction of -section_rename, -segment_rename, and -move_to_r._segment +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c -c -o main.o + ${CC} ${CCFLAGS} other.c -c -o other.o + ${LD} -arch ${ARCH} main.o other.o -preload -o main.preload \ + -e _foo -trace_symbol_layout \ + -move_to_ro_segment __ROM1 rom1.symbols \ + -rename_section __TEXT __cstring __ROM2 mycstrings \ + -rename_segment __TEXT __ROM3 \ + -move_to_rw_segment __RAM1 ram1.symbols \ + -rename_section __DATA __data __RAM2 mydata \ + -rename_segment __DATA __RAM3 \ + -segment_order __ROM1:__ROM2:__ROM3:__RAM1:__RAM2:__RAM3 + nm -m main.preload | grep _foo | grep __ROM1 | grep __text | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _s1 | grep __ROM1 | grep __text | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _mylocal | grep __ROM1 | grep __text | ${FAIL_IF_EMPTY} + size -l main.preload | grep __cstring | ${FAIL_IF_STDIN} + size -l main.preload | grep mycstrings | ${FAIL_IF_EMPTY} + size -l main.preload | grep __TEXT | ${FAIL_IF_STDIN} + nm -m main.preload | grep _mm | grep __ROM3 | grep __text | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _main | grep __ROM3 | grep __text | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _abc | grep __RAM1 | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _com | grep __RAM1 | ${FAIL_IF_EMPTY} + size -l main.preload | grep __DATA | ${FAIL_IF_STDIN} + nm -m main.preload | grep _def | grep __RAM2 | grep mydata | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _ghi | grep __RAM2 | grep mydata | ${FAIL_IF_EMPTY} + ${PASS_IFF} true + +clean: + rm -f main.preload main.o other.o diff --git a/unit-tests/test-cases/symbol-section-move/main.c b/unit-tests/test-cases/symbol-section-move/main.c new file mode 100644 index 0000000..407d4ad --- /dev/null +++ b/unit-tests/test-cases/symbol-section-move/main.c @@ -0,0 +1,54 @@ + +void mm() +{ +} + +static void s1() { + mm(); +} + +static void s2() { + mm(); +} + +int main() +{ + s1(); + s2(); + return 0; +} + +const char* version() { return "1.0"; } + +static int mylocal() +{ + return 0; +} + +void* mainget() { return mylocal; } + +double getpi() { return 3.1415926535; } + +void foo() +{ +} + +void bar() +{ +} + +extern void* __dso_handle; +void* x = &__dso_handle; + +int abc = 10; + +int def = 20; + +int ghi = 30; + +int com; + +int com3; +int com4; +int com5; + diff --git a/unit-tests/test-cases/symbol-section-move/other.c b/unit-tests/test-cases/symbol-section-move/other.c new file mode 100644 index 0000000..37d5047 --- /dev/null +++ b/unit-tests/test-cases/symbol-section-move/other.c @@ -0,0 +1,9 @@ + +static int mylocal() +{ + return 1; +} + +void* otherget() { return mylocal; } + + diff --git a/unit-tests/test-cases/symbol-section-move/ram1.symbols b/unit-tests/test-cases/symbol-section-move/ram1.symbols new file mode 100644 index 0000000..4b641d7 --- /dev/null +++ b/unit-tests/test-cases/symbol-section-move/ram1.symbols @@ -0,0 +1,3 @@ +_com +_abc + diff --git a/unit-tests/test-cases/symbol-section-move/rom1.symbols b/unit-tests/test-cases/symbol-section-move/rom1.symbols new file mode 100644 index 0000000..1d50eda --- /dev/null +++ b/unit-tests/test-cases/symbol-section-move/rom1.symbols @@ -0,0 +1,6 @@ +_foo +_s1 +main.o:_mylocal + + + diff --git a/unit-tests/test-cases/weak_import-undefined/Makefile b/unit-tests/test-cases/weak_import-undefined/Makefile index 56e888f..1d5fbba 100644 --- a/unit-tests/test-cases/weak_import-undefined/Makefile +++ b/unit-tests/test-cases/weak_import-undefined/Makefile @@ -37,4 +37,4 @@ all: ${PASS_IFF_GOOD_MACHO} weak clean: - rm -rf main + rm -rf weak -- 2.45.2