245 lines · cpp
1//===-- ObjDumper.cpp - Base dumper class -----------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// This file implements ObjDumper.11///12//===----------------------------------------------------------------------===//13 14#include "ObjDumper.h"15#include "llvm-readobj.h"16#include "llvm/Object/Archive.h"17#include "llvm/Object/Decompressor.h"18#include "llvm/Object/ObjectFile.h"19#include "llvm/Object/OffloadBinary.h"20#include "llvm/Object/OffloadBundle.h"21#include "llvm/Support/Error.h"22#include "llvm/Support/FormatVariadic.h"23#include "llvm/Support/ScopedPrinter.h"24#include "llvm/Support/raw_ostream.h"25#include <map>26 27namespace llvm {28 29static inline Error createError(const Twine &Msg) {30 return createStringError(object::object_error::parse_failed, Msg);31}32 33ObjDumper::ObjDumper(ScopedPrinter &Writer, StringRef ObjName) : W(Writer) {34 // Dumper reports all non-critical errors as warnings.35 // It does not print the same warning more than once.36 WarningHandler = [=](const Twine &Msg) {37 if (Warnings.insert(Msg.str()).second)38 reportWarning(createError(Msg), ObjName);39 return Error::success();40 };41}42 43ObjDumper::~ObjDumper() = default;44 45void ObjDumper::reportUniqueWarning(Error Err) const {46 reportUniqueWarning(toString(std::move(Err)));47}48 49void ObjDumper::reportUniqueWarning(const Twine &Msg) const {50 cantFail(WarningHandler(Msg),51 "WarningHandler should always return ErrorSuccess");52}53 54static void printAsPrintable(raw_ostream &W, const uint8_t *Start, size_t Len) {55 for (size_t i = 0; i < Len; i++)56 W << (isPrint(Start[i]) ? static_cast<char>(Start[i]) : '.');57}58 59void ObjDumper::printAsStringList(StringRef StringContent,60 size_t StringDataOffset) {61 size_t StrSize = StringContent.size();62 if (StrSize == 0)63 return;64 if (StrSize < StringDataOffset) {65 reportUniqueWarning("offset (0x" + Twine::utohexstr(StringDataOffset) +66 ") is past the end of the contents (size 0x" +67 Twine::utohexstr(StrSize) + ")");68 return;69 }70 71 const uint8_t *StrContent = StringContent.bytes_begin();72 // Some formats contain additional metadata at the start which should not be73 // interpreted as strings. Skip these bytes, but account for them in the74 // string offsets.75 const uint8_t *CurrentWord = StrContent + StringDataOffset;76 const uint8_t *StrEnd = StringContent.bytes_end();77 78 while (CurrentWord <= StrEnd) {79 size_t WordSize = strnlen(reinterpret_cast<const char *>(CurrentWord),80 StrEnd - CurrentWord);81 if (!WordSize) {82 CurrentWord++;83 continue;84 }85 W.startLine() << format("[%6tx] ", CurrentWord - StrContent);86 printAsPrintable(W.getOStream(), CurrentWord, WordSize);87 W.getOStream() << '\n';88 CurrentWord += WordSize + 1;89 }90}91 92void ObjDumper::printFileSummary(StringRef FileStr, object::ObjectFile &Obj,93 ArrayRef<std::string> InputFilenames,94 const object::Archive *A) {95 if (!FileStr.empty()) {96 W.getOStream() << "\n";97 W.printString("File", FileStr);98 }99 W.printString("Format", Obj.getFileFormatName());100 W.printString("Arch", Triple::getArchTypeName(Obj.getArch()));101 W.printString("AddressSize",102 std::string(formatv("{0}bit", 8 * Obj.getBytesInAddress())));103 this->printLoadName();104}105 106std::vector<object::SectionRef>107ObjDumper::getSectionRefsByNameOrIndex(const object::ObjectFile &Obj,108 ArrayRef<std::string> Sections) {109 std::vector<object::SectionRef> Ret;110 std::map<std::string, bool, std::less<>> SecNames;111 std::map<unsigned, bool> SecIndices;112 unsigned SecIndex;113 for (StringRef Section : Sections) {114 if (!Section.getAsInteger(0, SecIndex))115 SecIndices.emplace(SecIndex, false);116 else117 SecNames.emplace(std::string(Section), false);118 }119 120 SecIndex = Obj.isELF() ? 0 : 1;121 for (object::SectionRef SecRef : Obj.sections()) {122 StringRef SecName = unwrapOrError(Obj.getFileName(), SecRef.getName());123 auto NameIt = SecNames.find(SecName);124 if (NameIt != SecNames.end())125 NameIt->second = true;126 auto IndexIt = SecIndices.find(SecIndex);127 if (IndexIt != SecIndices.end())128 IndexIt->second = true;129 if (NameIt != SecNames.end() || IndexIt != SecIndices.end())130 Ret.push_back(SecRef);131 SecIndex++;132 }133 134 for (const std::pair<const std::string, bool> &S : SecNames)135 if (!S.second)136 reportWarning(137 createError(formatv("could not find section '{0}'", S.first).str()),138 Obj.getFileName());139 140 for (std::pair<unsigned, bool> S : SecIndices)141 if (!S.second)142 reportWarning(143 createError(formatv("could not find section {0}", S.first).str()),144 Obj.getFileName());145 146 return Ret;147}148 149static void maybeDecompress(const object::ObjectFile &Obj,150 StringRef SectionName, StringRef &SectionContent,151 SmallString<0> &Out) {152 Expected<object::Decompressor> Decompressor = object::Decompressor::create(153 SectionName, SectionContent, Obj.isLittleEndian(), Obj.is64Bit());154 if (!Decompressor)155 reportWarning(Decompressor.takeError(), Obj.getFileName());156 else if (auto Err = Decompressor->resizeAndDecompress(Out))157 reportWarning(std::move(Err), Obj.getFileName());158 else159 SectionContent = Out;160}161 162void ObjDumper::printSectionsAsString(const object::ObjectFile &Obj,163 ArrayRef<std::string> Sections,164 bool Decompress) {165 SmallString<0> Out;166 for (object::SectionRef Section :167 getSectionRefsByNameOrIndex(Obj, Sections)) {168 StringRef SectionName = unwrapOrError(Obj.getFileName(), Section.getName());169 W.getOStream() << '\n';170 W.startLine() << "String dump of section '" << SectionName << "':\n";171 172 StringRef SectionContent =173 unwrapOrError(Obj.getFileName(), Section.getContents());174 if (Decompress && Section.isCompressed())175 maybeDecompress(Obj, SectionName, SectionContent, Out);176 printAsStringList(SectionContent);177 }178}179 180void ObjDumper::printSectionsAsHex(const object::ObjectFile &Obj,181 ArrayRef<std::string> Sections,182 bool Decompress) {183 SmallString<0> Out;184 for (object::SectionRef Section :185 getSectionRefsByNameOrIndex(Obj, Sections)) {186 StringRef SectionName = unwrapOrError(Obj.getFileName(), Section.getName());187 W.getOStream() << '\n';188 W.startLine() << "Hex dump of section '" << SectionName << "':\n";189 190 StringRef SectionContent =191 unwrapOrError(Obj.getFileName(), Section.getContents());192 if (Decompress && Section.isCompressed())193 maybeDecompress(Obj, SectionName, SectionContent, Out);194 const uint8_t *SecContent = SectionContent.bytes_begin();195 const uint8_t *SecEnd = SecContent + SectionContent.size();196 197 for (const uint8_t *SecPtr = SecContent; SecPtr < SecEnd; SecPtr += 16) {198 const uint8_t *TmpSecPtr = SecPtr;199 uint8_t i;200 uint8_t k;201 202 W.startLine() << format_hex(Section.getAddress() + (SecPtr - SecContent),203 10);204 W.getOStream() << ' ';205 for (i = 0; TmpSecPtr < SecEnd && i < 4; ++i) {206 for (k = 0; TmpSecPtr < SecEnd && k < 4; k++, TmpSecPtr++) {207 uint8_t Val = *TmpSecPtr;208 W.getOStream() << format_hex_no_prefix(Val, 2);209 }210 W.getOStream() << ' ';211 }212 213 // We need to print the correct amount of spaces to match the format.214 // We are adding the (4 - i) last rows that are 8 characters each.215 // Then, the (4 - i) spaces that are in between the rows.216 // Least, if we cut in a middle of a row, we add the remaining characters,217 // which is (8 - (k * 2)).218 if (i < 4)219 W.getOStream() << format("%*c", (4 - i) * 8 + (4 - i), ' ');220 if (k < 4)221 W.getOStream() << format("%*c", 8 - k * 2, ' ');222 223 TmpSecPtr = SecPtr;224 for (i = 0; TmpSecPtr + i < SecEnd && i < 16; ++i)225 W.getOStream() << (isPrint(TmpSecPtr[i])226 ? static_cast<char>(TmpSecPtr[i])227 : '.');228 229 W.getOStream() << '\n';230 }231 }232}233 234void ObjDumper::printOffloading(const object::ObjectFile &Obj) {235 SmallVector<llvm::object::OffloadBundleFatBin> Bundles;236 if (Error Err = object::extractOffloadBundleFatBinary(Obj, Bundles))237 reportWarning(std::move(Err), Obj.getFileName());238 239 // Print out all the FatBin Bundles that are contained in this buffer.240 for (const auto &[Index, Bundle] : llvm::enumerate(Bundles))241 Bundle.printEntriesAsURI();242}243 244} // namespace llvm245