|  | //===- 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(); | 
|  | } |