mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-28 16:11:29 +00:00
339a8b7fcd
Use a mutex to protect the printing of errors and warnings and prevents interleaving. There are two sources of parallelism in dsymutil that could result in interleaved output: errors from different architectures being processed in parallel and errors from the analyze and clone steps which execute in lockstep. This patch addresses both by using a unique mutex across all error reporting.
1085 lines
40 KiB
C++
1085 lines
40 KiB
C++
//===- 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/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/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);
|
|
}
|
|
|
|
bool DwarfLinkerForBinary::createStreamer(const Triple &TheTriple,
|
|
raw_fd_ostream &OutFile) {
|
|
if (Options.NoOutput)
|
|
return true;
|
|
|
|
Streamer = std::make_unique<DwarfStreamer>(
|
|
Options.FileType, OutFile, Options.Translator,
|
|
[&](const Twine &Error, StringRef Context, const DWARFDie *) {
|
|
reportError(Error, Context);
|
|
},
|
|
[&](const Twine &Warning, StringRef Context, const DWARFDie *) {
|
|
reportWarning(Warning, Context);
|
|
});
|
|
return Streamer->init(TheTriple, "__DWARF");
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
ErrorOr<DWARFFile &>
|
|
DwarfLinkerForBinary::loadObject(const DebugMapObject &Obj,
|
|
const DebugMap &DebugMap,
|
|
remarks::RemarkLinker &RL) {
|
|
auto ErrorOrObj = loadObject(Obj, DebugMap.getTriple());
|
|
|
|
if (ErrorOrObj) {
|
|
ContextForLinking.push_back(
|
|
std::unique_ptr<DWARFContext>(DWARFContext::create(*ErrorOrObj)));
|
|
AddressMapForLinking.push_back(
|
|
std::make_unique<AddressManager>(*this, *ErrorOrObj, Obj));
|
|
|
|
ObjectsForLinking.push_back(std::make_unique<DWARFFile>(
|
|
Obj.getObjectFilename(), ContextForLinking.back().get(),
|
|
AddressMapForLinking.back().get(),
|
|
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 *ObjectsForLinking.back();
|
|
}
|
|
|
|
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 == 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();
|
|
}
|
|
|
|
void DwarfLinkerForBinary::copySwiftReflectionMetadata(
|
|
const llvm::dsymutil::DebugMapObject *Obj, DwarfStreamer *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 (!createStreamer(Map.getTriple(), OutFile))
|
|
return false;
|
|
|
|
ObjectsForLinking.clear();
|
|
ContextForLinking.clear();
|
|
AddressMapForLinking.clear();
|
|
|
|
DebugMap DebugMap(Map.getTriple(), Map.getBinaryPath());
|
|
|
|
DWARFLinker GeneralLinker(Streamer.get(), DwarfLinkerClient::Dsymutil);
|
|
|
|
remarks::RemarkLinker RL;
|
|
if (!Options.RemarksPrependPath.empty())
|
|
RL.setExternalFilePrependPath(Options.RemarksPrependPath);
|
|
GeneralLinker.setObjectPrefixMap(&Options.ObjectPrefixMap);
|
|
|
|
std::function<StringRef(StringRef)> TranslationLambda = [&](StringRef Input) {
|
|
assert(Options.Translator);
|
|
return Options.Translator(Input);
|
|
};
|
|
|
|
GeneralLinker.setVerbosity(Options.Verbose);
|
|
GeneralLinker.setStatistics(Options.Statistics);
|
|
GeneralLinker.setVerifyInputDWARF(Options.VerifyInputDWARF);
|
|
GeneralLinker.setNoOutput(Options.NoOutput);
|
|
GeneralLinker.setNoODR(Options.NoODR);
|
|
GeneralLinker.setUpdate(Options.Update);
|
|
GeneralLinker.setNumThreads(Options.Threads);
|
|
GeneralLinker.setPrependPath(Options.PrependPath);
|
|
GeneralLinker.setKeepFunctionForStatic(Options.KeepFunctionForStatic);
|
|
if (Options.Translator)
|
|
GeneralLinker.setStringsTranslator(TranslationLambda);
|
|
GeneralLinker.setWarningHandler(
|
|
[&](const Twine &Warning, StringRef Context, const DWARFDie *DIE) {
|
|
reportWarning(Warning, Context, DIE);
|
|
});
|
|
GeneralLinker.setErrorHandler(
|
|
[&](const Twine &Error, StringRef Context, const DWARFDie *DIE) {
|
|
reportError(Error, Context, DIE);
|
|
});
|
|
GeneralLinker.setInputVerificationHandler([&](const DWARFFile &File) {
|
|
reportWarning("input verification failed", File.FileName);
|
|
HasVerificationErrors = true;
|
|
});
|
|
objFileLoader Loader = [&DebugMap, &RL,
|
|
this](StringRef ContainerName,
|
|
StringRef Path) -> ErrorOr<DWARFFile &> {
|
|
auto &Obj = DebugMap.addDebugMapObject(
|
|
Path, sys::TimePoint<std::chrono::seconds>(), MachO::N_OSO);
|
|
|
|
if (auto ErrorOrObj = loadObject(Obj, DebugMap, RL)) {
|
|
return *ErrorOrObj;
|
|
} 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(), Streamer.get(),
|
|
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)
|
|
Streamer->emitSwiftAST((*ErrorOrMem)->getBuffer());
|
|
|
|
continue;
|
|
}
|
|
|
|
if (auto ErrorOrObj = loadObject(*Obj, Map, RL))
|
|
GeneralLinker.addObjectFile(*ErrorOrObj, Loader, OnCUDieLoaded);
|
|
else {
|
|
ObjectsForLinking.push_back(std::make_unique<DWARFFile>(
|
|
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)));
|
|
|
|
switch (Options.TheAccelTableKind) {
|
|
case DsymutilAccelTableKind::Apple:
|
|
GeneralLinker.addAccelTableKind(DwarfLinkerAccelTableKind::Apple);
|
|
break;
|
|
case DsymutilAccelTableKind::Dwarf:
|
|
GeneralLinker.addAccelTableKind(DwarfLinkerAccelTableKind::DebugNames);
|
|
break;
|
|
case DsymutilAccelTableKind::Pub:
|
|
GeneralLinker.addAccelTableKind(DwarfLinkerAccelTableKind::Pub);
|
|
break;
|
|
case DsymutilAccelTableKind::Default:
|
|
if (MaxDWARFVersion >= 5)
|
|
GeneralLinker.addAccelTableKind(DwarfLinkerAccelTableKind::DebugNames);
|
|
else
|
|
GeneralLinker.addAccelTableKind(DwarfLinkerAccelTableKind::Apple);
|
|
break;
|
|
case DsymutilAccelTableKind::None:
|
|
// Nothing to do.
|
|
break;
|
|
}
|
|
|
|
// 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() &&
|
|
Options.FileType == 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<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.
|
|
bool DwarfLinkerForBinary::AddressManager::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.
|
|
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<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;
|
|
}
|
|
|
|
std::vector<DwarfLinkerForBinary::AddressManager::ValidReloc>
|
|
DwarfLinkerForBinary::AddressManager::getRelocations(
|
|
const std::vector<ValidReloc> &Relocs, uint64_t StartPos, uint64_t EndPos) {
|
|
std::vector<DwarfLinkerForBinary::AddressManager::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;
|
|
}
|
|
|
|
void DwarfLinkerForBinary::AddressManager::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));
|
|
}
|
|
|
|
void DwarfLinkerForBinary::AddressManager::fillDieInfo(
|
|
const ValidReloc &Reloc, CompileUnit::DIEInfo &Info) {
|
|
Info.AddrAdjust = relocate(Reloc);
|
|
if (Reloc.Mapping->getValue().ObjectAddress)
|
|
Info.AddrAdjust -= uint64_t(*Reloc.Mapping->getValue().ObjectAddress);
|
|
Info.InDebugMap = true;
|
|
}
|
|
|
|
bool DwarfLinkerForBinary::AddressManager::hasValidRelocationAt(
|
|
const std::vector<ValidReloc> &AllRelocs, uint64_t StartOffset,
|
|
uint64_t EndOffset, CompileUnit::DIEInfo &Info) {
|
|
std::vector<ValidReloc> Relocs =
|
|
getRelocations(AllRelocs, StartOffset, EndOffset);
|
|
|
|
if (Relocs.size() == 0)
|
|
return false;
|
|
|
|
if (Linker.Options.Verbose)
|
|
printReloc(Relocs[0]);
|
|
fillDieInfo(Relocs[0], Info);
|
|
|
|
return true;
|
|
}
|
|
|
|
/// 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);
|
|
}
|
|
|
|
bool DwarfLinkerForBinary::AddressManager::isLiveVariable(
|
|
const DWARFDie &DIE, CompileUnit::DIEInfo &MyInfo) {
|
|
const auto *Abbrev = DIE.getAbbreviationDeclarationPtr();
|
|
|
|
std::optional<uint32_t> LocationIdx =
|
|
Abbrev->findAttributeIndex(dwarf::DW_AT_location);
|
|
if (!LocationIdx)
|
|
return false;
|
|
|
|
uint64_t Offset = DIE.getOffset() + getULEB128Size(Abbrev->getCode());
|
|
uint64_t LocationOffset, LocationEndOffset;
|
|
std::tie(LocationOffset, LocationEndOffset) =
|
|
getAttributeOffsets(Abbrev, *LocationIdx, Offset, *DIE.getDwarfUnit());
|
|
|
|
// FIXME: Support relocations debug_addr.
|
|
return hasValidRelocationAt(ValidDebugInfoRelocs, LocationOffset,
|
|
LocationEndOffset, MyInfo);
|
|
}
|
|
|
|
bool DwarfLinkerForBinary::AddressManager::isLiveSubprogram(
|
|
const DWARFDie &DIE, CompileUnit::DIEInfo &MyInfo) {
|
|
const auto *Abbrev = DIE.getAbbreviationDeclarationPtr();
|
|
|
|
std::optional<uint32_t> LowPcIdx =
|
|
Abbrev->findAttributeIndex(dwarf::DW_AT_low_pc);
|
|
if (!LowPcIdx)
|
|
return false;
|
|
|
|
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, MyInfo);
|
|
}
|
|
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,
|
|
MyInfo);
|
|
}
|
|
|
|
Linker.reportWarning("no base offset for address table", SrcFileName);
|
|
return false;
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
uint64_t
|
|
DwarfLinkerForBinary::AddressManager::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.
|
|
bool DwarfLinkerForBinary::AddressManager::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
|