#include "DebugInfoLinker.h"
#include "Error.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/DWARFLinker/DWARFLinker.h"
#include "llvm/DWARFLinker/DWARFStreamer.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/DWARF/DWARFExpression.h"
#include "llvm/Object/ObjectFile.h"
#include <memory>
#include <vector>
namespace llvm {
namespace dwarfutil {
class ObjFileAddressMap : public AddressesMap {
public:
ObjFileAddressMap(DWARFContext &Context, const Options &Options,
object::ObjectFile &ObjFile)
: Opts(Options) {
for (const object::SectionRef &Sect : ObjFile.sections()) {
if (!Sect.isText())
continue;
const uint64_t Size = Sect.getSize();
if (Size == 0)
continue;
const uint64_t StartAddr = Sect.getAddress();
TextAddressRanges.insert({StartAddr, StartAddr + Size});
}
for (std::unique_ptr<DWARFUnit> &CU : Context.compile_units()) {
Expected<llvm::DWARFAddressRangesVector> ARanges =
CU->getUnitDIE().getAddressRanges();
if (ARanges) {
for (auto &Range : *ARanges) {
if (!isDeadAddressRange(Range.LowPC, Range.HighPC, CU->getVersion(),
Options.Tombstone, CU->getAddressByteSize()))
DWARFAddressRanges.insert({Range.LowPC, Range.HighPC}, 0);
}
}
}
}
bool hasValidRelocs() override { return !DWARFAddressRanges.empty(); }
bool isLiveSubprogram(const DWARFDie &DIE,
CompileUnit::DIEInfo &Info) override {
assert((DIE.getTag() == dwarf::DW_TAG_subprogram ||
DIE.getTag() == dwarf::DW_TAG_label) &&
"Wrong type of input die");
if (Optional<uint64_t> LowPC =
dwarf::toAddress(DIE.find(dwarf::DW_AT_low_pc))) {
if (!isDeadAddress(*LowPC, DIE.getDwarfUnit()->getVersion(),
Opts.Tombstone,
DIE.getDwarfUnit()->getAddressByteSize())) {
Info.AddrAdjust = 0;
Info.InDebugMap = true;
return true;
}
}
return false;
}
bool isLiveVariable(const DWARFDie &DIE,
CompileUnit::DIEInfo &Info) override {
assert((DIE.getTag() == dwarf::DW_TAG_variable ||
DIE.getTag() == dwarf::DW_TAG_constant) &&
"Wrong type of input die");
if (Expected<DWARFLocationExpressionsVector> Loc =
DIE.getLocations(dwarf::DW_AT_location)) {
DWARFUnit *U = DIE.getDwarfUnit();
for (const auto &Entry : *Loc) {
DataExtractor Data(toStringRef(Entry.Expr),
U->getContext().isLittleEndian(), 0);
DWARFExpression Expression(Data, U->getAddressByteSize(),
U->getFormParams().Format);
bool HasLiveAddresses =
any_of(Expression, [&](const DWARFExpression::Operation &Op) {
return !Op.isError() &&
(Op.getCode() == dwarf::DW_OP_addr &&
!isDeadAddress(Op.getRawOperand(0), U->getVersion(),
Opts.Tombstone,
DIE.getDwarfUnit()->getAddressByteSize()));
});
if (HasLiveAddresses) {
Info.AddrAdjust = 0;
Info.InDebugMap = true;
return true;
}
}
} else {
consumeError(Loc.takeError());
}
return false;
}
bool applyValidRelocs(MutableArrayRef<char>, uint64_t, bool) override {
return false;
}
RangesTy &getValidAddressRanges() override { return DWARFAddressRanges; };
void clear() override { DWARFAddressRanges.clear(); }
llvm::Expected<uint64_t> relocateIndexedAddr(uint64_t, uint64_t) override {
return object::createError("no relocations in linked binary");
}
protected:
bool isInsideExecutableSectionsAddressRange(uint64_t LowPC,
Optional<uint64_t> HighPC) {
Optional<AddressRange> Range =
TextAddressRanges.getRangeThatContains(LowPC);
if (HighPC)
return Range.has_value() && Range->end() >= *HighPC;
return Range.has_value();
}
uint64_t isBFDDeadAddressRange(uint64_t LowPC, Optional<uint64_t> HighPC,
uint16_t Version) {
if (LowPC == 0)
return true;
if ((Version <= 4) && HighPC && (LowPC == 1 && *HighPC == 1))
return true;
return !isInsideExecutableSectionsAddressRange(LowPC, HighPC);
}
uint64_t isMAXPCDeadAddressRange(uint64_t LowPC, Optional<uint64_t> HighPC,
uint16_t Version, uint8_t AddressByteSize) {
if (Version <= 4 && HighPC) {
if (LowPC == (dwarf::computeTombstoneAddress(AddressByteSize) - 1))
return true;
} else if (LowPC == dwarf::computeTombstoneAddress(AddressByteSize))
return true;
if (!isInsideExecutableSectionsAddressRange(LowPC, HighPC))
warning("Address referencing invalid text section is not marked with "
"tombstone value");
return false;
}
bool isDeadAddressRange(uint64_t LowPC, Optional<uint64_t> HighPC,
uint16_t Version, TombstoneKind Tombstone,
uint8_t AddressByteSize) {
switch (Tombstone) {
case TombstoneKind::BFD:
return isBFDDeadAddressRange(LowPC, HighPC, Version);
case TombstoneKind::MaxPC:
return isMAXPCDeadAddressRange(LowPC, HighPC, Version, AddressByteSize);
case TombstoneKind::Universal:
return isBFDDeadAddressRange(LowPC, HighPC, Version) ||
isMAXPCDeadAddressRange(LowPC, HighPC, Version, AddressByteSize);
case TombstoneKind::Exec:
return !isInsideExecutableSectionsAddressRange(LowPC, HighPC);
}
llvm_unreachable("Unknown tombstone value");
}
bool isDeadAddress(uint64_t LowPC, uint16_t Version, TombstoneKind Tombstone,
uint8_t AddressByteSize) {
return isDeadAddressRange(LowPC, None, Version, Tombstone, AddressByteSize);
}
private:
RangesTy DWARFAddressRanges;
AddressRanges TextAddressRanges;
const Options &Opts;
};
static bool knownByDWARFUtil(StringRef SecName) {
return llvm::StringSwitch<bool>(SecName)
.Case(".debug_info", true)
.Case(".debug_types", true)
.Case(".debug_abbrev", true)
.Case(".debug_loc", true)
.Case(".debug_loclists", true)
.Case(".debug_frame", true)
.Case(".debug_aranges", true)
.Case(".debug_ranges", true)
.Case(".debug_rnglists", true)
.Case(".debug_line", true)
.Case(".debug_line_str", true)
.Case(".debug_addr", true)
.Case(".debug_macro", true)
.Case(".debug_macinfo", true)
.Case(".debug_str", true)
.Case(".debug_str_offsets", true)
.Default(false);
}
Error linkDebugInfo(object::ObjectFile &File, const Options &Options,
raw_pwrite_stream &OutStream) {
auto ReportWarn = [&](const Twine &Message, StringRef Context,
const DWARFDie *Die) {
warning(Message, Context);
if (!Options.Verbose || !Die)
return;
DIDumpOptions DumpOpts;
DumpOpts.ChildRecurseDepth = 0;
DumpOpts.Verbose = Options.Verbose;
WithColor::note() << " in DIE:\n";
Die->dump(errs(), 6, DumpOpts);
};
auto ReportErr = [&](const Twine &Message, StringRef Context,
const DWARFDie *) {
WithColor::error(errs(), Context) << Message << '\n';
};
DwarfStreamer OutStreamer(OutputFileType::Object, OutStream, nullptr,
ReportWarn, ReportWarn);
Triple TargetTriple = File.makeTriple();
if (!OutStreamer.init(TargetTriple, formatv("cannot create a stream for {0}",
TargetTriple.getTriple())
.str()))
return createStringError(std::errc::invalid_argument, "");
DWARFLinker DebugInfoLinker(&OutStreamer, DwarfLinkerClient::LLD);
DebugInfoLinker.setEstimatedObjfilesAmount(1);
DebugInfoLinker.setAccelTableKind(DwarfLinkerAccelTableKind::None);
DebugInfoLinker.setErrorHandler(ReportErr);
DebugInfoLinker.setWarningHandler(ReportWarn);
DebugInfoLinker.setNumThreads(Options.NumThreads);
DebugInfoLinker.setNoODR(!Options.DoODRDeduplication);
DebugInfoLinker.setVerbosity(Options.Verbose);
DebugInfoLinker.setUpdate(!Options.DoGarbageCollection);
std::vector<std::unique_ptr<DWARFFile>> ObjectsForLinking(1);
std::vector<std::unique_ptr<AddressesMap>> AddresssMapForLinking(1);
std::vector<std::string> EmptyWarnings;
std::unique_ptr<DWARFContext> Context = DWARFContext::create(File);
for (SectionName Sec : Context->getDWARFObj().getSectionNames()) {
if (isDebugSection(Sec.Name) && !knownByDWARFUtil(Sec.Name))
warning(
formatv("'{0}' is not currently supported: section will be skipped",
Sec.Name),
Options.InputFileName);
}
AddresssMapForLinking[0] =
std::make_unique<ObjFileAddressMap>(*Context, Options, File);
ObjectsForLinking[0] = std::make_unique<DWARFFile>(
File.getFileName(), &*Context, AddresssMapForLinking[0].get(),
EmptyWarnings);
for (size_t I = 0; I < ObjectsForLinking.size(); I++)
DebugInfoLinker.addObjectFile(*ObjectsForLinking[I]);
if (Error Err = DebugInfoLinker.link())
return Err;
OutStreamer.finish();
return Error::success();
}
} }