1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-26 04:32:44 +01:00
llvm-mirror/lib/Object/WasmObjectFile.cpp
Sam Clegg b1a2c7d2eb [WebAssembly] Remove debug names from symbol table
Get rid of DEBUG_FUNCTION_NAME symbols. When we actually debug
data, maybe we'll want somewhere to put it... but having a symbol
that just stores the name of another symbol seems odd.
It means you have multiple Symbols with the same name, one
containing the actual function and another containing the name!

Store the names in a vector on the WasmObjectFile when reading
them in. Also stash them on the WasmFunctions themselves.
The names are //not// "symbol names" or aliases or anything,
they're just the name that a debugger should show against the
function body itself. NB. The WasmObjectFile stores them so that
they can be exported in the YAML losslessly, and hence the tests
can be precise.

Enforce that the CODE section has been read in before reading
the "names" section. Requires minor adjustment to some tests.

Patch by Nicholas Wilson!

Differential Revision: https://reviews.llvm.org/D42075

llvm-svn: 322741
2018-01-17 19:28:43 +00:00

1161 lines
38 KiB
C++

//===- WasmObjectFile.cpp - Wasm object file implementation ---------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/Triple.h"
#include "llvm/BinaryFormat/Wasm.h"
#include "llvm/MC/SubtargetFeature.h"
#include "llvm/Object/Binary.h"
#include "llvm/Object/Error.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Object/SymbolicFile.h"
#include "llvm/Object/Wasm.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/LEB128.h"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <cstring>
#include <system_error>
#define DEBUG_TYPE "wasm-object"
using namespace llvm;
using namespace object;
Expected<std::unique_ptr<WasmObjectFile>>
ObjectFile::createWasmObjectFile(MemoryBufferRef Buffer) {
Error Err = Error::success();
auto ObjectFile = llvm::make_unique<WasmObjectFile>(Buffer, Err);
if (Err)
return std::move(Err);
return std::move(ObjectFile);
}
#define VARINT7_MAX ((1<<7)-1)
#define VARINT7_MIN (-(1<<7))
#define VARUINT7_MAX (1<<7)
#define VARUINT1_MAX (1)
static uint8_t readUint8(const uint8_t *&Ptr) { return *Ptr++; }
static uint32_t readUint32(const uint8_t *&Ptr) {
uint32_t Result = support::endian::read32le(Ptr);
Ptr += sizeof(Result);
return Result;
}
static int32_t readFloat32(const uint8_t *&Ptr) {
int32_t Result = 0;
memcpy(&Result, Ptr, sizeof(Result));
Ptr += sizeof(Result);
return Result;
}
static int64_t readFloat64(const uint8_t *&Ptr) {
int64_t Result = 0;
memcpy(&Result, Ptr, sizeof(Result));
Ptr += sizeof(Result);
return Result;
}
static uint64_t readULEB128(const uint8_t *&Ptr) {
unsigned Count;
uint64_t Result = decodeULEB128(Ptr, &Count);
Ptr += Count;
return Result;
}
static StringRef readString(const uint8_t *&Ptr) {
uint32_t StringLen = readULEB128(Ptr);
StringRef Return = StringRef(reinterpret_cast<const char *>(Ptr), StringLen);
Ptr += StringLen;
return Return;
}
static int64_t readLEB128(const uint8_t *&Ptr) {
unsigned Count;
uint64_t Result = decodeSLEB128(Ptr, &Count);
Ptr += Count;
return Result;
}
static uint8_t readVaruint1(const uint8_t *&Ptr) {
int64_t result = readLEB128(Ptr);
assert(result <= VARUINT1_MAX && result >= 0);
return result;
}
static int8_t readVarint7(const uint8_t *&Ptr) {
int64_t result = readLEB128(Ptr);
assert(result <= VARINT7_MAX && result >= VARINT7_MIN);
return result;
}
static uint8_t readVaruint7(const uint8_t *&Ptr) {
uint64_t result = readULEB128(Ptr);
assert(result <= VARUINT7_MAX);
return result;
}
static int32_t readVarint32(const uint8_t *&Ptr) {
int64_t result = readLEB128(Ptr);
assert(result <= INT32_MAX && result >= INT32_MIN);
return result;
}
static uint32_t readVaruint32(const uint8_t *&Ptr) {
uint64_t result = readULEB128(Ptr);
assert(result <= UINT32_MAX);
return result;
}
static int64_t readVarint64(const uint8_t *&Ptr) {
return readLEB128(Ptr);
}
static uint8_t readOpcode(const uint8_t *&Ptr) {
return readUint8(Ptr);
}
static Error readInitExpr(wasm::WasmInitExpr &Expr, const uint8_t *&Ptr) {
Expr.Opcode = readOpcode(Ptr);
switch (Expr.Opcode) {
case wasm::WASM_OPCODE_I32_CONST:
Expr.Value.Int32 = readVarint32(Ptr);
break;
case wasm::WASM_OPCODE_I64_CONST:
Expr.Value.Int64 = readVarint64(Ptr);
break;
case wasm::WASM_OPCODE_F32_CONST:
Expr.Value.Float32 = readFloat32(Ptr);
break;
case wasm::WASM_OPCODE_F64_CONST:
Expr.Value.Float64 = readFloat64(Ptr);
break;
case wasm::WASM_OPCODE_GET_GLOBAL:
Expr.Value.Global = readULEB128(Ptr);
break;
default:
return make_error<GenericBinaryError>("Invalid opcode in init_expr",
object_error::parse_failed);
}
uint8_t EndOpcode = readOpcode(Ptr);
if (EndOpcode != wasm::WASM_OPCODE_END) {
return make_error<GenericBinaryError>("Invalid init_expr",
object_error::parse_failed);
}
return Error::success();
}
static wasm::WasmLimits readLimits(const uint8_t *&Ptr) {
wasm::WasmLimits Result;
Result.Flags = readVaruint1(Ptr);
Result.Initial = readVaruint32(Ptr);
if (Result.Flags & wasm::WASM_LIMITS_FLAG_HAS_MAX)
Result.Maximum = readVaruint32(Ptr);
return Result;
}
static wasm::WasmTable readTable(const uint8_t *&Ptr) {
wasm::WasmTable Table;
Table.ElemType = readVarint7(Ptr);
Table.Limits = readLimits(Ptr);
return Table;
}
static Error readSection(WasmSection &Section, const uint8_t *&Ptr,
const uint8_t *Start, const uint8_t *Eof) {
Section.Offset = Ptr - Start;
Section.Type = readVaruint7(Ptr);
uint32_t Size = readVaruint32(Ptr);
if (Size == 0)
return make_error<StringError>("Zero length section",
object_error::parse_failed);
if (Ptr + Size > Eof)
return make_error<StringError>("Section too large",
object_error::parse_failed);
Section.Content = ArrayRef<uint8_t>(Ptr, Size);
Ptr += Size;
return Error::success();
}
WasmObjectFile::WasmObjectFile(MemoryBufferRef Buffer, Error &Err)
: ObjectFile(Binary::ID_Wasm, Buffer) {
LinkingData.DataSize = 0;
ErrorAsOutParameter ErrAsOutParam(&Err);
Header.Magic = getData().substr(0, 4);
if (Header.Magic != StringRef("\0asm", 4)) {
Err = make_error<StringError>("Bad magic number",
object_error::parse_failed);
return;
}
const uint8_t *Eof = getPtr(getData().size());
const uint8_t *Ptr = getPtr(4);
if (Ptr + 4 > Eof) {
Err = make_error<StringError>("Missing version number",
object_error::parse_failed);
return;
}
Header.Version = readUint32(Ptr);
if (Header.Version != wasm::WasmVersion) {
Err = make_error<StringError>("Bad version number",
object_error::parse_failed);
return;
}
WasmSection Sec;
while (Ptr < Eof) {
if ((Err = readSection(Sec, Ptr, getPtr(0), Eof)))
return;
if ((Err = parseSection(Sec)))
return;
Sections.push_back(Sec);
}
}
Error WasmObjectFile::parseSection(WasmSection &Sec) {
const uint8_t* Start = Sec.Content.data();
const uint8_t* End = Start + Sec.Content.size();
switch (Sec.Type) {
case wasm::WASM_SEC_CUSTOM:
return parseCustomSection(Sec, Start, End);
case wasm::WASM_SEC_TYPE:
return parseTypeSection(Start, End);
case wasm::WASM_SEC_IMPORT:
return parseImportSection(Start, End);
case wasm::WASM_SEC_FUNCTION:
return parseFunctionSection(Start, End);
case wasm::WASM_SEC_TABLE:
return parseTableSection(Start, End);
case wasm::WASM_SEC_MEMORY:
return parseMemorySection(Start, End);
case wasm::WASM_SEC_GLOBAL:
return parseGlobalSection(Start, End);
case wasm::WASM_SEC_EXPORT:
return parseExportSection(Start, End);
case wasm::WASM_SEC_START:
return parseStartSection(Start, End);
case wasm::WASM_SEC_ELEM:
return parseElemSection(Start, End);
case wasm::WASM_SEC_CODE:
return parseCodeSection(Start, End);
case wasm::WASM_SEC_DATA:
return parseDataSection(Start, End);
default:
return make_error<GenericBinaryError>("Bad section type",
object_error::parse_failed);
}
}
Error WasmObjectFile::parseNameSection(const uint8_t *Ptr, const uint8_t *End) {
llvm::DenseSet<uint64_t> Seen;
if (Functions.size() != FunctionTypes.size()) {
return make_error<GenericBinaryError>("Names must come after code section",
object_error::parse_failed);
}
while (Ptr < End) {
uint8_t Type = readVarint7(Ptr);
uint32_t Size = readVaruint32(Ptr);
const uint8_t *SubSectionEnd = Ptr + Size;
switch (Type) {
case wasm::WASM_NAMES_FUNCTION: {
uint32_t Count = readVaruint32(Ptr);
while (Count--) {
uint32_t Index = readVaruint32(Ptr);
if (!Seen.insert(Index).second)
return make_error<GenericBinaryError>("Function named more than once",
object_error::parse_failed);
StringRef Name = readString(Ptr);
if (!isValidFunctionIndex(Index) || Name.empty())
return make_error<GenericBinaryError>("Invalid name entry",
object_error::parse_failed);
DebugNames.push_back(wasm::WasmFunctionName{Index, Name});
if (Index >= NumImportedFunctions) {
// Override any existing name; the name specified by the "names"
// section is the Function's canonical name.
Functions[Index - NumImportedFunctions].Name = Name;
}
}
break;
}
// Ignore local names for now
case wasm::WASM_NAMES_LOCAL:
default:
Ptr += Size;
break;
}
if (Ptr != SubSectionEnd)
return make_error<GenericBinaryError>("Name sub-section ended prematurely",
object_error::parse_failed);
}
if (Ptr != End)
return make_error<GenericBinaryError>("Name section ended prematurely",
object_error::parse_failed);
return Error::success();
}
void WasmObjectFile::populateSymbolTable() {
// Add imports to symbol table
size_t GlobalIndex = 0;
size_t FunctionIndex = 0;
for (const wasm::WasmImport& Import : Imports) {
switch (Import.Kind) {
case wasm::WASM_EXTERNAL_GLOBAL:
assert(Import.Global.Type == wasm::WASM_TYPE_I32);
SymbolMap.try_emplace(Import.Field, Symbols.size());
Symbols.emplace_back(Import.Field, WasmSymbol::SymbolType::GLOBAL_IMPORT,
ImportSection, GlobalIndex++);
DEBUG(dbgs() << "Adding import: " << Symbols.back()
<< " sym index:" << Symbols.size() << "\n");
break;
case wasm::WASM_EXTERNAL_FUNCTION:
SymbolMap.try_emplace(Import.Field, Symbols.size());
Symbols.emplace_back(Import.Field,
WasmSymbol::SymbolType::FUNCTION_IMPORT,
ImportSection, FunctionIndex++, Import.SigIndex);
DEBUG(dbgs() << "Adding import: " << Symbols.back()
<< " sym index:" << Symbols.size() << "\n");
break;
default:
break;
}
}
// Add exports to symbol table
for (const wasm::WasmExport& Export : Exports) {
if (Export.Kind == wasm::WASM_EXTERNAL_FUNCTION ||
Export.Kind == wasm::WASM_EXTERNAL_GLOBAL) {
WasmSymbol::SymbolType ExportType =
Export.Kind == wasm::WASM_EXTERNAL_FUNCTION
? WasmSymbol::SymbolType::FUNCTION_EXPORT
: WasmSymbol::SymbolType::GLOBAL_EXPORT;
auto Pair = SymbolMap.try_emplace(Export.Name, Symbols.size());
if (Pair.second) {
Symbols.emplace_back(Export.Name, ExportType,
ExportSection, Export.Index);
DEBUG(dbgs() << "Adding export: " << Symbols.back()
<< " sym index:" << Symbols.size() << "\n");
} else {
uint32_t SymIndex = Pair.first->second;
const WasmSymbol &OldSym = Symbols[SymIndex];
WasmSymbol NewSym(Export.Name, ExportType, ExportSection, Export.Index);
NewSym.setAltIndex(OldSym.ElementIndex);
Symbols[SymIndex] = NewSym;
DEBUG(dbgs() << "Replacing existing symbol: " << NewSym
<< " sym index:" << SymIndex << "\n");
}
}
if (Export.Kind == wasm::WASM_EXTERNAL_FUNCTION) {
auto &Function = Functions[Export.Index - NumImportedFunctions];
if (Function.Name.empty()) {
// Use the export's name to set a name for the Function, but only if one
// hasn't already been set.
Function.Name = Export.Name;
}
}
}
}
Error WasmObjectFile::parseLinkingSection(const uint8_t *Ptr,
const uint8_t *End) {
HasLinkingSection = true;
if (Functions.size() != FunctionTypes.size()) {
return make_error<GenericBinaryError>(
"Linking data must come after code section", object_error::parse_failed);
}
// Only populate the symbol table with imports and exports if the object
// has a linking section (i.e. its a relocatable object file). Otherwise
// the global might not represent symbols at all.
populateSymbolTable();
while (Ptr < End) {
uint8_t Type = readVarint7(Ptr);
uint32_t Size = readVaruint32(Ptr);
const uint8_t *SubSectionEnd = Ptr + Size;
switch (Type) {
case wasm::WASM_SYMBOL_INFO: {
uint32_t Count = readVaruint32(Ptr);
while (Count--) {
StringRef Symbol = readString(Ptr);
uint32_t Flags = readVaruint32(Ptr);
auto iter = SymbolMap.find(Symbol);
if (iter == SymbolMap.end()) {
return make_error<GenericBinaryError>(
"Invalid symbol name in linking section: " + Symbol,
object_error::parse_failed);
}
uint32_t SymIndex = iter->second;
assert(SymIndex < Symbols.size());
Symbols[SymIndex].Flags = Flags;
DEBUG(dbgs() << "Set symbol flags index:"
<< SymIndex << " name:"
<< Symbols[SymIndex].Name << " expected:"
<< Symbol << " flags: " << Flags << "\n");
}
break;
}
case wasm::WASM_DATA_SIZE:
LinkingData.DataSize = readVaruint32(Ptr);
break;
case wasm::WASM_SEGMENT_INFO: {
uint32_t Count = readVaruint32(Ptr);
if (Count > DataSegments.size())
return make_error<GenericBinaryError>("Too many segment names",
object_error::parse_failed);
for (uint32_t i = 0; i < Count; i++) {
DataSegments[i].Data.Name = readString(Ptr);
DataSegments[i].Data.Alignment = readVaruint32(Ptr);
DataSegments[i].Data.Flags = readVaruint32(Ptr);
}
break;
}
case wasm::WASM_INIT_FUNCS: {
uint32_t Count = readVaruint32(Ptr);
LinkingData.InitFunctions.reserve(Count);
for (uint32_t i = 0; i < Count; i++) {
wasm::WasmInitFunc Init;
Init.Priority = readVaruint32(Ptr);
Init.FunctionIndex = readVaruint32(Ptr);
if (!isValidFunctionIndex(Init.FunctionIndex))
return make_error<GenericBinaryError>("Invalid function index: " +
Twine(Init.FunctionIndex),
object_error::parse_failed);
LinkingData.InitFunctions.emplace_back(Init);
}
break;
}
case wasm::WASM_COMDAT_INFO:
if (Error Err = parseLinkingSectionComdat(Ptr, SubSectionEnd))
return Err;
break;
default:
Ptr += Size;
break;
}
if (Ptr != SubSectionEnd)
return make_error<GenericBinaryError>(
"Linking sub-section ended prematurely", object_error::parse_failed);
}
if (Ptr != End)
return make_error<GenericBinaryError>("Linking section ended prematurely",
object_error::parse_failed);
return Error::success();
}
Error WasmObjectFile::parseLinkingSectionComdat(const uint8_t *&Ptr,
const uint8_t *End)
{
uint32_t ComdatCount = readVaruint32(Ptr);
StringSet<> ComdatSet;
while (ComdatCount--) {
StringRef Name = readString(Ptr);
if (Name.empty() || !ComdatSet.insert(Name).second)
return make_error<GenericBinaryError>("Bad/duplicate COMDAT name " + Twine(Name),
object_error::parse_failed);
Comdats.emplace_back(Name);
uint32_t Flags = readVaruint32(Ptr);
if (Flags != 0)
return make_error<GenericBinaryError>("Unsupported COMDAT flags",
object_error::parse_failed);
uint32_t EntryCount = readVaruint32(Ptr);
while (EntryCount--) {
unsigned Kind = readVaruint32(Ptr);
unsigned Index = readVaruint32(Ptr);
switch (Kind) {
default:
return make_error<GenericBinaryError>("Invalid COMDAT entry type",
object_error::parse_failed);
case wasm::WASM_COMDAT_DATA:
if (Index >= DataSegments.size())
return make_error<GenericBinaryError>("COMDAT data index out of range",
object_error::parse_failed);
if (!DataSegments[Index].Data.Comdat.empty())
return make_error<GenericBinaryError>("Data segment in two COMDATs",
object_error::parse_failed);
DataSegments[Index].Data.Comdat = Name;
break;
case wasm::WASM_COMDAT_FUNCTION:
if (Index < NumImportedFunctions || !isValidFunctionIndex(Index))
return make_error<GenericBinaryError>("COMDAT function index out of range",
object_error::parse_failed);
Index -= NumImportedFunctions;
if (!Functions[Index].Comdat.empty())
return make_error<GenericBinaryError>("Function in two COMDATs",
object_error::parse_failed);
Functions[Index].Comdat = Name;
break;
}
}
}
return Error::success();
}
WasmSection* WasmObjectFile::findCustomSectionByName(StringRef Name) {
for (WasmSection& Section : Sections) {
if (Section.Type == wasm::WASM_SEC_CUSTOM && Section.Name == Name)
return &Section;
}
return nullptr;
}
WasmSection* WasmObjectFile::findSectionByType(uint32_t Type) {
assert(Type != wasm::WASM_SEC_CUSTOM);
for (WasmSection& Section : Sections) {
if (Section.Type == Type)
return &Section;
}
return nullptr;
}
Error WasmObjectFile::parseRelocSection(StringRef Name, const uint8_t *Ptr,
const uint8_t *End) {
uint8_t SectionCode = readVarint7(Ptr);
WasmSection* Section = nullptr;
if (SectionCode == wasm::WASM_SEC_CUSTOM) {
StringRef Name = readString(Ptr);
Section = findCustomSectionByName(Name);
} else {
Section = findSectionByType(SectionCode);
}
if (!Section)
return make_error<GenericBinaryError>("Invalid section code",
object_error::parse_failed);
uint32_t RelocCount = readVaruint32(Ptr);
while (RelocCount--) {
wasm::WasmRelocation Reloc;
memset(&Reloc, 0, sizeof(Reloc));
Reloc.Type = readVaruint32(Ptr);
Reloc.Offset = readVaruint32(Ptr);
Reloc.Index = readVaruint32(Ptr);
switch (Reloc.Type) {
case wasm::R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
case wasm::R_WEBASSEMBLY_TABLE_INDEX_SLEB:
case wasm::R_WEBASSEMBLY_TABLE_INDEX_I32:
case wasm::R_WEBASSEMBLY_TYPE_INDEX_LEB:
case wasm::R_WEBASSEMBLY_GLOBAL_INDEX_LEB:
break;
case wasm::R_WEBASSEMBLY_MEMORY_ADDR_LEB:
case wasm::R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
case wasm::R_WEBASSEMBLY_MEMORY_ADDR_I32:
Reloc.Addend = readVarint32(Ptr);
break;
default:
return make_error<GenericBinaryError>("Bad relocation type: " +
Twine(Reloc.Type),
object_error::parse_failed);
}
Section->Relocations.push_back(Reloc);
}
if (Ptr != End)
return make_error<GenericBinaryError>("Reloc section ended prematurely",
object_error::parse_failed);
return Error::success();
}
Error WasmObjectFile::parseCustomSection(WasmSection &Sec,
const uint8_t *Ptr, const uint8_t *End) {
Sec.Name = readString(Ptr);
if (Sec.Name == "name") {
if (Error Err = parseNameSection(Ptr, End))
return Err;
} else if (Sec.Name == "linking") {
if (Error Err = parseLinkingSection(Ptr, End))
return Err;
} else if (Sec.Name.startswith("reloc.")) {
if (Error Err = parseRelocSection(Sec.Name, Ptr, End))
return Err;
}
return Error::success();
}
Error WasmObjectFile::parseTypeSection(const uint8_t *Ptr, const uint8_t *End) {
uint32_t Count = readVaruint32(Ptr);
Signatures.reserve(Count);
while (Count--) {
wasm::WasmSignature Sig;
Sig.ReturnType = wasm::WASM_TYPE_NORESULT;
int8_t Form = readVarint7(Ptr);
if (Form != wasm::WASM_TYPE_FUNC) {
return make_error<GenericBinaryError>("Invalid signature type",
object_error::parse_failed);
}
uint32_t ParamCount = readVaruint32(Ptr);
Sig.ParamTypes.reserve(ParamCount);
while (ParamCount--) {
uint32_t ParamType = readVarint7(Ptr);
Sig.ParamTypes.push_back(ParamType);
}
uint32_t ReturnCount = readVaruint32(Ptr);
if (ReturnCount) {
if (ReturnCount != 1) {
return make_error<GenericBinaryError>(
"Multiple return types not supported", object_error::parse_failed);
}
Sig.ReturnType = readVarint7(Ptr);
}
Signatures.push_back(Sig);
}
if (Ptr != End)
return make_error<GenericBinaryError>("Type section ended prematurely",
object_error::parse_failed);
return Error::success();
}
Error WasmObjectFile::parseImportSection(const uint8_t *Ptr, const uint8_t *End) {
ImportSection = Sections.size();
uint32_t Count = readVaruint32(Ptr);
Imports.reserve(Count);
for (uint32_t i = 0; i < Count; i++) {
wasm::WasmImport Im;
Im.Module = readString(Ptr);
Im.Field = readString(Ptr);
Im.Kind = readUint8(Ptr);
switch (Im.Kind) {
case wasm::WASM_EXTERNAL_FUNCTION:
NumImportedFunctions++;
Im.SigIndex = readVaruint32(Ptr);
break;
case wasm::WASM_EXTERNAL_GLOBAL:
NumImportedGlobals++;
Im.Global.Type = readVarint7(Ptr);
Im.Global.Mutable = readVaruint1(Ptr);
break;
case wasm::WASM_EXTERNAL_MEMORY:
Im.Memory = readLimits(Ptr);
break;
case wasm::WASM_EXTERNAL_TABLE:
Im.Table = readTable(Ptr);
if (Im.Table.ElemType != wasm::WASM_TYPE_ANYFUNC)
return make_error<GenericBinaryError>("Invalid table element type",
object_error::parse_failed);
break;
default:
return make_error<GenericBinaryError>(
"Unexpected import kind", object_error::parse_failed);
}
Imports.push_back(Im);
}
if (Ptr != End)
return make_error<GenericBinaryError>("Import section ended prematurely",
object_error::parse_failed);
return Error::success();
}
Error WasmObjectFile::parseFunctionSection(const uint8_t *Ptr, const uint8_t *End) {
uint32_t Count = readVaruint32(Ptr);
FunctionTypes.reserve(Count);
while (Count--) {
FunctionTypes.push_back(readVaruint32(Ptr));
}
if (Ptr != End)
return make_error<GenericBinaryError>("Function section ended prematurely",
object_error::parse_failed);
return Error::success();
}
Error WasmObjectFile::parseTableSection(const uint8_t *Ptr, const uint8_t *End) {
uint32_t Count = readVaruint32(Ptr);
Tables.reserve(Count);
while (Count--) {
Tables.push_back(readTable(Ptr));
if (Tables.back().ElemType != wasm::WASM_TYPE_ANYFUNC) {
return make_error<GenericBinaryError>("Invalid table element type",
object_error::parse_failed);
}
}
if (Ptr != End)
return make_error<GenericBinaryError>("Table section ended prematurely",
object_error::parse_failed);
return Error::success();
}
Error WasmObjectFile::parseMemorySection(const uint8_t *Ptr, const uint8_t *End) {
uint32_t Count = readVaruint32(Ptr);
Memories.reserve(Count);
while (Count--) {
Memories.push_back(readLimits(Ptr));
}
if (Ptr != End)
return make_error<GenericBinaryError>("Memory section ended prematurely",
object_error::parse_failed);
return Error::success();
}
Error WasmObjectFile::parseGlobalSection(const uint8_t *Ptr, const uint8_t *End) {
uint32_t Count = readVaruint32(Ptr);
Globals.reserve(Count);
while (Count--) {
wasm::WasmGlobal Global;
Global.Index = NumImportedGlobals + Globals.size();
Global.Type = readVarint7(Ptr);
Global.Mutable = readVaruint1(Ptr);
if (Error Err = readInitExpr(Global.InitExpr, Ptr))
return Err;
Globals.push_back(Global);
}
if (Ptr != End)
return make_error<GenericBinaryError>("Global section ended prematurely",
object_error::parse_failed);
return Error::success();
}
Error WasmObjectFile::parseExportSection(const uint8_t *Ptr, const uint8_t *End) {
ExportSection = Sections.size();
uint32_t Count = readVaruint32(Ptr);
Exports.reserve(Count);
for (uint32_t i = 0; i < Count; i++) {
wasm::WasmExport Ex;
Ex.Name = readString(Ptr);
Ex.Kind = readUint8(Ptr);
Ex.Index = readVaruint32(Ptr);
switch (Ex.Kind) {
case wasm::WASM_EXTERNAL_FUNCTION:
if (Ex.Index >= FunctionTypes.size() + NumImportedFunctions)
return make_error<GenericBinaryError>("Invalid function export",
object_error::parse_failed);
break;
case wasm::WASM_EXTERNAL_GLOBAL: {
if (Ex.Index >= Globals.size() + NumImportedGlobals)
return make_error<GenericBinaryError>("Invalid global export",
object_error::parse_failed);
break;
}
case wasm::WASM_EXTERNAL_MEMORY:
case wasm::WASM_EXTERNAL_TABLE:
break;
default:
return make_error<GenericBinaryError>(
"Unexpected export kind", object_error::parse_failed);
}
Exports.push_back(Ex);
}
if (Ptr != End)
return make_error<GenericBinaryError>("Export section ended prematurely",
object_error::parse_failed);
return Error::success();
}
bool WasmObjectFile::isValidFunctionIndex(uint32_t Index) const {
return Index < FunctionTypes.size() + NumImportedFunctions;
}
Error WasmObjectFile::parseStartSection(const uint8_t *Ptr, const uint8_t *End) {
StartFunction = readVaruint32(Ptr);
if (!isValidFunctionIndex(StartFunction))
return make_error<GenericBinaryError>("Invalid start function",
object_error::parse_failed);
return Error::success();
}
Error WasmObjectFile::parseCodeSection(const uint8_t *Ptr, const uint8_t *End) {
const uint8_t *CodeSectionStart = Ptr;
uint32_t FunctionCount = readVaruint32(Ptr);
if (FunctionCount != FunctionTypes.size()) {
return make_error<GenericBinaryError>("Invalid function count",
object_error::parse_failed);
}
while (FunctionCount--) {
wasm::WasmFunction Function;
const uint8_t *FunctionStart = Ptr;
uint32_t Size = readVaruint32(Ptr);
const uint8_t *FunctionEnd = Ptr + Size;
Function.Index = NumImportedFunctions + Functions.size();
Function.CodeSectionOffset = FunctionStart - CodeSectionStart;
Function.Size = FunctionEnd - FunctionStart;
uint32_t NumLocalDecls = readVaruint32(Ptr);
Function.Locals.reserve(NumLocalDecls);
while (NumLocalDecls--) {
wasm::WasmLocalDecl Decl;
Decl.Count = readVaruint32(Ptr);
Decl.Type = readVarint7(Ptr);
Function.Locals.push_back(Decl);
}
uint32_t BodySize = FunctionEnd - Ptr;
Function.Body = ArrayRef<uint8_t>(Ptr, BodySize);
Ptr += BodySize;
assert(Ptr == FunctionEnd);
Functions.push_back(Function);
}
if (Ptr != End)
return make_error<GenericBinaryError>("Code section ended prematurely",
object_error::parse_failed);
return Error::success();
}
Error WasmObjectFile::parseElemSection(const uint8_t *Ptr, const uint8_t *End) {
uint32_t Count = readVaruint32(Ptr);
ElemSegments.reserve(Count);
while (Count--) {
wasm::WasmElemSegment Segment;
Segment.TableIndex = readVaruint32(Ptr);
if (Segment.TableIndex != 0) {
return make_error<GenericBinaryError>("Invalid TableIndex",
object_error::parse_failed);
}
if (Error Err = readInitExpr(Segment.Offset, Ptr))
return Err;
uint32_t NumElems = readVaruint32(Ptr);
while (NumElems--) {
Segment.Functions.push_back(readVaruint32(Ptr));
}
ElemSegments.push_back(Segment);
}
if (Ptr != End)
return make_error<GenericBinaryError>("Elem section ended prematurely",
object_error::parse_failed);
return Error::success();
}
Error WasmObjectFile::parseDataSection(const uint8_t *Ptr, const uint8_t *End) {
const uint8_t *Start = Ptr;
uint32_t Count = readVaruint32(Ptr);
DataSegments.reserve(Count);
while (Count--) {
WasmSegment Segment;
Segment.Data.MemoryIndex = readVaruint32(Ptr);
if (Error Err = readInitExpr(Segment.Data.Offset, Ptr))
return Err;
uint32_t Size = readVaruint32(Ptr);
Segment.Data.Content = ArrayRef<uint8_t>(Ptr, Size);
Segment.Data.Alignment = 0;
Segment.Data.Flags = 0;
Segment.SectionOffset = Ptr - Start;
Ptr += Size;
DataSegments.push_back(Segment);
}
if (Ptr != End)
return make_error<GenericBinaryError>("Data section ended prematurely",
object_error::parse_failed);
return Error::success();
}
const uint8_t *WasmObjectFile::getPtr(size_t Offset) const {
return reinterpret_cast<const uint8_t *>(getData().substr(Offset, 1).data());
}
const wasm::WasmObjectHeader &WasmObjectFile::getHeader() const {
return Header;
}
void WasmObjectFile::moveSymbolNext(DataRefImpl &Symb) const { Symb.d.a++; }
uint32_t WasmObjectFile::getSymbolFlags(DataRefImpl Symb) const {
uint32_t Result = SymbolRef::SF_None;
const WasmSymbol &Sym = getWasmSymbol(Symb);
DEBUG(dbgs() << "getSymbolFlags: ptr=" << &Sym << " " << Sym << "\n");
if (Sym.isWeak())
Result |= SymbolRef::SF_Weak;
if (!Sym.isLocal())
Result |= SymbolRef::SF_Global;
if (Sym.isHidden())
Result |= SymbolRef::SF_Hidden;
switch (Sym.Type) {
case WasmSymbol::SymbolType::FUNCTION_IMPORT:
Result |= SymbolRef::SF_Undefined | SymbolRef::SF_Executable;
break;
case WasmSymbol::SymbolType::FUNCTION_EXPORT:
Result |= SymbolRef::SF_Executable;
break;
case WasmSymbol::SymbolType::GLOBAL_IMPORT:
Result |= SymbolRef::SF_Undefined;
break;
case WasmSymbol::SymbolType::GLOBAL_EXPORT:
break;
}
return Result;
}
basic_symbol_iterator WasmObjectFile::symbol_begin() const {
DataRefImpl Ref;
Ref.d.a = 0;
return BasicSymbolRef(Ref, this);
}
basic_symbol_iterator WasmObjectFile::symbol_end() const {
DataRefImpl Ref;
Ref.d.a = Symbols.size();
return BasicSymbolRef(Ref, this);
}
const WasmSymbol &WasmObjectFile::getWasmSymbol(const DataRefImpl &Symb) const {
return Symbols[Symb.d.a];
}
const WasmSymbol &WasmObjectFile::getWasmSymbol(const SymbolRef &Symb) const {
return getWasmSymbol(Symb.getRawDataRefImpl());
}
Expected<StringRef> WasmObjectFile::getSymbolName(DataRefImpl Symb) const {
return getWasmSymbol(Symb).Name;
}
Expected<uint64_t> WasmObjectFile::getSymbolAddress(DataRefImpl Symb) const {
return getSymbolValue(Symb);
}
uint64_t WasmObjectFile::getWasmSymbolValue(const WasmSymbol& Sym) const {
switch (Sym.Type) {
case WasmSymbol::SymbolType::FUNCTION_IMPORT:
case WasmSymbol::SymbolType::GLOBAL_IMPORT:
case WasmSymbol::SymbolType::FUNCTION_EXPORT:
return Sym.ElementIndex;
case WasmSymbol::SymbolType::GLOBAL_EXPORT: {
uint32_t GlobalIndex = Sym.ElementIndex - NumImportedGlobals;
assert(GlobalIndex < Globals.size());
const wasm::WasmGlobal &Global = Globals[GlobalIndex];
// WasmSymbols correspond only to I32_CONST globals
assert(Global.InitExpr.Opcode == wasm::WASM_OPCODE_I32_CONST);
return Global.InitExpr.Value.Int32;
}
}
llvm_unreachable("invalid symbol type");
}
uint64_t WasmObjectFile::getSymbolValueImpl(DataRefImpl Symb) const {
return getWasmSymbolValue(getWasmSymbol(Symb));
}
uint32_t WasmObjectFile::getSymbolAlignment(DataRefImpl Symb) const {
llvm_unreachable("not yet implemented");
return 0;
}
uint64_t WasmObjectFile::getCommonSymbolSizeImpl(DataRefImpl Symb) const {
llvm_unreachable("not yet implemented");
return 0;
}
Expected<SymbolRef::Type>
WasmObjectFile::getSymbolType(DataRefImpl Symb) const {
const WasmSymbol &Sym = getWasmSymbol(Symb);
switch (Sym.Type) {
case WasmSymbol::SymbolType::FUNCTION_IMPORT:
case WasmSymbol::SymbolType::FUNCTION_EXPORT:
return SymbolRef::ST_Function;
case WasmSymbol::SymbolType::GLOBAL_IMPORT:
case WasmSymbol::SymbolType::GLOBAL_EXPORT:
return SymbolRef::ST_Data;
}
llvm_unreachable("Unknown WasmSymbol::SymbolType");
return SymbolRef::ST_Other;
}
Expected<section_iterator>
WasmObjectFile::getSymbolSection(DataRefImpl Symb) const {
DataRefImpl Ref;
Ref.d.a = getWasmSymbol(Symb).Section;
return section_iterator(SectionRef(Ref, this));
}
void WasmObjectFile::moveSectionNext(DataRefImpl &Sec) const { Sec.d.a++; }
std::error_code WasmObjectFile::getSectionName(DataRefImpl Sec,
StringRef &Res) const {
const WasmSection &S = Sections[Sec.d.a];
#define ECase(X) \
case wasm::WASM_SEC_##X: \
Res = #X; \
break
switch (S.Type) {
ECase(TYPE);
ECase(IMPORT);
ECase(FUNCTION);
ECase(TABLE);
ECase(MEMORY);
ECase(GLOBAL);
ECase(EXPORT);
ECase(START);
ECase(ELEM);
ECase(CODE);
ECase(DATA);
case wasm::WASM_SEC_CUSTOM:
Res = S.Name;
break;
default:
return object_error::invalid_section_index;
}
#undef ECase
return std::error_code();
}
uint64_t WasmObjectFile::getSectionAddress(DataRefImpl Sec) const { return 0; }
uint64_t WasmObjectFile::getSectionIndex(DataRefImpl Sec) const {
return Sec.d.a;
}
uint64_t WasmObjectFile::getSectionSize(DataRefImpl Sec) const {
const WasmSection &S = Sections[Sec.d.a];
return S.Content.size();
}
std::error_code WasmObjectFile::getSectionContents(DataRefImpl Sec,
StringRef &Res) const {
const WasmSection &S = Sections[Sec.d.a];
// This will never fail since wasm sections can never be empty (user-sections
// must have a name and non-user sections each have a defined structure).
Res = StringRef(reinterpret_cast<const char *>(S.Content.data()),
S.Content.size());
return std::error_code();
}
uint64_t WasmObjectFile::getSectionAlignment(DataRefImpl Sec) const {
return 1;
}
bool WasmObjectFile::isSectionCompressed(DataRefImpl Sec) const {
return false;
}
bool WasmObjectFile::isSectionText(DataRefImpl Sec) const {
return getWasmSection(Sec).Type == wasm::WASM_SEC_CODE;
}
bool WasmObjectFile::isSectionData(DataRefImpl Sec) const {
return getWasmSection(Sec).Type == wasm::WASM_SEC_DATA;
}
bool WasmObjectFile::isSectionBSS(DataRefImpl Sec) const { return false; }
bool WasmObjectFile::isSectionVirtual(DataRefImpl Sec) const { return false; }
bool WasmObjectFile::isSectionBitcode(DataRefImpl Sec) const { return false; }
relocation_iterator WasmObjectFile::section_rel_begin(DataRefImpl Ref) const {
DataRefImpl RelocRef;
RelocRef.d.a = Ref.d.a;
RelocRef.d.b = 0;
return relocation_iterator(RelocationRef(RelocRef, this));
}
relocation_iterator WasmObjectFile::section_rel_end(DataRefImpl Ref) const {
const WasmSection &Sec = getWasmSection(Ref);
DataRefImpl RelocRef;
RelocRef.d.a = Ref.d.a;
RelocRef.d.b = Sec.Relocations.size();
return relocation_iterator(RelocationRef(RelocRef, this));
}
void WasmObjectFile::moveRelocationNext(DataRefImpl &Rel) const {
Rel.d.b++;
}
uint64_t WasmObjectFile::getRelocationOffset(DataRefImpl Ref) const {
const wasm::WasmRelocation &Rel = getWasmRelocation(Ref);
return Rel.Offset;
}
symbol_iterator WasmObjectFile::getRelocationSymbol(DataRefImpl Rel) const {
llvm_unreachable("not yet implemented");
SymbolRef Ref;
return symbol_iterator(Ref);
}
uint64_t WasmObjectFile::getRelocationType(DataRefImpl Ref) const {
const wasm::WasmRelocation &Rel = getWasmRelocation(Ref);
return Rel.Type;
}
void WasmObjectFile::getRelocationTypeName(
DataRefImpl Ref, SmallVectorImpl<char> &Result) const {
const wasm::WasmRelocation& Rel = getWasmRelocation(Ref);
StringRef Res = "Unknown";
#define WASM_RELOC(name, value) \
case wasm::name: \
Res = #name; \
break;
switch (Rel.Type) {
#include "llvm/BinaryFormat/WasmRelocs.def"
}
#undef WASM_RELOC
Result.append(Res.begin(), Res.end());
}
section_iterator WasmObjectFile::section_begin() const {
DataRefImpl Ref;
Ref.d.a = 0;
return section_iterator(SectionRef(Ref, this));
}
section_iterator WasmObjectFile::section_end() const {
DataRefImpl Ref;
Ref.d.a = Sections.size();
return section_iterator(SectionRef(Ref, this));
}
uint8_t WasmObjectFile::getBytesInAddress() const { return 4; }
StringRef WasmObjectFile::getFileFormatName() const { return "WASM"; }
Triple::ArchType WasmObjectFile::getArch() const { return Triple::wasm32; }
SubtargetFeatures WasmObjectFile::getFeatures() const {
return SubtargetFeatures();
}
bool WasmObjectFile::isRelocatableObject() const {
return HasLinkingSection;
}
const WasmSection &WasmObjectFile::getWasmSection(DataRefImpl Ref) const {
assert(Ref.d.a < Sections.size());
return Sections[Ref.d.a];
}
const WasmSection &
WasmObjectFile::getWasmSection(const SectionRef &Section) const {
return getWasmSection(Section.getRawDataRefImpl());
}
const wasm::WasmRelocation &
WasmObjectFile::getWasmRelocation(const RelocationRef &Ref) const {
return getWasmRelocation(Ref.getRawDataRefImpl());
}
const wasm::WasmRelocation &
WasmObjectFile::getWasmRelocation(DataRefImpl Ref) const {
assert(Ref.d.a < Sections.size());
const WasmSection& Sec = Sections[Ref.d.a];
assert(Ref.d.b < Sec.Relocations.size());
return Sec.Relocations[Ref.d.b];
}