| //===- 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/BitVector.h" |
| #include "llvm/ADT/DenseMap.h" |
| #include "llvm/ADT/DenseMapInfo.h" |
| #include "llvm/ADT/DenseSet.h" |
| #include "llvm/ADT/FoldingSet.h" |
| #include "llvm/ADT/Hashing.h" |
| #include "llvm/ADT/PointerIntPair.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/ADT/SmallString.h" |
| #include "llvm/ADT/StringMap.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/DWARFLinkerDeclContext.h" |
| #include "llvm/DWARFLinkerParallel/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 <algorithm> |
| #include <cassert> |
| #include <cinttypes> |
| #include <climits> |
| #include <cstdint> |
| #include <cstdlib> |
| #include <cstring> |
| #include <limits> |
| #include <map> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <system_error> |
| #include <tuple> |
| #include <utility> |
| #include <vector> |
| |
| namespace llvm { |
| |
| static mc::RegisterMCTargetOptionsFlags MOF; |
| |
| 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 { |
| std::lock_guard<std::mutex> Guard(ErrorHandlerMutex); |
| warn(Warning, Context); |
| dumpDIE(DIE, Options.Verbose); |
| } |
| |
| void DwarfLinkerForBinary::reportError(Twine Error, Twine Context, |
| const DWARFDie *DIE) const { |
| std::lock_guard<std::mutex> Guard(ErrorHandlerMutex); |
| error(Error, Context); |
| dumpDIE(DIE, Options.Verbose); |
| } |
| |
| ErrorOr<const object::ObjectFile &> |
| 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<FileError> FE) { |
| bool IsArchive = DMO.getObjectFilename().endswith(")"); |
| // 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<ECError> 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)); |
| } |
| |
| 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(); |
| } |
| |
| template <typename OutDWARFFile, typename AddressesMap> |
| ErrorOr<std::unique_ptr<OutDWARFFile>> |
| DwarfLinkerForBinary::loadObject(const DebugMapObject &Obj, |
| const DebugMap &DebugMap, |
| remarks::RemarkLinker &RL) { |
| auto ErrorOrObj = loadObject(Obj, DebugMap.getTriple()); |
| std::unique_ptr<OutDWARFFile> Res; |
| |
| if (ErrorOrObj) { |
| Res = std::make_unique<OutDWARFFile>( |
| Obj.getObjectFilename(), DWARFContext::create(*ErrorOrObj), |
| std::make_unique<AddressesMap>(*this, *ErrorOrObj, Obj), |
| Obj.empty() ? Obj.getWarnings() : EmptyWarnings); |
| |
| Error E = RL.link(*ErrorOrObj); |
| if (Error NewE = handleErrors( |
| std::move(E), [&](std::unique_ptr<FileError> EC) -> Error { |
| return remarksErrorHandler(Obj, *this, std::move(EC)); |
| })) |
| return errorToErrorCode(std::move(NewE)); |
| |
| return 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 == DWARFLinker::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<object::MachOObjectFile>(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<llvm::StringRef> 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<uint64_t> |
| 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<llvm::object::MachOObjectFile>(OF->getBinary())) { |
| for (auto &Section : MO->sections()) { |
| llvm::Expected<llvm::StringRef> 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<uint64_t> 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<uint64_t> &SectionToOffsetInDwarf, |
| const llvm::dsymutil::DebugMapObject *Obj, |
| std::vector<MachOUtils::DwarfRelocationApplicationInfo> &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<int64_t> { |
| 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<int64_t> SecondSymbolAddress; |
| auto Sym = It->getSymbol(); |
| if (Sym != MO->symbol_end()) { |
| Expected<StringRef> 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<StringError>( |
| "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(); |
| } |
| |
| template <typename OutStreamer> |
| void DwarfLinkerForBinary::copySwiftReflectionMetadata( |
| const llvm::dsymutil::DebugMapObject *Obj, OutStreamer *Streamer, |
| std::vector<uint64_t> &SectionToOffsetInDwarf, |
| std::vector<MachOUtils::DwarfRelocationApplicationInfo> |
| &RelocationsToApply) { |
| using binaryformat::Swift5ReflectionSectionKind; |
| auto OF = |
| llvm::object::ObjectFile::createObjectFile(Obj->getObjectFilename()); |
| if (!OF) { |
| llvm::consumeError(OF.takeError()); |
| return; |
| } |
| if (auto *MO = dyn_cast<llvm::object::MachOObjectFile>(OF->getBinary())) { |
| // Collect the swift reflection sections before emitting them. This is |
| // done so we control the order they're emitted. |
| std::array<std::optional<object::SectionRef>, |
| Swift5ReflectionSectionKind::last + 1> |
| SwiftSections; |
| for (auto &Section : MO->sections()) { |
| llvm::Expected<llvm::StringRef> 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<llvm::StringRef> SectionContents = Section.getContents(); |
| if (!SectionContents) |
| continue; |
| const auto *MO = |
| llvm::cast<llvm::object::MachOObjectFile>(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::LLVM) { |
| dwarflinker_parallel::DWARFLinker::OutputFileType DWARFLinkerOutputType; |
| switch (Options.FileType) { |
| case DWARFLinker::OutputFileType::Object: |
| DWARFLinkerOutputType = |
| dwarflinker_parallel::DWARFLinker::OutputFileType::Object; |
| break; |
| |
| case DWARFLinker::OutputFileType::Assembly: |
| DWARFLinkerOutputType = |
| dwarflinker_parallel::DWARFLinker::OutputFileType::Assembly; |
| break; |
| } |
| |
| return linkImpl<dwarflinker_parallel::DWARFLinker, |
| dwarflinker_parallel::DWARFFile, |
| AddressManager<dwarflinker_parallel::AddressesMap>>( |
| Map, DWARFLinkerOutputType); |
| } |
| |
| return linkImpl<DWARFLinker, DWARFFile, AddressManager<AddressesMap>>( |
| Map, Options.FileType); |
| } |
| |
| template <typename Linker> |
| 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 <typename Linker, typename OutDwarfFile, typename AddressMap> |
| bool DwarfLinkerForBinary::linkImpl( |
| const DebugMap &Map, typename Linker::OutputFileType ObjectType) { |
| |
| std::vector<std::unique_ptr<OutDwarfFile>> ObjectsForLinking; |
| |
| DebugMap DebugMap(Map.getTriple(), Map.getBinaryPath()); |
| |
| std::function<StringRef(StringRef)> TranslationLambda = [&](StringRef Input) { |
| assert(Options.Translator); |
| return Options.Translator(Input); |
| }; |
| |
| std::unique_ptr<Linker> 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); |
| |
| if (!Options.NoOutput) { |
| if (Error Err = GeneralLinker->createEmitter(Map.getTriple(), ObjectType, |
| OutFile)) { |
| handleAllErrors(std::move(Err), [&](const ErrorInfoBase &EI) { |
| reportError(EI.message(), "dwarf streamer init"); |
| }); |
| return false; |
| } |
| } |
| |
| 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 OutDwarfFile &File) { |
| reportWarning("input verification failed", File.FileName); |
| HasVerificationErrors = true; |
| }); |
| auto Loader = [&](StringRef ContainerName, |
| StringRef Path) -> ErrorOr<OutDwarfFile &> { |
| auto &Obj = DebugMap.addDebugMapObject( |
| Path, sys::TimePoint<std::chrono::seconds>(), MachO::N_OSO); |
| |
| if (ErrorOr<std::unique_ptr<OutDwarfFile>> ErrorOrObj = |
| loadObject<OutDwarfFile, AddressMap>(Obj, DebugMap, RL)) { |
| ObjectsForLinking.emplace_back(std::move(*ErrorOrObj)); |
| return *ObjectsForLinking.back(); |
| } 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.endswith(")"); |
| |
| 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<MachOUtils::DwarfRelocationApplicationInfo> RelocationsToApply; |
| if (!Options.NoOutput && !ReflectionSectionsPresentInBinary) { |
| auto SectionToOffsetInDwarf = |
| calculateStartOfStrippableReflectionSections(Map); |
| for (const auto &Obj : Map.objects()) |
| copySwiftReflectionMetadata(Obj.get(), GeneralLinker->getEmitter(), |
| SectionToOffsetInDwarf, RelocationsToApply); |
| } |
| |
| uint16_t MaxDWARFVersion = 0; |
| std::function<void(const DWARFUnit &Unit)> 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<std::chrono::seconds>( |
| 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) |
| GeneralLinker->getEmitter()->emitSwiftAST((*ErrorOrMem)->getBuffer()); |
| |
| continue; |
| } |
| |
| if (ErrorOr<std::unique_ptr<OutDwarfFile>> ErrorOrObj = |
| loadObject<OutDwarfFile, AddressMap>(*Obj, Map, RL)) { |
| ObjectsForLinking.emplace_back(std::move(*ErrorOrObj)); |
| GeneralLinker->addObjectFile(*ObjectsForLinking.back(), Loader, |
| OnCUDieLoaded); |
| } else { |
| ObjectsForLinking.push_back(std::make_unique<OutDwarfFile>( |
| Obj->getObjectFilename(), nullptr, nullptr, |
| Obj->empty() ? Obj->getWarnings() : EmptyWarnings)); |
| GeneralLinker->addObjectFile(*ObjectsForLinking.back()); |
| } |
| } |
| |
| // 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<Linker>(*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 (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, |
| *GeneralLinker->getEmitter()->getAsmPrinter().OutStreamer, OutFile, |
| RelocationsToApply); |
| |
| GeneralLinker->getEmitter()->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. |
| template <typename AddressesMapBase> |
| void DwarfLinkerForBinary::AddressManager<AddressesMapBase>:: |
| findValidRelocsMachO(const object::SectionRef &Section, |
| const object::MachOObjectFile &Obj, |
| const DebugMapObject &DMO, |
| std::vector<ValidReloc> &ValidRelocs) { |
| Expected<StringRef> 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<StringRef> 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); |
| } 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); |
| } |
| } |
| } |
| |
| /// Dispatch the valid relocation finding logic to the |
| /// appropriate handler depending on the object file format. |
| template <typename AddressesMapBase> |
| bool DwarfLinkerForBinary::AddressManager<AddressesMapBase>::findValidRelocs( |
| const object::SectionRef &Section, const object::ObjectFile &Obj, |
| const DebugMapObject &DMO, std::vector<ValidReloc> &Relocs) { |
| // Dispatch to the right handler depending on the file type. |
| if (auto *MachOObj = dyn_cast<object::MachOObjectFile>(&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. |
| template <typename AddressesMapBase> |
| bool DwarfLinkerForBinary::AddressManager<AddressesMapBase>:: |
| 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<StringRef> 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; |
| } |
| |
| template <typename AddressesMapBase> |
| std::vector< |
| typename DwarfLinkerForBinary::AddressManager<AddressesMapBase>::ValidReloc> |
| DwarfLinkerForBinary::AddressManager<AddressesMapBase>::getRelocations( |
| const std::vector<ValidReloc> &Relocs, uint64_t StartPos, uint64_t EndPos) { |
| std::vector< |
| DwarfLinkerForBinary::AddressManager<AddressesMapBase>::ValidReloc> |
| Res; |
| |
| auto CurReloc = partition_point(Relocs, [StartPos](const ValidReloc &Reloc) { |
| return Reloc.Offset < StartPos; |
| }); |
| |
| while (CurReloc != Relocs.end() && CurReloc->Offset >= StartPos && |
| CurReloc->Offset < EndPos) { |
| Res.push_back(*CurReloc); |
| CurReloc++; |
| } |
| |
| return Res; |
| } |
| |
| template <typename AddressesMapBase> |
| void DwarfLinkerForBinary::AddressManager<AddressesMapBase>::printReloc( |
| const ValidReloc &Reloc) { |
| const auto &Mapping = Reloc.Mapping->getValue(); |
| const uint64_t ObjectAddress = Mapping.ObjectAddress |
| ? uint64_t(*Mapping.ObjectAddress) |
| : std::numeric_limits<uint64_t>::max(); |
| |
| outs() << "Found valid debug map entry: " << Reloc.Mapping->getKey() << "\t" |
| << format("0x%016" PRIx64 " => 0x%016" PRIx64 "\n", ObjectAddress, |
| uint64_t(Mapping.BinaryAddress)); |
| } |
| |
| template <typename AddressesMapBase> |
| int64_t DwarfLinkerForBinary::AddressManager<AddressesMapBase>::getRelocValue( |
| const ValidReloc &Reloc) { |
| int64_t AddrAdjust = relocate(Reloc); |
| if (Reloc.Mapping->getValue().ObjectAddress) |
| AddrAdjust -= uint64_t(*Reloc.Mapping->getValue().ObjectAddress); |
| return AddrAdjust; |
| } |
| |
| template <typename AddressesMapBase> |
| std::optional<int64_t> |
| DwarfLinkerForBinary::AddressManager<AddressesMapBase>::hasValidRelocationAt( |
| const std::vector<ValidReloc> &AllRelocs, uint64_t StartOffset, |
| uint64_t EndOffset) { |
| std::vector<ValidReloc> Relocs = |
| getRelocations(AllRelocs, StartOffset, EndOffset); |
| |
| if (Relocs.size() == 0) |
| return std::nullopt; |
| |
| if (Linker.Options.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<uint64_t, uint64_t> |
| 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); |
| } |
| |
| template <typename AddressesMapBase> |
| std::optional<int64_t> DwarfLinkerForBinary::AddressManager<AddressesMapBase>:: |
| getExprOpAddressRelocAdjustment(DWARFUnit &U, |
| const DWARFExpression::Operation &Op, |
| uint64_t StartOffset, uint64_t EndOffset) { |
| switch (Op.getCode()) { |
| default: { |
| assert(false && "Specified operation does not have address operand"); |
| } break; |
| case dwarf::DW_OP_const4u: |
| case dwarf::DW_OP_const8u: |
| case dwarf::DW_OP_const4s: |
| case dwarf::DW_OP_const8s: |
| case dwarf::DW_OP_addr: { |
| return hasValidRelocationAt(ValidDebugInfoRelocs, StartOffset, EndOffset); |
| } break; |
| case dwarf::DW_OP_constx: |
| case dwarf::DW_OP_addrx: { |
| return hasValidRelocationAt(ValidDebugAddrRelocs, StartOffset, EndOffset); |
| } break; |
| } |
| |
| return std::nullopt; |
| } |
| |
| template <typename AddressesMapBase> |
| std::optional<int64_t> DwarfLinkerForBinary::AddressManager< |
| AddressesMapBase>::getSubprogramRelocAdjustment(const DWARFDie &DIE) { |
| const auto *Abbrev = DIE.getAbbreviationDeclarationPtr(); |
| |
| std::optional<uint32_t> 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); |
| } |
| 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<DWARFFormValue> AddrValue = DIE.find(dwarf::DW_AT_low_pc); |
| if (std::optional<uint64_t> AddrOffsetSectionBase = |
| DIE.getDwarfUnit()->getAddrOffsetSectionBase()) { |
| uint64_t StartOffset = *AddrOffsetSectionBase + AddrValue->getRawUValue(); |
| uint64_t EndOffset = |
| StartOffset + DIE.getDwarfUnit()->getAddressByteSize(); |
| return hasValidRelocationAt(ValidDebugAddrRelocs, StartOffset, EndOffset); |
| } |
| |
| Linker.reportWarning("no base offset for address table", SrcFileName); |
| return std::nullopt; |
| } |
| default: |
| return std::nullopt; |
| } |
| } |
| |
| template <typename AddressesMapBase> |
| uint64_t DwarfLinkerForBinary::AddressManager<AddressesMapBase>::relocate( |
| const ValidReloc &Reloc) const { |
| return Reloc.Mapping->getValue().BinaryAddress + Reloc.Addend; |
| } |
| |
| /// 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. |
| template <typename AddressesMapBase> |
| bool DwarfLinkerForBinary::AddressManager<AddressesMapBase>::applyValidRelocs( |
| MutableArrayRef<char> Data, uint64_t BaseOffset, bool IsLittleEndian) { |
| std::vector<ValidReloc> 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; |
| } |
| |
| } // namespace dsymutil |
| } // namespace llvm |