//===- tools/dsymutil/DwarfLinkerForBinary.cpp ----------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "DwarfLinkerForBinary.h" #include "BinaryHolder.h" #include "DebugMap.h" #include "MachOUtils.h" #include "dsymutil.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/BinaryFormat/Swift.h" #include "llvm/CodeGen/AccelTable.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/DIE.h" #include "llvm/CodeGen/NonRelocatableStringpool.h" #include "llvm/Config/config.h" #include "llvm/DWARFLinker/Classic/DWARFLinker.h" #include "llvm/DWARFLinker/Classic/DWARFStreamer.h" #include "llvm/DWARFLinker/Parallel/DWARFLinker.h" #include "llvm/DebugInfo/DIContext.h" #include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" #include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" #include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" #include "llvm/DebugInfo/DWARF/DWARFDie.h" #include "llvm/DebugInfo/DWARF/DWARFExpression.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" #include "llvm/DebugInfo/DWARF/DWARFSection.h" #include "llvm/DebugInfo/DWARF/DWARFUnit.h" #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCCodeEmitter.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDwarf.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSection.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCTargetOptions.h" #include "llvm/MC/MCTargetOptionsCommandFlags.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Object/MachO.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Object/SymbolicFile.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/DJB.h" #include "llvm/Support/DataExtractor.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/ThreadPool.h" #include "llvm/Support/ToolOutputFile.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetOptions.h" #include "llvm/TargetParser/Triple.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace llvm { static mc::RegisterMCTargetOptionsFlags MOF; using namespace dwarf_linker; namespace dsymutil { static void dumpDIE(const DWARFDie *DIE, bool Verbose) { if (!DIE || !Verbose) return; DIDumpOptions DumpOpts; DumpOpts.ChildRecurseDepth = 0; DumpOpts.Verbose = Verbose; WithColor::note() << " in DIE:\n"; DIE->dump(errs(), 6 /* Indent */, DumpOpts); } /// Report a warning to the user, optionally including information about a /// specific \p DIE related to the warning. void DwarfLinkerForBinary::reportWarning(Twine Warning, Twine Context, const DWARFDie *DIE) const { // FIXME: implement warning logging which does not block other threads. if (ErrorHandlerMutex.try_lock()) { warn(Warning, Context); dumpDIE(DIE, Options.Verbose); ErrorHandlerMutex.unlock(); } } void DwarfLinkerForBinary::reportError(Twine Error, Twine Context, const DWARFDie *DIE) const { // FIXME: implement error logging which does not block other threads. if (ErrorHandlerMutex.try_lock()) { error(Error, Context); dumpDIE(DIE, Options.Verbose); ErrorHandlerMutex.unlock(); } } ErrorOr DwarfLinkerForBinary::loadObject(const DebugMapObject &Obj, const Triple &Triple) { auto ObjectEntry = BinHolder.getObjectEntry(Obj.getObjectFilename(), Obj.getTimestamp()); if (!ObjectEntry) { auto Err = ObjectEntry.takeError(); reportWarning(Twine(Obj.getObjectFilename()) + ": " + toString(std::move(Err)), Obj.getObjectFilename()); return errorToErrorCode(std::move(Err)); } auto Object = ObjectEntry->getObject(Triple); if (!Object) { auto Err = Object.takeError(); reportWarning(Twine(Obj.getObjectFilename()) + ": " + toString(std::move(Err)), Obj.getObjectFilename()); return errorToErrorCode(std::move(Err)); } return *Object; } static Error remarksErrorHandler(const DebugMapObject &DMO, DwarfLinkerForBinary &Linker, std::unique_ptr FE) { bool IsArchive = DMO.getObjectFilename().ends_with(")"); // Don't report errors for missing remark files from static // archives. if (!IsArchive) return Error(std::move(FE)); std::string Message = FE->message(); Error E = FE->takeError(); Error NewE = handleErrors(std::move(E), [&](std::unique_ptr EC) { if (EC->convertToErrorCode() != std::errc::no_such_file_or_directory) return Error(std::move(EC)); Linker.reportWarning(Message, DMO.getObjectFilename()); return Error(Error::success()); }); if (!NewE) return Error::success(); return createFileError(FE->getFileName(), std::move(NewE)); } Error DwarfLinkerForBinary::emitRelocations( const DebugMap &DM, std::vector &ObjectsForLinking) { // Return early if the "Resources" directory is not being written to. if (!Options.ResourceDir) return Error::success(); RelocationMap RM(DM.getTriple(), DM.getBinaryPath()); for (auto &Obj : ObjectsForLinking) { if (!Obj.OutRelocs->isInitialized()) continue; Obj.OutRelocs->addValidRelocs(RM); } SmallString<128> InputPath; SmallString<128> Path; // Create the "Relocations" directory in the "Resources" directory, and // create an architecture-specific directory in the "Relocations" directory. StringRef ArchName = Triple::getArchName(RM.getTriple().getArch(), RM.getTriple().getSubArch()); sys::path::append(Path, *Options.ResourceDir, "Relocations", ArchName); if (std::error_code EC = sys::fs::create_directories(Path.str(), true, sys::fs::perms::all_all)) return errorCodeToError(EC); // Append the file name. sys::path::append(Path, sys::path::filename(DM.getBinaryPath())); Path.append(".yml"); std::error_code EC; raw_fd_ostream OS(Path.str(), EC, sys::fs::OF_Text); if (EC) return errorCodeToError(EC); RM.print(OS); return Error::success(); } static Error emitRemarks(const LinkOptions &Options, StringRef BinaryPath, StringRef ArchName, const remarks::RemarkLinker &RL) { // Make sure we don't create the directories and the file if there is nothing // to serialize. if (RL.empty()) return Error::success(); SmallString<128> InputPath; SmallString<128> Path; // Create the "Remarks" directory in the "Resources" directory. sys::path::append(Path, *Options.ResourceDir, "Remarks"); if (std::error_code EC = sys::fs::create_directories(Path.str(), true, sys::fs::perms::all_all)) return errorCodeToError(EC); // Append the file name. // For fat binaries, also append a dash and the architecture name. sys::path::append(Path, sys::path::filename(BinaryPath)); if (Options.NumDebugMaps > 1) { // More than one debug map means we have a fat binary. Path += '-'; Path += ArchName; } std::error_code EC; raw_fd_ostream OS(Options.NoOutput ? "-" : Path.str(), EC, Options.RemarksFormat == remarks::Format::Bitstream ? sys::fs::OF_None : sys::fs::OF_Text); if (EC) return errorCodeToError(EC); if (Error E = RL.serialize(OS, Options.RemarksFormat)) return E; return Error::success(); } ErrorOr> DwarfLinkerForBinary::loadObject( const DebugMapObject &Obj, const DebugMap &DebugMap, remarks::RemarkLinker &RL, std::shared_ptr DLBRM) { auto ErrorOrObj = loadObject(Obj, DebugMap.getTriple()); std::unique_ptr Res; if (ErrorOrObj) { auto Context = DWARFContext::create( *ErrorOrObj, DWARFContext::ProcessDebugRelocations::Process, nullptr, "", [&](Error Err) { handleAllErrors(std::move(Err), [&](ErrorInfoBase &Info) { reportError(Info.message()); }); }, [&](Error Warning) { handleAllErrors(std::move(Warning), [&](ErrorInfoBase &Info) { reportWarning(Info.message()); }); }); DLBRM->init(*Context); Res = std::make_unique( Obj.getObjectFilename(), std::move(Context), std::make_unique(*this, *ErrorOrObj, Obj, DLBRM), [&](StringRef FileName) { BinHolder.eraseObjectEntry(FileName); }); Error E = RL.link(*ErrorOrObj); if (Error NewE = handleErrors( std::move(E), [&](std::unique_ptr EC) -> Error { return remarksErrorHandler(Obj, *this, std::move(EC)); })) return errorToErrorCode(std::move(NewE)); return std::move(Res); } return ErrorOrObj.getError(); } static bool binaryHasStrippableSwiftReflectionSections( const DebugMap &Map, const LinkOptions &Options, BinaryHolder &BinHolder) { // If the input binary has strippable swift5 reflection sections, there is no // need to copy them to the .dSYM. Only copy them for binaries where the // linker omitted the reflection metadata. if (!Map.getBinaryPath().empty() && Options.FileType == DWARFLinkerBase::OutputFileType::Object) { auto ObjectEntry = BinHolder.getObjectEntry(Map.getBinaryPath()); // If ObjectEntry or Object has an error, no binary exists, therefore no // reflection sections exist. if (!ObjectEntry) { // Any errors will be diagnosed later in the main loop, ignore them here. llvm::consumeError(ObjectEntry.takeError()); return false; } auto Object = ObjectEntry->getObjectAs(Map.getTriple()); if (!Object) { // Any errors will be diagnosed later in the main loop, ignore them here. llvm::consumeError(Object.takeError()); return false; } for (auto &Section : Object->sections()) { llvm::Expected NameOrErr = Object->getSectionName(Section.getRawDataRefImpl()); if (!NameOrErr) { llvm::consumeError(NameOrErr.takeError()); continue; } NameOrErr->consume_back("__TEXT"); auto ReflectionSectionKind = Object->mapReflectionSectionNameToEnumValue(*NameOrErr); if (Object->isReflectionSectionStrippable(ReflectionSectionKind)) { return true; } } } return false; } /// Calculate the start of the strippable swift reflection sections in Dwarf. /// Note that there's an assumption that the reflection sections will appear /// in alphabetic order. static std::vector calculateStartOfStrippableReflectionSections(const DebugMap &Map) { using llvm::binaryformat::Swift5ReflectionSectionKind; uint64_t AssocTySize = 0; uint64_t FieldMdSize = 0; for (const auto &Obj : Map.objects()) { auto OF = llvm::object::ObjectFile::createObjectFile(Obj->getObjectFilename()); if (!OF) { llvm::consumeError(OF.takeError()); continue; } if (auto *MO = dyn_cast(OF->getBinary())) { for (auto &Section : MO->sections()) { llvm::Expected NameOrErr = MO->getSectionName(Section.getRawDataRefImpl()); if (!NameOrErr) { llvm::consumeError(NameOrErr.takeError()); continue; } NameOrErr->consume_back("__TEXT"); auto ReflSectionKind = MO->mapReflectionSectionNameToEnumValue(*NameOrErr); switch (ReflSectionKind) { case Swift5ReflectionSectionKind::assocty: AssocTySize += Section.getSize(); break; case Swift5ReflectionSectionKind::fieldmd: FieldMdSize += Section.getSize(); break; default: break; } } } } // Initialize the vector with enough space to fit every reflection section // kind. std::vector SectionToOffset(Swift5ReflectionSectionKind::last, 0); SectionToOffset[Swift5ReflectionSectionKind::assocty] = 0; SectionToOffset[Swift5ReflectionSectionKind::fieldmd] = llvm::alignTo(AssocTySize, 4); SectionToOffset[Swift5ReflectionSectionKind::reflstr] = llvm::alignTo( SectionToOffset[Swift5ReflectionSectionKind::fieldmd] + FieldMdSize, 4); return SectionToOffset; } void DwarfLinkerForBinary::collectRelocationsToApplyToSwiftReflectionSections( const object::SectionRef &Section, StringRef &Contents, const llvm::object::MachOObjectFile *MO, const std::vector &SectionToOffsetInDwarf, const llvm::dsymutil::DebugMapObject *Obj, std::vector &RelocationsToApply) const { for (auto It = Section.relocation_begin(); It != Section.relocation_end(); ++It) { object::DataRefImpl RelocDataRef = It->getRawDataRefImpl(); MachO::any_relocation_info MachOReloc = MO->getRelocation(RelocDataRef); if (!object::MachOObjectFile::isMachOPairedReloc( MO->getAnyRelocationType(MachOReloc), MO->getArch())) { reportWarning( "Unimplemented relocation type in strippable reflection section ", Obj->getObjectFilename()); continue; } auto CalculateAddressOfSymbolInDwarfSegment = [&]() -> std::optional { auto Symbol = It->getSymbol(); auto SymbolAbsoluteAddress = Symbol->getAddress(); if (!SymbolAbsoluteAddress) return {}; auto Section = Symbol->getSection(); if (!Section) { llvm::consumeError(Section.takeError()); return {}; } if ((*Section)->getObject()->section_end() == *Section) return {}; auto SectionStart = (*Section)->getAddress(); auto SymbolAddressInSection = *SymbolAbsoluteAddress - SectionStart; auto SectionName = (*Section)->getName(); if (!SectionName) return {}; auto ReflSectionKind = MO->mapReflectionSectionNameToEnumValue(*SectionName); int64_t SectionStartInLinkedBinary = SectionToOffsetInDwarf[ReflSectionKind]; auto Addr = SectionStartInLinkedBinary + SymbolAddressInSection; return Addr; }; // The first symbol should always be in the section we're currently // iterating over. auto FirstSymbolAddress = CalculateAddressOfSymbolInDwarfSegment(); ++It; bool ShouldSubtractDwarfVM = false; // For the second symbol there are two possibilities. std::optional SecondSymbolAddress; auto Sym = It->getSymbol(); if (Sym != MO->symbol_end()) { Expected SymbolName = Sym->getName(); if (SymbolName) { if (const auto *Mapping = Obj->lookupSymbol(*SymbolName)) { // First possibility: the symbol exists in the binary, and exists in a // non-strippable section (for example, typeref, or __TEXT,__const), // in which case we look up its address in the binary, which dsymutil // will copy verbatim. SecondSymbolAddress = Mapping->getValue().BinaryAddress; // Since the symbols live in different segments, we have to substract // the start of the Dwarf's vmaddr so the value calculated points to // the correct place. ShouldSubtractDwarfVM = true; } } } if (!SecondSymbolAddress) { // Second possibility, this symbol is not present in the main binary, and // must be in one of the strippable sections (for example, reflstr). // Calculate its address in the same way as we did the first one. SecondSymbolAddress = CalculateAddressOfSymbolInDwarfSegment(); } if (!FirstSymbolAddress || !SecondSymbolAddress) continue; auto SectionName = Section.getName(); if (!SectionName) continue; int32_t Addend; memcpy(&Addend, Contents.data() + It->getOffset(), sizeof(int32_t)); int32_t Value = (*SecondSymbolAddress + Addend) - *FirstSymbolAddress; auto ReflSectionKind = MO->mapReflectionSectionNameToEnumValue(*SectionName); uint64_t AddressFromDwarfVM = SectionToOffsetInDwarf[ReflSectionKind] + It->getOffset(); RelocationsToApply.emplace_back(AddressFromDwarfVM, Value, ShouldSubtractDwarfVM); } } Error DwarfLinkerForBinary::copySwiftInterfaces(StringRef Architecture) const { std::error_code EC; SmallString<128> InputPath; SmallString<128> Path; sys::path::append(Path, *Options.ResourceDir, "Swift", Architecture); if ((EC = sys::fs::create_directories(Path.str(), true, sys::fs::perms::all_all))) return make_error( "cannot create directory: " + toString(errorCodeToError(EC)), EC); unsigned BaseLength = Path.size(); for (auto &I : ParseableSwiftInterfaces) { StringRef ModuleName = I.first; StringRef InterfaceFile = I.second; if (!Options.PrependPath.empty()) { InputPath.clear(); sys::path::append(InputPath, Options.PrependPath, InterfaceFile); InterfaceFile = InputPath; } sys::path::append(Path, ModuleName); Path.append(".swiftinterface"); if (Options.Verbose) outs() << "copy parseable Swift interface " << InterfaceFile << " -> " << Path.str() << '\n'; // copy_file attempts an APFS clone first, so this should be cheap. if ((EC = sys::fs::copy_file(InterfaceFile, Path.str()))) reportWarning(Twine("cannot copy parseable Swift interface ") + InterfaceFile + ": " + toString(errorCodeToError(EC))); Path.resize(BaseLength); } return Error::success(); } void DwarfLinkerForBinary::copySwiftReflectionMetadata( const llvm::dsymutil::DebugMapObject *Obj, classic::DwarfStreamer *Streamer, std::vector &SectionToOffsetInDwarf, std::vector &RelocationsToApply) { using binaryformat::Swift5ReflectionSectionKind; auto OF = llvm::object::ObjectFile::createObjectFile(Obj->getObjectFilename()); if (!OF) { llvm::consumeError(OF.takeError()); return; } if (auto *MO = dyn_cast(OF->getBinary())) { // Collect the swift reflection sections before emitting them. This is // done so we control the order they're emitted. std::array, Swift5ReflectionSectionKind::last + 1> SwiftSections; for (auto &Section : MO->sections()) { llvm::Expected NameOrErr = MO->getSectionName(Section.getRawDataRefImpl()); if (!NameOrErr) { llvm::consumeError(NameOrErr.takeError()); continue; } NameOrErr->consume_back("__TEXT"); auto ReflSectionKind = MO->mapReflectionSectionNameToEnumValue(*NameOrErr); if (MO->isReflectionSectionStrippable(ReflSectionKind)) SwiftSections[ReflSectionKind] = Section; } // Make sure we copy the sections in alphabetic order. auto SectionKindsToEmit = {Swift5ReflectionSectionKind::assocty, Swift5ReflectionSectionKind::fieldmd, Swift5ReflectionSectionKind::reflstr}; for (auto SectionKind : SectionKindsToEmit) { if (!SwiftSections[SectionKind]) continue; auto &Section = *SwiftSections[SectionKind]; llvm::Expected SectionContents = Section.getContents(); if (!SectionContents) continue; const auto *MO = llvm::cast(Section.getObject()); collectRelocationsToApplyToSwiftReflectionSections( Section, *SectionContents, MO, SectionToOffsetInDwarf, Obj, RelocationsToApply); // Update the section start with the current section's contribution, so // the next section we copy from a different .o file points to the correct // place. SectionToOffsetInDwarf[SectionKind] += Section.getSize(); Streamer->emitSwiftReflectionSection(SectionKind, *SectionContents, Section.getAlignment().value(), Section.getSize()); } } } bool DwarfLinkerForBinary::link(const DebugMap &Map) { if (Options.DWARFLinkerType == DsymutilDWARFLinkerType::Parallel) return linkImpl(Map, Options.FileType); return linkImpl(Map, Options.FileType); } template void setAcceleratorTables(Linker &GeneralLinker, DsymutilAccelTableKind TableKind, uint16_t MaxDWARFVersion) { switch (TableKind) { case DsymutilAccelTableKind::Apple: GeneralLinker.addAccelTableKind(Linker::AccelTableKind::Apple); return; case DsymutilAccelTableKind::Dwarf: GeneralLinker.addAccelTableKind(Linker::AccelTableKind::DebugNames); return; case DsymutilAccelTableKind::Pub: GeneralLinker.addAccelTableKind(Linker::AccelTableKind::Pub); return; case DsymutilAccelTableKind::Default: if (MaxDWARFVersion >= 5) GeneralLinker.addAccelTableKind(Linker::AccelTableKind::DebugNames); else GeneralLinker.addAccelTableKind(Linker::AccelTableKind::Apple); return; case DsymutilAccelTableKind::None: // Nothing to do. return; } llvm_unreachable("All cases handled above!"); } template bool DwarfLinkerForBinary::linkImpl( const DebugMap &Map, typename Linker::OutputFileType ObjectType) { std::vector ObjectsForLinking; DebugMap DebugMap(Map.getTriple(), Map.getBinaryPath()); std::function TranslationLambda = [&](StringRef Input) { assert(Options.Translator); return Options.Translator(Input); }; std::unique_ptr GeneralLinker = Linker::createLinker( [&](const Twine &Error, StringRef Context, const DWARFDie *DIE) { reportError(Error, Context, DIE); }, [&](const Twine &Warning, StringRef Context, const DWARFDie *DIE) { reportWarning(Warning, Context, DIE); }, Options.Translator ? TranslationLambda : nullptr); std::unique_ptr Streamer; if (!Options.NoOutput) { if (Expected> StreamerOrErr = classic::DwarfStreamer::createStreamer( Map.getTriple(), ObjectType, OutFile, Options.Translator, [&](const Twine &Warning, StringRef Context, const DWARFDie *DIE) { reportWarning(Warning, Context, DIE); })) Streamer = std::move(*StreamerOrErr); else { handleAllErrors(StreamerOrErr.takeError(), [&](const ErrorInfoBase &EI) { reportError(EI.message(), "dwarf streamer init"); }); return false; } if constexpr (std::is_same::value) { GeneralLinker->setOutputDWARFHandler( Map.getTriple(), [&](std::shared_ptr Section) { Streamer->emitSectionContents(Section->getContents(), Section->getKind()); }); } else GeneralLinker->setOutputDWARFEmitter(Streamer.get()); } remarks::RemarkLinker RL; if (!Options.RemarksPrependPath.empty()) RL.setExternalFilePrependPath(Options.RemarksPrependPath); RL.setKeepAllRemarks(Options.RemarksKeepAll); GeneralLinker->setObjectPrefixMap(&Options.ObjectPrefixMap); GeneralLinker->setVerbosity(Options.Verbose); GeneralLinker->setStatistics(Options.Statistics); GeneralLinker->setVerifyInputDWARF(Options.VerifyInputDWARF); GeneralLinker->setNoODR(Options.NoODR); GeneralLinker->setUpdateIndexTablesOnly(Options.Update); GeneralLinker->setNumThreads(Options.Threads); GeneralLinker->setPrependPath(Options.PrependPath); GeneralLinker->setKeepFunctionForStatic(Options.KeepFunctionForStatic); GeneralLinker->setInputVerificationHandler( [&](const DWARFFile &File, llvm::StringRef Output) { std::lock_guard Guard(ErrorHandlerMutex); if (Options.Verbose) errs() << Output; warn("input verification failed", File.FileName); HasVerificationErrors = true; }); auto Loader = [&](StringRef ContainerName, StringRef Path) -> ErrorOr { auto &Obj = DebugMap.addDebugMapObject( Path, sys::TimePoint(), MachO::N_OSO); auto DLBRelocMap = std::make_shared(); if (ErrorOr> ErrorOrObj = loadObject(Obj, DebugMap, RL, DLBRelocMap)) { ObjectsForLinking.emplace_back(std::move(*ErrorOrObj), DLBRelocMap); return *ObjectsForLinking.back().Object; } else { // Try and emit more helpful warnings by applying some heuristics. StringRef ObjFile = ContainerName; bool IsClangModule = sys::path::extension(Path).equals(".pcm"); bool IsArchive = ObjFile.ends_with(")"); if (IsClangModule) { StringRef ModuleCacheDir = sys::path::parent_path(Path); if (sys::fs::exists(ModuleCacheDir)) { // If the module's parent directory exists, we assume that the // module cache has expired and was pruned by clang. A more // adventurous dsymutil would invoke clang to rebuild the module // now. if (!ModuleCacheHintDisplayed) { WithColor::note() << "The clang module cache may have expired since " "this object file was built. Rebuilding the " "object file will rebuild the module cache.\n"; ModuleCacheHintDisplayed = true; } } else if (IsArchive) { // If the module cache directory doesn't exist at all and the // object file is inside a static library, we assume that the // static library was built on a different machine. We don't want // to discourage module debugging for convenience libraries within // a project though. if (!ArchiveHintDisplayed) { WithColor::note() << "Linking a static library that was built with " "-gmodules, but the module cache was not found. " "Redistributable static libraries should never be " "built with module debugging enabled. The debug " "experience will be degraded due to incomplete " "debug information.\n"; ArchiveHintDisplayed = true; } } } return ErrorOrObj.getError(); } llvm_unreachable("Unhandled DebugMap object"); }; GeneralLinker->setSwiftInterfacesMap(&ParseableSwiftInterfaces); bool ReflectionSectionsPresentInBinary = false; // If there is no output specified, no point in checking the binary for swift5 // reflection sections. if (!Options.NoOutput) { ReflectionSectionsPresentInBinary = binaryHasStrippableSwiftReflectionSections(Map, Options, BinHolder); } std::vector RelocationsToApply; if (!Options.NoOutput && !ReflectionSectionsPresentInBinary) { auto SectionToOffsetInDwarf = calculateStartOfStrippableReflectionSections(Map); for (const auto &Obj : Map.objects()) copySwiftReflectionMetadata(Obj.get(), Streamer.get(), SectionToOffsetInDwarf, RelocationsToApply); } uint16_t MaxDWARFVersion = 0; std::function OnCUDieLoaded = [&MaxDWARFVersion](const DWARFUnit &Unit) { MaxDWARFVersion = std::max(Unit.getVersion(), MaxDWARFVersion); }; for (const auto &Obj : Map.objects()) { // N_AST objects (swiftmodule files) should get dumped directly into the // appropriate DWARF section. if (Obj->getType() == MachO::N_AST) { if (Options.Verbose) outs() << "DEBUG MAP OBJECT: " << Obj->getObjectFilename() << "\n"; StringRef File = Obj->getObjectFilename(); auto ErrorOrMem = MemoryBuffer::getFile(File); if (!ErrorOrMem) { reportWarning("Could not open '" + File + "'"); continue; } sys::fs::file_status Stat; if (auto Err = sys::fs::status(File, Stat)) { reportWarning(Err.message()); continue; } if (!Options.NoTimestamp) { // The modification can have sub-second precision so we need to cast // away the extra precision that's not present in the debug map. auto ModificationTime = std::chrono::time_point_cast( Stat.getLastModificationTime()); if (Obj->getTimestamp() != sys::TimePoint<>() && ModificationTime != Obj->getTimestamp()) { // Not using the helper here as we can easily stream TimePoint<>. WithColor::warning() << File << ": timestamp mismatch between swift interface file (" << sys::TimePoint<>(ModificationTime) << ") and debug map (" << sys::TimePoint<>(Obj->getTimestamp()) << ")\n"; continue; } } // Copy the module into the .swift_ast section. if (!Options.NoOutput) Streamer->emitSwiftAST((*ErrorOrMem)->getBuffer()); continue; } auto DLBRelocMap = std::make_shared(); if (ErrorOr> ErrorOrObj = loadObject(*Obj, Map, RL, DLBRelocMap)) { ObjectsForLinking.emplace_back(std::move(*ErrorOrObj), DLBRelocMap); GeneralLinker->addObjectFile(*ObjectsForLinking.back().Object, Loader, OnCUDieLoaded); } else { ObjectsForLinking.push_back( {std::make_unique(Obj->getObjectFilename(), nullptr, nullptr), DLBRelocMap}); GeneralLinker->addObjectFile(*ObjectsForLinking.back().Object); } } // If we haven't seen any CUs, pick an arbitrary valid Dwarf version anyway. if (MaxDWARFVersion == 0) MaxDWARFVersion = 3; if (Error E = GeneralLinker->setTargetDWARFVersion(MaxDWARFVersion)) return error(toString(std::move(E))); setAcceleratorTables(*GeneralLinker, Options.TheAccelTableKind, MaxDWARFVersion); // link debug info for loaded object files. if (Error E = GeneralLinker->link()) return error(toString(std::move(E))); StringRef ArchName = Map.getTriple().getArchName(); if (Error E = emitRemarks(Options, Map.getBinaryPath(), ArchName, RL)) return error(toString(std::move(E))); if (Options.NoOutput) return true; if (Error E = emitRelocations(Map, ObjectsForLinking)) return error(toString(std::move(E))); if (Options.ResourceDir && !ParseableSwiftInterfaces.empty()) { StringRef ArchName = Triple::getArchTypeName(Map.getTriple().getArch()); if (auto E = copySwiftInterfaces(ArchName)) return error(toString(std::move(E))); } if (Map.getTriple().isOSDarwin() && !Map.getBinaryPath().empty() && ObjectType == Linker::OutputFileType::Object) return MachOUtils::generateDsymCompanion( Options.VFS, Map, Options.Translator, *Streamer->getAsmPrinter().OutStreamer, OutFile, RelocationsToApply); Streamer->finish(); return true; } /// Iterate over the relocations of the given \p Section and /// store the ones that correspond to debug map entries into the /// ValidRelocs array. void DwarfLinkerForBinary::AddressManager::findValidRelocsMachO( const object::SectionRef &Section, const object::MachOObjectFile &Obj, const DebugMapObject &DMO, std::vector &ValidRelocs) { Expected ContentsOrErr = Section.getContents(); if (!ContentsOrErr) { consumeError(ContentsOrErr.takeError()); Linker.reportWarning("error reading section", DMO.getObjectFilename()); return; } DataExtractor Data(*ContentsOrErr, Obj.isLittleEndian(), 0); bool SkipNext = false; for (const object::RelocationRef &Reloc : Section.relocations()) { if (SkipNext) { SkipNext = false; continue; } object::DataRefImpl RelocDataRef = Reloc.getRawDataRefImpl(); MachO::any_relocation_info MachOReloc = Obj.getRelocation(RelocDataRef); if (object::MachOObjectFile::isMachOPairedReloc(Obj.getAnyRelocationType(MachOReloc), Obj.getArch())) { SkipNext = true; Linker.reportWarning("unsupported relocation in " + *Section.getName() + " section.", DMO.getObjectFilename()); continue; } unsigned RelocSize = 1 << Obj.getAnyRelocationLength(MachOReloc); uint64_t Offset64 = Reloc.getOffset(); if ((RelocSize != 4 && RelocSize != 8)) { Linker.reportWarning("unsupported relocation in " + *Section.getName() + " section.", DMO.getObjectFilename()); continue; } uint64_t OffsetCopy = Offset64; // Mach-o uses REL relocations, the addend is at the relocation offset. uint64_t Addend = Data.getUnsigned(&OffsetCopy, RelocSize); uint64_t SymAddress; int64_t SymOffset; if (Obj.isRelocationScattered(MachOReloc)) { // The address of the base symbol for scattered relocations is // stored in the reloc itself. The actual addend will store the // base address plus the offset. SymAddress = Obj.getScatteredRelocationValue(MachOReloc); SymOffset = int64_t(Addend) - SymAddress; } else { SymAddress = Addend; SymOffset = 0; } auto Sym = Reloc.getSymbol(); if (Sym != Obj.symbol_end()) { Expected SymbolName = Sym->getName(); if (!SymbolName) { consumeError(SymbolName.takeError()); Linker.reportWarning("error getting relocation symbol name.", DMO.getObjectFilename()); continue; } if (const auto *Mapping = DMO.lookupSymbol(*SymbolName)) ValidRelocs.emplace_back(Offset64, RelocSize, Addend, Mapping->getKey(), Mapping->getValue()); } else if (const auto *Mapping = DMO.lookupObjectAddress(SymAddress)) { // Do not store the addend. The addend was the address of the symbol in // the object file, the address in the binary that is stored in the debug // map doesn't need to be offset. ValidRelocs.emplace_back(Offset64, RelocSize, SymOffset, Mapping->getKey(), Mapping->getValue()); } } } /// Dispatch the valid relocation finding logic to the /// appropriate handler depending on the object file format. bool DwarfLinkerForBinary::AddressManager::findValidRelocs( const object::SectionRef &Section, const object::ObjectFile &Obj, const DebugMapObject &DMO, std::vector &Relocs) { // Dispatch to the right handler depending on the file type. if (auto *MachOObj = dyn_cast(&Obj)) findValidRelocsMachO(Section, *MachOObj, DMO, Relocs); else Linker.reportWarning(Twine("unsupported object file type: ") + Obj.getFileName(), DMO.getObjectFilename()); if (Relocs.empty()) return false; // Sort the relocations by offset. We will walk the DIEs linearly in // the file, this allows us to just keep an index in the relocation // array that we advance during our walk, rather than resorting to // some associative container. See DwarfLinkerForBinary::NextValidReloc. llvm::sort(Relocs); return true; } /// Look for relocations in the debug_info and debug_addr section that match /// entries in the debug map. These relocations will drive the Dwarf link by /// indicating which DIEs refer to symbols present in the linked binary. /// \returns whether there are any valid relocations in the debug info. bool DwarfLinkerForBinary::AddressManager::findValidRelocsInDebugSections( const object::ObjectFile &Obj, const DebugMapObject &DMO) { // Find the debug_info section. bool FoundValidRelocs = false; for (const object::SectionRef &Section : Obj.sections()) { StringRef SectionName; if (Expected NameOrErr = Section.getName()) SectionName = *NameOrErr; else consumeError(NameOrErr.takeError()); SectionName = SectionName.substr(SectionName.find_first_not_of("._")); if (SectionName == "debug_info") FoundValidRelocs |= findValidRelocs(Section, Obj, DMO, ValidDebugInfoRelocs); if (SectionName == "debug_addr") FoundValidRelocs |= findValidRelocs(Section, Obj, DMO, ValidDebugAddrRelocs); } return FoundValidRelocs; } std::vector DwarfLinkerForBinary::AddressManager::getRelocations( const std::vector &Relocs, uint64_t StartPos, uint64_t EndPos) { std::vector Res; auto CurReloc = partition_point(Relocs, [StartPos](const ValidReloc &Reloc) { return (uint64_t)Reloc.Offset < StartPos; }); while (CurReloc != Relocs.end() && CurReloc->Offset >= StartPos && (uint64_t)CurReloc->Offset < EndPos) { Res.push_back(*CurReloc); CurReloc++; } return Res; } void DwarfLinkerForBinary::AddressManager::printReloc(const ValidReloc &Reloc) { const auto &Mapping = Reloc.SymbolMapping; const uint64_t ObjectAddress = Mapping.ObjectAddress ? uint64_t(*Mapping.ObjectAddress) : std::numeric_limits::max(); outs() << "Found valid debug map entry: " << Reloc.SymbolName << "\t" << format("0x%016" PRIx64 " => 0x%016" PRIx64 "\n", ObjectAddress, uint64_t(Mapping.BinaryAddress)); } int64_t DwarfLinkerForBinary::AddressManager::getRelocValue(const ValidReloc &Reloc) { int64_t AddrAdjust = relocate(Reloc); if (Reloc.SymbolMapping.ObjectAddress) AddrAdjust -= uint64_t(*Reloc.SymbolMapping.ObjectAddress); return AddrAdjust; } std::optional DwarfLinkerForBinary::AddressManager::hasValidRelocationAt( const std::vector &AllRelocs, uint64_t StartOffset, uint64_t EndOffset, bool Verbose) { std::vector Relocs = getRelocations(AllRelocs, StartOffset, EndOffset); if (Relocs.size() == 0) return std::nullopt; if (Verbose) printReloc(Relocs[0]); return getRelocValue(Relocs[0]); } /// Get the starting and ending (exclusive) offset for the /// attribute with index \p Idx descibed by \p Abbrev. \p Offset is /// supposed to point to the position of the first attribute described /// by \p Abbrev. /// \return [StartOffset, EndOffset) as a pair. static std::pair getAttributeOffsets(const DWARFAbbreviationDeclaration *Abbrev, unsigned Idx, uint64_t Offset, const DWARFUnit &Unit) { DataExtractor Data = Unit.getDebugInfoExtractor(); for (unsigned I = 0; I < Idx; ++I) DWARFFormValue::skipValue(Abbrev->getFormByIndex(I), Data, &Offset, Unit.getFormParams()); uint64_t End = Offset; DWARFFormValue::skipValue(Abbrev->getFormByIndex(Idx), Data, &End, Unit.getFormParams()); return std::make_pair(Offset, End); } std::optional DwarfLinkerForBinary::AddressManager::getExprOpAddressRelocAdjustment( DWARFUnit &U, const DWARFExpression::Operation &Op, uint64_t StartOffset, uint64_t EndOffset, bool Verbose) { switch (Op.getCode()) { default: { assert(false && "Specified operation does not have address operand"); } break; case dwarf::DW_OP_const2u: case dwarf::DW_OP_const4u: case dwarf::DW_OP_const8u: case dwarf::DW_OP_const2s: case dwarf::DW_OP_const4s: case dwarf::DW_OP_const8s: case dwarf::DW_OP_addr: { return hasValidRelocationAt(ValidDebugInfoRelocs, StartOffset, EndOffset, Verbose); } break; case dwarf::DW_OP_constx: case dwarf::DW_OP_addrx: { return hasValidRelocationAt(ValidDebugAddrRelocs, StartOffset, EndOffset, Verbose); } break; } return std::nullopt; } std::optional DwarfLinkerForBinary::AddressManager::getSubprogramRelocAdjustment( const DWARFDie &DIE, bool Verbose) { const auto *Abbrev = DIE.getAbbreviationDeclarationPtr(); std::optional LowPcIdx = Abbrev->findAttributeIndex(dwarf::DW_AT_low_pc); if (!LowPcIdx) return std::nullopt; dwarf::Form Form = Abbrev->getFormByIndex(*LowPcIdx); switch (Form) { case dwarf::DW_FORM_addr: { uint64_t Offset = DIE.getOffset() + getULEB128Size(Abbrev->getCode()); uint64_t LowPcOffset, LowPcEndOffset; std::tie(LowPcOffset, LowPcEndOffset) = getAttributeOffsets(Abbrev, *LowPcIdx, Offset, *DIE.getDwarfUnit()); return hasValidRelocationAt(ValidDebugInfoRelocs, LowPcOffset, LowPcEndOffset, Verbose); } case dwarf::DW_FORM_addrx: case dwarf::DW_FORM_addrx1: case dwarf::DW_FORM_addrx2: case dwarf::DW_FORM_addrx3: case dwarf::DW_FORM_addrx4: { std::optional AddrValue = DIE.find(dwarf::DW_AT_low_pc); if (std::optional AddressOffset = DIE.getDwarfUnit()->getIndexedAddressOffset( AddrValue->getRawUValue())) return hasValidRelocationAt( ValidDebugAddrRelocs, *AddressOffset, *AddressOffset + DIE.getDwarfUnit()->getAddressByteSize(), Verbose); Linker.reportWarning("no base offset for address table", SrcFileName); return std::nullopt; } default: return std::nullopt; } } std::optional DwarfLinkerForBinary::AddressManager::getLibraryInstallName() { return LibInstallName; } uint64_t DwarfLinkerForBinary::AddressManager::relocate(const ValidReloc &Reloc) const { return Reloc.SymbolMapping.BinaryAddress + Reloc.Addend; } void DwarfLinkerForBinary::AddressManager::updateAndSaveValidRelocs( bool IsDWARF5, uint64_t OriginalUnitOffset, int64_t LinkedOffset, uint64_t StartOffset, uint64_t EndOffset) { std::vector InRelocs = getRelocations(ValidDebugInfoRelocs, StartOffset, EndOffset); if (IsDWARF5) InRelocs = getRelocations(ValidDebugAddrRelocs, StartOffset, EndOffset); DwarfLinkerRelocMap->updateAndSaveValidRelocs( IsDWARF5, InRelocs, OriginalUnitOffset, LinkedOffset); } void DwarfLinkerForBinary::AddressManager::updateRelocationsWithUnitOffset( uint64_t OriginalUnitOffset, uint64_t OutputUnitOffset) { DwarfLinkerRelocMap->updateRelocationsWithUnitOffset(OriginalUnitOffset, OutputUnitOffset); } /// Apply the valid relocations found by findValidRelocs() to /// the buffer \p Data, taking into account that Data is at \p BaseOffset /// in the debug_info section. /// /// Like for findValidRelocs(), this function must be called with /// monotonic \p BaseOffset values. /// /// \returns whether any reloc has been applied. bool DwarfLinkerForBinary::AddressManager::applyValidRelocs( MutableArrayRef Data, uint64_t BaseOffset, bool IsLittleEndian) { std::vector Relocs = getRelocations( ValidDebugInfoRelocs, BaseOffset, BaseOffset + Data.size()); for (const ValidReloc &CurReloc : Relocs) { assert(CurReloc.Offset - BaseOffset < Data.size()); assert(CurReloc.Offset - BaseOffset + CurReloc.Size <= Data.size()); char Buf[8]; uint64_t Value = relocate(CurReloc); for (unsigned I = 0; I != CurReloc.Size; ++I) { unsigned Index = IsLittleEndian ? I : (CurReloc.Size - I - 1); Buf[I] = uint8_t(Value >> (Index * 8)); } assert(CurReloc.Size <= sizeof(Buf)); memcpy(&Data[CurReloc.Offset - BaseOffset], Buf, CurReloc.Size); } return Relocs.size() > 0; } void DwarfLinkerForBinaryRelocationMap::init(DWARFContext &Context) { for (const std::unique_ptr &CU : Context.compile_units()) StoredValidDebugInfoRelocsMap.insert( std::make_pair(CU->getOffset(), std::vector())); // FIXME: Support relocations debug_addr (DWARF5). } void DwarfLinkerForBinaryRelocationMap::addValidRelocs(RelocationMap &RM) { for (const auto &DebugInfoRelocs : StoredValidDebugInfoRelocsMap) { for (const auto &InfoReloc : DebugInfoRelocs.second) RM.addRelocationMapEntry(InfoReloc); } // FIXME: Support relocations debug_addr (DWARF5). } void DwarfLinkerForBinaryRelocationMap::updateRelocationsWithUnitOffset( uint64_t OriginalUnitOffset, uint64_t OutputUnitOffset) { std::vector &StoredValidDebugInfoRelocs = StoredValidDebugInfoRelocsMap[OriginalUnitOffset]; for (ValidReloc &R : StoredValidDebugInfoRelocs) { R.Offset = (uint64_t)R.Offset + OutputUnitOffset; } // FIXME: Support relocations debug_addr (DWARF5). } void DwarfLinkerForBinaryRelocationMap::updateAndSaveValidRelocs( bool IsDWARF5, std::vector &InRelocs, uint64_t UnitOffset, int64_t LinkedOffset) { std::vector &OutRelocs = StoredValidDebugInfoRelocsMap[UnitOffset]; if (IsDWARF5) OutRelocs = StoredValidDebugAddrRelocsMap[UnitOffset]; for (ValidReloc &R : InRelocs) { OutRelocs.emplace_back(R.Offset + LinkedOffset, R.Size, R.Addend, R.SymbolName, R.SymbolMapping); } } } // namespace dsymutil } // namespace llvm