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
.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
B3B672441406D44300A376BB /* Snapshot.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; name = Snapshot.h; path = src/ld/Snapshot.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
B3C7A09914295B9C005FC714 /* compile_stubs */ = {isa = PBXFileReference; lastKnownFileType = text.script.csh; path = compile_stubs; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
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 = "<group>"; 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 = "<group>"; tabWidth = 4; usesTabs = 1; };
+ F91B7B0318987D5F0099486F /* AddressSpace.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AddressSpace.hpp; sourceTree = "<group>"; };
+ F91B7B0418987D5F0099486F /* DwarfInstructions.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DwarfInstructions.hpp; sourceTree = "<group>"; };
+ F91B7B0518987D5F0099486F /* DwarfParser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DwarfParser.hpp; sourceTree = "<group>"; };
+ F91B7B0618987D5F0099486F /* InternalMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InternalMacros.h; sourceTree = "<group>"; };
+ F91B7B0718987D5F0099486F /* Registers.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Registers.hpp; sourceTree = "<group>"; };
F92D9C2710657AAB00FF369B /* stub_x86_64_classic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_x86_64_classic.hpp; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
F933D9460929277C0083EAC8 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = FileAbstraction.hpp; path = src/abstraction/FileAbstraction.hpp; sourceTree = "<group>"; 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 = "<group>"; tabWidth = 4; usesTabs = 1; };
name = Products;
sourceTree = "<group>";
};
+ F91B7B0218987D5F0099486F /* libunwind */ = {
+ isa = PBXGroup;
+ children = (
+ F91B7B0318987D5F0099486F /* AddressSpace.hpp */,
+ F91B7B0418987D5F0099486F /* DwarfInstructions.hpp */,
+ F91B7B0518987D5F0099486F /* DwarfParser.hpp */,
+ F91B7B0618987D5F0099486F /* InternalMacros.h */,
+ F91B7B0718987D5F0099486F /* Registers.hpp */,
+ );
+ path = libunwind;
+ sourceTree = "<group>";
+ };
F9AA650B1051BD2B003E3539 /* passes */ = {
isa = PBXGroup;
children = (
F9AA65861051E750003E3539 /* parsers */ = {
isa = PBXGroup;
children = (
+ F91B7B0218987D5F0099486F /* libunwind */,
F9AA6784105700C2003E3539 /* opaque_section_file.cpp */,
F9AA6785105700C2003E3539 /* opaque_section_file.h */,
F9AA65D71051EC4A003E3539 /* archive_file.cpp */,
#define N_SYMBOL_RESOLVER 0x100
#endif
+#ifndef N_AST
+ #define N_AST 0x32
+#endif
+
#ifndef LC_FUNCTION_STARTS
#define LC_FUNCTION_STARTS 0x26
#endif
};
#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)
#define UNWIND_ARM64_DWARF_SECTION_OFFSET 0x00FFFFFF
+
#ifndef LC_SOURCE_VERSION
#define LC_SOURCE_VERSION 0x2A
struct source_version_command {
#define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t) 8)
#endif
+
struct ArchInfo {
const char* archName;
cpu_type_t cpuType;
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;
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;
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",
+ // <rdar://problem/17593430> 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 ) {
}
// 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;
}
}
+ 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);
}
-void InputFiles::addLinkerOptionLibraries(ld::Internal& state)
+void InputFiles::addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHandler& handler)
{
if ( _options.outputKind() == Options::kObjectFile )
return;
ld::dylib::File* dylibReader = dynamic_cast<ld::dylib::File*>(reader);
if ( dylibReader != NULL ) {
if ( ! dylibReader->installPathVersionSpecific() ) {
+ dylibReader->forEachAtom(handler);
dylibReader->setImplicitlyLinked();
this->addDylib(dylibReader, info);
}
if ( ! this->libraryAlreadyLoaded(info.path) ) {
info.ordinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal();
try {
+ //<rdar://problem/17787306> -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<ld::dylib::File*>(reader);
ld::archive::File* archiveReader = dynamic_cast<ld::archive::File*>(reader);
if ( dylibReader != NULL ) {
+ dylibReader->forEachAtom(handler);
dylibReader->setImplicitlyLinked();
this->addDylib(dylibReader, info);
}
_searchLibraries.push_back(LibraryInfo(archiveReader));
if ( _options.dumpDependencyInfo() )
_options.dumpDependency(Options::depArchive, archiveReader->path());
+ //<rdar://problem/17787306> -force_load_swift_libs
+ if (info.options.fForceLoad) {
+ archiveReader->forEachAtom(handler);
+ }
}
else {
throwf("linker option dylib at %s is not a dylib", info.path);
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);
}
}
markExplicitlyLinkedDylibs();
- addLinkerOptionLibraries(state);
+ addLinkerOptionLibraries(state, handler);
createIndirectDylibs();
createOpaqueFileSections();
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;
}
// add implicitly linked dylibs
if ( _options.nameSpace() == Options::kTwoLevelNameSpace ) {
+ std::vector<ld::dylib::File*> 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);
}
}
}
+ // <rdar://problem/15002251> 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");
bool inferredArch() const { return _inferredArch; }
- void addLinkerOptionLibraries(ld::Internal& state);
+ void addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHandler& handler);
void createIndirectDylibs();
// for -print_statistics
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<macho_nlist<P> > _globals;
mutable std::vector<macho_nlist<P> > _locals;
int SymbolTableAtom<A>::_s_anonNameIndex = 1;
+template <typename A>
+bool SymbolTableAtom<A>::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 <typename A>
bool SymbolTableAtom<A>::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 )
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;
// <rdar://problem/6783167> support auto hidden weak symbols: .weak_def_can_be_hidden
// 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
// 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);
// 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<ld::Internal::FinalSection*>::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 <typename A>
// for x86_64 the reloc base address starts at __DATA segment
for (std::vector<ld::Internal::FinalSection*>::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 <typename A>
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),
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),
throw "internal error";
}
+const std::vector<const char*>* Options::sectionOrder(const char* segName) const
+{
+ for (std::vector<SectionOrderList>::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) {
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 char*>::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
}
+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<SymbolsMove>& 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<SymbolsMove>::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<SymbolsMove>::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 )
fKextsUseStubs = true;
}
else if ( strcmp(argv[i], "-dependency_info") == 0 ) {
+ snapshotArgCount = 0;
++i;
// previously handled by buildSearchPaths()
}
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 <existing-segment> <new-segment>";
+ 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 <segment> <symbol-list-file>";
+ 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 <segment> <symbol-list-file>";
+ 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 <segment-list>";
+ 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 <segment> <section-list>";
+ 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 <option>";
+ fASTFilePaths.push_back(path);
+ }
+ else if ( strcmp(arg, "-force_load_swift_libs") == 0 ) {
+ fForceLoadSwiftLibs = true;
+ }
// put this last so that it does not interfer with other options starting with 'i'
else if ( strncmp(arg, "-i", 2) == 0 ) {
const char* colon = strchr(arg, ':');
if (getenv("LD_SPLITSEGS_NEW_LIBRARIES") != NULL)
fSplitSegs = true;
- if (getenv("LD_NO_ENCRYPT") != NULL)
+ if (getenv("LD_NO_ENCRYPT") != NULL) {
fEncryptable = false;
+ fMarkAppExtensionSafe = true; // temporary
+ fCheckAppExtensionSafe = false;
+ }
+
+ if (getenv("LD_APPLICATION_EXTENSION_SAFE") != NULL) {
+ fMarkAppExtensionSafe = true;
+ fCheckAppExtensionSafe = false;
+ }
if (getenv("LD_ALLOW_CPU_SUBTYPE_MISMATCHES") != NULL)
fAllowCpuSubtypeMismatches = true;
break;
}
- // only iOS main executables should be encrypted
- if ( fOutputKind != Options::kDynamicExecutable )
- fEncryptable = false;
+ // only iOS executables should be encryptable
+ switch ( fOutputKind ) {
+ case Options::kObjectFile:
+ case Options::kDyld:
+ case Options::kStaticExecutable:
+ case Options::kPreload:
+ case Options::kKextBundle:
+ fEncryptable = false;
+ break;
+ case Options::kDynamicExecutable:
+ break;
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ // <rdar://problem/16293398> Add LC_ENCRYPTION_INFO load command to bundled frameworks
+ if ( fIOSVersionMin < ld::iOS_8_0 )
+ fEncryptable = false;
+ break;
+ }
if ( (fArchitecture != CPU_TYPE_ARM) && (fArchitecture != CPU_TYPE_ARM64) )
fEncryptable = false;
fPositionIndependentExecutable = true;
}
+ // Simulator defaults to PIE
+ if ( fTargetIOSSimulator && (fOutputKind == kDynamicExecutable) )
+ fPositionIndependentExecutable = true;
+
// -no_pie anywhere on command line disable PIE
if ( fDisablePositionIndependentExecutable )
fPositionIndependentExecutable = false;
if ( fMacVersionMin >= ld::mac10_7 ) {
fTLVSupport = true;
}
- else if ( (fArchitecture == CPU_TYPE_ARM64) && (fIOSVersionMin >= 0x00080000) ) {
+ else if ( (fArchitecture == CPU_TYPE_ARM64) && (fIOSVersionMin >= ld::iOS_8_0) ) {
fTLVSupport = true;
}
case Options::kDynamicExecutable:
if ( fEntryPointLoadCommandForceOn ) {
fEntryPointLoadCommand = true;
- fEntryName = "_main";
+ if ( fEntryName == NULL )
+ fEntryName = "_main";
}
else if ( fEntryPointLoadCommandForceOff ) {
fNeedsThreadLoadCommand = true;
+ if ( fEntryName == NULL )
+ fEntryName = "start";
}
else {
- if ( minOS(ld::mac10_8, ld::iOS_6_0) ) {
+ // <rdar://problem/16310363> Linker should look for "_main" not "start" when building for sim regardless of min OS
+ if ( minOS(ld::mac10_8, ld::iOS_6_0) || fTargetIOSSimulator ) {
fEntryPointLoadCommand = true;
- fEntryName = "_main";
- }
- else
+ if ( fEntryName == NULL )
+ fEntryName = "_main";
+ if ( strcmp(fEntryName, "start") == 0 ) {
+ warning("Ignoring '-e start' because entry point 'start' is not used for the targeted OS version");
+ fEntryName = "_main";
+ }
+ }
+ else {
fNeedsThreadLoadCommand = true;
+ if ( fEntryName == NULL )
+ fEntryName = "start";
+ }
}
break;
case Options::kObjectFile:
case Options::kPreload:
case Options::kDyld:
fNeedsThreadLoadCommand = true;
+ if ( fEntryName == NULL )
+ fEntryName = "start"; // Perhaps these should have no default and require -e
break;
}
}
// <rdar://problem/12258065> ARM64 needs 16KB page size for user land code
- if ( fArchitecture == CPU_TYPE_ARM64 ) {
- if ( fSegmentAlignment == 4096 ) {
+ // <rdar://problem/15974532> make armv7[s] use 16KB pages in user land code for iOS 8 or later
+ if ( fSegmentAlignment == 4096 ) {
+ if ( (fArchitecture == CPU_TYPE_ARM64)
+ || ((fArchitecture == CPU_TYPE_ARM) && (fIOSVersionMin >= ld::iOS_8_0) &&
+ ((fSubArchitecture == CPU_SUBTYPE_ARM_V7S) || (fSubArchitecture == CPU_SUBTYPE_ARM_V7))) ) {
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
case Options::kDynamicLibrary:
}
}
}
-
+
// <rdar://problem/13624134> linker should not convert dwarf unwind if .o file has compact unwind section
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
if ( (fOutputKind != Options::kDynamicExecutable) && (fDyldEnvironExtras.size() != 0) )
throw "-dyld_env can only used used when created main executables";
- // -rename_sections can only be used in -r mode
- if ( (fSectionRenames.size() != 0) && (fOutputKind != Options::kObjectFile) )
- throw "-rename_sections can only used used in -r mode";
+ // -segment_order can only be used with -preload
+ if ( !fSegmentOrder.empty() && (fOutputKind != Options::kPreload) )
+ throw "-segment_order can only used used with -preload output";
+
+ // <rdar://problem/17598404> warn if building an embedded iOS dylib for pre-iOS 8
+ if ( (fOutputKind == Options::kDynamicLibrary) && (fIOSVersionMin != ld::iOSVersionUnset) && (fDylibInstallName != NULL) ) {
+ if ( (fIOSVersionMin < ld::iOS_8_0) && (fDylibInstallName[0] == '@') )
+ warning("embedded dylibs/frameworks only run on iOS 8 or later");
+ }
}
uint8_t alignment;
};
+ struct SectionOrderList {
+ const char* segmentName;
+ std::vector<const char*> sectionOrder;
+ };
+
struct OrderedSymbol {
const char* symbolName;
const char* objectFileName;
const char* toSection;
};
+ struct SegmentRename {
+ const char* fromSegment;
+ const char* toSegment;
+ };
enum { depLinkerVersion=0x00, depObjectFile=0x10, depDirectDylib=0x10, depIndirectDylib=0x10,
depUpwardDirectDylib=0x10, depUpwardIndirectDylib=0x10, depArchive=0x10,
bool hasNonExecutableHeap() const { return fNonExecutableHeap; }
UndefinesIterator initialUndefinesBegin() const { return &fInitialUndefines[0]; }
UndefinesIterator initialUndefinesEnd() const { return &fInitialUndefines[fInitialUndefines.size()]; }
+ const std::vector<const char*>& initialUndefines() const { return fInitialUndefines; }
bool printWhyLive(const char* name) const;
uint32_t minimumHeaderPad() const { return fMinimumHeaderPad; }
bool maxMminimumHeaderPad() const { return fMaxMinimumHeaderPad; }
bool makeEncryptable() const { return fEncryptable; }
bool needsUnwindInfoSection() const { return fAddCompactUnwindEncoding; }
const std::vector<const char*>& llvmOptions() const{ return fLLVMOptions; }
+ const std::vector<const char*>& segmentOrder() const{ return fSegmentOrder; }
+ const std::vector<const char*>* sectionOrder(const char* segName) const;
const std::vector<const char*>& dyldEnvironExtras() const{ return fDyldEnvironExtras; }
+ const std::vector<const char*>& astFilePaths() const{ return fASTFilePaths; }
bool makeCompressedDyldInfo() const { return fMakeCompressedDyldInfo; }
bool hasExportedSymbolOrder();
bool exportedSymbolOrder(const char* sym, unsigned int* order) const;
bool ignoreOptimizationHints() const { return fIgnoreOptimizationHints; }
bool generateDtraceDOF() const { return fGenerateDtraceDOF; }
bool allowBranchIslands() const { return fAllowBranchIslands; }
+ bool traceSymbolLayout() const { return fTraceSymbolLayout; }
+ bool markAppExtensionSafe() const { return fMarkAppExtensionSafe; }
+ bool checkDylibsAreAppExtensionSafe() const { return fCheckAppExtensionSafe; }
+ bool forceLoadSwiftLibs() const { return fForceLoadSwiftLibs; }
bool hasWeakBitTweaks() const;
bool forceWeak(const char* symbolName) const;
bool forceNotWeak(const char* symbolName) const;
FileInfo findFramework(const char* frameworkName) const;
FileInfo findLibrary(const char* rootName, bool dylibsOnly=false) const;
const std::vector<SectionRename>& sectionRenames() const { return fSectionRenames; }
+ const std::vector<SegmentRename>& segmentRenames() const { return fSegmentRenames; }
+ bool moveRoSymbol(const char* symName, const char* filePath, const char*& seg, bool& wildCardMatch) const;
+ bool moveRwSymbol(const char* symName, const char* filePath, const char*& seg, bool& wildCardMatch) const;
private:
typedef std::unordered_map<const char*, unsigned int, ld::CStringHash, ld::CStringEquals> NameToOrder;
class SetWithWildcards {
public:
void insert(const char*);
- bool contains(const char*) const;
+ bool contains(const char*, bool* wildCardMatch=NULL) const;
+ bool containsWithPrefix(const char* symbol, const char* file, bool& wildCardMatch) const;
bool containsNonWildcard(const char*) const;
bool empty() const { return fRegular.empty() && fWildCard.empty(); }
bool hasWildCards() const { return !fWildCard.empty(); }
std::vector<const char*> fWildCard;
};
+ struct SymbolsMove {
+ const char* toSegment;
+ SetWithWildcards symbols;
+ };
void parse(int argc, const char* argv[]);
void checkIllegalOptionCombinations();
uint32_t parseProtection(const char* prot);
void loadSymbolOrderFile(const char* fileOfExports, NameToOrder& orderMapping);
void addSectionRename(const char* srcSegment, const char* srcSection, const char* dstSegment, const char* dstSection);
-
+ void addSegmentRename(const char* srcSegment, const char* dstSegment);
+ void addSymbolMove(const char* dstSegment, const char* symbolList, std::vector<SymbolsMove>& list, const char* optionName);
// ObjectFile::ReaderOptions fReaderOptions;
bool fIgnoreOptimizationHints;
bool fGenerateDtraceDOF;
bool fAllowBranchIslands;
+ bool fTraceSymbolLayout;
+ bool fMarkAppExtensionSafe;
+ bool fCheckAppExtensionSafe;
+ bool fForceLoadSwiftLibs;
DebugInfoStripping fDebugInfoStripping;
const char* fTraceOutputFile;
ld::MacVersionMin fMacVersionMin;
std::vector<const char*> fFrameworkSearchPaths;
std::vector<const char*> fSDKPaths;
std::vector<const char*> fDyldEnvironExtras;
+ std::vector<const char*> fSegmentOrder;
+ std::vector<const char*> fASTFilePaths;
+ std::vector<SectionOrderList> fSectionOrder;
std::vector< std::vector<const char*> > fLinkerOptions;
std::vector<SectionRename> fSectionRenames;
+ std::vector<SegmentRename> fSegmentRenames;
+ std::vector<SymbolsMove> fSymbolsMovesData;
+ std::vector<SymbolsMove> fSymbolsMovesCode;
+ std::vector<SymbolsMove> fSymbolsMovesZeroFill;
bool fSaveTempFiles;
mutable Snapshot fLinkSnapshot;
bool fSnapshotRequested;
// work backwards from end of segment and lay out sections so that extra room goes to padding atom
uint64_t addr = 0;
uint64_t textSegPageSize = _options.segPageSize("__TEXT");
+ if ( _options.sharedRegionEligible() && (_options.iOSVersionMin() >= ld::iOS_8_0) && (textSegPageSize == 0x4000) )
+ textSegPageSize = 0x1000;
for (std::vector<ld::Internal::FinalSection*>::reverse_iterator it = state.sections.rbegin(); it != state.sections.rend(); ++it) {
ld::Internal::FinalSection* sect = *it;
if ( strcmp(sect->segmentName(), "__TEXT") != 0 )
uint64_t OutputFile::get64BE(uint8_t* loc) { return BigEndian::get64(*(uint64_t*)loc); }
void OutputFile::set64BE(uint8_t* loc, uint64_t value) { BigEndian::set64(*(uint64_t*)loc, value); }
+#if SUPPORT_ARCH_arm64
+
static uint32_t makeNOP() {
return 0xD503201F;
}
int64_t delta = (addr2 - addr1);
return ( (delta < 1024*1024) && (delta > -1024*1024) );
}
+#endif // SUPPORT_ARCH_arm64
void OutputFile::setInfo(ld::Internal& state, const ld::Atom* atom, uint8_t* buffer, const std::map<uint32_t, const Fixup*>& usedByHints,
uint32_t offsetInAtom, uint32_t delta, InstructionInfo* info)
info->instruction = get32LE(info->instructionContent);
}
+#if SUPPORT_ARCH_arm64
static bool isPageKind(const ld::Fixup* fixup, bool mustBeGOT=false)
{
if ( fixup == NULL )
}
return false;
}
+#endif // SUPPORT_ARCH_arm64
#define LOH_ASSERT(cond) \
break;
case ld::Fixup::kindSetTargetImageOffset:
accumulator = addressOf(state, fit, &toTarget) - mhAddress;
+ thumbTarget = targetIsThumb(state, fit);
+ if ( thumbTarget )
+ accumulator |= 1;
break;
case ld::Fixup::kindSetTargetSectionOffset:
accumulator = sectionOffsetOf(state, fit);
if ( islandfit->kind == ld::Fixup::kindIslandTarget ) {
const ld::Atom* islandTarget = NULL;
uint64_t islandTargetAddress = addressOf(state, islandfit, &islandTarget);
+ if ( !fit->contentDetlaToAddendOnly ) {
+ if ( targetIsThumb(state, islandfit) ) {
+ // Thumb to thumb branch, we will be generating a bl instruction.
+ // Delta is always even, so mask out thumb bit in target.
+ islandTargetAddress &= -2ULL;
+ }
+ else {
+ // Target is not thumb, we will be generating a blx instruction
+ // Since blx cannot have the low bit set, set bit[1] of the target to
+ // bit[1] of the base address, so that the difference is a multiple of
+ // 4 bytes.
+ islandTargetAddress &= -3ULL;
+ islandTargetAddress |= ((atom->finalAddress() + fit->offsetInAtom ) & 2LL);
+ }
+ }
delta = islandTargetAddress - (atom->finalAddress() + fit->offsetInAtom + 4);
if ( checkThumbBranch22Displacement(delta) ) {
toTarget = islandTarget;
is_bl = ((instruction & 0xD000F800) == 0xD000F000);
is_blx = ((instruction & 0xD000F800) == 0xC000F000);
is_b = ((instruction & 0xD000F800) == 0x9000F000);
- // If the target is not thumb, we will be generating a blx instruction
- // Since blx cannot have the low bit set, set bit[1] of the target to
- // bit[1] of the base address, so that the difference is a multiple of
- // 4 bytes.
- if ( !thumbTarget && !fit->contentDetlaToAddendOnly ) {
- accumulator &= -3ULL;
- accumulator |= ((atom->finalAddress() + fit->offsetInAtom ) & 2LL);
+ if ( !fit->contentDetlaToAddendOnly ) {
+ if ( thumbTarget ) {
+ // Thumb to thumb branch, we will be generating a bl instruction.
+ // Delta is always even, so mask out thumb bit in target.
+ accumulator &= -2ULL;
+ }
+ else {
+ // Target is not thumb, we will be generating a blx instruction
+ // Since blx cannot have the low bit set, set bit[1] of the target to
+ // bit[1] of the base address, so that the difference is a multiple of
+ // 4 bytes.
+ accumulator &= -3ULL;
+ accumulator |= ((atom->finalAddress() + fit->offsetInAtom ) & 2LL);
+ }
}
// The pc added will be +4 from the pc
delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 4);
+ // <rdar://problem/16652542> support bl in very large .o files
+ if ( fit->contentDetlaToAddendOnly ) {
+ while ( delta < (-16777216LL) )
+ delta += 0x2000000;
+ }
rangeCheckThumbBranch22(delta, state, atom, fit);
if ( _options.preferSubArchitecture() && _options.archSupportsThumb2() ) {
// The instruction is really two instructions:
}
}
+#if SUPPORT_ARCH_arm64
// after all fixups are done on atom, if there are potential optimizations, do those
if ( (usedByHints.size() != 0) && (_options.outputKind() != Options::kObjectFile) && !_options.ignoreOptimizationHints() ) {
// fill in second part of usedByHints map, so we can see the target of fixups that might be optimized
}
}
}
-
-
-
+#endif // SUPPORT_ARCH_arm64
}
else
(const_cast<ld::Atom*>(atom))->setSymbolTableInclusion(ld::Atom::symbolTableIn);
}
+ else if ( sect->type() == ld::Section::typeTempAlias ) {
+ assert(_options.outputKind() == Options::kObjectFile);
+ _importedAtoms.push_back(atom);
+ continue;
+ }
if ( atom->symbolTableInclusion() == ld::Atom::symbolTableNotInFinalLinkedImages )
(const_cast<ld::Atom*>(atom))->setSymbolTableInclusion(ld::Atom::symbolTableIn);
}
else if ( (atom->contentType() == ld::Atom::typeCFI) && (strcmp(name, "FDE") == 0) ) {
for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) {
if ( (fit->kind == ld::Fixup::kindSetTargetAddress) && (fit->clusterSize == ld::Fixup::k1of4) ) {
- assert(fit->binding == ld::Fixup::bindingDirectlyBound);
- if ( fit->u.target->section().type() == ld::Section::typeCode) {
+ if ( (fit->binding == ld::Fixup::bindingDirectlyBound)
+ && (fit->u.target->section().type() == ld::Section::typeCode) ) {
strcpy(buffer, "FDE for: ");
strlcat(buffer, fit->u.target->name(), 4096);
name = buffer;
return path;
}
+static time_t fileModTime(const char* path) {
+ struct stat statBuffer;
+ if ( stat(path, &statBuffer) == 0 ) {
+ return statBuffer.st_mtime;
+ }
+ return 0;
+}
+
+
void OutputFile::synthesizeDebugNotes(ld::Internal& state)
{
// -S means don't synthesize debug map
// sort by file ordinal then atom ordinal
std::sort(atomsNeedingDebugNotes.begin(), atomsNeedingDebugNotes.end(), DebugNoteSorter());
+ // <rdar://problem/17689030> Add -add_ast_path option to linker which add N_AST stab entry to output
+ const std::vector<const char*>& astPaths = _options.astFilePaths();
+ for (std::vector<const char*>::const_iterator it=astPaths.begin(); it != astPaths.end(); it++) {
+ const char* path = *it;
+ // emit N_AST
+ ld::relocatable::File::Stab astStab;
+ astStab.atom = NULL;
+ astStab.type = N_AST;
+ astStab.other = 0;
+ astStab.desc = 0;
+ astStab.value = fileModTime(path);
+ astStab.string = path;
+ state.stabs.push_back(astStab);
+ }
+
// synthesize "debug notes" and add them to master stabs vector
const char* dirPath = NULL;
const char* filename = NULL;
const ld::File* atomFile = atom->file();
const ld::relocatable::File* atomObjFile = dynamic_cast<const ld::relocatable::File*>(atomFile);
//fprintf(stderr, "debug note for %s\n", atom->name());
- const char* newPath = atom->translationUnitSource();
- if ( newPath != NULL ) {
- const char* newDirPath;
- const char* newFilename;
- const char* lastSlash = strrchr(newPath, '/');
- if ( lastSlash == NULL )
- continue;
- newFilename = lastSlash+1;
- char* temp = strdup(newPath);
- newDirPath = temp;
- // gdb like directory SO's to end in '/', but dwarf DW_AT_comp_dir usually does not have trailing '/'
- temp[lastSlash-newPath+1] = '\0';
+ const char* newPath = atom->translationUnitSource();
+ if ( newPath != NULL ) {
+ const char* newDirPath;
+ const char* newFilename;
+ const char* lastSlash = strrchr(newPath, '/');
+ if ( lastSlash == NULL )
+ continue;
+ newFilename = lastSlash+1;
+ char* temp = strdup(newPath);
+ newDirPath = temp;
+ // gdb like directory SO's to end in '/', but dwarf DW_AT_comp_dir usually does not have trailing '/'
+ temp[lastSlash-newPath+1] = '\0';
// need SO's whenever the translation unit source file changes
- if ( (filename == NULL) || (strcmp(newFilename,filename) != 0) ) {
+ if ( (filename == NULL) || (strcmp(newFilename,filename) != 0) || (strcmp(newDirPath,dirPath) != 0)) {
if ( filename != NULL ) {
// translation unit change, emit ending SO
ld::relocatable::File::Stab endFileStab;
}
}
}
-
+
}
}
}
+static void userReadableSwiftVersion(uint8_t value, char versionString[64])
+{
+ switch (value) {
+ case 1:
+ strcpy(versionString, "1.0");
+ break;
+ case 2:
+ strcpy(versionString, "1.1");
+ break;
+ default:
+ sprintf(versionString, "unknown ABI version 0x%02X", value);
+ }
+}
+
void Resolver::doFile(const ld::File& file)
{
const ld::relocatable::File* objFile = dynamic_cast<const ld::relocatable::File*>(&file);
break;
}
+ // verify all files use same version of Swift language
+ if ( file.swiftVersion() != 0 ) {
+ if ( _internal.swiftVersion == 0 ) {
+ _internal.swiftVersion = file.swiftVersion();
+ }
+ else if ( file.swiftVersion() != _internal.swiftVersion ) {
+ char fileVersion[64];
+ char otherVersion[64];
+ userReadableSwiftVersion(file.swiftVersion(), fileVersion);
+ userReadableSwiftVersion(_internal.swiftVersion, otherVersion);
+ if ( file.swiftVersion() > _internal.swiftVersion ) {
+ throwf("%s compiled with newer version of Swift language (%s) than previous files (%s)",
+ file.path(), fileVersion, otherVersion);
+ }
+ else {
+ throwf("%s compiled with older version of Swift language (%s) than previous files (%s)",
+ file.path(), fileVersion, otherVersion);
+ }
+ }
+ }
+
// in -r mode, if any .o files have dwarf then add UUID to output .o file
if ( objFile->debugInfo() == ld::relocatable::File::kDebugInfoDwarf )
_internal.someObjectFileHasDwarf = true;
}
break;
}
+ if ( _options.checkDylibsAreAppExtensionSafe() && !dylibFile->appExtensionSafe() ) {
+ warning("linking against dylib not safe for use in application extensions: %s", file.path());
+ }
+ const char* depInstallName = dylibFile->installPath();
+ // <rdar://problem/17229513> embedded frameworks are only supported on iOS 8 and later
+ if ( (depInstallName != NULL) && (depInstallName[0] != '/') ) {
+ if ( (_options.iOSVersionMin() != iOSVersionUnset) && (_options.iOSVersionMin() < iOS_8_0) ) {
+ // <rdar://problem/17598404> only warn about linking against embedded dylib if it is built for iOS 8 or later
+ if ( dylibFile->iOSMinVersion() >= iOS_8_0 )
+ throwf("embedded dylibs/frameworks are only supported on iOS 8.0 and later (%s)", depInstallName);
+ }
+ }
+ if ( _options.sharedRegionEligible() ) {
+ assert(depInstallName != NULL);
+ if ( depInstallName[0] == '@' )
+ warning("invalid -install_name (%s) in dependent dylib (%s). Dylibs/frameworks which might go in dyld shared cache "
+ "cannot link with dylib that uses @rpath, @loaderpath, etc.", depInstallName, dylibFile->path());
+ if ( (strncmp(depInstallName, "/usr/lib/", 9) != 0) && (strncmp(depInstallName, "/System/Library/", 16) != 0) )
+ warning("invalid -install_name (%s) in dependent dylib (%s). Dylibs/frameworks which might go in dyld shared cache "
+ "cannot link with dylibs that won't be in the shared cache", depInstallName, dylibFile->path());
+ }
}
}
// remember if any atoms are proxies that require LTO
if ( atom.contentType() == ld::Atom::typeLTOtemporary )
_haveLLVMObjs = true;
-
+
+ // remember if any atoms are aliases
+ if ( atom.section().type() == ld::Section::typeTempAlias )
+ _haveAliases = true;
+
if ( _options.deadCodeStrip() ) {
// add to set of dead-strip-roots, all symbols that the compiler marks as don't strip
if ( atom.dontDeadStrip() )
}
}
- // Use linker options to resolve an remaining undefined symbols
+ // Use linker options to resolve any remaining undefined symbols
if ( !_internal.linkerOptionLibraries.empty() || !_internal.linkerOptionFrameworks.empty() ) {
std::vector<const char*> undefineNames;
_symbolTable.undefines(undefineNames);
_internal.entryPoint = this->entryPoint(true);
}
-
+void Resolver::syncAliases()
+{
+ if ( !_haveAliases || (_options.outputKind() == Options::kObjectFile) )
+ return;
+
+ // Set attributes of alias to match its found target
+ for (std::vector<const ld::Atom*>::iterator it = _atoms.begin(); it != _atoms.end(); ++it) {
+ const ld::Atom* atom = *it;
+ if ( atom->section().type() == ld::Section::typeTempAlias ) {
+ assert(atom->fixupsBegin() != atom->fixupsEnd());
+ for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) {
+ const ld::Atom* target;
+ ld::Atom::Scope scope;
+ assert(fit->kind == ld::Fixup::kindNoneFollowOn);
+ switch ( fit->binding ) {
+ case ld::Fixup::bindingByNameUnbound:
+ break;
+ case ld::Fixup::bindingsIndirectlyBound:
+ target = _internal.indirectBindingTable[fit->u.bindingIndex];
+ assert(target != NULL);
+ scope = atom->scope();
+ (const_cast<Atom*>(atom))->setAttributesFromAtom(*target);
+ // alias has same attributes as target, except for scope
+ (const_cast<Atom*>(atom))->setScope(scope);
+ break;
+ default:
+ assert(0 && "internal error: unexpected alias binding");
+ }
+ }
+ }
+ }
+}
void Resolver::removeCoalescedAwayAtoms()
{
optOpt.linkerDeadStripping = _options.deadCodeStrip();
optOpt.needsUnwindInfoSection = _options.needsUnwindInfoSection();
optOpt.keepDwarfUnwind = _options.keepDwarfUnwind();
+ optOpt.verboseOptimizationHints = _options.verboseOptimizationHints();
optOpt.arch = _options.architecture();
optOpt.mcpu = _options.mcpuLTO();
optOpt.llvmOptions = &_options.llvmOptions();
+ optOpt.initialUndefines = &_options.initialUndefines();
std::vector<const ld::Atom*> newAtoms;
std::vector<const char*> additionalUndefines;
}
// <rdar://problem/14609792> add any auto-link libraries requested by LTO output to dylibs to search
- _inputFiles.addLinkerOptionLibraries(_internal);
+ _inputFiles.addLinkerOptionLibraries(_internal, *this);
_inputFiles.createIndirectDylibs();
// resolve new undefines (e.g calls to _malloc and _memcpy that llvm compiler conjures up)
this->deadStripOptimize();
this->checkUndefines();
this->checkDylibSymbolCollisions();
+ this->syncAliases();
this->removeCoalescedAwayAtoms();
this->fillInEntryPoint();
this->linkTimeOptimize();
_symbolTable(opts, state.indirectBindingTable),
_haveLLVMObjs(false),
_completedInitialObjectFiles(false),
- _ltoCodeGenFinished(false) {}
+ _ltoCodeGenFinished(false),
+ _haveAliases(false) {}
virtual void doAtom(const ld::Atom&);
void fillInInternalState();
void fillInHelpersInInternalState();
void removeCoalescedAwayAtoms();
+ void syncAliases();
void fillInEntryPoint();
void linkTimeOptimize();
void convertReferencesToIndirect(const ld::Atom& atom);
bool _haveLLVMObjs;
bool _completedInitialObjectFiles;
bool _ltoCodeGenFinished;
+ bool _haveAliases;
};
void SymbolTable::removeDeadAtoms()
{
+ // remove dead atoms from: _byNameTable, _byNameReverseTable, and _indirectBindingTable
+ std::vector<const char*> namesToRemove;
for (NameToSlot::iterator it=_byNameTable.begin(); it != _byNameTable.end(); ++it) {
IndirectBindingSlot slot = it->second;
const ld::Atom* atom = _indirectBindingTable[slot];
if ( !atom->live() && !atom->dontDeadStrip() ) {
//fprintf(stderr, "removing from symbolTable[%u] %s\n", slot, atom->name());
_indirectBindingTable[slot] = NULL;
+ // <rdar://problem/16025786> need to completely remove dead atoms from symbol table
+ _byNameReverseTable.erase(slot);
+ // can't remove while iterating, do it after iteration
+ namesToRemove.push_back(it->first);
}
}
}
+ for (std::vector<const char*>::iterator it = namesToRemove.begin(); it != namesToRemove.end(); ++it) {
+ _byNameTable.erase(*it);
+ }
+
+ // remove dead atoms from _nonLazyPointerTable
+ for (ReferencesToSlot::iterator it=_nonLazyPointerTable.begin(); it != _nonLazyPointerTable.end(); ) {
+ const ld::Atom* atom = it->first;
+ assert(atom != NULL);
+ if ( !atom->live() && !atom->dontDeadStrip() )
+ it = _nonLazyPointerTable.erase(it);
+ else
+ ++it;
+ }
+
+ // remove dead atoms from _cstringTable
+ for (CStringToSlot::iterator it=_cstringTable.begin(); it != _cstringTable.end(); ) {
+ const ld::Atom* atom = it->first;
+ assert(atom != NULL);
+ if ( !atom->live() && !atom->dontDeadStrip() )
+ it = _cstringTable.erase(it);
+ else
+ ++it;
+ }
+
+ // remove dead atoms from _utf16Table
+ for (UTF16StringToSlot::iterator it=_utf16Table.begin(); it != _utf16Table.end(); ) {
+ const ld::Atom* atom = it->first;
+ assert(atom != NULL);
+ if ( !atom->live() && !atom->dontDeadStrip() )
+ it = _utf16Table.erase(it);
+ else
+ ++it;
+ }
+
+ // remove dead atoms from _cfStringTable
+ for (ReferencesToSlot::iterator it=_cfStringTable.begin(); it != _cfStringTable.end(); ) {
+ const ld::Atom* atom = it->first;
+ assert(atom != NULL);
+ if ( !atom->live() && !atom->dontDeadStrip() )
+ it = _cfStringTable.erase(it);
+ else
+ ++it;
+ }
+
+ // remove dead atoms from _literal4Table
+ for (ContentToSlot::iterator it=_literal4Table.begin(); it != _literal4Table.end(); ) {
+ const ld::Atom* atom = it->first;
+ assert(atom != NULL);
+ if ( !atom->live() && !atom->dontDeadStrip() )
+ it = _literal4Table.erase(it);
+ else
+ ++it;
+ }
+
+ // remove dead atoms from _literal8Table
+ for (ContentToSlot::iterator it=_literal8Table.begin(); it != _literal8Table.end(); ) {
+ const ld::Atom* atom = it->first;
+ assert(atom != NULL);
+ if ( !atom->live() && !atom->dontDeadStrip() )
+ it = _literal8Table.erase(it);
+ else
+ ++it;
+ }
+
+ // remove dead atoms from _literal16Table
+ for (ContentToSlot::iterator it=_literal16Table.begin(); it != _literal16Table.end(); ) {
+ const ld::Atom* atom = it->first;
+ assert(atom != NULL);
+ if ( !atom->live() && !atom->dontDeadStrip() )
+ it = _literal16Table.erase(it);
+ else
+ ++it;
+ }
}
+
// find existing or create new slot
SymbolTable::IndirectBindingSlot SymbolTable::findSlotForContent(const ld::Atom* atom, const ld::Atom** existingAtom)
{
InternalState(const Options& opts) : _options(opts), _atomsOrderedInSections(false) { }
virtual ld::Internal::FinalSection* addAtom(const ld::Atom& atom);
virtual ld::Internal::FinalSection* getFinalSection(const ld::Section&);
+ ld::Internal::FinalSection* getFinalSection(const char* seg, const char* sect, ld::Section::Type type);
uint64_t assignFileOffsets();
void setSectionSizesAndAlignments();
class FinalSection : public ld::Internal::FinalSection
{
public:
- FinalSection(const ld::Section& sect, uint32_t sectionsSeen, bool objFile);
+ FinalSection(const ld::Section& sect, uint32_t sectionsSeen, const Options&);
static int sectionComparer(const void* l, const void* r);
static const ld::Section& outputSection(const ld::Section& sect, bool mergeZeroFill);
static const ld::Section& objectOutputSection(const ld::Section& sect, const Options&);
private:
friend class InternalState;
- static uint32_t sectionOrder(const ld::Section& sect, uint32_t sectionsSeen);
- static uint32_t segmentOrder(const ld::Section& sect, bool objFile);
+ static uint32_t sectionOrder(const ld::Section& sect, uint32_t sectionsSeen, const Options& options);
+ static uint32_t segmentOrder(const ld::Section& sect, const Options& options);
uint32_t _segmentOrder;
uint32_t _sectionOrder;
}
-InternalState::FinalSection::FinalSection(const ld::Section& sect, uint32_t sectionsSeen, bool objFile)
+InternalState::FinalSection::FinalSection(const ld::Section& sect, uint32_t sectionsSeen, const Options& opts)
: ld::Internal::FinalSection(sect),
- _segmentOrder(segmentOrder(sect, objFile)),
- _sectionOrder(sectionOrder(sect, sectionsSeen))
+ _segmentOrder(segmentOrder(sect, opts)),
+ _sectionOrder(sectionOrder(sect, sectionsSeen, opts))
{
//fprintf(stderr, "FinalSection(%s, %s) _segmentOrder=%d, _sectionOrder=%d\n",
// this->segmentName(), this->sectionName(), _segmentOrder, _sectionOrder);
case ld::Section::typeLiteral4:
case ld::Section::typeLiteral8:
case ld::Section::typeLiteral16:
- return _s_TEXT_const;
+ if ( strcmp(sect.segmentName(), "__TEXT") == 0 )
+ return _s_TEXT_const;
+ break;
case ld::Section::typeUnclassified:
if ( strcmp(sect.segmentName(), "__DATA") == 0 ) {
if ( strcmp(sect.sectionName(), "__datacoal_nt") == 0 )
}
break;
case ld::Section::typeTentativeDefs:
- if ( mergeZeroFill )
- return _s_DATA_zerofill;
- else
- return _s_DATA_common;
+ if ( (strcmp(sect.segmentName(), "__DATA") == 0) && (strcmp(sect.sectionName(), "__comm/tent") == 0) ) {
+ if ( mergeZeroFill )
+ return _s_DATA_zerofill;
+ else
+ return _s_DATA_common;
+ }
break;
// FIX ME: more
default:
const ld::Section& InternalState::FinalSection::objectOutputSection(const ld::Section& sect, const Options& options)
{
- const std::vector<Options::SectionRename>& renames = options.sectionRenames();
- for ( std::vector<Options::SectionRename>::const_iterator it=renames.begin(); it != renames.end(); ++it) {
- if ( (strcmp(sect.sectionName(), it->fromSection) == 0) && (strcmp(sect.segmentName(), it->fromSegment) == 0) ) {
- ld::Section* s = new ld::Section(it->toSegment, it->toSection, sect.type());
- return *s;
- }
- }
-
-
// in -r mode the only section that ever changes is __tenative -> __common with -d option
if ( (sect.type() == ld::Section::typeTentativeDefs) && options.makeTentativeDefinitionsReal())
return _s_DATA_common;
return sect;
}
-uint32_t InternalState::FinalSection::segmentOrder(const ld::Section& sect, bool objFile)
+uint32_t InternalState::FinalSection::segmentOrder(const ld::Section& sect, const Options& options)
{
- if ( strcmp(sect.segmentName(), "__PAGEZERO") == 0 )
- return 0;
- if ( strcmp(sect.segmentName(), "__HEADER") == 0 ) // only used with -preload
- return 0;
- if ( strcmp(sect.segmentName(), "__TEXT") == 0 )
- return 1;
- // in -r mode, want __DATA last so zerofill sections are at end
- if ( strcmp(sect.segmentName(), "__DATA") == 0 )
- return (objFile ? 5 : 2);
- if ( strcmp(sect.segmentName(), "__OBJC") == 0 )
- return 3;
- if ( strcmp(sect.segmentName(), "__IMPORT") == 0 )
- return 4;
-
- // layout non-standard segments in order seen (+10 to shift beyond standard segments)
+ if ( options.outputKind() == Options::kPreload ) {
+ if ( strcmp(sect.segmentName(), "__HEADER") == 0 )
+ return 0;
+ const std::vector<const char*>& order = options.segmentOrder();
+ for (size_t i=0; i != order.size(); ++i) {
+ if ( strcmp(sect.segmentName(), order[i]) == 0 )
+ return i+1;
+ }
+ if ( strcmp(sect.segmentName(), "__TEXT") == 0 )
+ return order.size()+1;
+ if ( strcmp(sect.segmentName(), "__DATA") == 0 )
+ return order.size()+2;
+ }
+ else {
+ if ( strcmp(sect.segmentName(), "__PAGEZERO") == 0 )
+ return 0;
+ if ( strcmp(sect.segmentName(), "__TEXT") == 0 )
+ return 1;
+ // in -r mode, want __DATA last so zerofill sections are at end
+ if ( strcmp(sect.segmentName(), "__DATA") == 0 )
+ return (options.outputKind() == Options::kObjectFile) ? 5 : 2;
+ if ( strcmp(sect.segmentName(), "__OBJC") == 0 )
+ return 3;
+ if ( strcmp(sect.segmentName(), "__IMPORT") == 0 )
+ return 4;
+ }
+ // layout non-standard segments in order seen (+100 to shift beyond standard segments)
for (uint32_t i=0; i < _s_segmentsSeen.size(); ++i) {
if ( strcmp(_s_segmentsSeen[i], sect.segmentName()) == 0 )
- return i+10;
+ return i+100;
}
_s_segmentsSeen.push_back(sect.segmentName());
- return _s_segmentsSeen.size()-1+10;
+ return _s_segmentsSeen.size()-1+100;
}
-uint32_t InternalState::FinalSection::sectionOrder(const ld::Section& sect, uint32_t sectionsSeen)
+uint32_t InternalState::FinalSection::sectionOrder(const ld::Section& sect, uint32_t sectionsSeen, const Options& options)
{
if ( sect.type() == ld::Section::typeFirstSection )
return 0;
return 1;
if ( sect.type() == ld::Section::typeLastSection )
return INT_MAX;
+ const std::vector<const char*>* sectionList = options.sectionOrder(sect.segmentName());
+ if ( (options.outputKind() == Options::kPreload) && (sectionList != NULL) ) {
+ uint32_t count = 10;
+ for (std::vector<const char*>::const_iterator it=sectionList->begin(); it != sectionList->end(); ++it, ++count) {
+ if ( strcmp(*it, sect.sectionName()) == 0 )
+ return count;
+ }
+ }
if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) {
switch ( sect.type() ) {
case ld::Section::typeCode:
// <rdar://problem/14348664> __DATA,__const section should be near __mod_init_func not __data
if ( strcmp(sect.sectionName(), "__const") == 0 )
return 14;
+ // <rdar://problem/17125893> Linker should put __cfstring near __const
+ if ( strcmp(sect.sectionName(), "__cfstring") == 0 )
+ return 15;
// <rdar://problem/7435296> Reorder sections to reduce page faults in object files
else if ( strcmp(sect.sectionName(), "__objc_classlist") == 0 )
return 20;
return 21;
else if ( strcmp(sect.sectionName(), "__objc_catlist") == 0 )
return 22;
- else if ( strcmp(sect.sectionName(), "__objc_protolist") == 0 )
+ else if ( strcmp(sect.sectionName(), "__objc_nlcatlist") == 0 )
return 23;
- else if ( strcmp(sect.sectionName(), "__objc_imageinfo") == 0 )
+ else if ( strcmp(sect.sectionName(), "__objc_protolist") == 0 )
return 24;
- else if ( strcmp(sect.sectionName(), "__objc_const") == 0 )
+ else if ( strcmp(sect.sectionName(), "__objc_imageinfo") == 0 )
return 25;
- else if ( strcmp(sect.sectionName(), "__objc_selrefs") == 0 )
+ else if ( strcmp(sect.sectionName(), "__objc_const") == 0 )
return 26;
- else if ( strcmp(sect.sectionName(), "__objc_msgrefs") == 0 )
+ else if ( strcmp(sect.sectionName(), "__objc_selrefs") == 0 )
return 27;
- else if ( strcmp(sect.sectionName(), "__objc_protorefs") == 0 )
+ else if ( strcmp(sect.sectionName(), "__objc_msgrefs") == 0 )
return 28;
- else if ( strcmp(sect.sectionName(), "__objc_classrefs") == 0 )
+ else if ( strcmp(sect.sectionName(), "__objc_protorefs") == 0 )
return 29;
- else if ( strcmp(sect.sectionName(), "__objc_superrefs") == 0 )
+ else if ( strcmp(sect.sectionName(), "__objc_classrefs") == 0 )
return 30;
- else if ( strcmp(sect.sectionName(), "__objc_data") == 0 )
+ else if ( strcmp(sect.sectionName(), "__objc_superrefs") == 0 )
return 31;
+ else if ( strcmp(sect.sectionName(), "__objc_ivar") == 0 )
+ return 32;
+ else if ( strcmp(sect.sectionName(), "__objc_data") == 0 )
+ return 33;
else
return sectionsSeen+40;
}
ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom)
{
- ld::Internal::FinalSection* fs = this->getFinalSection(atom.section());
+ ld::Internal::FinalSection* fs = NULL;
+ const char* sectName = atom.section().sectionName();
+ ld::Section::Type sectType = atom.section().type();
+ const ld::File* f = atom.file();
+ const char* path = (f != NULL) ? f->path() : NULL;
+ if ( atom.section().type() == ld::Section::typeTentativeDefs ) {
+ // tentative defintions don't have a real section name yet
+ sectType = ld::Section::typeZeroFill;
+ if ( _options.mergeZeroFill() )
+ sectName = FinalSection::_s_DATA_zerofill.sectionName();
+ else
+ sectName = FinalSection::_s_DATA_common.sectionName();
+ }
+ // Support for -move_to_r._segment
+ if ( atom.symbolTableInclusion() == ld::Atom::symbolTableIn ) {
+ const char* dstSeg;
+ //fprintf(stderr, "%s\n", atom.name());
+ bool wildCardMatch;
+ if ( _options.moveRwSymbol(atom.name(), path, dstSeg, wildCardMatch) ) {
+ if ( (sectType != ld::Section::typeZeroFill)
+ && (sectType != ld::Section::typeUnclassified)
+ && (sectType != ld::Section::typeTentativeDefs) ) {
+ if ( !wildCardMatch )
+ warning("cannot move symbol '%s' from file %s to segment '%s' because symbol is not data (is %d)", atom.name(), path, dstSeg, sectType);
+ }
+ else {
+ if ( _options.traceSymbolLayout() )
+ printf("symbol '%s', -move_to_rw_segment mapped it to %s/%s\n", atom.name(), dstSeg, sectName);
+ fs = this->getFinalSection(dstSeg, sectName, sectType);
+ }
+ }
+ if ( (fs == NULL) && _options.moveRoSymbol(atom.name(), path, dstSeg, wildCardMatch) ) {
+ if ( atom.section().type() != ld::Section::typeCode ) {
+ if ( !wildCardMatch )
+ warning("cannot move symbol '%s' from file %s to segment '%s' because symbol is not code (is %d)", atom.name(), path, dstSeg, sectType);
+ }
+ else {
+ if ( _options.traceSymbolLayout() )
+ printf("symbol '%s', -move_to_ro_segment mapped it to %s/%s\n", atom.name(), dstSeg, sectName);
+ fs = this->getFinalSection(dstSeg, sectName, ld::Section::typeCode);
+ }
+ }
+ }
+ // support for -rename_section and -rename_segment
+ if ( fs == NULL ) {
+ const std::vector<Options::SectionRename>& sectRenames = _options.sectionRenames();
+ const std::vector<Options::SegmentRename>& segRenames = _options.segmentRenames();
+ for ( std::vector<Options::SectionRename>::const_iterator it=sectRenames.begin(); it != sectRenames.end(); ++it) {
+ if ( (strcmp(sectName, it->fromSection) == 0) && (strcmp(atom.section().segmentName(), it->fromSegment) == 0) ) {
+ if ( _options.traceSymbolLayout() )
+ printf("symbol '%s', -rename_section mapped it to %s/%s\n", atom.name(), it->toSegment, it->toSection);
+ fs = this->getFinalSection(it->toSegment, it->toSection, sectType);
+ }
+ }
+ if ( fs == NULL ) {
+ for ( std::vector<Options::SegmentRename>::const_iterator it=segRenames.begin(); it != segRenames.end(); ++it) {
+ if ( strcmp(atom.section().segmentName(), it->fromSegment) == 0 ) {
+ if ( _options.traceSymbolLayout() )
+ printf("symbol '%s', -rename_segment mapped it to %s/%s\n", atom.name(), it->toSegment, sectName);
+ fs = this->getFinalSection(it->toSegment, sectName, sectType);
+ }
+ }
+ }
+ }
+
+
+ // if no override, use default location
+ if ( fs == NULL ) {
+ fs = this->getFinalSection(atom.section());
+ if ( _options.traceSymbolLayout() && (atom.symbolTableInclusion() == ld::Atom::symbolTableIn) )
+ printf("symbol '%s', use default mapping to %s/%s\n", atom.name(), fs->segmentName(), fs->sectionName());
+ }
+
//fprintf(stderr, "InternalState::doAtom(%p), name=%s, sect=%s, finalsect=%p\n", &atom, atom.name(), atom.section().sectionName(), fs);
#ifndef NDEBUG
validateFixups(atom);
return fs;
}
+
+
+ld::Internal::FinalSection* InternalState::getFinalSection(const char* seg, const char* sect, ld::Section::Type type)
+{
+ for (std::vector<ld::Internal::FinalSection*>::iterator it=sections.begin(); it != sections.end(); ++it) {
+ if ( (strcmp((*it)->segmentName(),seg) == 0) && (strcmp((*it)->sectionName(),sect) == 0) )
+ return *it;
+ }
+ return this->getFinalSection(*new ld::Section(seg, sect, type, false));
+}
+
ld::Internal::FinalSection* InternalState::getFinalSection(const ld::Section& inputSection)
{
const ld::Section* baseForFinalSection = &inputSection;
}
// otherwise, create a new final section
- bool objFile = false;
switch ( _options.outputKind() ) {
case Options::kStaticExecutable:
case Options::kDynamicExecutable:
//fprintf(stderr, "_sectionInToFinalMap[%p] = %p\n", &inputSection, pos->second);
return pos->second;
}
- objFile = true;
break;
}
InternalState::FinalSection* result = new InternalState::FinalSection(*baseForFinalSection,
- _sectionInToFinalMap.size(), objFile);
+ _sectionInToFinalMap.size(), _options);
_sectionInToFinalMap[baseForFinalSection] = result;
- //fprintf(stderr, "_sectionInToFinalMap[%p] = %p\n", baseForFinalSection, result);
+ //fprintf(stderr, "_sectionInToFinalMap[%p(%s)] = %p\n", baseForFinalSection, baseForFinalSection->sectionName(), result);
sections.push_back(result);
return result;
}
ld::Internal::FinalSection* otherSect = *sit;
if ( ! _options.hasCustomSegmentAddress(otherSect->segmentName()) )
continue;
+ if ( otherSect->size == 0 )
+ continue;
+ if ( sect->size == 0 )
+ continue;
if ( sect->address > otherSect->address ) {
if ( (otherSect->address+otherSect->size) > sect->address ) {
overlappingFixedSection = otherSect;
fprintf(stderr, "Section layout:\n");
for (std::vector<ld::Internal::FinalSection*>::iterator it = sections.begin(); it != sections.end(); ++it) {
ld::Internal::FinalSection* sect = *it;
- if ( sect->isSectionHidden() )
- continue;
+ //if ( sect->isSectionHidden() )
+ // continue;
fprintf(stderr, " address:0x%08llX, alignment:2^%d, size:0x%08llX, padBytes:%d, section:%s/%s\n",
sect->address, sect->alignment, sect->size, sect->alignmentPaddingBytes,
sect->segmentName(), sect->sectionName());
virtual bool forEachAtom(AtomHandler&) const = 0;
virtual bool justInTimeforEachAtom(const char* name, AtomHandler&) const = 0;
virtual ObjcConstraint objCConstraint() const { return objcConstraintNone; }
+ virtual uint8_t swiftVersion() const { return 0; }
virtual uint32_t cpuSubType() const { return 0; }
virtual uint32_t subFileCount() const { return 1; }
bool fileExists() const { return _modTime != 0; }
mac10_9=0x000A0900, mac10_Future=0x10000000 };
enum IOSVersionMin { iOSVersionUnset=0, iOS_2_0=0x00020000, iOS_3_1=0x00030100,
iOS_4_2=0x00040200, iOS_4_3=0x00040300, iOS_5_0=0x00050000,
- iOS_6_0=0x00060000, iOS_7_0=0x00070000,
+ iOS_6_0=0x00060000, iOS_7_0=0x00070000, iOS_8_0=0x00080000,
iOS_Future=0x10000000};
namespace relocatable {
virtual bool allSymbolsAreWeakImported() const = 0;
virtual const void* codeSignatureDR() const = 0;
virtual bool installPathVersionSpecific() const { return false; }
+ virtual bool appExtensionSafe() const = 0;
+ virtual MacVersionMin macMinVersion() const { return macVersionUnset; }
+ virtual IOSVersionMin iOSMinVersion() const { return iOSVersionUnset; }
+
protected:
const char* _dylibInstallPath;
uint32_t _dylibTimeStamp;
{
public:
enum Type { typeUnclassified, typeCode, typePageZero, typeImportProxies, typeLinkEdit, typeMachHeader, typeStack,
- typeLiteral4, typeLiteral8, typeLiteral16, typeConstants, typeTempLTO,
+ typeLiteral4, typeLiteral8, typeLiteral16, typeConstants, typeTempLTO, typeTempAlias,
typeCString, typeNonStdCString, typeCStringPointer, typeUTF16Strings, typeCFString, typeObjC1Classes,
typeCFI, typeLSDA, typeDtraceDOF, typeUnwindInfo, typeObjCClassRefs, typeObjC2CategoryList,
typeZeroFill, typeTentativeDefs, typeLazyPointer, typeStub, typeNonLazyPointer, typeDyldInfo,
contentAddendOnly(false), contentDetlaToAddendOnly(false), contentIgnoresAddend(false)
{ u.addend = addend; }
-#if SUPPORT_ARCH_arm64
Fixup(Kind k, uint32_t lohKind, uint32_t off1, uint32_t off2) :
offsetInAtom(off1), kind(k), clusterSize(k1of1),
weakImport(false), binding(Fixup::bindingNone), contentAddendOnly(false),
extra.info.delta2 = (off2 - off1) >> 2;
u.addend = extra.addend;
}
-#endif
bool firstInCluster() const {
return false;
}
-#if SUPPORT_ARCH_arm64
union LOH_arm64 {
uint64_t addend;
struct {
delta4 : 14;
} info;
};
-#endif
};
virtual LineInfo::iterator beginLineInfo() const { return NULL; }
virtual LineInfo::iterator endLineInfo() const { return NULL; }
-protected:
- enum AddressMode { modeSectionOffset, modeFinalAddress };
-
void setAttributesFromAtom(const Atom& a) {
_section = a._section;
_alignmentModulus = a._alignmentModulus;
_weakImportState = a._weakImportState;
}
+protected:
+ enum AddressMode { modeSectionOffset, modeFinalAddress };
+
const Section * _section;
uint64_t _address;
uint16_t _alignmentModulus;
lazyBindingHelper(NULL), compressedFastBinderProxy(NULL),
objcObjectConstraint(ld::File::objcConstraintNone),
objcDylibConstraint(ld::File::objcConstraintNone),
- cpuSubType(0),
+ swiftVersion(0), cpuSubType(0),
allObjectFilesScatterable(true),
someObjectFileHasDwarf(false), usingHugeSections(false),
hasThreadLocalVariableDefinitions(false),
const Atom* compressedFastBinderProxy;
ld::File::ObjcConstraint objcObjectConstraint;
ld::File::ObjcConstraint objcDylibConstraint;
+ uint8_t swiftVersion;
uint32_t cpuSubType;
bool allObjectFilesScatterable;
bool someObjectFileHasDwarf;
#define CFI_INVALID_ADDRESS ((pint_t)(-1))
+
namespace libunwind {
///
return encoding;
}
+
} // namespace libunwind
/// Information about a frame layout and registers saved determined
/// by "running" the dwarf FDE "instructions"
///
- enum { kMaxRegisterNumber = 120 };
+ enum { kMaxRegisterNumber = 300 };
enum RegisterSavedWhere { kRegisterUnused, kRegisterInCFA, kRegisterOffsetFromCFA,
kRegisterInRegister, kRegisterAtExpression, kRegisterIsExpression } ;
struct RegisterLocation {
}
+///
+/// Registers_arm holds the register state of a thread in a 32-bit arm process.
+///
+class Registers_arm {
+public:
+ Registers_arm();
+
+ bool validRegister(int num) const;
+ uint32_t getRegister(int num) const;
+ void setRegister(int num, uint32_t value);
+ bool validFloatRegister(int num) const;
+ unw_fpreg_t getFloatRegister(int num) const;
+ void setFloatRegister(int num, unw_fpreg_t value);
+ bool validVectorRegister(int num) const;
+ v128 getVectorRegister(int num) const;
+ void setVectorRegister(int num, v128 value);
+ const char *getRegisterName(int num);
+ void jumpto();
+
+ uint32_t getSP() const { return _registers[13]; }
+ void setSP(uint32_t value) { _registers[13] = value; }
+ uint32_t getIP() const { return _registers[15]; }
+ void setIP(uint32_t value) { _registers[15] = value; }
+
+private:
+ uint32_t _registers[16];
+};
+
+inline Registers_arm::Registers_arm() {
+ bzero(&_registers, sizeof(_registers));
+}
+
} // namespace libunwind
// for adding references to symbols outside bitcode file
void addReference(const char* nm)
{ _undefs.push_back(ld::Fixup(0, ld::Fixup::k1of1,
- ld::Fixup::kindNone, false, nm)); }
+ ld::Fixup::kindNone, false, strdup(nm))); }
private:
ld::File& _file;
virtual LinkerOptionsList* linkerOptions() const { return NULL; }
+ void release();
lto_module_t module() { return _module; }
class InternalAtom& internalAtom() { return _internalAtom; }
void setDebugInfo(ld::relocatable::File::DebugInfoKind k,
virtual void doAtom(const class ld::Atom&);
virtual void doFile(const class ld::File&) { }
+
const OptimizeOptions& _options;
std::vector<const char*>& _additionalUndefines;
std::vector<const ld::Atom*>& _newAtoms;
objOpts.forceDwarfConversion = false;
objOpts.neverConvertDwarf = false;
objOpts.verboseOptimizationHints = options.verboseOptimizationHints;
+
objOpts.subType = 0;
// mach-o parsing is done in-memory, but need path for debug notes
const bool log = false;
// create llvm module
+#if LTO_API_VERSION >= 9
+ _module = ::lto_module_create_from_memory_with_path(content, contentLength, pth);
+ if ( _module == NULL )
+#endif
_module = ::lto_module_create_from_memory(content, contentLength);
if ( _module == NULL )
throwf("could not parse object file %s: '%s', using libLTO version '%s'", pth, ::lto_get_error_message(), ::lto_get_version());
}
File::~File()
+{
+ this->release();
+}
+
+void File::release()
{
if ( _module != NULL )
::lto_module_dispose(_module);
+ _module = NULL;
}
bool File::forEachAtom(ld::File::AtomHandler& handler) const
ld::Atom::Alignment a, bool ah)
: ld::Atom(f._section, d, c, s, ld::Atom::typeLTOtemporary,
ld::Atom::symbolTableIn, false, false, false, a),
- _file(f), _name(nm), _compiledAtom(NULL)
+ _file(f), _name(strdup(nm)), _compiledAtom(NULL)
{
if ( ah )
this->setAutoHide();
void Parser::ltoDiagnosticHandler(lto_codegen_diagnostic_severity_t severity, const char* message, void*)
{
switch ( severity ) {
+#if LTO_API_VERSION >= 10
+ case LTO_DS_REMARK:
+ fprintf(stderr, "ld: LTO remark: %s\n", message);
+ break;
+#endif
case LTO_DS_NOTE:
case LTO_DS_WARNING:
warning("%s", message);
if ( logBitcodeFiles ) fprintf(stderr, "lto_codegen_add_module(%s)\n", f->path());
if ( ::lto_codegen_add_module(generator, f->module()) )
throwf("lto: could not merge in %s because '%s', using libLTO version '%s'", f->path(), ::lto_get_error_message(), ::lto_get_version());
+ // <rdar://problem/15471128> linker should release module as soon as possible
+ f->release();
lastOrdinal = f->ordinal();
}
}
}
}
- else {
+ else if ( atom->scope() >= ld::Atom::scopeLinkageUnit ) {
llvmAtoms[atom->name()] = (Atom*)atom;
}
}
if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because referenced by a mach-o atom\n", name);
::lto_codegen_add_must_preserve_symbol(generator, name);
}
+ else if ( options.relocatable && hasNonllvmAtoms ) {
+ // <rdar://problem/14334895> ld -r mode but merging in some mach-o files, so need to keep libLTO from optimizing away anything
+ if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because -r mode disable LTO dead stripping\n", name);
+ ::lto_codegen_add_must_preserve_symbol(generator, name);
+ }
}
+ // <rdar://problem/16165191> tell code generator to preserve initial undefines
+ for( std::vector<const char*>::const_iterator it=options.initialUndefines->begin(); it != options.initialUndefines->end(); ++it) {
+ if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because it is an initial undefine\n", *it);
+ ::lto_codegen_add_must_preserve_symbol(generator, *it);
+ }
+
// special case running ld -r on all bitcode files to produce another bitcode file (instead of mach-o)
if ( options.relocatable && !hasNonllvmAtoms ) {
if ( ! ::lto_codegen_write_merged_modules(generator, options.outputFilePath) ) {
fprintf(stderr, "llvmAtoms:\n");
for (CStringToAtom::iterator it = llvmAtoms.begin(); it != llvmAtoms.end(); ++it) {
const char* name = it->first;
- //Atom* atom = it->second;
- fprintf(stderr, "\t%s\n", name);
+ Atom* atom = it->second;
+ fprintf(stderr, "\t%p\t%s\n", atom, name);
}
fprintf(stderr, "deadllvmAtoms:\n");
for (CStringToAtom::iterator it = deadllvmAtoms.begin(); it != deadllvmAtoms.end(); ++it) {
const char* name = it->first;
- //Atom* atom = it->second;
- fprintf(stderr, "\t%s\n", name);
+ Atom* atom = it->second;
+ fprintf(stderr, "\t%p\t%s\n", atom, name);
}
}
AtomSyncer syncer(additionalUndefines, newAtoms, llvmAtoms, deadllvmAtoms, options);
void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom)
{
+ static const bool log = false;
static const ld::Atom* lastProxiedAtom = NULL;
static const ld::File* lastProxiedFile = NULL;
// update proxy atoms to point to real atoms and find new atoms
const char* name = machoAtom.name();
- if ( machoAtom.scope() >= ld::Atom::scopeLinkageUnit ) {
- CStringToAtom::iterator pos = _llvmAtoms.find(name);
- if ( pos != _llvmAtoms.end() ) {
- // turn Atom into a proxy for this mach-o atom
- pos->second->setCompiledAtom(machoAtom);
- lastProxiedAtom = &machoAtom;
- lastProxiedFile = pos->second->file();
- }
- else {
- // an atom of this name was not in the allAtoms list the linker gave us
- if ( _deadllvmAtoms.find(name) != _deadllvmAtoms.end() ) {
- // this corresponding to an atom that the linker coalesced away or marked not-live
- if ( _options.linkerDeadStripping ) {
- // llvm seems to want this atom and -dead_strip is enabled, so it will be deleted if not needed, so add back
- Atom* llvmAtom = _deadllvmAtoms[name];
- llvmAtom->setCompiledAtom(machoAtom);
- _newAtoms.push_back(&machoAtom);
- }
- else {
- // Don't pass it back as a new atom
- }
- }
- else
- {
- // this is something new that lto conjured up, tell ld its new
+ CStringToAtom::iterator pos = _llvmAtoms.find(name);
+ if ( pos != _llvmAtoms.end() ) {
+ // turn Atom into a proxy for this mach-o atom
+ pos->second->setCompiledAtom(machoAtom);
+ lastProxiedAtom = &machoAtom;
+ lastProxiedFile = pos->second->file();
+ if (log) fprintf(stderr, "AtomSyncer, mach-o atom %p synced to lto atom %p (name=%s)\n", &machoAtom, pos->second, machoAtom.name());
+ }
+ else {
+ // an atom of this name was not in the allAtoms list the linker gave us
+ if ( _deadllvmAtoms.find(name) != _deadllvmAtoms.end() ) {
+ // this corresponding to an atom that the linker coalesced away or marked not-live
+ if ( _options.linkerDeadStripping ) {
+ // llvm seems to want this atom and -dead_strip is enabled, so it will be deleted if not needed, so add back
+ Atom* llvmAtom = _deadllvmAtoms[name];
+ llvmAtom->setCompiledAtom(machoAtom);
_newAtoms.push_back(&machoAtom);
+ if (log) fprintf(stderr, "AtomSyncer, mach-o atom %p matches dead lto atom %p but adding back (name=%s)\n", &machoAtom, llvmAtom, machoAtom.name());
}
+ else {
+ // Don't pass it back as a new atom
+ if (log) fprintf(stderr, "AtomSyncer, mach-o atom %p matches dead lto atom %p (name=%s)\n", &machoAtom, _deadllvmAtoms[name], machoAtom.name());
+ }
}
- }
- else {
- // ld only knew about non-static atoms, so this one must be new
- _newAtoms.push_back(&machoAtom);
- // <rdar://problem/15469363> if new static atom in same section as previous non-static atom, assign to same file as previous
- if ( (lastProxiedAtom != NULL) && (lastProxiedAtom->section() == machoAtom.section()) ) {
- ld::Atom* ma = const_cast<ld::Atom*>(&machoAtom);
- ma->setFile(lastProxiedFile);
+ else
+ {
+ // this is something new that lto conjured up, tell ld its new
+ _newAtoms.push_back(&machoAtom);
+ // <rdar://problem/15469363> if new static atom in same section as previous non-static atom, assign to same file as previous
+ if ( (lastProxiedAtom != NULL) && (lastProxiedAtom->section() == machoAtom.section()) ) {
+ ld::Atom* ma = const_cast<ld::Atom*>(&machoAtom);
+ ma->setFile(lastProxiedFile);
+ }
+ if (log) fprintf(stderr, "AtomSyncer, mach-o atom %p is totally new (name=%s)\n", &machoAtom, machoAtom.name());
}
}
// adjust fixups to go through proxy atoms
- //fprintf(stderr, "adjusting fixups in atom: %s\n", machoAtom.name());
+ if (log) fprintf(stderr, " adjusting fixups in atom: %s\n", machoAtom.name());
for (ld::Fixup::iterator fit=machoAtom.fixupsBegin(); fit != machoAtom.fixupsEnd(); ++fit) {
switch ( fit->binding ) {
case ld::Fixup::bindingNone:
// don't know if this target has been seen by linker before or if it is new
// be conservative and tell linker it is new
_additionalUndefines.push_back(fit->u.name);
- //fprintf(stderr, " by name ref to: %s\n", fit->u.name);
+ if (log) fprintf(stderr, " adding by-name symbol %s\n", fit->u.name);
break;
case ld::Fixup::bindingDirectlyBound:
// If mach-o atom is referencing another mach-o atom then
// reference is not going through Atom proxy. Fix it here to ensure that all
// llvm symbol references always go through Atom proxy.
- if ( fit->u.target->scope() != ld::Atom::scopeTranslationUnit ) {
+ {
const char* targetName = fit->u.target->name();
- CStringToAtom::iterator pos = _llvmAtoms.find(targetName);
- if ( pos != _llvmAtoms.end() ) {
- fit->u.target = pos->second;
+ CStringToAtom::iterator post = _llvmAtoms.find(targetName);
+ if ( post != _llvmAtoms.end() ) {
+ const ld::Atom* t = post->second;
+ if (log) fprintf(stderr, " updating direct reference to %p to be ref to %p: %s\n", fit->u.target, t, targetName);
+ fit->u.target = t;
}
else {
// <rdar://problem/12859831> Don't unbind follow-on reference into by-name reference
cpu_type_t arch;
const char* mcpu;
const std::vector<const char*>* llvmOptions;
+ const std::vector<const char*>* initialUndefines;
};
extern bool optimize( const std::vector<const ld::Atom*>& allAtoms,
virtual bool forEachAtom(ld::File::AtomHandler&) const;
virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const;
virtual ld::File::ObjcConstraint objCConstraint() const { return _objcContraint; }
+ virtual uint8_t swiftVersion() const { return _swiftVersion; }
// overrides of ld::dylib::File
virtual void processIndirectLibraries(ld::dylib::File::DylibHandler*, bool);
virtual bool allSymbolsAreWeakImported() const;
virtual const void* codeSignatureDR() const { return _codeSignatureDR; }
virtual bool installPathVersionSpecific() const { return _installPathOverride; }
+ virtual bool appExtensionSafe() const { return _appExtensionSafe; };
+ virtual ld::MacVersionMin macMinVersion() const { return _macMinVersionInDylib; }
+ virtual ld::IOSVersionMin iOSMinVersion() const { return _iOSMinVersionInDylib; }
protected:
bool _linkingFlat;
bool _implicitlyLinkPublicDylibs;
ld::File::ObjcConstraint _objcContraint;
+ uint8_t _swiftVersion;
ld::Section _importProxySection;
ld::Section _flatDummySection;
std::vector<Dependent> _dependentDylibs;
bool _wrongOS;
bool _installPathOverride;
bool _indirectDylibsProcessed;
+ bool _appExtensionSafe;
+ ld::MacVersionMin _macMinVersionInDylib;
+ ld::IOSVersionMin _iOSMinVersionInDylib;
static bool _s_logHashtable;
};
: 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<P>* header = (const macho_header<P>*)fileContent;
const uint32_t cmd_count = header->ncmds();
|| (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<P>* dynamicInfo = NULL;
if ( _addVersionLoadCommand && !indirectDylib )
throw "building for iOS Simulator, but linking against dylib built for MacOSX";
}
+ _macMinVersionInDylib = (ld::MacVersionMin)((macho_version_min_command<P>*)cmd)->version();
break;
case LC_VERSION_MIN_IPHONEOS:
if ( _macVersionMin != ld::macVersionUnset ) {
if ( _addVersionLoadCommand && !indirectDylib )
throw "building for MacOSX, but linking against dylib built for iOS Simulator";
}
+ _iOSMinVersionInDylib = (ld::IOSVersionMin)((macho_version_min_command<P>*)cmd)->version();
break;
case LC_CODE_SIGNATURE:
codeSignature = (macho_linkedit_data_command<P>* )cmd;
_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());
*subResult = CPU_SUBTYPE_ARM64_ALL;
return true;
}
- if ( Parser<ppc>::validFile(fileContent, false) ) {
- *result = CPU_TYPE_POWERPC;
- const macho_header<Pointer32<BigEndian> >* header = (const macho_header<Pointer32<BigEndian> >*)fileContent;
- *subResult = header->cpusubtype();
- return true;
- }
- if ( Parser<ppc64>::validFile(fileContent, false) ) {
- *result = CPU_TYPE_POWERPC64;
- *subResult = CPU_SUBTYPE_POWERPC_ALL;
- return true;
- }
return false;
}
_dwarfDebugInfoSect(NULL), _dwarfDebugAbbrevSect(NULL),
_dwarfDebugLineSect(NULL), _dwarfDebugStringSect(NULL),
_objConstraint(ld::File::objcConstraintNone),
+ _swiftVersion(0),
_cpuSubType(0),
_canScatterAtoms(false) {}
virtual ~File();
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:
const uint8_t* _fileContent;
Section<A>** _sectionsArray;
uint8_t* _atomsArray;
+ uint8_t* _aliasAtomsArray;
uint32_t _sectionsArrayCount;
uint32_t _atomsArrayCount;
+ uint32_t _aliasAtomsArrayCount;
std::vector<ld::Fixup> _fixups;
std::vector<ld::Atom::UnwindInfo> _unwindInfos;
std::vector<ld::Atom::LineInfo> _lineInfos;
const macho_section<P>* _dwarfDebugLineSect;
const macho_section<P>* _dwarfDebugStringSect;
ld::File::ObjcConstraint _objConstraint;
+ uint8_t _swiftVersion;
uint32_t _cpuSubType;
bool _canScatterAtoms;
std::vector<std::vector<const char*> > _linkerOptions;
class Atom<A>* _beginAtoms;
class Atom<A>* _endAtoms;
bool _hasAliases;
+ std::set<const class Atom<A>*> _altEntries;
};
public:
CFISection(Parser<A>& parser, File<A>& f, const macho_section<typename A::P>* s)
: Section<A>(f, s) { }
- uint32_t cfiCount();
+ uint32_t cfiCount(Parser<A>& parser);
virtual ld::Atom::ContentType contentType() { return ld::Atom::typeCFI; }
virtual uint32_t computeAtomCount(class Parser<A>& parser, struct Parser<A>::LabelAndCFIBreakIterator& it, const struct Parser<A>::CFI_CU_InfoArrays&);
}
+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 <typename A>
class Parser
{
static bool isThumbFromSymbol(const macho_nlist<P>& sym);
static bool weakImportFromSymbol(const macho_nlist<P>& sym);
static bool resolverFromSymbol(const macho_nlist<P>& sym);
+ static bool altEntryFromSymbol(const macho_nlist<P>& sym);
uint32_t symbolIndexFromIndirectSectionAddress(pint_t,const macho_section<P>*);
const macho_section<P>* firstMachOSection() { return _sectionsStart; }
const macho_section<P>* machOSectionFromSectionIndex(uint32_t index);
bool forceDwarfConversion() { return _forceDwarfConversion; }
bool verboseOptimizationHints() { return _verboseOptimizationHints; }
bool neverConvertDwarf() { return _neverConvertDwarf; }
-
+
macho_data_in_code_entry<P>* dataInCodeStart() { return _dataInCodeStart; }
macho_data_in_code_entry<P>* dataInCodeEnd() { return _dataInCodeEnd; }
const uint8_t* optimizationHintsStart() { return _lohStart; }
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);
File<A>* _file;
const macho_nlist<P>* _symbols;
uint32_t _symbolCount;
+ uint32_t _indirectSymbolCount;
const char* _strings;
uint32_t _stringsSize;
const uint32_t* _indirectTable;
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),
return false;
}
+template <>
+typename arm::P::uint_t Parser<arm>::realAddr(typename arm::P::uint_t addr)
+{
+ return addr & (-2);
+}
+
+template <typename A>
+typename A::P::uint_t Parser<A>::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; \
// 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<A>::CFI_Atom_Info, cfiArray, countOfCFIs, 1024);
// stack allocate (if not too large) a copy of __eh_frame to apply relocations to
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;
}
}
+ // 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();
}
-
template <> uint8_t Parser<x86>::loadCommandSizeMask() { return 0x03; }
template <> uint8_t Parser<x86_64>::loadCommandSizeMask() { return 0x07; }
template <> uint8_t Parser<arm>::loadCommandSizeMask() { return 0x03; }
}
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);
}
}
+template <typename A>
+void Parser<A>::appendAliasAtoms(uint8_t* p)
+{
+ for (uint32_t i=0; i < this->_symbolCount; ++i) {
+ const macho_nlist<P>& 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 <typename A>
int Parser<A>::sectionIndexSorter(void* extra, const void* l, const void* r)
{
_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<A>::makeSectionName(sect), sect->size(), _file->path());
return ( sym.n_desc() & N_SYMBOL_RESOLVER );
}
+template <typename A>
+bool Parser<A>::altEntryFromSymbol(const macho_nlist<P>& sym)
+{
+ return ( sym.n_desc() & N_ALT_ENTRY );
+}
+
/* Skip over a LEB128 value (signed or unsigned). */
static void
template <typename A>
-const char* Parser<A>::getDwarfString(uint64_t form, const uint8_t* p)
+const char* Parser<A>::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 <typename A>
+uint64_t Parser<A>::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;
}
case N_LSYM:
case N_RSYM:
case N_PSYM:
+ case N_AST:
// not associated with an atom, just copy
stab.string = symString;
break;
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;
+ }
}
}
return _dwarfTranslationUnitPath;
}
-
+
template <typename A>
bool File<A>::forEachAtom(ld::File::AtomHandler& handler) const
handler.doAtom(*((Atom<A>*)p));
p += sizeof(Atom<A>);
}
- 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 <typename A>
}
// arm does not have zero cost exceptions
-template <> uint32_t CFISection<arm>::cfiCount() { return 0; }
+template <>
+uint32_t CFISection<arm>::cfiCount(Parser<arm>& parser)
+{
+ return 0;
+}
template <typename A>
-uint32_t CFISection<A>::cfiCount()
+uint32_t CFISection<A>::cfiCount(Parser<A>& parser)
{
// create ObjectAddressSpace object for use by libunwind
OAS oas(*this, (uint8_t*)this->file().fileContent()+this->_machOSection->offset());
assert(count == 0);
}
+
+
+
template <>
void CFISection<arm64>::cfiParse(class Parser<arm64>& parser, uint8_t* buffer,
libunwind::CFI_Atom_Info<CFISection<arm64>::OAS>::CFI_Atom_Info cfiArray[],
}
}
-
-
#if SUPPORT_ARCH_arm64
template <>
void CFISection<arm64>::addCiePersonalityFixups(class Parser<arm64>& parser, const CFI_Atom_Info* cieInfo)
}
#endif
+
template <typename A>
void CFISection<A>::addCiePersonalityFixups(class Parser<A>& parser, const CFI_Atom_Info* cieInfo)
{
else {
const pint_t* content = (pint_t*)(this->file().fileContent() + this->_machOSection->offset() + reloc->r_address());
pint_t personalityAddr = *content;
- Section<x86_64>* 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;
}
#endif
+
template <typename A>
const char* CUSection<A>::personalityName(class Parser<A>& parser, const macho_relocation_info<P>* reloc)
{
new (allocatedSpace) Atom<A>(*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;
if ((instruction & 0xFE000000) == 0xFA000000)
displacement += ((instruction & 0x01000000) >> 23);
if ( reloc->r_extern() ) {
- target.addend = srcAddr + displacement;
+ dstAddr = srcAddr + displacement;
+ // <rdar://problem/16652542> support large .o files
+ if ( srcAddr > 0x2000000 ) {
+ dstAddr -= ((srcAddr + 0x1FFFFFF) & 0xFC000000);
+ }
+ target.addend = dstAddr;
if ( externSymbolIsThumbDef )
target.addend &= -2; // remove thumb bit
}
dstAddr &= 0xFFFFFFFC;
if ( reloc->r_extern() ) {
- target.addend = dstAddr;
+ // <rdar://problem/16652542> 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);
}
}
}
+ 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<A>::makeSectionName(sect));
+
+ Atom<A>* end = &_endAtoms[-1];
+ for(Atom<A>* p = _beginAtoms; p < end; ++p) {
+ Atom<A>* nextAtom = &p[1];
+ if ( _altEntries.count(nextAtom) != 0 ) {
+ typename Parser<A>::SourceLocation src(p, 0);
+ parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindNoneFollowOn, nextAtom);
+ typename Parser<A>::SourceLocation src2(nextAtom, 0);
+ parser.addFixup(src2, ld::Fixup::k1of1, ld::Fixup::kindNoneGroupSubordinate, p);
+ }
+ }
+ }
// <rdar://problem/9218847> track data-in-code
if ( parser.hasDataInCodeLabels() && (this->type() == ld::Section::typeCode) ) {
_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; }
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; }
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
private:
const char* _name;
- const ld::Atom* _target;
TargetAndOffset _finalTarget;
};
_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; }
_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; }
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; }
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
}
private:
const char* _name;
- const ld::Atom* _target;
TargetAndOffset _finalTarget;
};
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;
UnwindInfoAtom<A>::UnwindInfoAtom(const std::vector<UnwindEntry>& 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<UnwindEntry> uniqueEntries;
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);
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);
}
}
_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<P>);
const uint32_t commonEncodingsArrayCount = commonEncodings.size();
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<ld::Fixup>::iterator it = _fixups.begin(); it != _fixups.end(); ++it) {
it->offsetInAtom += fixupSlide;
}
entryTable[i].set_functionOffset(0);
entryTable[i].set_encoding(info.encoding);
// add fixup for address part of entry
- uint32_t offset = (uint8_t*)(&entryTable[i]) - _pagesForDelete;
+ uint32_t offset = (uint8_t*)(&entryTable[i]) - _pageAlignedPages;
this->addRegularAddressFixup(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);
}
}
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);
}
}
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"; }
template <typename A>
ObjCImageInfoAtom<A>::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))
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);
}
template <typename A>
class Category : public ObjCData<A> {
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);
template <typename A>
-const ld::Atom* Category<A>::getClass(ld::Internal& state, const ld::Atom* contentAtom)
+const ld::Atom* Category<A>::getClass(ld::Internal& state, const ld::Atom* contentAtom, bool& hasAddend)
{
- return ObjCData<A>::getPointerInContent(state, contentAtom, sizeof(pint_t)); // category_t.cls
+ return ObjCData<A>::getPointerInContent(state, contentAtom, sizeof(pint_t), &hasAddend); // category_t.cls
}
template <typename A>
// ignore categories also in __objc_nlcatlist
if ( nlcatListAtoms.count(categoryAtom) != 0 )
continue;
- const ld::Atom* categoryOnClassAtom = Category<A>::getClass(state, categoryAtom);
+ const ld::Atom* categoryOnClassAtom = Category<A>::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
+ // <rdar://problem/16107696> for now, back off optimization on new style classes
+ if ( hasAddend != 0 )
+ continue;
+ // <rdar://problem/17249777> 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<const ld::Atom*>();
#if SUPPORT_ARCH_x86_64
case CPU_TYPE_X86_64:
state.addAtom(*new ObjCImageInfoAtom<x86_64>(state.objcObjectConstraint, compaction,
- true));
+ true, state.swiftVersion));
break;
#endif
#if SUPPORT_ARCH_i386
case CPU_TYPE_I386:
state.addAtom(*new ObjCImageInfoAtom<x86>(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<arm>(state.objcObjectConstraint, compaction,
- true));
+ true, state.swiftVersion));
break;
#endif
#if SUPPORT_ARCH_arm64
case CPU_TYPE_ARM64:
state.addAtom(*new ObjCImageInfoAtom<arm64>(state.objcObjectConstraint, compaction,
- true));
+ true, state.swiftVersion));
break;
#endif
default:
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<const char*, const ld::Atom*, CStringHash, CStringEquals> NameToAtom;
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)
{
}
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;
}
}
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;
}
}
void Layout::doPass()
{
+ const bool log = false;
+ if ( log ) {
+ fprintf(stderr, "Unordered atoms:\n");
+ for (std::vector<ld::Internal::FinalSection*>::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) {
+ ld::Internal::FinalSection* sect = *sit;
+ for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
+ const ld::Atom* atom = *ait;
+ 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();
// sort atoms in each section
for (std::vector<ld::Internal::FinalSection*>::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<ld::Internal::FinalSection*>::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) {
- // ld::Internal::FinalSection* sect = *sit;
- // for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
- // const ld::Atom* atom = *ait;
- // fprintf(stderr, "\t%p\t%s\t%s\n", atom, sect->sectionName(), atom->name());
- // }
- //}
-
+ if ( log ) {
+ fprintf(stderr, "Sorted atoms:\n");
+ for (std::vector<ld::Internal::FinalSection*>::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) {
+ ld::Internal::FinalSection* sect = *sit;
+ for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
+ const ld::Atom* atom = *ait;
+ fprintf(stderr, "\t%p\t%s\t%s\n", atom, sect->sectionName(), atom->name());
+ }
+ }
+ }
}
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;
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;
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");
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;
}
#endif
+
+
template <>
const char* UnwindPrinter<x86_64>::personalityName(const macho_relocation_info<x86_64::P>* reloc)
{
}
#endif
+
template <typename A>
bool UnwindPrinter<A>::hasExernReloc(uint64_t sectionOffset, const char** personalityStr, pint_t* addr)
{
}
else {
functionNameStr = this->functionName(entry->codeStart(), &offsetInFunction);
+ funcAddress = entry->codeStart();
}
if ( offsetInFunction == 0 )
printf(" start: 0x%08llX %s\n", (uint64_t)funcAddress, functionNameStr);
if ( UnwindPrinter<arm64>::validFile(p + offset) )
UnwindPrinter<arm64>::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:
#if SUPPORT_ARCH_arm64
onlyArchs.insert(CPU_TYPE_ARM64);
#endif
+ onlyArchs.insert(CPU_TYPE_ARM);
}
// process each file
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
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)
--- /dev/null
+##
+# Copyright (c) 2014 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# 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
--- /dev/null
+
+ .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
--- /dev/null
+extern void bar();
+extern int result;
+
+int myresult = 1;
+
+int mymain()
+{
+ bar();
+ return result;
+}
+
+void mybar()
+{
+
+}
+
##
-# Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
+# Copyright (c) 2006-2014 Apple Computer, Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
# 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
_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
--- /dev/null
+##
+# Copyright (c) 2014 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# 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
--- /dev/null
+#include <stdio.h>
+
+int foo()
+{
+ fprintf(stdout, "hello foo\n");
+ return 0;
+}
\ No newline at end of file
--- /dev/null
+#include <stdio.h>
+
+int mymain()
+{
+ fprintf(stdout, "hello mymain\n");
+ return 0;
+}
\ No newline at end of file
--- /dev/null
+##
+# Copyright (c) 2014 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# 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
--- /dev/null
+void bar() {}
--- /dev/null
+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 };
+
+
--- /dev/null
+
+#include <stdio.h>
+
+
+void foo(int x)
+{
+ printf("hello, world %d\n", x);
+}
+
+int main()
+{
+ foo(10);
+ return 0;
+}
+
--- /dev/null
+##
+# Copyright (c) 2014 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# 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
--- /dev/null
+
+extern const char* mystring;
+
+const char** myp = &mystring;
--- /dev/null
+ const char* mystring = "hello";
--- /dev/null
+
+extern const char** myp;
+
+
+const char** entry(int i) {
+ if ( i ) {
+ *myp = "help";
+ }
+ return myp;
+}
+
--- /dev/null
+##
+# Copyright (c) 2014 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# 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
--- /dev/null
+
+extern const char* mystring;
+
+const char** myp = &mystring;
--- /dev/null
+ const char* mystring = "hello";
--- /dev/null
+
+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
--- /dev/null
+##
+# Copyright (c) 2014 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# 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
--- /dev/null
+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;
+}
+
--- /dev/null
+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;
+
--- /dev/null
+
+static int mylocal()
+{
+ return 1;
+}
+
+void* otherget() { return mylocal; }
+
+
--- /dev/null
+main.o:*
+_abc
+_com4
+
+
--- /dev/null
+foo.o:*
+_mainget
+
+
+
+
--- /dev/null
+##
+# Copyright (c) 2014 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# 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
--- /dev/null
+
+
+
+ .section __MYSEG,__my_xxx
+_x: .long 0
+
+
+ .section __MYSEG,__my_yyy
+_y: .long 0
+
+
+ .section __MYSEG,__my_zzz
+_z: .long 0
+
+
--- /dev/null
+
+
+void entry() {
+}
+
--- /dev/null
+ sectname __my_yyy
+ sectname __my_ccc
+ sectname __my_aaa
+ sectname __my_bbb
+ sectname __my_ddd
+ sectname __my_xxx
+ sectname __my_zzz
--- /dev/null
+ sectname __my_iii
+ sectname __my_aaa
+ sectname __my_jjj
+ sectname __my_bbb
+ sectname __my_ddd
+ sectname __my_xxx
+ sectname __my_zzz
--- /dev/null
+
+
+ .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
+
+
--- /dev/null
+##
+# Copyright (c) 2014 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# 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
--- /dev/null
+
+extern const char* mystring;
+
+const char** myp = &mystring;
+
+int com;
+
+const char* inc() {
+ ++com;
+ return "";
+}
+
+
--- /dev/null
+const char* mystring = "hello";
+
+int var = 10;
+
+const char* incget() {
+ ++var;
+ return mystring;
+}
+
--- /dev/null
+ segname __ROM2
+ segname __ROM
+ segname __RAM
+ segname __ZF
--- /dev/null
+
+extern const char** myp;
+
+
+const char** entry(int i) {
+ if ( i ) {
+ *myp = "help";
+ }
+ return myp;
+}
+
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;
--- /dev/null
+##
+# Copyright (c) 2014 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# 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
--- /dev/null
+
+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;
+
--- /dev/null
+
+static int mylocal()
+{
+ return 1;
+}
+
+void* otherget() { return mylocal; }
+
+
--- /dev/null
+_com
+_abc
+
--- /dev/null
+_foo
+_s1
+main.o:_mylocal
+
+
+
${PASS_IFF_GOOD_MACHO} weak
clean:
- rm -rf main
+ rm -rf weak