This allows `llvm-dwarfdump` to decode the DWARF 5 opcode `DW_OP_implicit_pointer` (0xa0). GCC makes use of this opcode in recent versions. LLVM contains some (unfinished) support as well. With existing usage in the ecosystem, adding decoding support here seems reasonable.
412 lines
14 KiB
C++
412 lines
14 KiB
C++
//===- DWARFDebugLoc.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 "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/BinaryFormat/Dwarf.h"
|
|
#include "llvm/DebugInfo/DIContext.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFAddressRange.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFExpression.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFLocationExpression.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFUnit.h"
|
|
#include "llvm/Support/Format.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <algorithm>
|
|
#include <cinttypes>
|
|
#include <cstdint>
|
|
|
|
using namespace llvm;
|
|
using object::SectionedAddress;
|
|
|
|
namespace llvm {
|
|
class DWARFObject;
|
|
}
|
|
|
|
namespace {
|
|
class DWARFLocationInterpreter {
|
|
std::optional<object::SectionedAddress> Base;
|
|
std::function<std::optional<object::SectionedAddress>(uint32_t)> LookupAddr;
|
|
|
|
public:
|
|
DWARFLocationInterpreter(
|
|
std::optional<object::SectionedAddress> Base,
|
|
std::function<std::optional<object::SectionedAddress>(uint32_t)>
|
|
LookupAddr)
|
|
: Base(Base), LookupAddr(std::move(LookupAddr)) {}
|
|
|
|
Expected<std::optional<DWARFLocationExpression>>
|
|
Interpret(const DWARFLocationEntry &E);
|
|
};
|
|
} // namespace
|
|
|
|
static Error createResolverError(uint32_t Index, unsigned Kind) {
|
|
return make_error<ResolverError>(Index, (dwarf::LoclistEntries)Kind);
|
|
}
|
|
|
|
Expected<std::optional<DWARFLocationExpression>>
|
|
DWARFLocationInterpreter::Interpret(const DWARFLocationEntry &E) {
|
|
switch (E.Kind) {
|
|
case dwarf::DW_LLE_end_of_list:
|
|
return std::nullopt;
|
|
case dwarf::DW_LLE_base_addressx: {
|
|
Base = LookupAddr(E.Value0);
|
|
if (!Base)
|
|
return createResolverError(E.Value0, E.Kind);
|
|
return std::nullopt;
|
|
}
|
|
case dwarf::DW_LLE_startx_endx: {
|
|
std::optional<SectionedAddress> LowPC = LookupAddr(E.Value0);
|
|
if (!LowPC)
|
|
return createResolverError(E.Value0, E.Kind);
|
|
std::optional<SectionedAddress> HighPC = LookupAddr(E.Value1);
|
|
if (!HighPC)
|
|
return createResolverError(E.Value1, E.Kind);
|
|
return DWARFLocationExpression{
|
|
DWARFAddressRange{LowPC->Address, HighPC->Address, LowPC->SectionIndex},
|
|
E.Loc};
|
|
}
|
|
case dwarf::DW_LLE_startx_length: {
|
|
std::optional<SectionedAddress> LowPC = LookupAddr(E.Value0);
|
|
if (!LowPC)
|
|
return createResolverError(E.Value0, E.Kind);
|
|
return DWARFLocationExpression{DWARFAddressRange{LowPC->Address,
|
|
LowPC->Address + E.Value1,
|
|
LowPC->SectionIndex},
|
|
E.Loc};
|
|
}
|
|
case dwarf::DW_LLE_offset_pair: {
|
|
if (!Base) {
|
|
return createStringError(inconvertibleErrorCode(),
|
|
"Unable to resolve location list offset pair: "
|
|
"Base address not defined");
|
|
}
|
|
DWARFAddressRange Range{Base->Address + E.Value0, Base->Address + E.Value1,
|
|
Base->SectionIndex};
|
|
if (Range.SectionIndex == SectionedAddress::UndefSection)
|
|
Range.SectionIndex = E.SectionIndex;
|
|
return DWARFLocationExpression{Range, E.Loc};
|
|
}
|
|
case dwarf::DW_LLE_default_location:
|
|
return DWARFLocationExpression{std::nullopt, E.Loc};
|
|
case dwarf::DW_LLE_base_address:
|
|
Base = SectionedAddress{E.Value0, E.SectionIndex};
|
|
return std::nullopt;
|
|
case dwarf::DW_LLE_start_end:
|
|
return DWARFLocationExpression{
|
|
DWARFAddressRange{E.Value0, E.Value1, E.SectionIndex}, E.Loc};
|
|
case dwarf::DW_LLE_start_length:
|
|
return DWARFLocationExpression{
|
|
DWARFAddressRange{E.Value0, E.Value0 + E.Value1, E.SectionIndex},
|
|
E.Loc};
|
|
default:
|
|
llvm_unreachable("unreachable locations list kind");
|
|
}
|
|
}
|
|
|
|
static void dumpExpression(raw_ostream &OS, DIDumpOptions DumpOpts,
|
|
ArrayRef<uint8_t> Data, bool IsLittleEndian,
|
|
unsigned AddressSize, DWARFUnit *U) {
|
|
DWARFDataExtractor Extractor(Data, IsLittleEndian, AddressSize);
|
|
std::optional<dwarf::DwarfFormat> Format;
|
|
if (U)
|
|
Format = U->getFormat();
|
|
DWARFExpression(Extractor, AddressSize, Format).print(OS, DumpOpts, U);
|
|
}
|
|
|
|
bool DWARFLocationTable::dumpLocationList(
|
|
uint64_t *Offset, raw_ostream &OS, std::optional<SectionedAddress> BaseAddr,
|
|
const DWARFObject &Obj, DWARFUnit *U, DIDumpOptions DumpOpts,
|
|
unsigned Indent) const {
|
|
DWARFLocationInterpreter Interp(
|
|
BaseAddr, [U](uint32_t Index) -> std::optional<SectionedAddress> {
|
|
if (U)
|
|
return U->getAddrOffsetSectionItem(Index);
|
|
return std::nullopt;
|
|
});
|
|
OS << format("0x%8.8" PRIx64 ": ", *Offset);
|
|
Error E = visitLocationList(Offset, [&](const DWARFLocationEntry &E) {
|
|
Expected<std::optional<DWARFLocationExpression>> Loc = Interp.Interpret(E);
|
|
if (!Loc || DumpOpts.DisplayRawContents)
|
|
dumpRawEntry(E, OS, Indent, DumpOpts, Obj);
|
|
if (Loc && *Loc) {
|
|
OS << "\n";
|
|
OS.indent(Indent);
|
|
if (DumpOpts.DisplayRawContents)
|
|
OS << " => ";
|
|
|
|
DIDumpOptions RangeDumpOpts(DumpOpts);
|
|
RangeDumpOpts.DisplayRawContents = false;
|
|
if (Loc.get()->Range)
|
|
Loc.get()->Range->dump(OS, Data.getAddressSize(), RangeDumpOpts, &Obj);
|
|
else
|
|
OS << "<default>";
|
|
}
|
|
if (!Loc)
|
|
consumeError(Loc.takeError());
|
|
|
|
if (E.Kind != dwarf::DW_LLE_base_address &&
|
|
E.Kind != dwarf::DW_LLE_base_addressx &&
|
|
E.Kind != dwarf::DW_LLE_end_of_list) {
|
|
OS << ": ";
|
|
dumpExpression(OS, DumpOpts, E.Loc, Data.isLittleEndian(),
|
|
Data.getAddressSize(), U);
|
|
}
|
|
return true;
|
|
});
|
|
if (E) {
|
|
DumpOpts.RecoverableErrorHandler(std::move(E));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
Error DWARFLocationTable::visitAbsoluteLocationList(
|
|
uint64_t Offset, std::optional<SectionedAddress> BaseAddr,
|
|
std::function<std::optional<SectionedAddress>(uint32_t)> LookupAddr,
|
|
function_ref<bool(Expected<DWARFLocationExpression>)> Callback) const {
|
|
DWARFLocationInterpreter Interp(BaseAddr, std::move(LookupAddr));
|
|
return visitLocationList(&Offset, [&](const DWARFLocationEntry &E) {
|
|
Expected<std::optional<DWARFLocationExpression>> Loc = Interp.Interpret(E);
|
|
if (!Loc)
|
|
return Callback(Loc.takeError());
|
|
if (*Loc)
|
|
return Callback(**Loc);
|
|
return true;
|
|
});
|
|
}
|
|
|
|
void DWARFDebugLoc::dump(raw_ostream &OS, const DWARFObject &Obj,
|
|
DIDumpOptions DumpOpts,
|
|
std::optional<uint64_t> DumpOffset) const {
|
|
auto BaseAddr = std::nullopt;
|
|
unsigned Indent = 12;
|
|
if (DumpOffset) {
|
|
dumpLocationList(&*DumpOffset, OS, BaseAddr, Obj, nullptr, DumpOpts,
|
|
Indent);
|
|
} else {
|
|
uint64_t Offset = 0;
|
|
StringRef Separator;
|
|
bool CanContinue = true;
|
|
while (CanContinue && Data.isValidOffset(Offset)) {
|
|
OS << Separator;
|
|
Separator = "\n";
|
|
|
|
CanContinue = dumpLocationList(&Offset, OS, BaseAddr, Obj, nullptr,
|
|
DumpOpts, Indent);
|
|
OS << '\n';
|
|
}
|
|
}
|
|
}
|
|
|
|
Error DWARFDebugLoc::visitLocationList(
|
|
uint64_t *Offset,
|
|
function_ref<bool(const DWARFLocationEntry &)> Callback) const {
|
|
DataExtractor::Cursor C(*Offset);
|
|
while (true) {
|
|
uint64_t SectionIndex;
|
|
uint64_t Value0 = Data.getRelocatedAddress(C);
|
|
uint64_t Value1 = Data.getRelocatedAddress(C, &SectionIndex);
|
|
|
|
DWARFLocationEntry E;
|
|
|
|
// The end of any given location list is marked by an end of list entry,
|
|
// which consists of a 0 for the beginning address offset and a 0 for the
|
|
// ending address offset. A beginning offset of 0xff...f marks the base
|
|
// address selection entry.
|
|
if (Value0 == 0 && Value1 == 0) {
|
|
E.Kind = dwarf::DW_LLE_end_of_list;
|
|
} else if (Value0 == (Data.getAddressSize() == 4 ? -1U : -1ULL)) {
|
|
E.Kind = dwarf::DW_LLE_base_address;
|
|
E.Value0 = Value1;
|
|
E.SectionIndex = SectionIndex;
|
|
} else {
|
|
E.Kind = dwarf::DW_LLE_offset_pair;
|
|
E.Value0 = Value0;
|
|
E.Value1 = Value1;
|
|
E.SectionIndex = SectionIndex;
|
|
unsigned Bytes = Data.getU16(C);
|
|
// A single location description describing the location of the object...
|
|
Data.getU8(C, E.Loc, Bytes);
|
|
}
|
|
|
|
if (!C)
|
|
return C.takeError();
|
|
if (!Callback(E) || E.Kind == dwarf::DW_LLE_end_of_list)
|
|
break;
|
|
}
|
|
*Offset = C.tell();
|
|
return Error::success();
|
|
}
|
|
|
|
void DWARFDebugLoc::dumpRawEntry(const DWARFLocationEntry &Entry,
|
|
raw_ostream &OS, unsigned Indent,
|
|
DIDumpOptions DumpOpts,
|
|
const DWARFObject &Obj) const {
|
|
uint64_t Value0, Value1;
|
|
switch (Entry.Kind) {
|
|
case dwarf::DW_LLE_base_address:
|
|
Value0 = Data.getAddressSize() == 4 ? -1U : -1ULL;
|
|
Value1 = Entry.Value0;
|
|
break;
|
|
case dwarf::DW_LLE_offset_pair:
|
|
Value0 = Entry.Value0;
|
|
Value1 = Entry.Value1;
|
|
break;
|
|
case dwarf::DW_LLE_end_of_list:
|
|
return;
|
|
default:
|
|
llvm_unreachable("Not possible in DWARF4!");
|
|
}
|
|
OS << '\n';
|
|
OS.indent(Indent);
|
|
OS << '(' << format_hex(Value0, 2 + Data.getAddressSize() * 2) << ", "
|
|
<< format_hex(Value1, 2 + Data.getAddressSize() * 2) << ')';
|
|
DWARFFormValue::dumpAddressSection(Obj, OS, DumpOpts, Entry.SectionIndex);
|
|
}
|
|
|
|
Error DWARFDebugLoclists::visitLocationList(
|
|
uint64_t *Offset, function_ref<bool(const DWARFLocationEntry &)> F) const {
|
|
|
|
DataExtractor::Cursor C(*Offset);
|
|
bool Continue = true;
|
|
while (Continue) {
|
|
DWARFLocationEntry E;
|
|
E.Kind = Data.getU8(C);
|
|
switch (E.Kind) {
|
|
case dwarf::DW_LLE_end_of_list:
|
|
break;
|
|
case dwarf::DW_LLE_base_addressx:
|
|
E.Value0 = Data.getULEB128(C);
|
|
break;
|
|
case dwarf::DW_LLE_startx_endx:
|
|
E.Value0 = Data.getULEB128(C);
|
|
E.Value1 = Data.getULEB128(C);
|
|
break;
|
|
case dwarf::DW_LLE_startx_length:
|
|
E.Value0 = Data.getULEB128(C);
|
|
// Pre-DWARF 5 has different interpretation of the length field. We have
|
|
// to support both pre- and standartized styles for the compatibility.
|
|
if (Version < 5)
|
|
E.Value1 = Data.getU32(C);
|
|
else
|
|
E.Value1 = Data.getULEB128(C);
|
|
break;
|
|
case dwarf::DW_LLE_offset_pair:
|
|
E.Value0 = Data.getULEB128(C);
|
|
E.Value1 = Data.getULEB128(C);
|
|
E.SectionIndex = SectionedAddress::UndefSection;
|
|
break;
|
|
case dwarf::DW_LLE_default_location:
|
|
break;
|
|
case dwarf::DW_LLE_base_address:
|
|
E.Value0 = Data.getRelocatedAddress(C, &E.SectionIndex);
|
|
break;
|
|
case dwarf::DW_LLE_start_end:
|
|
E.Value0 = Data.getRelocatedAddress(C, &E.SectionIndex);
|
|
E.Value1 = Data.getRelocatedAddress(C);
|
|
break;
|
|
case dwarf::DW_LLE_start_length:
|
|
E.Value0 = Data.getRelocatedAddress(C, &E.SectionIndex);
|
|
E.Value1 = Data.getULEB128(C);
|
|
break;
|
|
default:
|
|
cantFail(C.takeError());
|
|
return createStringError(errc::illegal_byte_sequence,
|
|
"LLE of kind %x not supported", (int)E.Kind);
|
|
}
|
|
|
|
if (E.Kind != dwarf::DW_LLE_base_address &&
|
|
E.Kind != dwarf::DW_LLE_base_addressx &&
|
|
E.Kind != dwarf::DW_LLE_end_of_list) {
|
|
unsigned Bytes = Version >= 5 ? Data.getULEB128(C) : Data.getU16(C);
|
|
// A single location description describing the location of the object...
|
|
Data.getU8(C, E.Loc, Bytes);
|
|
}
|
|
|
|
if (!C)
|
|
return C.takeError();
|
|
Continue = F(E) && E.Kind != dwarf::DW_LLE_end_of_list;
|
|
}
|
|
*Offset = C.tell();
|
|
return Error::success();
|
|
}
|
|
|
|
void DWARFDebugLoclists::dumpRawEntry(const DWARFLocationEntry &Entry,
|
|
raw_ostream &OS, unsigned Indent,
|
|
DIDumpOptions DumpOpts,
|
|
const DWARFObject &Obj) const {
|
|
size_t MaxEncodingStringLength = 0;
|
|
#define HANDLE_DW_LLE(ID, NAME) \
|
|
MaxEncodingStringLength = std::max(MaxEncodingStringLength, \
|
|
dwarf::LocListEncodingString(ID).size());
|
|
#include "llvm/BinaryFormat/Dwarf.def"
|
|
|
|
OS << "\n";
|
|
OS.indent(Indent);
|
|
StringRef EncodingString = dwarf::LocListEncodingString(Entry.Kind);
|
|
// Unsupported encodings should have been reported during parsing.
|
|
assert(!EncodingString.empty() && "Unknown loclist entry encoding");
|
|
OS << format("%-*s(", MaxEncodingStringLength, EncodingString.data());
|
|
unsigned FieldSize = 2 + 2 * Data.getAddressSize();
|
|
switch (Entry.Kind) {
|
|
case dwarf::DW_LLE_end_of_list:
|
|
case dwarf::DW_LLE_default_location:
|
|
break;
|
|
case dwarf::DW_LLE_startx_endx:
|
|
case dwarf::DW_LLE_startx_length:
|
|
case dwarf::DW_LLE_offset_pair:
|
|
case dwarf::DW_LLE_start_end:
|
|
case dwarf::DW_LLE_start_length:
|
|
OS << format_hex(Entry.Value0, FieldSize) << ", "
|
|
<< format_hex(Entry.Value1, FieldSize);
|
|
break;
|
|
case dwarf::DW_LLE_base_addressx:
|
|
case dwarf::DW_LLE_base_address:
|
|
OS << format_hex(Entry.Value0, FieldSize);
|
|
break;
|
|
}
|
|
OS << ')';
|
|
switch (Entry.Kind) {
|
|
case dwarf::DW_LLE_base_address:
|
|
case dwarf::DW_LLE_start_end:
|
|
case dwarf::DW_LLE_start_length:
|
|
DWARFFormValue::dumpAddressSection(Obj, OS, DumpOpts, Entry.SectionIndex);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void DWARFDebugLoclists::dumpRange(uint64_t StartOffset, uint64_t Size,
|
|
raw_ostream &OS, const DWARFObject &Obj,
|
|
DIDumpOptions DumpOpts) {
|
|
if (!Data.isValidOffsetForDataOfSize(StartOffset, Size)) {
|
|
OS << "Invalid dump range\n";
|
|
return;
|
|
}
|
|
uint64_t Offset = StartOffset;
|
|
StringRef Separator;
|
|
bool CanContinue = true;
|
|
while (CanContinue && Offset < StartOffset + Size) {
|
|
OS << Separator;
|
|
Separator = "\n";
|
|
|
|
CanContinue = dumpLocationList(&Offset, OS, /*BaseAddr=*/std::nullopt, Obj,
|
|
nullptr, DumpOpts, /*Indent=*/12);
|
|
OS << '\n';
|
|
}
|
|
}
|
|
|
|
void llvm::ResolverError::log(raw_ostream &OS) const {
|
|
OS << format("unable to resolve indirect address %u for: %s", Index,
|
|
dwarf::LocListEncodingString(Kind).data());
|
|
}
|
|
|
|
char llvm::ResolverError::ID;
|