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)) != llvm::file_magic::wasm_object)
123 const uint8_t *Ptr = data.data() +
sizeof(llvm::wasm::WasmMagic);
125 uint32_t version = llvm::support::endian::read32le(Ptr);
126 return version == llvm::wasm::WasmVersion;
149 if (!extractor_sp || !extractor_sp->HasData()) {
152 LLDB_LOGF(log,
"Failed to create ObjectFileWasm instance for file %s",
156 extractor_sp = std::make_shared<DataExtractor>(data_sp);
160 assert(extractor_sp);
163 "Failed to create ObjectFileWasm instance: invalid Wasm header");
169 if (extractor_sp->GetByteSize() < length) {
173 "Failed to create ObjectFileWasm instance: cannot read file %s",
177 extractor_sp = std::make_shared<DataExtractor>(data_sp);
182 module_sp, extractor_sp, data_offset, file, file_offset, length));
183 ArchSpec spec = objfile_up->GetArchitecture();
184 if (spec && objfile_up->SetModulesArchitecture(spec)) {
186 "%p ObjectFileWasm::CreateInstance() module = %p (%s), file = %s",
187 static_cast<void *
>(objfile_up.get()),
188 static_cast<void *
>(objfile_up->GetModule().get()),
189 objfile_up->GetModule()->GetSpecificationDescription().c_str(),
190 file ? file->
GetPath().c_str() :
"<NULL>");
191 return objfile_up.release();
194 LLDB_LOGF(log,
"Failed to create ObjectFileWasm instance");
205 std::unique_ptr<ObjectFileWasm> objfile_up(
207 ArchSpec spec = objfile_up->GetArchitecture();
208 if (spec && objfile_up->SetModulesArchitecture(spec))
209 return objfile_up.release();
216 const uint32_t kBufferSize = 1024;
219 llvm::DataExtractor data = section_header_data.
GetAsLLVM();
220 llvm::DataExtractor::Cursor c(0);
226 uint8_t section_id = data.getU8(c);
227 uint64_t payload_len = data.getULEB128(c);
229 return !llvm::errorToBool(c.takeError());
231 if (payload_len > std::numeric_limits<uint32_t>::max())
234 if (section_id == llvm::wasm::WASM_SEC_CUSTOM) {
239 llvm::Expected<std::string> sect_name =
GetWasmString(data, c);
242 "failed to parse section name: {0}");
246 if (payload_len < c.tell() - prev_offset)
249 uint32_t section_length = payload_len - (c.tell() - prev_offset);
252 *offset_ptr += (c.tell() + section_length);
253 }
else if (section_id <= llvm::wasm::WASM_SEC_LAST_KNOWN) {
255 static_cast<uint32_t
>(payload_len),
257 *offset_ptr += (c.tell() + payload_len);
292 :
ObjectFile(module_sp, file, offset, length, extractor_sp, data_offset),
293 m_arch(
"wasm32-unknown-unknown-wasm") {
301 :
ObjectFile(module_sp, process_sp, header_addr,
303 m_arch(
"wasm32-unknown-unknown-wasm") {}
319 llvm::DataExtractor data = import_data.
GetAsLLVM();
320 llvm::DataExtractor::Cursor c(0);
322 llvm::Expected<uint32_t> count =
GetULEB32(data, c);
324 return count.takeError();
326 uint32_t function_imports = 0;
327 for (uint32_t i = 0; c && i < *count; ++i) {
330 llvm::Expected<std::string> module_name =
GetWasmString(data, c);
332 return llvm::joinErrors(
333 llvm::createStringError(
"failed to parse module name"),
334 module_name.takeError());
335 llvm::Expected<std::string> field_name =
GetWasmString(data, c);
337 return llvm::joinErrors(
338 llvm::createStringError(
"failed to parse field name"),
339 field_name.takeError());
341 uint8_t kind = data.getU8(c);
342 if (kind == llvm::wasm::WASM_EXTERNAL_FUNCTION)
351 return c.takeError();
353 return function_imports;
356static llvm::Expected<std::vector<WasmFunction>>
360 llvm::Expected<uint32_t> function_count =
GetULEB32(data, offset);
362 return function_count.takeError();
364 std::vector<WasmFunction> functions;
365 functions.reserve(*function_count);
367 for (uint32_t i = 0; i < *function_count; ++i) {
368 llvm::Expected<uint32_t> function_size =
GetULEB32(data, offset);
370 return function_size.takeError();
374 functions.push_back({offset, *function_size});
376 std::optional<lldb::offset_t> next_offset =
377 llvm::checkedAddUnsigned<lldb::offset_t>(offset, *function_size);
379 return llvm::createStringError(
"function offset overflows 64 bits");
380 offset = *next_offset;
405 llvm::Expected<uint32_t> segment_count =
GetULEB32(data, offset);
407 return segment_count.takeError();
409 std::vector<WasmSegment> segments;
410 segments.reserve(*segment_count);
412 for (uint32_t i = 0; i < *segment_count; ++i) {
413 llvm::Expected<uint32_t> flags =
GetULEB32(data, offset);
415 return flags.takeError();
423 segment.
type = (*flags & llvm::wasm::WASM_DATA_SEGMENT_IS_PASSIVE)
427 if (*flags & llvm::wasm::WASM_DATA_SEGMENT_HAS_MEMINDEX) {
429 llvm::Expected<uint32_t> memidx =
GetULEB32(data, offset);
431 return memidx.takeError();
438 llvm::Expected<uint32_t> segment_size =
GetULEB32(data, offset);
440 return segment_size.takeError();
443 segment.
size = *segment_size;
444 segments.push_back(segment);
446 std::optional<lldb::offset_t> next_offset =
447 llvm::checkedAddUnsigned<lldb::offset_t>(offset, *segment_size);
449 return llvm::createStringError(
"segment offset overflows 64 bits");
450 offset = *next_offset;
456static llvm::Expected<std::vector<Symbol>>
458 const std::vector<WasmFunction> &functions,
459 std::vector<WasmSegment> &segments,
460 uint32_t num_imported_functions) {
462 llvm::DataExtractor data = name_data.
GetAsLLVM();
463 llvm::DataExtractor::Cursor c(0);
464 std::vector<Symbol> symbols;
465 while (c && c.tell() < data.size()) {
466 const uint8_t type = data.getU8(c);
467 llvm::Expected<uint32_t> size =
GetULEB32(data, c);
469 return size.takeError();
472 case llvm::wasm::WASM_NAMES_FUNCTION: {
473 const uint64_t count = data.getULEB128(c);
474 if (count > std::numeric_limits<uint32_t>::max())
475 return llvm::createStringError(
"function count overflows uint32_t");
477 for (uint64_t i = 0; c && i < count; ++i) {
478 llvm::Expected<uint32_t> idx =
GetULEB32(data, c);
480 return idx.takeError();
483 return name.takeError();
484 if (*idx >= num_imported_functions + functions.size())
487 if (*idx < num_imported_functions) {
498 const WasmFunction &func = functions[*idx - num_imported_functions];
509 case llvm::wasm::WASM_NAMES_DATA_SEGMENT: {
510 llvm::Expected<uint32_t> count =
GetULEB32(data, c);
512 return count.takeError();
513 for (uint32_t i = 0; c && i < *count; ++i) {
514 llvm::Expected<uint32_t> idx =
GetULEB32(data, c);
516 return idx.takeError();
519 return name.takeError();
520 if (*idx >= segments.size())
523 segments[i].name = *name;
527 case llvm::wasm::WASM_NAMES_GLOBAL:
528 case llvm::wasm::WASM_NAMES_LOCAL:
530 std::optional<lldb::offset_t> offset =
531 llvm::checkedAddUnsigned<lldb::offset_t>(c.tell(), *size);
533 return llvm::createStringError(
"offset overflows 64 bits");
539 return c.takeError();
555 if (Name.consume_front(
".debug_") || Name.consume_front(
".zdebug_"))
560std::optional<ObjectFileWasm::section_info>
563 if (sect_info.id == section_id)
569std::optional<ObjectFileWasm::section_info>
572 if (sect_info.name == section_name)
593 offset_t file_offset = sect_info.offset & 0xffffffff;
594 addr_t vm_addr = sect_info.offset;
595 size_t vm_size = sect_info.size;
597 if (llvm::wasm::WASM_SEC_CODE == sect_info.id) {
610 section_name = sect_info.name;
617 SectionSP section_sp = std::make_shared<Section>(
637 std::vector<WasmFunction> functions;
638 std::vector<WasmSegment> segments;
641 if (std::optional<section_info> info =
644 llvm::Expected<std::vector<WasmFunction>> maybe_functions =
646 if (!maybe_functions) {
648 "Failed to parse Wasm code section: {0}");
650 functions = *maybe_functions;
656 if (std::optional<section_info> info =
659 llvm::Expected<uint32_t> num_imports =
ParseImports(import_data);
662 "Failed to parse Wasm import section: {0}");
669 std::optional<section_info> data_info =
673 llvm::Expected<std::vector<WasmSegment>> maybe_segments =
675 if (!maybe_segments) {
677 "Failed to parse Wasm data section: {0}");
679 segments = *maybe_segments;
685 llvm::Expected<std::vector<Symbol>> symbols =
ParseNames(
690 "Failed to parse Wasm names: {0}");
700 if (segment.memory_index != 0) {
701 LLDB_LOG(log,
"Skipping segment {0}: non-zero memory index is "
702 "currently unsupported");
707 LLDB_LOG(log,
"Skipping segment {0}: unsupported init expression");
714 ? segment.init_expr_offset
715 : data_info->offset + segment.section_offset;
717 data_info->GetFileOffset() + segment.GetFileOffset();
718 SectionSP segment_sp = std::make_shared<Section>(
730 GetModule()->GetSectionList()->AddSection(segment_sp);
735 bool value_is_offset) {
761 size_t num_loaded_sections = 0;
766 const size_t num_sections = section_list->
GetSize();
767 for (
size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) {
770 section_sp, load_address | section_sp->GetFileOffset())) {
771 ++num_loaded_sections;
775 return num_loaded_sections > 0;
782 size = std::min(
static_cast<uint64_t
>(size),
GetByteSize() - offset);
789 auto data_up = std::make_unique<DataBufferHeap>(size, 0);
791 size_t bytes_read = process_sp->ReadMemory(
792 offset, data_up->GetBytes(), data_up->GetByteSize(), readmem_error);
793 if (bytes_read > 0) {
798 size = std::min(
static_cast<uint64_t
>(size),
809 static ConstString g_sect_name_external_debug_info(
"external_debug_info");
812 if (g_sect_name_external_debug_info == sect_info.name) {
813 const uint32_t kBufferSize = 1024;
817 llvm::DataExtractor data = section_header_data.
GetAsLLVM();
818 llvm::DataExtractor::Cursor c(0);
819 llvm::Expected<std::string> symbols_url =
GetWasmString(data, c);
821 llvm::consumeError(symbols_url.takeError());
835 std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
838 ostream << static_cast<void *>(
this) <<
": ";
840 ostream <<
"ObjectFileWasm, file = '";
842 ostream <<
"', arch = ";
858 << llvm::format_hex(sh.
offset, 10) <<
" "
859 << llvm::format_hex(sh.
size, 10) <<
" " << llvm::format_hex(sh.
id, 6)
864 ostream <<
"Section Headers\n";
865 ostream <<
"IDX name addr size id\n";
866 ostream <<
"==== ---------------- ---------- ---------- ------\n";
871 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 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 bool ValidateModuleHeader(llvm::ArrayRef< uint8_t > data)
Checks whether the data buffer starts with a valid Wasm module header.
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.
static size_t GetModuleSpecifications(const FileSpec &file, lldb::DataExtractorSP &extractor_sp, lldb::offset_t data_offset, lldb::offset_t file_offset, lldb::offset_t length, ModuleSpecList &specs)
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
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