ld::Atom::definitionRegular, ld::Atom::combineNever,
ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified,
ld::Atom::symbolTableNotIn, false, false, false,
- (opts.outputKind() == Options::kPreload) ? ld::Atom::Alignment(0) : ld::Atom::Alignment(12) ),
+ (opts.outputKind() == Options::kPreload) ? ld::Atom::Alignment(0) : ld::Atom::Alignment(log2(opts.segmentAlignment())) ),
_options(opts), _state(state), _writer(writer), _address(0), _uuidCmdInOutputBuffer(NULL), _linkeditCmdOffset(0), _symboltableCmdOffset(0)
{
bzero(_uuid, 16);
if ( cmd->fileoff() == 0 )
cmd->set_fileoff(fsect->fileOffset);
cmd->set_vmsize(fsect->address + fsect->size - cmd->vmaddr());
- if ( (fsect->type() != ld::Section::typeZeroFill) && (fsect->type() != ld::Section::typeTentativeDefs) )
+ if ( !sectionTakesNoDiskSpace(fsect) )
cmd->set_filesize(fsect->fileOffset + fsect->size - cmd->fileoff());
++msect;
}
{
uint32_t sz = alignedSize(sizeof(macho_dylib_command<P>) + strlen(dylib->installPath()) + 1);
macho_dylib_command<P>* cmd = (macho_dylib_command<P>*)p;
+ bool weakLink = dylib->forcedWeakLinked() || dylib->allSymbolsAreWeakImported();
+ bool upward = dylib->willBeUpwardDylib() && _options.useUpwardDylibs();
+ bool reExport = dylib->willBeReExported() && _options.useSimplifiedDylibReExports();
+ if ( weakLink && upward )
+ warning("cannot weak upward link. Dropping weak for %s", dylib->installPath());
+ if ( weakLink && reExport )
+ warning("cannot weak re-export a dylib. Dropping weak for %s", dylib->installPath());
if ( dylib->willBeLazyLoadedDylib() )
cmd->set_cmd(LC_LAZY_LOAD_DYLIB);
- else if ( dylib->forcedWeakLinked() || dylib->allSymbolsAreWeakImported() )
- cmd->set_cmd(LC_LOAD_WEAK_DYLIB);
- else if ( dylib->willBeReExported() && _options.useSimplifiedDylibReExports() )
+ else if ( reExport )
cmd->set_cmd(LC_REEXPORT_DYLIB);
- else if ( dylib->willBeUpwardDylib() && _options.useUpwardDylibs() )
+ else if ( upward )
cmd->set_cmd(LC_LOAD_UPWARD_DYLIB);
+ else if ( weakLink )
+ cmd->set_cmd(LC_LOAD_WEAK_DYLIB);
else
cmd->set_cmd(LC_LOAD_DYLIB);
cmd->set_cmdsize(sz);
ld::Section CustomStackAtom::_s_section("__UNIXSTACK", "__stack", ld::Section::typeStack, true);
+static bool isCompilerSupportLib(const char* path) {
+ const char* libName = strrchr(path, '/');
+ return ( (libName != NULL) && (strncmp(libName, "/libclang_rt", 12) == 0) );
+}
+
const char* InputFiles::fileArch(const uint8_t* p, unsigned len)
{
ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib)
{
// map in whole file
- uint64_t len = info.fileLen;
+ struct stat stat_buf;
int fd = ::open(info.path, O_RDONLY, 0);
if ( fd == -1 )
throwf("can't open file, errno=%d", errno);
- if ( info.fileLen < 20 )
- throwf("file too small (length=%llu)", info.fileLen);
-
- uint8_t* p = (uint8_t*)::mmap(NULL, info.fileLen, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
+ if ( ::fstat(fd, &stat_buf) != 0 )
+ throwf("fstat(%s) failed, errno=%d\n", info.path, errno);
+ if ( stat_buf.st_size < 20 )
+ throwf("file too small (length=%llu)", stat_buf.st_size);
+ int64_t len = stat_buf.st_size;
+ uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
if ( p == (uint8_t*)(-1) )
throwf("can't map file, errno=%d", errno);
if ( sliceFound ) {
uint32_t fileOffset = OSSwapBigToHostInt32(archs[sliceToUse].offset);
len = OSSwapBigToHostInt32(archs[sliceToUse].size);
- if ( fileOffset+len > info.fileLen ) {
+ if ( fileOffset+len > stat_buf.st_size ) {
// <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;
+ int64_t newFileLen = stat_buf.st_size;
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);
+ fileOffset, fileOffset+len, stat_buf.st_size);
}
}
// if requested architecture is page aligned within fat file, then remap just that portion of file
if ( (fileOffset & 0x00000FFF) == 0 ) {
// unmap whole file
- munmap((caddr_t)p, info.fileLen);
+ munmap((caddr_t)p, stat_buf.st_size);
// re-map just part we need
p = (uint8_t*)::mmap(NULL, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, fileOffset);
if ( p == (uint8_t*)(-1) )
archOpts.verboseLoad = _options.whyLoad();
archOpts.logAllFiles = _options.logAllFiles();
// Set ObjSource Kind, libclang_rt is compiler static library
- const char* libName = strrchr(info.path, '/');
- if ( (libName != NULL) && (strncmp(libName, "/libclang_rt", 12) == 0) )
+ if ( isCompilerSupportLib(info.path) )
archOpts.objOpts.srcKind = ld::relocatable::File::kSourceCompilerArchive;
else
archOpts.objOpts.srcKind = ld::relocatable::File::kSourceArchive;
void InputFiles::logTraceInfo(const char* format, ...) const
{
- // one time open() of custom LD_TRACE_FILE
- static int trace_file = -1;
- if ( trace_file == -1 ) {
- const char *trace_file_path = _options.traceOutputFile();
- if ( trace_file_path != NULL ) {
- trace_file = open(trace_file_path, O_WRONLY | O_APPEND | O_CREAT, 0666);
- if ( trace_file == -1 )
- throwf("Could not open or create trace file (errno=%d): %s", errno, trace_file_path);
- }
- else {
- trace_file = fileno(stderr);
- }
- }
-
char trace_buffer[MAXPATHLEN * 2];
va_list ap;
va_start(ap, format);
int length = vsnprintf(trace_buffer, sizeof(trace_buffer), format, ap);
va_end(ap);
- char* buffer_ptr = trace_buffer;
-
- while (length > 0) {
- ssize_t amount_written = write(trace_file, buffer_ptr, length);
- if(amount_written == -1)
- /* Failure to write shouldn't fail the build. */
- return;
- buffer_ptr += amount_written;
- length -= amount_written;
- }
+ _options.writeToTraceFile(trace_buffer, length);
}
// <rdar://problem/9740166> force loaded archives should be in LD_TRACE
if ( (info.options.fForceLoad || _options.fullyLoadArchives()) && (_options.traceArchives() || _options.traceEmitJSON()) )
logArchive(archive);
+
+ if ( isCompilerSupportLib(info.path) && (info.options.fForceLoad || _options.fullyLoadArchives()) )
+ state.forceLoadCompilerRT = true;
+
_searchLibraries.push_back(LibraryInfo(archive));
if ( _options.dumpDependencyInfo() )
_options.dumpDependency(Options::depArchive, archive->path());
entry.flags |= EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION;
entry.other = this->_writer.compressedOrdinalForAtom(atom);
if ( entry.other == BIND_SPECIAL_DYLIB_SELF ) {
- warning("not adding explict export for symbol %s because it is already re-exported from dylib %s", entry.name, atom->file()->path());
+ warning("not adding explict export for symbol %s because it is already re-exported from dylib %s", entry.name, atom->safeFilePath());
continue;
}
if ( atom->isAlias() ) {
relocs.push_back(reloc1);
break;
+ case ld::Fixup::kindStoreARM64TLVPLoadPageOff12:
+ case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12:
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(symbolNum);
+ reloc1.set_r_pcrel(false);
+ reloc1.set_r_length(2);
+ reloc1.set_r_extern(external);
+ reloc1.set_r_type(ARM64_RELOC_TLVP_LOAD_PAGEOFF12);
+ relocs.push_back(reloc1);
+ break;
+
+ case ld::Fixup::kindStoreARM64TLVPLoadPage21:
+ case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21:
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(symbolNum);
+ reloc1.set_r_pcrel(true);
+ reloc1.set_r_length(2);
+ reloc1.set_r_extern(external);
+ reloc1.set_r_type(ARM64_RELOC_TLVP_LOAD_PAGE21);
+ relocs.push_back(reloc1);
+ break;
case ld::Fixup::kindStoreLittleEndian64:
case ld::Fixup::kindStoreTargetAddressLittleEndian64:
p = path;
if ( stat(p, &statBuffer) == 0 ) {
if (p != path) path = strdup(p);
- fileLen = statBuffer.st_size;
modTime = statBuffer.st_mtime;
return true;
}
fClientName(NULL),
fUmbrellaName(NULL), fInitFunctionName(NULL), fDotOutputFile(NULL), fExecutablePath(NULL),
fBundleLoader(NULL), fDtraceScriptName(NULL), fSegAddrTablePath(NULL), fMapPath(NULL),
- fDyldInstallPath("/usr/lib/dyld"), fTempLtoObjectPath(NULL), fOverridePathlibLTO(NULL), fLtoCpu(NULL),
+ fDyldInstallPath("/usr/lib/dyld"), fLtoCachePath(NULL), fTempLtoObjectPath(NULL), fOverridePathlibLTO(NULL), fLtoCpu(NULL),
fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fSourceVersion(0), fSDKVersion(0), fExecutableStack(false),
fNonExecutableHeap(false), fDisableNonExecutableHeap(false),
fMinimumHeaderPad(32), fSegmentAlignment(4096),
fPlatform(kPlatformUnknown), fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL),
fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset), fWatchOSVersionMin(ld::wOSVersionUnset),
fSaveTempFiles(false), fSnapshotRequested(false), fPipelineFifo(NULL),
- fDependencyInfoPath(NULL), fDependencyFileDescriptor(-1), fMaxDefaultCommonAlign(0)
+ fDependencyInfoPath(NULL), fDependencyFileDescriptor(-1), fTraceFileDescriptor(-1), fMaxDefaultCommonAlign(0)
{
this->checkForClassic(argc, argv);
this->parsePreCommandLineEnvironmentSettings();
Options::~Options()
{
- if ( fDependencyFileDescriptor != -1 )
- ::close(fDependencyFileDescriptor);
+ if ( fDependencyFileDescriptor != -1 )
+ ::close(fDependencyFileDescriptor);
+
+ if ( fTraceFileDescriptor != -1 )
+ ::close(fTraceFileDescriptor);
}
bool Options::errorBecauseOfWarnings() const
break;
}
- // If we found a text-based stub file, check if it should be used.
- if ( !tbdInfo.missing() ) {
- if (tapi::LinkerInterfaceFile::shouldPreferTextBasedStubFile(tbdInfo.path)) {
- result = tbdInfo;
- return true;
- }
- }
FileInfo dylibInfo;
{
bool found = dylibInfo.checkFileExists(*this, path.c_str());
printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), path.c_str());
}
- // There is only a text-based stub file.
- if ( !tbdInfo.missing() && dylibInfo.missing() ) {
- result = tbdInfo;
- return true;
- }
- // There is only a dynamic library file.
- else if ( tbdInfo.missing() && !dylibInfo.missing() ) {
- result = dylibInfo;
- return true;
+ // There is only a text-based stub file or a dynamic library file.
+ if ( tbdInfo.missing() != dylibInfo.missing() ) {
+ result = tbdInfo.missing() ? dylibInfo : tbdInfo;
}
// There are both - a text-based stub file and a dynamic library file.
else if ( !tbdInfo.missing() && !dylibInfo.missing() ) {
- // If the files are still in synv we can use and should use the text-based stub file.
- if (tapi::LinkerInterfaceFile::areEquivalent(tbdInfo.path, dylibInfo.path)) {
+ // Check if we should prefer the text-based stub file (installapi).
+ if (tapi::LinkerInterfaceFile::shouldPreferTextBasedStubFile(tbdInfo.path)) {
+ result = tbdInfo;
+ }
+ // If the files are still in sync we can use and should use the text-based stub file.
+ else if (tapi::LinkerInterfaceFile::areEquivalent(tbdInfo.path, dylibInfo.path)) {
result = tbdInfo;
}
// Otherwise issue a warning and fall-back to the dynamic library file.
else {
warning("text-based stub file %s and library file %s are out of sync. Falling back to library file for linking.", tbdInfo.path, dylibInfo.path);
result = dylibInfo;
-
}
- return true;
+ } else {
+ return false;
}
- return false;
+ return true;
}
static bool startsWith(const std::string& str, const std::string& prefix)
if ( fReverseMapPath != NULL && !fHideSymbols ) {
throw "-bitcode_symbol_map can only be used with -bitcode_hide_symbols";
}
- if ( fBitcodeKind != kBitcodeProcess &&
- fOutputKind != Options::kObjectFile ) {
- throw "-bitcode_process_mode can only be used together with -r";
- }
// auto fix up the process type for strip -S.
// when there is only one input and output type is object file, downgrade kBitcodeProcess to kBitcodeAsData.
if ( fOutputKind == Options::kObjectFile && fInputFiles.size() == 1 && fBitcodeKind == Options::kBitcodeProcess )
}
+void Options::writeToTraceFile(const char* buffer, size_t len) const
+{
+ // one time open() of custom LD_TRACE_FILE
+ if ( fTraceFileDescriptor == -1 ) {
+ if ( fTraceOutputFile != NULL ) {
+ fTraceFileDescriptor = open(fTraceOutputFile, O_WRONLY | O_APPEND | O_CREAT, 0666);
+ if ( fTraceFileDescriptor == -1 )
+ throwf("Could not open or create trace file (errno=%d): %s", errno, fTraceOutputFile);
+ }
+ else {
+ fTraceFileDescriptor = fileno(stderr);
+ }
+ }
+
+ while (len > 0) {
+ ssize_t amountWritten = write(fTraceFileDescriptor, buffer, len);
+ if ( amountWritten == -1 )
+ /* Failure to write shouldn't fail the build. */
+ return;
+ buffer += amountWritten;
+ len -= amountWritten;
+ }
+}
+
}
}
+ static void userReadableSwiftVersion(uint8_t value, char versionString[64])
+ {
+ switch (value) {
+ case 1:
+ strcpy(versionString, "1.0");
+ break;
+ case 2:
+ strcpy(versionString, "1.1");
+ break;
+ case 3:
+ strcpy(versionString, "2.0");
+ break;
+ case 4:
+ strcpy(versionString, "3.0");
+ break;
+ default:
+ sprintf(versionString, "unknown ABI version 0x%02X", value);
+ }
+ }
+
+
class FileInfo {
public:
const char* path;
- uint64_t fileLen;
time_t modTime;
LibraryOptions options;
ld::File::Ordinal ordinal;
// The use pattern for FileInfo is to create one on the stack in a leaf function and return
// it to the calling frame by copy. Therefore the copy constructor steals the path string from
// the source, which dies with the stack frame.
- FileInfo(FileInfo const &other) : path(other.path), fileLen(other.fileLen), modTime(other.modTime), options(other.options), ordinal(other.ordinal), fromFileList(other.fromFileList), inputFileSlot(-1) { ((FileInfo&)other).path = NULL; };
+ FileInfo(FileInfo const &other) : path(other.path), modTime(other.modTime), options(other.options), ordinal(other.ordinal), fromFileList(other.fromFileList), inputFileSlot(-1) { ((FileInfo&)other).path = NULL; };
FileInfo &operator=(FileInfo other) {
std::swap(path, other.path);
- std::swap(fileLen, other.fileLen);
std::swap(modTime, other.modTime);
std::swap(options, other.options);
std::swap(ordinal, other.ordinal);
}
// Create an empty FileInfo. The path can be set implicitly by checkFileExists().
- FileInfo() : path(NULL), fileLen(0), modTime(-1), options(), fromFileList(false) {};
+ FileInfo() : path(NULL), modTime(-1), options(), fromFileList(false) {};
// Create a FileInfo for a specific path, but does not stat the file.
- FileInfo(const char *_path) : path(strdup(_path)), fileLen(0), modTime(-1), options(), fromFileList(false) {};
+ FileInfo(const char *_path) : path(strdup(_path)), modTime(-1), options(), fromFileList(false) {};
~FileInfo() { if (path) ::free((void*)path); }
uint8_t maxDefaultCommonAlign() const { return fMaxDefaultCommonAlign; }
bool hasDataSymbolMoves() const { return !fSymbolsMovesData.empty(); }
bool hasCodeSymbolMoves() const { return !fSymbolsMovesCode.empty(); }
+ void writeToTraceFile(const char* buffer, size_t len) const;
static uint32_t parseVersionNumber32(const char*);
const char* fPipelineFifo;
const char* fDependencyInfoPath;
mutable int fDependencyFileDescriptor;
+ mutable int fTraceFileDescriptor;
uint8_t fMaxDefaultCommonAlign;
};
// Leave ADRP as-is
set32LE(infoB.instructionContent, makeNOP());
ldrInfoC.offset += addInfoB.addend;
+ ldrInfoC.baseReg = adrpInfoA.destReg;
set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
if ( _options.verboseOptimizationHints() )
fprintf(stderr, "adrp-add-ldr at 0x%08llX T2 transformed to ADRP/LDR \n", infoC.instructionAddress);
}
catch (const char* msg) {
if ( atom->file() != NULL )
- throwf("%s in '%s' from %s", msg, atom->name(), atom->file()->path());
+ throwf("%s in '%s' from %s", msg, atom->name(), atom->safeFilePath());
else
throwf("%s in '%s'", msg, atom->name());
}
// <rdar://problem/12264302> Don't use mmap on non-hfs volumes
struct statfs fsInfo;
if ( statfs(_options.outputFilePath(), &fsInfo) != -1 ) {
- if ( strcmp(fsInfo.f_fstypename, "hfs") == 0) {
+ if ( (strcmp(fsInfo.f_fstypename, "hfs") == 0) || (strcmp(fsInfo.f_fstypename, "apfs") == 0) ) {
(void)unlink(_options.outputFilePath());
outputIsMappableFile = true;
}
end[1] = '\0';
struct statfs fsInfo;
if ( statfs(dirPath, &fsInfo) != -1 ) {
- if ( strcmp(fsInfo.f_fstypename, "hfs") == 0) {
+ if ( (strcmp(fsInfo.f_fstypename, "hfs") == 0) || (strcmp(fsInfo.f_fstypename, "apfs") == 0) ) {
outputIsMappableFile = true;
}
}
warning("PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not allowed in code signed PIE, "
"but used in %s from %s. "
"To fix this warning, don't compile with -mdynamic-no-pic or link with -Wl,-no_pie",
- atom->name(), atom->file()->path());
+ atom->name(), atom->safeFilePath());
}
}
this->pieDisabled = true;
}
else if ( (target->scope() == ld::Atom::scopeGlobal) && (target->combine() == ld::Atom::combineByName) ) {
- throwf("illegal text-relocoation (direct reference) to (global,weak) %s in %s from %s in %s", target->name(), target->file()->path(), atom->name(), atom->file()->path());
+ throwf("illegal text-relocoation (direct reference) to (global,weak) %s in %s from %s in %s", target->name(), target->safeFilePath(), atom->name(), atom->safeFilePath());
}
else {
if ( (target->file() != NULL) && (atom->file() != NULL) )
- throwf("illegal text-relocation to '%s' in %s from '%s' in %s", target->name(), target->file()->path(), atom->name(), atom->file()->path());
+ throwf("illegal text-relocation to '%s' in %s from '%s' in %s", target->name(), target->safeFilePath(), atom->name(), atom->safeFilePath());
else
throwf("illegal text reloc in '%s' to '%s'", atom->name(), target->name());
}
const char* demangledName = strdup(_options.demangleSymbol(atom->name()));
warning("direct access in function '%s' from file '%s' to global weak symbol '%s' from file '%s' means the weak symbol cannot be overridden at runtime. "
"This was likely caused by different translation units being compiled with different visibility settings.",
- demangledName, atom->file()->path(), _options.demangleSymbol(target->name()), target->file()->path());
+ demangledName, atom->safeFilePath(), _options.demangleSymbol(target->name()), target->safeFilePath());
}
return;
}
const char* demangledName = strdup(_options.demangleSymbol(atom->name()));
warning("direct access in function '%s' from file '%s' to global weak symbol '%s' from file '%s' means the weak symbol cannot be overridden at runtime. "
"This was likely caused by different translation units being compiled with different visibility settings.",
- demangledName, atom->file()->path(), _options.demangleSymbol(target->name()), target->file()->path());
+ demangledName, atom->safeFilePath(), _options.demangleSymbol(target->name()), target->safeFilePath());
}
return;
}
if ( target == NULL )
return;
+ const uint64_t pointerSize = (_options.architecture() & CPU_ARCH_ABI64) ? 8 : 4;
bool inReadOnlySeg = ((_options.initialSegProtection(sect->segmentName()) & VM_PROT_WRITE) == 0);
bool needsRebase = false;
bool needsBinding = false;
if ( (targetAddress+checkAddend) > sctEnd ) {
warning("data symbol %s from %s has pointer to %s + 0x%08llX. "
"That large of an addend may disable %s from being put in the dyld shared cache.",
- atom->name(), atom->file()->path(), target->name(), addend, _options.installPath() );
+ atom->name(), atom->safeFilePath(), target->name(), addend, _options.installPath() );
}
}
}
}
}
+ if ( ((address & (pointerSize-1)) != 0) && (rebaseType == REBASE_TYPE_POINTER) ) {
+ if ( (pointerSize == 8) && ((address & 7) == 4) ) {
+ // for now, don't warning about 8-byte pointers only 4-byte aligned
+ }
+ else {
+ warning("pointer not aligned at address 0x%llX (%s + %lld from %s)",
+ address, atom->name(), (address - atom->finalAddress()), atom->safeFilePath());
+ }
+ }
_rebaseInfo.push_back(RebaseInfo(rebaseType, address));
}
if ( needsBinding ) {
noteTextReloc(atom, target);
sect->hasExternalRelocs = true; // so dyld knows to change permissions on __TEXT segment
}
+ if ( ((address & (pointerSize-1)) != 0) && (type == BIND_TYPE_POINTER) ) {
+ if ( (pointerSize == 8) && ((address & 7) == 4) ) {
+ // for now, don't warning about 8-byte pointers only 4-byte aligned
+ }
+ else {
+ warning("pointer not aligned at address 0x%llX (%s + %lld from %s)",
+ address, atom->name(), (address - atom->finalAddress()), atom->safeFilePath());
+ }
+ }
_bindingInfo.push_back(BindingInfo(type, this->compressedOrdinalForAtom(target), target->name(), weak_import, address, addend));
}
if ( needsLazyBinding ) {
assert(minusTarget->definition() != ld::Atom::definitionProxy);
assert(target != NULL);
assert(target->definition() != ld::Atom::definitionProxy);
- // make sure target is not global and weak
- if ( (target->scope() == ld::Atom::scopeGlobal) && (target->combine() == ld::Atom::combineByName)
- && (atom->section().type() != ld::Section::typeCFI)
- && (atom->section().type() != ld::Section::typeDtraceDOF)
- && (atom->section().type() != ld::Section::typeUnwindInfo)
- && (minusTarget != target) ) {
- // ok for __eh_frame and __uwind_info to use pointer diffs to global weak symbols
- throwf("bad codegen, pointer diff in %s to global weak symbol %s", atom->name(), target->name());
+ // check if target of pointer-diff is global and weak
+ if ( (target->scope() == ld::Atom::scopeGlobal) && (target->combine() == ld::Atom::combineByName) && (target->definition() == ld::Atom::definitionRegular) ) {
+ if ( (atom->section().type() == ld::Section::typeCFI)
+ || (atom->section().type() == ld::Section::typeDtraceDOF)
+ || (atom->section().type() == ld::Section::typeUnwindInfo) ) {
+ // ok for __eh_frame and __uwind_info to use pointer diffs to global weak symbols
+ return;
+ }
+ // Have direct reference to weak-global. This should be an indrect reference
+ const char* demangledName = strdup(_options.demangleSymbol(atom->name()));
+ warning("direct access in function '%s' from file '%s' to global weak symbol '%s' from file '%s' means the weak symbol cannot be overridden at runtime. "
+ "This was likely caused by different translation units being compiled with different visibility settings.",
+ demangledName, atom->safeFilePath(), _options.demangleSymbol(target->name()), target->safeFilePath());
}
return;
}
case ld::Fixup::kindStoreThumbLow16:
// no way to encode rebasing of binding for these instructions
if ( _options.outputSlidable() || (target->definition() == ld::Atom::definitionProxy) )
- throwf("no supported runtime lo16 relocation in %s from %s to %s", atom->name(), atom->file()->path(), target->name());
+ throwf("no supported runtime lo16 relocation in %s from %s to %s", atom->name(), atom->safeFilePath(), target->name());
break;
case ld::Fixup::kindStoreARMHigh16:
case ld::Fixup::kindStoreThumbHigh16:
// no way to encode rebasing of binding for these instructions
if ( _options.outputSlidable() || (target->definition() == ld::Atom::definitionProxy) )
- throwf("no supported runtime hi16 relocation in %s from %s to %s", atom->name(), atom->file()->path(), target->name());
+ throwf("no supported runtime hi16 relocation in %s from %s to %s", atom->name(), atom->safeFilePath(), target->name());
break;
default:
}
}
+static std::string realPathString(const char* path)
+{
+ char realName[MAXPATHLEN];
+ if ( realpath(path, realName) != NULL )
+ return realName;
+ return path;
+}
+
void OutputFile::writeJSONEntry(ld::Internal& state)
{
if ( _options.traceEmitJSON() && (_options.UUIDMode() != Options::kUUIDNone) && (_options.traceOutputFile() != NULL) ) {
if (dynamicList.size() > 0) {
jsonEntry += ",\"dynamic\":[";
for (const ld::dylib::File* dylib : dynamicList) {
- jsonEntry += "\"" + std::string(dylib->path()) + "\"";
+ jsonEntry += "\"" + realPathString(dylib->path()) + "\"";
if ((dylib != dynamicList.back())) {
jsonEntry += ",";
}
if (upwardList.size() > 0) {
jsonEntry += ",\"upward-dynamic\":[";
for (const ld::dylib::File* dylib : upwardList) {
- jsonEntry += "\"" + std::string(dylib->path()) + "\"";
+ jsonEntry += "\"" + realPathString(dylib->path()) + "\"";
if ((dylib != upwardList.back())) {
jsonEntry += ",";
}
if (reexportList.size() > 0) {
jsonEntry += ",\"re-exports\":[";
for (const ld::dylib::File* dylib : reexportList) {
- jsonEntry += "\"" + std::string(dylib->path()) + "\"";
+ jsonEntry += "\"" + realPathString(dylib->path()) + "\"";
if ((dylib != reexportList.back())) {
jsonEntry += ",";
}
if (state.archivePaths.size() > 0) {
jsonEntry += ",\"archives\":[";
for (const std::string& archivePath : state.archivePaths) {
- jsonEntry += "\"" + std::string(archivePath) + "\"";
+ jsonEntry += "\"" + realPathString(archivePath.c_str()) + "\"";
if ((archivePath != state.archivePaths.back())) {
jsonEntry += ",";
}
}
jsonEntry += "]";
}
+
+ if (state.bundleLoader != NULL) {
+ jsonEntry += ",\"bundle-loader\":";
+ jsonEntry += "\"" + realPathString(state.bundleLoader->path()) + "\"";
+ }
+
jsonEntry += "}\n";
// Write the JSON entry to the trace file.
- std::ofstream out(_options.traceOutputFile(), ios::app);
- out << jsonEntry;
+ _options.writeToTraceFile(jsonEntry.c_str(), jsonEntry.size());
}
}
}
}
-static void userReadableSwiftVersion(uint8_t value, char versionString[64])
-{
- switch (value) {
- case 1:
- strcpy(versionString, "1.0");
- break;
- case 2:
- strcpy(versionString, "1.1");
- break;
- case 3:
- strcpy(versionString, "2.0");
- break;
- case 4:
- strcpy(versionString, "3.0");
- break;
- default:
- sprintf(versionString, "unknown ABI version 0x%02X", value);
- }
-}
void Resolver::doFile(const ld::File& file)
{
// Resolve bitcode section in the object file
if ( _options.bundleBitcode() ) {
if ( objFile->getBitcode() == NULL ) {
- // No bitcode section, figure out if the object file comes from LTO/compiler static library
- if (objFile->sourceKind() != ld::relocatable::File::kSourceLTO &&
- objFile->sourceKind() != ld::relocatable::File::kSourceCompilerArchive ) {
+ // Handle the special case for compiler_rt objects. Add the file to the list to be process.
+ if ( objFile->sourceKind() == ld::relocatable::File::kSourceCompilerArchive ) {
+ _internal.filesFromCompilerRT.push_back(objFile);
+ } else if (objFile->sourceKind() != ld::relocatable::File::kSourceLTO ) {
+ // No bitcode section, figure out if the object file comes from LTO/compiler static library
switch ( _options.platform() ) {
case Options::kPlatformOSX:
case Options::kPlatformUnknown:
else if ( file.swiftVersion() != _internal.swiftVersion ) {
char fileVersion[64];
char otherVersion[64];
- userReadableSwiftVersion(file.swiftVersion(), fileVersion);
- userReadableSwiftVersion(_internal.swiftVersion, otherVersion);
+ Options::userReadableSwiftVersion(file.swiftVersion(), fileVersion);
+ Options::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 if ( file.swiftVersion() != _internal.swiftVersion ) {
char fileVersion[64];
char otherVersion[64];
- userReadableSwiftVersion(file.swiftVersion(), fileVersion);
- userReadableSwiftVersion(_internal.swiftVersion, otherVersion);
+ Options::userReadableSwiftVersion(file.swiftVersion(), fileVersion);
+ Options::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 if ( _options.outputKind() == Options::kDynamicLibrary ) {
if ( atom.file() != NULL )
- warning("target OS does not support re-exporting symbol %s from %s\n", _options.demangleSymbol(name), atom.file()->path());
+ warning("target OS does not support re-exporting symbol %s from %s\n", _options.demangleSymbol(name), atom.safeFilePath());
else
warning("target OS does not support re-exporting symbol %s\n", _options.demangleSymbol(name));
}
}
else {
if ( atom.file() != NULL )
- warning("cannot export hidden symbol %s from %s", _options.demangleSymbol(name), atom.file()->path());
+ warning("cannot export hidden symbol %s from %s", _options.demangleSymbol(name), atom.safeFilePath());
else
warning("cannot export hidden symbol %s", _options.demangleSymbol(name));
}
(const_cast<ld::Atom*>(&atom))->setScope(ld::Atom::scopeGlobal);
}
else {
- throwf("requested re-export symbol %s is not from a dylib, but from %s\n", _options.demangleSymbol(name), atom.file()->path());
+ throwf("requested re-export symbol %s is not from a dylib, but from %s\n", _options.demangleSymbol(name), atom.safeFilePath());
}
}
break;
//fprintf(stderr, "demote %s to hidden\n", name);
}
if ( _options.canReExportSymbols() && _options.shouldReExport(name) ) {
- throwf("requested re-export symbol %s is not from a dylib, but from %s\n", _options.demangleSymbol(name), atom.file()->path());
+ throwf("requested re-export symbol %s is not from a dylib, but from %s\n", _options.demangleSymbol(name), atom.safeFilePath());
}
break;
}
//fprintf(stderr, "markLive(%p) %s\n", &atom, atom.name());
// if -why_live cares about this symbol, then dump chain
if ( (previous->referer != NULL) && _options.printWhyLive(atom.name()) ) {
- fprintf(stderr, "%s from %s\n", atom.name(), atom.file()->path());
+ fprintf(stderr, "%s from %s\n", atom.name(), atom.safeFilePath());
int depth = 1;
for(WhyLiveBackChain* p = previous; p != NULL; p = p->previous, ++depth) {
for(int i=depth; i > 0; --i)
fprintf(stderr, " ");
- fprintf(stderr, "%s from %s\n", p->referer->name(), p->referer->file()->path());
+ fprintf(stderr, "%s from %s\n", p->referer->name(), p->referer->safeFilePath());
}
}
++foundReferenceCount;
}
else if ( atom->contentType() == ld::Atom::typeCFI ) {
- fprintf(stderr, " Dwarf Exception Unwind Info (__eh_frame) in %s\n", pathLeafName(atom->file()->path()));
+ fprintf(stderr, " Dwarf Exception Unwind Info (__eh_frame) in %s\n", pathLeafName(atom->safeFilePath()));
++foundReferenceCount;
}
else {
- fprintf(stderr, " %s in %s\n", _options.demangleSymbol(atom->name()), pathLeafName(atom->file()->path()));
+ fprintf(stderr, " %s in %s\n", _options.demangleSymbol(atom->name()), pathLeafName(atom->safeFilePath()));
++foundReferenceCount;
break; // if undefined used twice in a function, only show first
}
optOpt.pie = _options.positionIndependentExecutable();
optOpt.mainExecutable = _options.linkingMainExecutable();;
optOpt.staticExecutable = (_options.outputKind() == Options::kStaticExecutable);
+ optOpt.preload = (_options.outputKind() == Options::kPreload);
optOpt.relocatable = (_options.outputKind() == Options::kObjectFile);
optOpt.allowTextRelocs = _options.allowTextRelocs();
optOpt.linkerDeadStripping = _options.deadCodeStrip();
optOpt.armUsesZeroCostExceptions = _options.armUsesZeroCostExceptions();
optOpt.simulator = _options.targetIOSSimulator();
optOpt.ignoreMismatchPlatform = ((_options.outputKind() == Options::kPreload) || (_options.outputKind() == Options::kStaticExecutable));
- optOpt.bitcodeBundle = _options.bundleBitcode();
+ optOpt.bitcodeBundle = (_options.bundleBitcode() && (_options.bitcodeKind() != Options::kBitcodeMarker));
optOpt.maxDefaultCommonAlignment = _options.maxDefaultCommonAlign();
optOpt.arch = _options.architecture();
optOpt.mcpu = _options.mcpuLTO();
// if -dead_strip on command line
if ( _options.deadCodeStrip() ) {
- // clear liveness bit
+ // run through all atoms again and make live_section LTO atoms are preserved from dead_stripping if needed
+ _dontDeadStripIfReferencesLive.clear();
for (std::vector<const ld::Atom*>::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) {
+ const ld::Atom* atom = *it;
+ if ( atom->dontDeadStripIfReferencesLive() ) {
+ _dontDeadStripIfReferencesLive.push_back(atom);
+ }
+
+ // clear liveness bit
(const_cast<ld::Atom*>(*it))->setLive((*it)->dontDeadStrip());
}
// and re-compute dead code
// check if file is already in the list, add it if not
bool found = false;
for (DuplicatedSymbolAtomList::iterator it = atoms->begin(); !found && it != atoms->end(); it++)
- if (strcmp((*it)->file()->path(), atom->file()->path()) == 0)
+ if (strcmp((*it)->safeFilePath(), atom->safeFilePath()) == 0)
found = true;
if (!found)
atoms->push_back(atom);
foundDuplicate = true;
fprintf(stderr, "duplicate symbol %s in:\n", symbolIt->first);
for (DuplicatedSymbolAtomList::iterator atomIt = atoms->begin(); atomIt != atoms->end(); atomIt++) {
- fprintf(stderr, " %s\n", (*atomIt)->file()->path());
+ fprintf(stderr, " %s\n", (*atomIt)->safeFilePath());
}
}
}
case Options::kCommonsIgnoreDylibs:
if ( _options.warnCommons() )
warning("using common symbol %s from %s and ignoring defintion from dylib %s",
- proxy.name(), proxy.file()->path(), dylib.file()->path());
+ proxy.name(), proxy.safeFilePath(), dylib.safeFilePath());
pickAtom(dylib);
break;
case Options::kCommonsOverriddenByDylibs:
if ( _options.warnCommons() )
warning("replacing common symbol %s from %s with true definition from dylib %s",
- proxy.name(), proxy.file()->path(), dylib.file()->path());
+ proxy.name(), proxy.safeFilePath(), dylib.safeFilePath());
pickAtom(proxy);
break;
case Options::kCommonsConflictsDylibsError:
throwf("common symbol %s from %s conflicts with defintion from dylib %s",
- proxy.name(), proxy.file()->path(), dylib.file()->path());
+ proxy.name(), proxy.safeFilePath(), dylib.safeFilePath());
}
}
} else if ( _atomB.combine() == ld::Atom::combineByName ) {
pickAtomA();
} else {
- throwf("symbol %s exported from both %s and %s\n", _atomA.name(), _atomA.file()->path(), _atomB.file()->path());
+ throwf("symbol %s exported from both %s and %s\n", _atomA.name(), _atomA.safeFilePath(), _atomB.safeFilePath());
}
}
break;
case ld::Atom::definitionTentative:
if ( _atomB.size() > _atomA.size() ) {
- const char* atomApath = (_atomA.file() != NULL) ? _atomA.file()->path() : "<internal>";
- const char* atomBpath = (_atomB.file() != NULL) ? _atomB.file()->path() : "<internal>";
warning("tentative definition of '%s' with size %llu from '%s' is being replaced by real definition of smaller size %llu from '%s'",
- _atomA.name(), _atomB.size(), atomBpath, _atomA.size(), atomApath);
+ _atomA.name(), _atomB.size(), _atomB.safeFilePath(), _atomA.size(), _atomA.safeFilePath());
}
pickAtomA();
break;
switch (_atomB.definition()) {
case ld::Atom::definitionRegular:
if ( _atomA.size() > _atomB.size() ) {
- const char* atomApath = (_atomA.file() != NULL) ? _atomA.file()->path() : "<internal>";
- const char* atomBpath = (_atomB.file() != NULL) ? _atomB.file()->path() : "<internal>";
warning("tentative definition of '%s' with size %llu from '%s' is being replaced by real definition of smaller size %llu from '%s'",
- _atomA.name(), _atomA.size(),atomApath, _atomB.size(), atomBpath);
+ _atomA.name(), _atomA.size(),_atomA.safeFilePath(), _atomB.size(), _atomB.safeFilePath());
}
pickAtomB();
break;
void SymbolTable::markCoalescedAway(const ld::Atom* atom)
{
// remove this from list of all atoms used
- //fprintf(stderr, "markCoalescedAway(%p) from %s\n", atom, atom->file()->path());
+ //fprintf(stderr, "markCoalescedAway(%p) from %s\n", atom, atom->safeFilePath());
(const_cast<ld::Atom*>(atom))->setCoalescedAway();
//
//ReferencesHash obj;
//for(ReferencesHashToSlot::iterator it=_byReferencesTable.begin(); it != _byReferencesTable.end(); ++it) {
// if ( obj.operator()(it->first) == 0x2F3AC0EAC744EA70 ) {
- // fprintf(stderr, "hash=0x2F3AC0EAC744EA70 for %p %s from %s\n", it->first, it->first->name(), it->first->file()->path());
+ // fprintf(stderr, "hash=0x2F3AC0EAC744EA70 for %p %s from %s\n", it->first, it->first->name(), it->first->safeFilePath());
//
// }
//}
virtual bool hasLongBranchStubs() { return false; }
virtual LinkerOptionsList* linkerOptions() const = 0;
virtual SourceKind sourceKind() const { return kSourceUnknown; }
+ virtual const uint8_t* fileContent() const { return nullptr; }
};
} // namespace relocatable
_definition = a._definition;
_combine = a._combine;
_dontDeadStrip = a._dontDeadStrip;
+ _dontDeadStripIfRefLive = a._dontDeadStripIfRefLive;
_thumb = a._thumb;
_autoHide = a._autoHide;
_contentType = a._contentType;
_weakImportState = a._weakImportState;
}
+ const char* safeFilePath() const {
+ const File* f = this->file();
+ if ( f != NULL )
+ return f->path();
+ else
+ return "<internal>";
+ }
+
protected:
enum AddressMode { modeSectionOffset, modeFinalAddress };
};
-
+
// utility classes for using std::unordered_map with c-strings
struct CStringHash {
size_t operator()(const char* __s) const {
hasThreadLocalVariableDefinitions(false),
hasWeakExternalSymbols(false),
someObjectHasOptimizationHints(false),
- dropAllBitcode(false), embedMarkerOnly(false) { }
+ dropAllBitcode(false), embedMarkerOnly(false),
+ forceLoadCompilerRT(false) { }
std::vector<FinalSection*> sections;
std::vector<ld::dylib::File*> dylibs;
CStringSet linkerOptionFrameworks;
std::vector<const ld::Atom*> indirectBindingTable;
std::vector<const ld::relocatable::File*> filesWithBitcode;
+ std::vector<const ld::relocatable::File*> filesFromCompilerRT;
std::vector<const ld::Atom*> deadAtoms;
std::unordered_set<const char*> allUndefProxies;
const ld::dylib::File* bundleLoader;
bool someObjectHasOptimizationHints;
bool dropAllBitcode;
bool embedMarkerOnly;
+ bool forceLoadCompilerRT;
std::vector<std::string> ltoBitcodePath;
};
break;
}
}
+ if ( pcRange == 0 ) {
+ warn(ref, pcStart, "FDE found for zero size function");
+ break;
+ }
//fprintf(stderr, "FDE for func at 0x%08X, alreadyHaveCU=%d\n", (uint32_t)entry->u.fdeInfo.function.targetAddress, alreadyHaveCU);
if ( alreadyHaveCU && !forceDwarfConversion ) {
if ( keepDwarfWhichHasCU )
#include <stdlib.h>
#include <sys/param.h>
#include <sys/fcntl.h>
+#include <sys/mman.h>
#include <sys/stat.h>
#include <errno.h>
#include <pthread.h>
static bool sHasTriedLocalContext;
bool mergeIntoGenerator(lto_code_gen_t generator, bool useSetModule);
#if LTO_API_VERSION >= 18
- void addToThinGenerator(thinlto_code_gen_t generator);
+ void addToThinGenerator(thinlto_code_gen_t generator, int id);
#endif
private:
friend class Atom;
std::vector<const ld::Atom*>& newAtoms,
std::vector<const char*>& additionalUndefines);
+#if LTO_API_VERSION >= 18
static thinlto_code_gen_t init_thinlto_codegen(const std::vector<File*>& files,
const std::vector<const ld::Atom*>& allAtoms,
ld::Internal& state,
const OptimizeOptions& options,
CStringToAtom& deadllvmAtoms,
CStringToAtom& llvmAtoms);
+#endif
static std::vector<File*> _s_files;
static bool _s_llvmOptionsProcessed;
}
#if LTO_API_VERSION >= 18
-void File::addToThinGenerator(thinlto_code_gen_t generator) {
+void File::addToThinGenerator(thinlto_code_gen_t generator, int id) {
assert(!_module && "Expected module to be disposed");
- ::thinlto_codegen_add_module(generator, _path, (const char *)_content, _contentLength);
+ std::string pathWithID = _path;
+ pathWithID += std::to_string(id);
+ ::thinlto_codegen_add_module(generator, pathWithID.c_str(), (const char *)_content, _contentLength);
}
#endif
else
return LTO_CODEGEN_PIC_MODEL_STATIC;
}
+ else if ( options.preload ) {
+ if ( options.pie )
+ return LTO_CODEGEN_PIC_MODEL_DYNAMIC;
+ else
+ return LTO_CODEGEN_PIC_MODEL_STATIC;
+ }
else {
if ( options.pie )
return LTO_CODEGEN_PIC_MODEL_DYNAMIC;
return true;
}
+#if LTO_API_VERSION >= 18
// Create the ThinLTO codegenerator
thinlto_code_gen_t Parser::init_thinlto_codegen(const std::vector<File*>& files,
const std::vector<const ld::Atom*>& allAtoms,
return thingenerator;
}
+#endif
// Full LTO processing
bool Parser::optimizeThinLTO(const std::vector<File*>& files,
ld::File::Ordinal lastOrdinal;
+ int FileId = 0;
for (auto *f : files) {
if ( logBitcodeFiles) fprintf(stderr, "thinlto_codegen_add_module(%s)\n", f->path());
- f->addToThinGenerator(thingenerator);
+ f->addToThinGenerator(thingenerator, FileId++);
lastOrdinal = f->ordinal();
}
thinlto_codegen_set_codegen_only(thingenerator, true);
#endif
+ // If object_path_lto is used, we switch to a file-based API: libLTO will
+ // generate the files on disk and we'll map them on-demand.
+
+#if LTO_API_VERSION >= 21
+ bool useFileBasedAPI = (options.tmpObjectFilePath && ::lto_api_version() >= 21);
+ if ( useFileBasedAPI )
+ thinlto_set_generated_objects_dir(thingenerator, options.tmpObjectFilePath);
+#endif
+
// run code generator
thinlto_codegen_process(thingenerator);
- auto numObjects = thinlto_module_get_num_objects(thingenerator);
- if (!numObjects)
+
+ unsigned numObjects;
+#if LTO_API_VERSION >= 21
+ if ( useFileBasedAPI )
+ numObjects = thinlto_module_get_num_object_files(thingenerator);
+ else
+#endif
+ numObjects = thinlto_module_get_num_objects(thingenerator);
+ if ( numObjects == 0 )
throwf("could not do ThinLTO codegen (thinlto_codegen_process didn't produce any object): '%s', using libLTO version '%s'", ::lto_get_error_message(), ::lto_get_version());
// if requested, save off objects files
if ( fd != -1 ) {
::write(fd, machOFile.Buffer, machOFile.Size);
::close(fd);
- } else {
+ }
+ else {
warning("unable to write temporary ThinLTO output: %s", tempMachoPath.c_str());
}
}
}
-
// mach-o parsing is done in-memory, but need path for debug notes
std::string macho_dirpath = "/tmp/thinlto.o";
if ( options.tmpObjectFilePath != NULL ) {
macho_dirpath = options.tmpObjectFilePath;
struct stat statBuffer;
if( stat(macho_dirpath.c_str(), &statBuffer) != 0 || !S_ISDIR(statBuffer.st_mode) ) {
+ unlink(macho_dirpath.c_str());
if ( mkdir(macho_dirpath.c_str(), 0700) !=0 ) {
warning("unable to create ThinLTO output directory for temporary object files: %s", macho_dirpath.c_str());
}
}
}
+ auto get_thinlto_buffer_or_load_file = [&] (unsigned ID) {
+#if LTO_API_VERSION >= 21
+ if ( useFileBasedAPI ) {
+ const char* path = thinlto_module_get_object_file(thingenerator, ID);
+ // map in whole file
+ struct stat stat_buf;
+ int fd = ::open(path, O_RDONLY, 0);
+ if ( fd == -1 )
+ throwf("can't open thinlto file '%s', errno=%d", path, errno);
+ if ( ::fstat(fd, &stat_buf) != 0 )
+ throwf("fstat thinlto file '%s' failed, errno=%d\n", path, errno);
+ size_t len = stat_buf.st_size;
+ if ( len < 20 )
+ throwf("ThinLTO file '%s' too small (length=%zu)", path, len);
+ const char* p = (const char*)::mmap(NULL, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
+ if ( p == (const char*)(-1) )
+ throwf("can't map file, errno=%d", errno);
+ ::close(fd);
+ return LTOObjectBuffer{ p, len };
+ }
+#endif
+ return thinlto_module_get_object(thingenerator, ID);
+ };
+
auto ordinal = ld::File::Ordinal::LTOOrdinal().nextFileListOrdinal();
for (unsigned bufID = 0; bufID < numObjects; ++bufID) {
- auto machOFile = thinlto_module_get_object(thingenerator, bufID);
+ auto machOFile = get_thinlto_buffer_or_load_file(bufID);
if (!machOFile.Size) {
warning("Ignoring empty buffer generated by ThinLTO");
continue;
}
// mach-o parsing is done in-memory, but need path for debug notes
- std::string tmp_path = macho_dirpath + "/" + std::to_string(bufID) + ".o";
-
- // if needed, save temp mach-o file to specific location
- if ( options.tmpObjectFilePath != NULL ) {
+ std::string tmp_path;
+#if LTO_API_VERSION >= 21
+ if ( useFileBasedAPI ) {
+ tmp_path = thinlto_module_get_object_file(thingenerator, bufID);
+ }
+ else
+#endif
+ if ( options.tmpObjectFilePath != NULL) {
+ tmp_path = macho_dirpath + "/" + std::to_string(bufID) + ".o";
+ // if needed, save temp mach-o file to specific location
int fd = ::open(tmp_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
if ( fd != -1) {
::write(fd, (const uint8_t *)machOFile.Buffer, machOFile.Size);
CStringToAtom::const_iterator pos = _llvmAtoms.find(name);
if ( pos != _llvmAtoms.end() ) {
// turn Atom into a proxy for this mach-o atom
+ if (pos->second->scope() == ld::Atom::scopeLinkageUnit) {
+ if (log) fprintf(stderr, "demote %s to hidden after LTO\n", name);
+ (const_cast<ld::Atom*>(&machoAtom))->setScope(ld::Atom::scopeLinkageUnit);
+ }
pos->second->setCompiledAtom(machoAtom);
_lastProxiedAtom = &machoAtom;
_lastProxiedFile = pos->second->file();
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, llvmAtom->second, machoAtom.name());
+ if ( llvmAtom->second->coalescedAway() ) {
+ if (log) fprintf(stderr, "AtomSyncer: dead coalesced atom %s\n", machoAtom.name());
+ // <rdar://problem/28269547>
+ // We told libLTO to keep a weak atom that will replaced by an native mach-o atom.
+ // We also need to remove any atoms directly dependent on this (FDE, LSDA).
+ for (ld::Fixup::iterator fit=machoAtom.fixupsBegin(), fend=machoAtom.fixupsEnd(); fit != fend; ++fit) {
+ switch ( fit->kind ) {
+ case ld::Fixup::kindNoneGroupSubordinate:
+ case ld::Fixup::kindNoneGroupSubordinateFDE:
+ case ld::Fixup::kindNoneGroupSubordinateLSDA:
+ assert(fit->binding == ld::Fixup::bindingDirectlyBound);
+ (const_cast<ld::Atom*>(fit->u.target))->setCoalescedAway();
+ if (log) fprintf(stderr, "AtomSyncer: mark coalesced-away subordinate atom %s\n", fit->u.target->name());
+ break;
+ default:
+ break;
+ }
+ }
+ }
}
}
else
bool pie;
bool mainExecutable;
bool staticExecutable;
+ bool preload;
bool relocatable;
bool allowTextRelocs;
bool linkerDeadStripping;
virtual ld::Bitcode* getBitcode() const { return _bitcode.get(); }
virtual SourceKind sourceKind() const { return _srcKind; }
- const uint8_t* fileContent() { return _fileContent; }
+ virtual const uint8_t* fileContent() const { return _fileContent; }
private:
friend class Atom<A>;
friend class Section<A>;
#define STACK_ALLOC_IF_SMALL(_type, _name, _actual_count, _maxCount) \
_type* _name = NULL; \
uint32_t _name##_count = 1; \
- if ( _actual_count > _maxCount ) \
+ uint32_t _name##_stack_count = _actual_count; \
+ if ( _actual_count > _maxCount ) { \
_name = (_type*)malloc(sizeof(_type) * _actual_count); \
+ _name##_stack_count = 1; \
+ } \
else \
_name##_count = _actual_count; \
- _type _name##_buffer[_name##_count]; \
+ _type _name##_buffer[_name##_stack_count]; \
if ( _name == NULL ) \
_name = _name##_buffer;
// create lists of address that already have compact unwind and thus don't need the dwarf parsed
unsigned cuLsdaCount = 0;
- pint_t cuStarts[countOfCUs];
+ STACK_ALLOC_IF_SMALL(pint_t, cuStarts, countOfCUs, 1024);
for (uint32_t i=0; i < countOfCUs; ++i) {
if ( CUSection<A>::encodingMeansUseDwarf(cuInfoArray[i].compactUnwindInfo) )
cuStarts[i] = -1;
bool Section<x86_64>::addRelocFixup(class Parser<x86_64>& parser, const macho_relocation_info<P>* reloc)
{
const macho_section<P>* sect = this->machoSection();
+ if ( sect == NULL ) {
+ warning("malformed mach-o, relocations not supported on section %s", this->sectionName());
+ return false;
+ }
uint64_t srcAddr = sect->addr() + reloc->r_address();
Parser<x86_64>::SourceLocation src;
Parser<x86_64>::TargetDesc target;
: Base(strdup(path), mTime, ord, platform, linkMinOSVersion, allowWeakImports, linkingFlatNamespace,
hoistImplicitPublicDylibs, allowSimToMacOSX, addVers)
{
+ std::unique_ptr<tapi::LinkerInterfaceFile> file;
+ std::string errorMessage;
+
+// <rdar://problem/29038544> Support $ld$weak symbols in .tbd files
+#if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 1) || (TAPI_API_VERSION_MAJOR > 1))
+ // Check if the library supports the new create API.
+ if (tapi::APIVersion::isAtLeast(1, 1)) {
+ tapi::ParsingFlags flags = tapi::ParsingFlags::None;
+ if (enforceDylibSubtypesMatch)
+ flags |= tapi::ParsingFlags::ExactCpuSubType;
+
+ if (!allowWeakImports)
+ flags |= tapi::ParsingFlags::DisallowWeakImports;
+
+ file.reset(tapi::LinkerInterfaceFile::create(
+ path, fileContent, fileLength, cpuType, cpuSubType, flags,
+ tapi::PackedVersion32(linkMinOSVersion), errorMessage));
+ } else {
+ auto matchingType = enforceDylibSubtypesMatch ?
+ tapi::CpuSubTypeMatching::Exact : tapi::CpuSubTypeMatching::ABI_Compatible;
+
+ file.reset(tapi::LinkerInterfaceFile::create(
+ path, fileContent, fileLength, cpuType, cpuSubType, matchingType,
+ tapi::PackedVersion32(linkMinOSVersion), errorMessage));
+ }
+#else
auto matchingType = enforceDylibSubtypesMatch ?
- tapi::CpuSubTypeMatching::Exact : tapi::CpuSubTypeMatching::ABI_Compatible;
+ tapi::CpuSubTypeMatching::Exact : tapi::CpuSubTypeMatching::ABI_Compatible;
- std::string errorMessage;
- auto file = std::unique_ptr<tapi::LinkerInterfaceFile>(
- tapi::LinkerInterfaceFile::create(path, fileContent, fileLength, cpuType,
- cpuSubType, matchingType,
- tapi::PackedVersion32(linkMinOSVersion), errorMessage));
+ file.reset(tapi::LinkerInterfaceFile::create(
+ path, fileContent, fileLength, cpuType, cpuSubType, matchingType,
+ tapi::PackedVersion32(linkMinOSVersion), errorMessage));
+#endif
if (file == nullptr)
throw strdup(errorMessage.c_str());
ld::Internal& _state;
};
+#if LTO_API_VERSION >= 7
+static void ltoDiagnosticHandler(lto_codegen_diagnostic_severity_t severity, const char* message, void*)
+{
+ switch ( severity ) {
+#if LTO_API_VERSION >= 10
+ case LTO_DS_REMARK:
+#endif
+ case LTO_DS_NOTE:
+ break; // ignore remarks and notes
+ case LTO_DS_WARNING:
+ warning("%s", message);
+ break;
+ case LTO_DS_ERROR:
+ throwf("%s", message);
+ }
+}
+#endif
+
+
BitcodeAtom::BitcodeAtom()
: ld::Atom(bitcodeBundleSection,
ld::Atom::definitionRegular, ld::Atom::combineNever,
_lto_get_asm_symbol_num == NULL || _lto_get_asm_symbol_name == NULL || ::lto_api_version() < 14 )
throwf("loaded libLTO doesn't support -bitcode_hide_symbols: %s", ::lto_get_version());
_obfuscator = ::lto_codegen_create_in_local_context();
+
+#if LTO_API_VERSION >= 7
+ lto_codegen_set_diagnostic_handler(_obfuscator, ltoDiagnosticHandler, NULL);
+#endif
+
#if LTO_API_VERSION >= 14
lto_codegen_set_should_internalize(_obfuscator, false);
#endif
strcmp(key, "hide-symbols") == 0 ||
strcmp(key, "platform") == 0 ||
strcmp(key, "sdkversion") == 0 ||
+ strcmp(key, "swift-version") == 0 ||
strcmp(key, "dylibs/lib") == 0 ||
strcmp(key, "link-options/option") == 0 ) {
xar_prop_create(dst, key, val);
BitcodeHandler bitcodeHandler((char*)ltoTemp.getContent(), ltoTemp.getSize());
bitcodeHandler.populateMustPreserveSymbols(obfuscator);
}
+ // add must preserve symbols from compiler_rt input.
+ for ( auto &f : _state.filesFromCompilerRT ) {
+ std::vector<const char*> symbols;
+ if ( f->fileContent() && mach_o::relocatable::getNonLocalSymbols(f->fileContent(), symbols) ) {
+ for ( auto &sym : symbols)
+ obfuscator->addMustPreserveSymbols(sym);
+ }
+ }
// special symbols supplied by linker
obfuscator->addMustPreserveSymbols("___dso_handle");
if ( xar_prop_create((xar_file_t)linkXML, "sdkversion", _options.getSDKVersionStr().c_str()) != 0 )
throwf("could not add SDK version to bitcode bundle");
+ // Write swift version into header
+ if (_state.swiftVersion != 0) {
+ char swiftVersion[64];
+ Options::userReadableSwiftVersion(_state.swiftVersion, swiftVersion);
+ if ( xar_prop_create((xar_file_t)linkXML, "swift-version", swiftVersion) )
+ throwf("could not add swift version to bitcode bundle");
+ }
+
// Write dylibs
char sdkRoot[PATH_MAX];
if ( _options.sdkPaths().empty() || (realpath(_options.sdkPaths().front(), sdkRoot) == NULL) )
}
}
+ // Write if compiler_rt is force loaded
+ if (_state.forceLoadCompilerRT) {
+ if ( xar_prop_create((xar_file_t)linkXML, "rt-forceload", "1") != 0 )
+ throwf("could not add compiler_rt force_load info to bitcode bundle");
+ }
+
// Write link-line into archive
for ( auto &it : linkCmd ) {
if (xar_prop_create((xar_file_t)linkXML, "link-options/option", it.c_str()) != 0)
DeDupAliasAtom(const ld::Atom* dupOf, const ld::Atom* replacement) :
ld::Atom(dupOf->section(), ld::Atom::definitionRegular, ld::Atom::combineNever,
dupOf->scope(), dupOf->contentType(), ld::Atom::symbolTableIn,
- false, false, true, 0),
+ false, false, true, dupOf->alignment()),
_dedupOf(dupOf),
_fixup(0, ld::Fixup::k1of1, ld::Fixup::kindNoneFollowOn, ld::Fixup::bindingDirectlyBound, replacement) {
if ( dupOf->autoHide() )
// A helper for std::unordered_map<> that compares functions
struct atom_equal {
- struct VisitedSet {
- std::unordered_set<const ld::Atom*> atoms1;
- std::unordered_set<const ld::Atom*> atoms2;
+ struct BackChain {
+ BackChain* prev;
+ const Atom* inCallChain1;
+ const Atom* inCallChain2;
+
+ bool inCallChain(const Atom* target) {
+ for (BackChain* p = this; p->prev != NULL; p = p->prev) {
+ if ( (p->inCallChain1 == target) || (p->inCallChain2 == target) )
+ return true;
+ }
+ return false;
+ }
};
- static bool sameFixups(const ld::Atom* atom1, const ld::Atom* atom2, VisitedSet& visited) {
+ static bool sameFixups(const ld::Atom* atom1, const ld::Atom* atom2, BackChain& backChain) {
++sFixupCompareCount;
//fprintf(stderr, "sameFixups(%s,%s)\n", atom1->name(), atom2->name());
Fixup::iterator f1 = atom1->fixupsBegin();
return false;
if ( target1->section().type() != ld::Section::typeCode )
return false;
- // to support co-recursive functions, don't call equals() on targets we've already visited
- if ( ((visited.atoms1.count(target1) == 0) || (visited.atoms2.count(target2) == 0)) && !equal(target1, target2, visited) )
- return false;
- }
+ // to support co-recursive functions, don't recurse into equals() for targets already in the back chain
+ if ( !backChain.inCallChain(target1) || !backChain.inCallChain(target2) ) {
+ BackChain nextBackChain;
+ nextBackChain.prev = &backChain;
+ nextBackChain.inCallChain1 = target1;
+ nextBackChain.inCallChain2 = target2;
+ if ( !equal(target1, target2, nextBackChain) )
+ return false;
+ }
+ }
++f1;
++f2;
return true;
}
- static bool equal(const ld::Atom* atom1, const ld::Atom* atom2, VisitedSet& visited) {
+ static bool equal(const ld::Atom* atom1, const ld::Atom* atom2, BackChain& backChain) {
+ if ( atom1->size() != atom2->size() )
+ return false;
if ( atom_hashing::hash(atom1) != atom_hashing::hash(atom2) )
return false;
- visited.atoms1.insert(atom1);
- visited.atoms2.insert(atom2);
- return sameFixups(atom1, atom2, visited);
+ if ( memcmp(atom1->rawContentPointer(), atom2->rawContentPointer(), atom1->size()) != 0 )
+ return false;
+ bool result = sameFixups(atom1, atom2, backChain);
+ //fprintf(stderr, "sameFixups(%s,%s) = %d\n", atom1->name(), atom2->name(), result);
+ return result;
}
bool operator()(const ld::Atom* atom1, const ld::Atom* atom2) const {
- VisitedSet visited;
- return equal(atom1, atom2, visited);
+ BackChain backChain = { NULL, atom1, atom2 };
+ return equal(atom1, atom2, backChain);
}
};
if ( log ) {
fprintf(stderr, "atoms before pruning:\n");
for (const ld::Atom* atom : textSection->atoms)
- fprintf(stderr, " %p (size=%llu) %sp\n", atom, atom->size(), atom->name());
+ fprintf(stderr, " %p (size=%llu) %s\n", atom, atom->size(), atom->name());
}
// remove replaced atoms from section
textSection->atoms.erase(std::remove_if(textSection->atoms.begin(), textSection->atoms.end(),
if ( log ) {
fprintf(stderr, "atoms after pruning:\n");
for (const ld::Atom* atom : textSection->atoms)
- fprintf(stderr, " %p (size=%llu) %sp\n", atom, atom->size(), atom->name());
+ fprintf(stderr, " %p (size=%llu) %s\n", atom, atom->size(), atom->name());
}
//fprintf(stderr, "hash-count=%lu, fixup-compares=%lu, atom-count=%u\n", sHashCount, sFixupCompareCount, atomsBeingComparedCount);
if ( baseMethodListMethodNameAtoms.count(target) != 0 ) {
warning("%s method '%s' in category from %s overrides method from class in %s",
(meta ? "meta" : "instance"), target->rawContentPointer(),
- categoryMethodListAtom->file()->path(), baseMethodList->file()->path() );
+ categoryMethodListAtom->safeFilePath(), baseMethodList->safeFilePath() );
}
if ( categoryMethodNameAtoms.count(target) != 0 ) {
warning("%s method '%s' in category from %s conflicts with same method from another category",
(meta ? "meta" : "instance"), target->rawContentPointer(),
- categoryMethodListAtom->file()->path());
+ categoryMethodListAtom->safeFilePath());
}
categoryMethodNameAtoms.insert(target);
}
if (Category<A>::getClassProperties(state, categoryAtom)) {
haveCategoriesWithNonNullClassProperties = true;
- // fprintf(stderr, "category in file %s has non-null class properties\n", categoryAtom->file()->path());
+ // fprintf(stderr, "category in file %s has non-null class properties\n", categoryAtom->safeFilePath());
}
if (!categoryAtom->file()->objcHasCategoryClassPropertiesField()) {
haveCategoriesWithoutClassPropertyStorage = true;
- // fprintf(stderr, "category in file %s has no storage for class properties\n", categoryAtom->file()->path());
+ // fprintf(stderr, "category in file %s has no storage for class properties\n", categoryAtom->safeFilePath());
}
}
}
AtomToOrdinal::iterator pos = _ordinalOverrideMap.find(nextAtom);
if ( pos == _ordinalOverrideMap.end() ) {
_ordinalOverrideMap[nextAtom] = index++;
- if (_s_log ) fprintf(stderr, "override ordinal %u assigned to %s in cluster from %s\n", index, nextAtom->name(), nextAtom->file()->path());
+ if (_s_log ) fprintf(stderr, "override ordinal %u assigned to %s in cluster from %s\n", index, nextAtom->name(), nextAtom->safeFilePath());
}
else {
if (_s_log ) fprintf(stderr, "could not order %s as %u because it was already laid out earlier by %s as %u\n",
}
else {
_ordinalOverrideMap[atom] = index;
- if (_s_log ) fprintf(stderr, "override ordinal %u assigned to %s from %s\n", index, atom->name(), atom->file()->path());
+ if (_s_log ) fprintf(stderr, "override ordinal %u assigned to %s from %s\n", index, atom->name(), atom->safeFilePath());
}
++matchCount;
}
// 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;
- 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);
+ switch ( sect->type() ) {
+ case ld::Section::typeTempAlias:
+ case ld::Section::typeStub:
+ case ld::Section::typeLazyPointer:
+ case ld::Section::typeLazyPointerClose:
+ case ld::Section::typeStubClose:
+ case ld::Section::typeNonLazyPointer:
+ // these sections are already sorted by pass that created them
+ break;
+ default:
+ if ( log ) fprintf(stderr, "sorting section %s\n", sect->sectionName());
+ std::sort(sect->atoms.begin(), sect->atoms.end(), _comparer);
+ break;
+ }
}
if ( log ) {
rightString = (char*)cstringAtom->rawContentPointer();
}
}
- assert(leftString != NULL);
- assert(rightString != NULL);
- diff = strcmp(leftString, rightString);
- if ( diff != 0 )
- return (diff < 0);
+ if ( leftString != rightString ) {
+ assert(leftString != NULL);
+ assert(rightString != NULL);
+ diff = strcmp(leftString, rightString);
+ if ( diff != 0 )
+ return (diff < 0);
+ }
}
else if ( left->section().type() == ld::Section::typeLiteral4 ) {
// if literal sort by content
case ld::Fixup::kindStoreThumbHigh16:
printf(", then store high-16 in Thumb movt");
break;
+#if SUPPORT_ARCH_arm64
case ld::Fixup::kindStoreARM64Branch26:
printf(", then store as ARM64 26-bit pcrel branch");
break;
case ld::Fixup::kindStoreARM64PCRelToGOT:
printf(", then store as 32-bit delta to GOT entry");
break;
- case ld::Fixup::kindStoreARM64PointerToGOT32:
- printf(", then store as 32-bit pointer to GOT entry");
- break;
+#endif
case ld::Fixup::kindDtraceExtra:
printf("dtrace static probe extra info");
break;
case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear:
printf("Thumb dtrace static is-enabled site");
break;
+#if SUPPORT_ARCH_arm64
case ld::Fixup::kindStoreARM64DtraceCallSiteNop:
printf("ARM64 dtrace static probe site");
break;
case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear:
printf("ARM64 dtrace static is-enabled site");
break;
+#endif
case ld::Fixup::kindLazyTarget:
printf("lazy reference to external symbol %s", referenceTargetAtomName(ref));
break;
case ld::Fixup::kindSetTargetTLVTemplateOffsetLittleEndian64:
printf("tlv template offset of %s", referenceTargetAtomName(ref));
break;
+#if SUPPORT_ARCH_arm64
case ld::Fixup::kindStoreTargetAddressARM64Branch26:
printf("ARM64 store 26-bit pcrel branch to %s", referenceTargetAtomName(ref));
break;
case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12:
printf("ARM64 store 12-bit page offset of lea for TLV of %s", referenceTargetAtomName(ref));
break;
+#endif
//default:
// printf("unknown fixup");
// break;
switch (header->filetype()) {
case MH_EXECUTE:
case MH_DYLIB:
+ case MH_DYLIB_STUB:
case MH_BUNDLE:
case MH_DYLINKER:
case MH_KEXT_BUNDLE:
return true;
+ default:
+ return false;
}
return false;
}
case LC_LOAD_UPWARD_DYLIB:
case LC_VERSION_MIN_MACOSX:
case LC_VERSION_MIN_IPHONEOS:
+ case LC_VERSION_MIN_TVOS:
+ case LC_VERSION_MIN_WATCHOS:
case LC_FUNCTION_STARTS:
case LC_DYLD_ENVIRONMENT:
case LC_DATA_IN_CODE:
--- /dev/null
+##
+# Copyright (c) 2009 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# <rdar://problem/25585305> __asan_globals section is stripped out when building -flto
+#
+
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} -flto -o foo foo.c -Wl,-dead_strip,-exported_symbol,_my_global -v
+ # Check symbols that are expected to be preserved
+ nm -m foo | grep _global_metadata | ${FAIL_IF_EMPTY}
+ nm -m foo | grep _liveness_binder | ${FAIL_IF_EMPTY}
+ nm -m foo | grep _main | ${FAIL_IF_EMPTY}
+ nm -m foo | grep _my_global | ${FAIL_IF_EMPTY}
+
+ # Check symbols that are expected to be stripped
+ nm -m foo | grep unused | ${FAIL_IF_STDIN}
+
+clean:
+ rm foo
--- /dev/null
+
+int my_global = 0;
+
+__attribute__ ((section ("__DATA,__asan_globals,regular")))
+struct { void *ptr; } global_metadata = { .ptr = &my_global };
+
+__attribute__ ((used, section ("__DATA,__asan_liveness,regular,live_support")))
+struct { void *a, *b; } liveness_binder = { .a = &global_metadata, .b = &my_global };
+
+int unused_global = 0;
+
+__attribute__ ((section ("__DATA,__asan_globals,regular")))
+struct { void *ptr; } unused_global_metadata = { .ptr = &unused_global };
+
+__attribute__ ((used, section ("__DATA,__asan_liveness,regular,live_support")))
+struct { void *a, *b; } unused_liveness_binder = { .a = &unused_global_metadata, .b = &unused_global };
+
+
+int main(int argc, char *argv[])
+{
+ return my_global;
+}
--- /dev/null
+##
+# Copyright (c) 2009 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# <rdar://problem/25585305> __asan_globals section is stripped out when building -flto
+#
+
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} -c a.c
+ ${CC} ${CCFLAGS} -c b.c -flto
+ ${CC} ${CCFLAGS} a.o b.o -unexported_symbols_list unexp.list
+ # Check that unexported symbols are hidden
+ nm a.out | grep "t _bar" | ${FAIL_IF_EMPTY}
+ nm a.out | grep "t _foo" | ${FAIL_IF_EMPTY}
+
+clean:
+ rm a.out b.o a.o
--- /dev/null
+void foo();
+void bar() {
+ foo();
+}
+
+int main() {
+}
--- /dev/null
+void foo() {
+
+}
\ No newline at end of file
--- /dev/null
+_foo
+_bar