20#include "llvm/ADT/ArrayRef.h"
21#include "llvm/ADT/SmallVector.h"
22#include "llvm/ADT/StringRef.h"
23#include "llvm/BinaryFormat/Magic.h"
24#include "llvm/BinaryFormat/Wasm.h"
25#include "llvm/Support/CheckedArithmetic.h"
26#include "llvm/Support/Endian.h"
27#include "llvm/Support/Format.h"
42 const uint64_t value = data.GetULEB128(&offset);
43 if (value > std::numeric_limits<uint32_t>::max())
44 return llvm::createStringError(
"ULEB exceeds 32 bits");
49static inline llvm::Expected<uint32_t>
50GetULEB32(llvm::DataExtractor &data, llvm::DataExtractor::Cursor &c) {
51 const uint64_t value = data.getULEB128(c);
54 if (value > std::numeric_limits<uint32_t>::max())
55 return llvm::createStringError(
"ULEB exceeds 32 bits");
60static inline llvm::Expected<std::string>
61GetWasmString(llvm::DataExtractor &data, llvm::DataExtractor::Cursor &c) {
62 llvm::Expected<uint32_t> len =
GetULEB32(data, c);
64 return len.takeError();
66 llvm::SmallVector<uint8_t, 32> str_storage;
67 data.getU8(c, str_storage, *len);
71 return std::string(toStringRef(llvm::ArrayRef(str_storage)));
82 uint8_t opcode = data.
GetU8(&offset);
84 case llvm::wasm::WASM_OPCODE_I32_CONST:
85 case llvm::wasm::WASM_OPCODE_I64_CONST:
88 case llvm::wasm::WASM_OPCODE_GLOBAL_GET:
91 case llvm::wasm::WASM_OPCODE_F32_CONST:
92 case llvm::wasm::WASM_OPCODE_F64_CONST:
96 case llvm::wasm::WASM_OPCODE_REF_NULL:
103 opcode = data.
GetU8(&offset);
104 if (opcode == llvm::wasm::WASM_OPCODE_END)
105 return init_expr_offset;
110 opcode = data.
GetU8(&offset);
111 }
while (opcode != llvm::wasm::WASM_OPCODE_END);
120 if (llvm::identify_magic(toStringRef(data_sp->GetData())) !=
121 llvm::file_magic::wasm_object)
124 const uint8_t *Ptr = data_sp->GetBytes() +
sizeof(llvm::wasm::WasmMagic);
126 uint32_t version = llvm::support::endian::read32le(Ptr);
127 return version == llvm::wasm::WasmVersion;
150 if (!extractor_sp || !extractor_sp->HasData()) {
153 LLDB_LOGF(log,
"Failed to create ObjectFileWasm instance for file %s",
157 extractor_sp = std::make_shared<DataExtractor>(data_sp);
161 assert(extractor_sp);
164 "Failed to create ObjectFileWasm instance: invalid Wasm header");
170 if (extractor_sp->GetByteSize() < length) {
174 "Failed to create ObjectFileWasm instance: cannot read file %s",
178 extractor_sp = std::make_shared<DataExtractor>(data_sp);
183 module_sp, extractor_sp, data_offset, file, file_offset, length));
184 ArchSpec spec = objfile_up->GetArchitecture();
185 if (spec && objfile_up->SetModulesArchitecture(spec)) {
187 "%p ObjectFileWasm::CreateInstance() module = %p (%s), file = %s",
188 static_cast<void *
>(objfile_up.get()),
189 static_cast<void *
>(objfile_up->GetModule().get()),
190 objfile_up->GetModule()->GetSpecificationDescription().c_str(),
191 file ? file->
GetPath().c_str() :
"<NULL>");
192 return objfile_up.release();
195 LLDB_LOGF(log,
"Failed to create ObjectFileWasm instance");
206 std::unique_ptr<ObjectFileWasm> objfile_up(
208 ArchSpec spec = objfile_up->GetArchitecture();
209 if (spec && objfile_up->SetModulesArchitecture(spec))
210 return objfile_up.release();
217 const uint32_t kBufferSize = 1024;
220 llvm::DataExtractor data = section_header_data.
GetAsLLVM();
221 llvm::DataExtractor::Cursor c(0);
227 uint8_t section_id = data.getU8(c);
228 uint64_t payload_len = data.getULEB128(c);
230 return !llvm::errorToBool(c.takeError());
232 if (payload_len > std::numeric_limits<uint32_t>::max())
235 if (section_id == llvm::wasm::WASM_SEC_CUSTOM) {
240 llvm::Expected<std::string> sect_name =
GetWasmString(data, c);
243 "failed to parse section name: {0}");
247 if (payload_len < c.tell() - prev_offset)
250 uint32_t section_length = payload_len - (c.tell() - prev_offset);
253 *offset_ptr += (c.tell() + section_length);
254 }
else if (section_id <= llvm::wasm::WASM_SEC_LAST_KNOWN) {
256 static_cast<uint32_t
>(payload_len),
258 *offset_ptr += (c.tell() + payload_len);
293 :
ObjectFile(module_sp, file, offset, length, extractor_sp, data_offset),
294 m_arch(
"wasm32-unknown-unknown-wasm") {
302 :
ObjectFile(module_sp, process_sp, header_addr,
304 m_arch(
"wasm32-unknown-unknown-wasm") {}
320 llvm::DataExtractor data = import_data.
GetAsLLVM();
321 llvm::DataExtractor::Cursor c(0);
323 llvm::Expected<uint32_t> count =
GetULEB32(data, c);
325 return count.takeError();
327 uint32_t function_imports = 0;
328 for (uint32_t i = 0; c && i < *count; ++i) {
332 return llvm::createStringError(
"failed to parse module name");
334 return llvm::createStringError(
"failed to parse field name");
336 uint8_t kind = data.getU8(c);
337 if (kind == llvm::wasm::WASM_EXTERNAL_FUNCTION)
346 return c.takeError();
348 return function_imports;
351static llvm::Expected<std::vector<WasmFunction>>
355 llvm::Expected<uint32_t> function_count =
GetULEB32(data, offset);
357 return function_count.takeError();
359 std::vector<WasmFunction> functions;
360 functions.reserve(*function_count);
362 for (uint32_t i = 0; i < *function_count; ++i) {
363 llvm::Expected<uint32_t> function_size =
GetULEB32(data, offset);
365 return function_size.takeError();
369 functions.push_back({offset, *function_size});
371 std::optional<lldb::offset_t> next_offset =
372 llvm::checkedAddUnsigned<lldb::offset_t>(offset, *function_size);
374 return llvm::createStringError(
"function offset overflows 64 bits");
375 offset = *next_offset;
400 llvm::Expected<uint32_t> segment_count =
GetULEB32(data, offset);
402 return segment_count.takeError();
404 std::vector<WasmSegment> segments;
405 segments.reserve(*segment_count);
407 for (uint32_t i = 0; i < *segment_count; ++i) {
408 llvm::Expected<uint32_t> flags =
GetULEB32(data, offset);
410 return flags.takeError();
418 segment.
type = (*flags & llvm::wasm::WASM_DATA_SEGMENT_IS_PASSIVE)
422 if (*flags & llvm::wasm::WASM_DATA_SEGMENT_HAS_MEMINDEX) {
424 llvm::Expected<uint32_t> memidx =
GetULEB32(data, offset);
426 return memidx.takeError();
433 llvm::Expected<uint32_t> segment_size =
GetULEB32(data, offset);
435 return segment_size.takeError();
438 segment.
size = *segment_size;
439 segments.push_back(segment);
441 std::optional<lldb::offset_t> next_offset =
442 llvm::checkedAddUnsigned<lldb::offset_t>(offset, *segment_size);
444 return llvm::createStringError(
"segment offset overflows 64 bits");
445 offset = *next_offset;
451static llvm::Expected<std::vector<Symbol>>
453 const std::vector<WasmFunction> &functions,
454 std::vector<WasmSegment> &segments,
455 uint32_t num_imported_functions) {
457 llvm::DataExtractor data = name_data.
GetAsLLVM();
458 llvm::DataExtractor::Cursor c(0);
459 std::vector<Symbol> symbols;
460 while (c && c.tell() < data.size()) {
461 const uint8_t type = data.getU8(c);
462 llvm::Expected<uint32_t> size =
GetULEB32(data, c);
464 return size.takeError();
467 case llvm::wasm::WASM_NAMES_FUNCTION: {
468 const uint64_t count = data.getULEB128(c);
469 if (count > std::numeric_limits<uint32_t>::max())
470 return llvm::createStringError(
"function count overflows uint32_t");
472 for (uint64_t i = 0; c && i < count; ++i) {
473 llvm::Expected<uint32_t> idx =
GetULEB32(data, c);
475 return idx.takeError();
478 return name.takeError();
479 if (*idx >= num_imported_functions + functions.size())
482 if (*idx < num_imported_functions) {
493 const WasmFunction &func = functions[*idx - num_imported_functions];
504 case llvm::wasm::WASM_NAMES_DATA_SEGMENT: {
505 llvm::Expected<uint32_t> count =
GetULEB32(data, c);
507 return count.takeError();
508 for (uint32_t i = 0; c && i < *count; ++i) {
509 llvm::Expected<uint32_t> idx =
GetULEB32(data, c);
511 return idx.takeError();
514 return name.takeError();
515 if (*idx >= segments.size())
518 segments[i].name = *name;
522 case llvm::wasm::WASM_NAMES_GLOBAL:
523 case llvm::wasm::WASM_NAMES_LOCAL:
525 std::optional<lldb::offset_t> offset =
526 llvm::checkedAddUnsigned<lldb::offset_t>(c.tell(), *size);
528 return llvm::createStringError(
"offset overflows 64 bits");
534 return c.takeError();
550 if (Name.consume_front(
".debug_") || Name.consume_front(
".zdebug_"))
555std::optional<ObjectFileWasm::section_info>
558 if (sect_info.id == section_id)
564std::optional<ObjectFileWasm::section_info>
567 if (sect_info.name == section_name)
588 offset_t file_offset = sect_info.offset & 0xffffffff;
589 addr_t vm_addr = sect_info.offset;
590 size_t vm_size = sect_info.size;
592 if (llvm::wasm::WASM_SEC_CODE == sect_info.id) {
605 section_name = sect_info.name;
612 SectionSP section_sp = std::make_shared<Section>(
632 std::vector<WasmFunction> functions;
633 std::vector<WasmSegment> segments;
636 if (std::optional<section_info> info =
639 llvm::Expected<std::vector<WasmFunction>> maybe_functions =
641 if (!maybe_functions) {
643 "Failed to parse Wasm code section: {0}");
645 functions = *maybe_functions;
651 if (std::optional<section_info> info =
654 llvm::Expected<uint32_t> num_imports =
ParseImports(import_data);
657 "Failed to parse Wasm import section: {0}");
664 std::optional<section_info> data_info =
668 llvm::Expected<std::vector<WasmSegment>> maybe_segments =
670 if (!maybe_segments) {
672 "Failed to parse Wasm data section: {0}");
674 segments = *maybe_segments;
680 llvm::Expected<std::vector<Symbol>> symbols =
ParseNames(
685 "Failed to parse Wasm names: {0}");
695 if (segment.memory_index != 0) {
696 LLDB_LOG(log,
"Skipping segment {0}: non-zero memory index is "
697 "currently unsupported");
702 LLDB_LOG(log,
"Skipping segment {0}: unsupported init expression");
709 ? segment.init_expr_offset
710 : data_info->offset + segment.section_offset;
712 data_info->GetFileOffset() + segment.GetFileOffset();
713 SectionSP segment_sp = std::make_shared<Section>(
725 GetModule()->GetSectionList()->AddSection(segment_sp);
730 bool value_is_offset) {
756 size_t num_loaded_sections = 0;
761 const size_t num_sections = section_list->
GetSize();
762 for (
size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) {
765 section_sp, load_address | section_sp->GetFileOffset())) {
766 ++num_loaded_sections;
770 return num_loaded_sections > 0;
777 size = std::min(
static_cast<uint64_t
>(size),
GetByteSize() - offset);
784 auto data_up = std::make_unique<DataBufferHeap>(size, 0);
786 size_t bytes_read = process_sp->ReadMemory(
787 offset, data_up->GetBytes(), data_up->GetByteSize(), readmem_error);
788 if (bytes_read > 0) {
793 size = std::min(
static_cast<uint64_t
>(size),
804 static ConstString g_sect_name_external_debug_info(
"external_debug_info");
807 if (g_sect_name_external_debug_info == sect_info.name) {
808 const uint32_t kBufferSize = 1024;
812 llvm::DataExtractor data = section_header_data.
GetAsLLVM();
813 llvm::DataExtractor::Cursor c(0);
814 llvm::Expected<std::string> symbols_url =
GetWasmString(data, c);
816 llvm::consumeError(symbols_url.takeError());
830 std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
833 ostream << static_cast<void *>(
this) <<
": ";
835 ostream <<
"ObjectFileWasm, file = '";
837 ostream <<
"', arch = ";
853 << llvm::format_hex(sh.
offset, 10) <<
" "
854 << llvm::format_hex(sh.
size, 10) <<
" " << llvm::format_hex(sh.
id, 6)
859 ostream <<
"Section Headers\n";
860 ostream <<
"IDX name addr size id\n";
861 ostream <<
"==== ---------------- ---------- ---------- ------\n";
866 ostream <<
"[" << llvm::format_decimal(idx, 2) <<
"] ";
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
#define LLDB_LOGF(log,...)
#define LLDB_LOG_ERROR(log, error,...)
static SectionType GetSectionTypeFromName(llvm::StringRef Name)
static lldb::offset_t GetWasmOffsetFromInitExpr(DataExtractor &data, lldb::offset_t &offset)
An "init expr" refers to a constant expression used to determine the initial value of certain element...
static bool ValidateModuleHeader(const DataBufferSP &data_sp)
Checks whether the data buffer starts with a valid Wasm module header.
static llvm::Expected< std::string > GetWasmString(llvm::DataExtractor &data, llvm::DataExtractor::Cursor &c)
Helper to read a Wasm string, whcih is encoded as a vector of UTF-8 codes.
static llvm::Expected< uint32_t > GetULEB32(DataExtractor &data, lldb::offset_t &offset)
Helper to read a 32-bit ULEB using LLDB's DataExtractor.
static llvm::Expected< uint32_t > ParseImports(DataExtractor &import_data)
static llvm::Expected< std::vector< WasmFunction > > ParseFunctions(DataExtractor &data)
static llvm::Expected< std::vector< Symbol > > ParseNames(SectionSP code_section_sp, DataExtractor &name_data, const std::vector< WasmFunction > &functions, std::vector< WasmSegment > &segments, uint32_t num_imported_functions)
static const uint32_t kWasmHeaderSize
static llvm::Expected< std::vector< WasmSegment > > ParseData(DataExtractor &data)
#define LLDB_PLUGIN_DEFINE(PluginName)
An architecture specification class.
const char * GetArchitectureName() const
Returns a static string representing the current architecture.
A uniqued constant string class.
llvm::StringRef GetStringRef() const
Get the string value as a llvm::StringRef.
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
lldb::ModuleSP GetModule() const
Get const accessor for the module pointer.
void Append(const ModuleSpec &spec)
std::unique_ptr< lldb_private::SectionList > m_sections_up
static lldb::DataBufferSP MapFileData(const FileSpec &file, uint64_t Size, uint64_t Offset)
const lldb::addr_t m_memory_addr
Set if the object file only exists in memory.
static lldb::SectionType GetDWARFSectionTypeFromName(llvm::StringRef name)
Parses the section type from a section name for DWARF sections.
DataExtractorNSP m_data_nsp
The data for this object file so things can be parsed lazily.
virtual SectionList * GetSectionList(bool update_module_section_list=true)
Gets the section list for the currently selected architecture (and object for archives).
ObjectFile(const lldb::ModuleSP &module_sp, const FileSpec *file_spec_ptr, lldb::offset_t file_offset, lldb::offset_t length, lldb::DataExtractorSP extractor_sp, lldb::offset_t data_offset)
Construct with a parent module, offset, and header data.
bool IsInMemory() const
Returns true if the object file exists only in memory.
lldb::ProcessWP m_process_wp
virtual lldb::addr_t GetByteSize() const
static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description, ABICreateInstance create_callback)
static bool UnregisterPlugin(ABICreateInstance create_callback)
size_t AddSection(const lldb::SectionSP §ion_sp)
void Dump(llvm::raw_ostream &s, unsigned indent, Target *target, bool show_header, uint32_t depth) const
lldb::SectionSP GetSectionAtIndex(size_t idx) const
A stream class that can stream formatted output to a file.
llvm::raw_ostream & AsRawOstream()
Returns a raw_ostream that forwards the data to this Stream object.
size_t Indent(llvm::StringRef s="")
Indent the current line in the stream.
unsigned GetIndentLevel() const
Get the current indentation level.
uint32_t AddSymbol(const Symbol &symbol)
bool SetSectionLoadAddress(const lldb::SectionSP §ion, lldb::addr_t load_addr, bool warn_multiple=false)
Generic Wasm object file reader.
ArchSpec GetArchitecture() override
Get the ArchSpec for this object file.
std::optional< FileSpec > GetExternalDebugInfoFileSpec()
A Wasm module that has external DWARF debug information should contain a custom section named "extern...
bool SetLoadAddress(lldb_private::Target &target, lldb::addr_t value, bool value_is_offset) override
Sets the load address for an entire module, assuming a rigid slide of sections, if possible in the im...
bool DecodeNextSection(lldb::offset_t *offset_ptr)
Wasm section decoding routines.
lldb::ByteOrder GetByteOrder() const override
Gets whether endian swapping should occur when extracting data from this object file.
void CreateSections(SectionList &unified_section_list) override
ObjectFileWasm(const lldb::ModuleSP &module_sp, lldb::DataExtractorSP extractor_sp, lldb::offset_t data_offset, const FileSpec *file, lldb::offset_t offset, lldb::offset_t length)
std::optional< section_info > GetSectionInfo(uint32_t section_id)
void Dump(Stream *s) override
Dump a description of this object to a Stream.
static llvm::StringRef GetPluginNameStatic()
std::vector< section_info > m_sect_infos
static size_t GetModuleSpecifications(const FileSpec &file, lldb::DataBufferSP &data_sp, lldb::offset_t data_offset, lldb::offset_t file_offset, lldb::offset_t length, ModuleSpecList &specs)
void DumpSectionHeader(llvm::raw_ostream &ostream, const section_info &sh)
Wasm section header dump routines.
void DumpSectionHeaders(llvm::raw_ostream &ostream)
static ObjectFile * CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataExtractorSP extractor_sp, lldb::offset_t data_offset, const FileSpec *file, lldb::offset_t file_offset, lldb::offset_t length)
static char ID
LLVM RTTI support.
std::vector< Symbol > m_symbols
void ParseSymtab(lldb_private::Symtab &symtab) override
Parse the symbol table into the provides symbol table object.
uint32_t GetAddressByteSize() const override
Gets the address size in bytes for the current object file.
static ObjectFile * CreateMemoryInstance(const lldb::ModuleSP &module_sp, lldb::WritableDataBufferSP data_sp, const lldb::ProcessSP &process_sp, lldb::addr_t header_addr)
bool ParseHeader() override
ObjectFile Protocol.
uint32_t m_num_imported_functions
static const char * GetPluginDescriptionStatic()
DataExtractor ReadImageData(lldb::offset_t offset, uint32_t size)
Read a range of bytes from the Wasm module.
#define LLDB_INVALID_ADDRESS
#define LLDB_INVALID_OFFSET
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
std::shared_ptr< lldb_private::Process > ProcessSP
std::shared_ptr< lldb_private::DataBuffer > DataBufferSP
std::shared_ptr< lldb_private::Section > SectionSP
std::shared_ptr< lldb_private::WritableDataBuffer > WritableDataBufferSP
std::shared_ptr< lldb_private::DataExtractor > DataExtractorSP
std::shared_ptr< lldb_private::Module > ModuleSP
lldb::offset_t section_offset
lldb::offset_t section_offset
lldb::offset_t GetFileOffset() const
lldb::offset_t init_expr_offset