forked from OSchip/llvm-project
183 lines
7.3 KiB
C++
183 lines
7.3 KiB
C++
//===- DWARFDebugAddr.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/DWARFDebugAddr.h"
|
|
#include "llvm/BinaryFormat/Dwarf.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFUnit.h"
|
|
|
|
using namespace llvm;
|
|
|
|
void DWARFDebugAddrTable::clear() {
|
|
HeaderData = {};
|
|
Addrs.clear();
|
|
invalidateLength();
|
|
}
|
|
|
|
Error DWARFDebugAddrTable::extract(DWARFDataExtractor Data,
|
|
uint64_t *OffsetPtr,
|
|
uint16_t Version,
|
|
uint8_t AddrSize,
|
|
std::function<void(Error)> WarnCallback) {
|
|
clear();
|
|
HeaderOffset = *OffsetPtr;
|
|
// Read and verify the length field.
|
|
if (!Data.isValidOffsetForDataOfSize(*OffsetPtr, sizeof(uint32_t)))
|
|
return createStringError(errc::invalid_argument,
|
|
"section is not large enough to contain a "
|
|
".debug_addr table length at offset 0x%"
|
|
PRIx64, *OffsetPtr);
|
|
uint16_t UnitVersion;
|
|
if (Version == 0) {
|
|
WarnCallback(createStringError(errc::invalid_argument,
|
|
"DWARF version is not defined in CU,"
|
|
" assuming version 5"));
|
|
UnitVersion = 5;
|
|
} else {
|
|
UnitVersion = Version;
|
|
}
|
|
// TODO: Add support for DWARF64.
|
|
Format = dwarf::DwarfFormat::DWARF32;
|
|
if (UnitVersion >= 5) {
|
|
HeaderData.Length = Data.getU32(OffsetPtr);
|
|
if (HeaderData.Length == dwarf::DW_LENGTH_DWARF64) {
|
|
invalidateLength();
|
|
return createStringError(errc::not_supported,
|
|
"DWARF64 is not supported in .debug_addr at offset 0x%" PRIx64,
|
|
HeaderOffset);
|
|
}
|
|
if (HeaderData.Length + sizeof(uint32_t) < sizeof(Header)) {
|
|
uint32_t TmpLength = getLength();
|
|
invalidateLength();
|
|
return createStringError(errc::invalid_argument,
|
|
".debug_addr table at offset 0x%" PRIx64
|
|
" has too small length (0x%" PRIx32
|
|
") to contain a complete header",
|
|
HeaderOffset, TmpLength);
|
|
}
|
|
uint64_t End = HeaderOffset + getLength();
|
|
if (!Data.isValidOffsetForDataOfSize(HeaderOffset, End - HeaderOffset)) {
|
|
uint32_t TmpLength = getLength();
|
|
invalidateLength();
|
|
return createStringError(errc::invalid_argument,
|
|
"section is not large enough to contain a .debug_addr table "
|
|
"of length 0x%" PRIx32 " at offset 0x%" PRIx64,
|
|
TmpLength, HeaderOffset);
|
|
}
|
|
|
|
HeaderData.Version = Data.getU16(OffsetPtr);
|
|
HeaderData.AddrSize = Data.getU8(OffsetPtr);
|
|
HeaderData.SegSize = Data.getU8(OffsetPtr);
|
|
DataSize = getDataSize();
|
|
} else {
|
|
HeaderData.Version = UnitVersion;
|
|
HeaderData.AddrSize = AddrSize;
|
|
// TODO: Support for non-zero SegSize.
|
|
HeaderData.SegSize = 0;
|
|
DataSize = Data.size();
|
|
}
|
|
|
|
// Perform basic validation of the remaining header fields.
|
|
|
|
// We support DWARF version 5 for now as well as pre-DWARF5
|
|
// implementations of .debug_addr table, which doesn't contain a header
|
|
// and consists only of a series of addresses.
|
|
if (HeaderData.Version > 5) {
|
|
return createStringError(errc::not_supported, "version %" PRIu16
|
|
" of .debug_addr section at offset 0x%" PRIx64 " is not supported",
|
|
HeaderData.Version, HeaderOffset);
|
|
}
|
|
// FIXME: For now we just treat version mismatch as an error,
|
|
// however the correct way to associate a .debug_addr table
|
|
// with a .debug_info table is to look at the DW_AT_addr_base
|
|
// attribute in the info table.
|
|
if (HeaderData.Version != UnitVersion)
|
|
return createStringError(errc::invalid_argument,
|
|
".debug_addr table at offset 0x%" PRIx64
|
|
" has version %" PRIu16
|
|
" which is different from the version suggested"
|
|
" by the DWARF unit header: %" PRIu16,
|
|
HeaderOffset, HeaderData.Version, UnitVersion);
|
|
if (HeaderData.AddrSize != 4 && HeaderData.AddrSize != 8)
|
|
return createStringError(errc::not_supported,
|
|
".debug_addr table at offset 0x%" PRIx64
|
|
" has unsupported address size %" PRIu8,
|
|
HeaderOffset, HeaderData.AddrSize);
|
|
if (HeaderData.AddrSize != AddrSize && AddrSize != 0)
|
|
return createStringError(errc::invalid_argument,
|
|
".debug_addr table at offset 0x%" PRIx64
|
|
" has address size %" PRIu8
|
|
" which is different from CU address size %" PRIu8,
|
|
HeaderOffset, HeaderData.AddrSize, AddrSize);
|
|
|
|
// TODO: add support for non-zero segment selector size.
|
|
if (HeaderData.SegSize != 0)
|
|
return createStringError(errc::not_supported,
|
|
".debug_addr table at offset 0x%" PRIx64
|
|
" has unsupported segment selector size %" PRIu8,
|
|
HeaderOffset, HeaderData.SegSize);
|
|
if (DataSize % HeaderData.AddrSize != 0) {
|
|
invalidateLength();
|
|
return createStringError(errc::invalid_argument,
|
|
".debug_addr table at offset 0x%" PRIx64
|
|
" contains data of size %" PRIu32
|
|
" which is not a multiple of addr size %" PRIu8,
|
|
HeaderOffset, DataSize, HeaderData.AddrSize);
|
|
}
|
|
Data.setAddressSize(HeaderData.AddrSize);
|
|
uint32_t AddrCount = DataSize / HeaderData.AddrSize;
|
|
for (uint32_t I = 0; I < AddrCount; ++I)
|
|
if (HeaderData.AddrSize == 4)
|
|
Addrs.push_back(Data.getU32(OffsetPtr));
|
|
else
|
|
Addrs.push_back(Data.getU64(OffsetPtr));
|
|
return Error::success();
|
|
}
|
|
|
|
void DWARFDebugAddrTable::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
|
|
if (DumpOpts.Verbose)
|
|
OS << format("0x%8.8" PRIx32 ": ", HeaderOffset);
|
|
OS << format("Addr Section: length = 0x%8.8" PRIx32
|
|
", version = 0x%4.4" PRIx16 ", "
|
|
"addr_size = 0x%2.2" PRIx8 ", seg_size = 0x%2.2" PRIx8 "\n",
|
|
HeaderData.Length, HeaderData.Version, HeaderData.AddrSize,
|
|
HeaderData.SegSize);
|
|
|
|
if (Addrs.size() > 0) {
|
|
const char *AddrFmt = (HeaderData.AddrSize == 4) ? "0x%8.8" PRIx64 "\n"
|
|
: "0x%16.16" PRIx64 "\n";
|
|
OS << "Addrs: [\n";
|
|
for (uint64_t Addr : Addrs)
|
|
OS << format(AddrFmt, Addr);
|
|
OS << "]\n";
|
|
}
|
|
}
|
|
|
|
Expected<uint64_t> DWARFDebugAddrTable::getAddrEntry(uint32_t Index) const {
|
|
if (Index < Addrs.size())
|
|
return Addrs[Index];
|
|
return createStringError(errc::invalid_argument,
|
|
"Index %" PRIu32 " is out of range of the "
|
|
".debug_addr table at offset 0x%" PRIx64,
|
|
Index, HeaderOffset);
|
|
}
|
|
|
|
uint32_t DWARFDebugAddrTable::getLength() const {
|
|
if (HeaderData.Length == 0)
|
|
return 0;
|
|
// TODO: DWARF64 support.
|
|
return HeaderData.Length + sizeof(uint32_t);
|
|
}
|
|
|
|
uint32_t DWARFDebugAddrTable::getDataSize() const {
|
|
if (DataSize != 0)
|
|
return DataSize;
|
|
if (getLength() == 0)
|
|
return 0;
|
|
return getLength() - getHeaderSize();
|
|
}
|