mirror of
https://github.com/llvm/llvm-project.git
synced 2025-05-02 18:26:04 +00:00

Summary: This ensures that object files will continue to validate as WebAssembly modules in the presence of bulk memory operations. Engines that don't support bulk memory operations will not recognize the DataCount section and will report validation errors, but that's ok because object files aren't supposed to be run directly anyway. Reviewers: aheejin, dschuff, sbc100 Subscribers: jgravelle-google, hiraditya, sunfish, rupprecht, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D60623 llvm-svn: 358315
255 lines
8.1 KiB
C++
255 lines
8.1 KiB
C++
//===-- WasmDumper.cpp - Wasm-specific object file dumper -----------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements the Wasm-specific dumper for llvm-readobj.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "Error.h"
|
|
#include "ObjDumper.h"
|
|
#include "llvm-readobj.h"
|
|
#include "llvm/Object/Wasm.h"
|
|
#include "llvm/Support/ScopedPrinter.h"
|
|
|
|
using namespace llvm;
|
|
using namespace object;
|
|
|
|
namespace {
|
|
|
|
static const EnumEntry<unsigned> WasmSymbolTypes[] = {
|
|
#define ENUM_ENTRY(X) \
|
|
{ #X, wasm::WASM_SYMBOL_TYPE_##X }
|
|
ENUM_ENTRY(FUNCTION), ENUM_ENTRY(DATA), ENUM_ENTRY(GLOBAL),
|
|
ENUM_ENTRY(SECTION), ENUM_ENTRY(EVENT),
|
|
#undef ENUM_ENTRY
|
|
};
|
|
|
|
static const EnumEntry<uint32_t> WasmSectionTypes[] = {
|
|
#define ENUM_ENTRY(X) \
|
|
{ #X, wasm::WASM_SEC_##X }
|
|
ENUM_ENTRY(CUSTOM), ENUM_ENTRY(TYPE), ENUM_ENTRY(IMPORT),
|
|
ENUM_ENTRY(FUNCTION), ENUM_ENTRY(TABLE), ENUM_ENTRY(MEMORY),
|
|
ENUM_ENTRY(GLOBAL), ENUM_ENTRY(EVENT), ENUM_ENTRY(EXPORT),
|
|
ENUM_ENTRY(START), ENUM_ENTRY(ELEM), ENUM_ENTRY(CODE),
|
|
ENUM_ENTRY(DATA), ENUM_ENTRY(DATACOUNT),
|
|
#undef ENUM_ENTRY
|
|
};
|
|
|
|
static const EnumEntry<unsigned> WasmSymbolFlags[] = {
|
|
#define ENUM_ENTRY(X) \
|
|
{ #X, wasm::WASM_SYMBOL_##X }
|
|
ENUM_ENTRY(BINDING_GLOBAL),
|
|
ENUM_ENTRY(BINDING_WEAK),
|
|
ENUM_ENTRY(BINDING_LOCAL),
|
|
ENUM_ENTRY(VISIBILITY_DEFAULT),
|
|
ENUM_ENTRY(VISIBILITY_HIDDEN),
|
|
ENUM_ENTRY(UNDEFINED),
|
|
ENUM_ENTRY(EXPORTED),
|
|
#undef ENUM_ENTRY
|
|
};
|
|
|
|
class WasmDumper : public ObjDumper {
|
|
public:
|
|
WasmDumper(const WasmObjectFile *Obj, ScopedPrinter &Writer)
|
|
: ObjDumper(Writer), Obj(Obj) {}
|
|
|
|
void printFileHeaders() override;
|
|
void printSectionHeaders() override;
|
|
void printRelocations() override;
|
|
void printUnwindInfo() override { llvm_unreachable("unimplemented"); }
|
|
void printStackMap() const override { llvm_unreachable("unimplemented"); }
|
|
|
|
protected:
|
|
void printSymbol(const SymbolRef &Sym);
|
|
void printRelocation(const SectionRef &Section, const RelocationRef &Reloc);
|
|
|
|
private:
|
|
void printSymbols() override;
|
|
void printDynamicSymbols() override { llvm_unreachable("unimplemented"); }
|
|
|
|
const WasmObjectFile *Obj;
|
|
};
|
|
|
|
void WasmDumper::printFileHeaders() {
|
|
W.printHex("Version", Obj->getHeader().Version);
|
|
}
|
|
|
|
void WasmDumper::printRelocation(const SectionRef &Section,
|
|
const RelocationRef &Reloc) {
|
|
SmallString<64> RelocTypeName;
|
|
uint64_t RelocType = Reloc.getType();
|
|
Reloc.getTypeName(RelocTypeName);
|
|
const wasm::WasmRelocation &WasmReloc = Obj->getWasmRelocation(Reloc);
|
|
|
|
StringRef SymName;
|
|
symbol_iterator SI = Reloc.getSymbol();
|
|
if (SI != Obj->symbol_end())
|
|
SymName = error(SI->getName());
|
|
|
|
bool HasAddend = false;
|
|
switch (RelocType) {
|
|
case wasm::R_WASM_MEMORY_ADDR_LEB:
|
|
case wasm::R_WASM_MEMORY_ADDR_SLEB:
|
|
case wasm::R_WASM_MEMORY_ADDR_I32:
|
|
case wasm::R_WASM_FUNCTION_OFFSET_I32:
|
|
case wasm::R_WASM_SECTION_OFFSET_I32:
|
|
HasAddend = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (opts::ExpandRelocs) {
|
|
DictScope Group(W, "Relocation");
|
|
W.printNumber("Type", RelocTypeName, RelocType);
|
|
W.printHex("Offset", Reloc.getOffset());
|
|
if (!SymName.empty())
|
|
W.printString("Symbol", SymName);
|
|
else
|
|
W.printHex("Index", WasmReloc.Index);
|
|
if (HasAddend)
|
|
W.printNumber("Addend", WasmReloc.Addend);
|
|
} else {
|
|
raw_ostream &OS = W.startLine();
|
|
OS << W.hex(Reloc.getOffset()) << " " << RelocTypeName << " ";
|
|
if (!SymName.empty())
|
|
OS << SymName;
|
|
else
|
|
OS << WasmReloc.Index;
|
|
if (HasAddend)
|
|
OS << " " << WasmReloc.Addend;
|
|
OS << "\n";
|
|
}
|
|
}
|
|
|
|
void WasmDumper::printRelocations() {
|
|
ListScope D(W, "Relocations");
|
|
|
|
int SectionNumber = 0;
|
|
for (const SectionRef &Section : Obj->sections()) {
|
|
bool PrintedGroup = false;
|
|
StringRef Name;
|
|
error(Section.getName(Name));
|
|
++SectionNumber;
|
|
|
|
for (const RelocationRef &Reloc : Section.relocations()) {
|
|
if (!PrintedGroup) {
|
|
W.startLine() << "Section (" << SectionNumber << ") " << Name << " {\n";
|
|
W.indent();
|
|
PrintedGroup = true;
|
|
}
|
|
|
|
printRelocation(Section, Reloc);
|
|
}
|
|
|
|
if (PrintedGroup) {
|
|
W.unindent();
|
|
W.startLine() << "}\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
void WasmDumper::printSymbols() {
|
|
ListScope Group(W, "Symbols");
|
|
|
|
for (const SymbolRef &Symbol : Obj->symbols())
|
|
printSymbol(Symbol);
|
|
}
|
|
|
|
void WasmDumper::printSectionHeaders() {
|
|
ListScope Group(W, "Sections");
|
|
for (const SectionRef &Section : Obj->sections()) {
|
|
const WasmSection &WasmSec = Obj->getWasmSection(Section);
|
|
DictScope SectionD(W, "Section");
|
|
W.printEnum("Type", WasmSec.Type, makeArrayRef(WasmSectionTypes));
|
|
W.printNumber("Size", static_cast<uint64_t>(WasmSec.Content.size()));
|
|
W.printNumber("Offset", WasmSec.Offset);
|
|
switch (WasmSec.Type) {
|
|
case wasm::WASM_SEC_CUSTOM:
|
|
W.printString("Name", WasmSec.Name);
|
|
if (WasmSec.Name == "linking") {
|
|
const wasm::WasmLinkingData &LinkingData = Obj->linkingData();
|
|
if (!LinkingData.InitFunctions.empty()) {
|
|
ListScope Group(W, "InitFunctions");
|
|
for (const wasm::WasmInitFunc &F : LinkingData.InitFunctions)
|
|
W.startLine() << F.Symbol << " (priority=" << F.Priority << ")\n";
|
|
}
|
|
}
|
|
break;
|
|
case wasm::WASM_SEC_DATA: {
|
|
ListScope Group(W, "Segments");
|
|
for (const WasmSegment &Segment : Obj->dataSegments()) {
|
|
const wasm::WasmDataSegment &Seg = Segment.Data;
|
|
DictScope Group(W, "Segment");
|
|
if (!Seg.Name.empty())
|
|
W.printString("Name", Seg.Name);
|
|
W.printNumber("Size", static_cast<uint64_t>(Seg.Content.size()));
|
|
if (Seg.Offset.Opcode == wasm::WASM_OPCODE_I32_CONST)
|
|
W.printNumber("Offset", Seg.Offset.Value.Int32);
|
|
}
|
|
break;
|
|
}
|
|
case wasm::WASM_SEC_MEMORY:
|
|
ListScope Group(W, "Memories");
|
|
for (const wasm::WasmLimits &Memory : Obj->memories()) {
|
|
DictScope Group(W, "Memory");
|
|
W.printNumber("InitialPages", Memory.Initial);
|
|
if (Memory.Flags & wasm::WASM_LIMITS_FLAG_HAS_MAX) {
|
|
W.printNumber("MaxPages", WasmSec.Offset);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (opts::SectionRelocations) {
|
|
ListScope D(W, "Relocations");
|
|
for (const RelocationRef &Reloc : Section.relocations())
|
|
printRelocation(Section, Reloc);
|
|
}
|
|
|
|
if (opts::SectionData) {
|
|
W.printBinaryBlock("SectionData", WasmSec.Content);
|
|
}
|
|
}
|
|
}
|
|
|
|
void WasmDumper::printSymbol(const SymbolRef &Sym) {
|
|
DictScope D(W, "Symbol");
|
|
WasmSymbol Symbol = Obj->getWasmSymbol(Sym.getRawDataRefImpl());
|
|
W.printString("Name", Symbol.Info.Name);
|
|
W.printEnum("Type", Symbol.Info.Kind, makeArrayRef(WasmSymbolTypes));
|
|
W.printFlags("Flags", Symbol.Info.Flags, makeArrayRef(WasmSymbolFlags));
|
|
|
|
if (Symbol.Info.Flags & wasm::WASM_SYMBOL_UNDEFINED) {
|
|
W.printString("ImportName", Symbol.Info.ImportName);
|
|
W.printString("ImportModule", Symbol.Info.ImportModule);
|
|
}
|
|
if (Symbol.Info.Kind != wasm::WASM_SYMBOL_TYPE_DATA) {
|
|
W.printHex("ElementIndex", Symbol.Info.ElementIndex);
|
|
} else if (!(Symbol.Info.Flags & wasm::WASM_SYMBOL_UNDEFINED)) {
|
|
W.printHex("Offset", Symbol.Info.DataRef.Offset);
|
|
W.printHex("Segment", Symbol.Info.DataRef.Segment);
|
|
W.printHex("Size", Symbol.Info.DataRef.Size);
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace llvm {
|
|
|
|
std::error_code createWasmDumper(const object::ObjectFile *Obj,
|
|
ScopedPrinter &Writer,
|
|
std::unique_ptr<ObjDumper> &Result) {
|
|
const auto *WasmObj = dyn_cast<WasmObjectFile>(Obj);
|
|
assert(WasmObj && "createWasmDumper called with non-wasm object");
|
|
|
|
Result.reset(new WasmDumper(WasmObj, Writer));
|
|
return readobj_error::success;
|
|
}
|
|
|
|
} // namespace llvm
|