800 lines · cpp
1//===-- ObjectFileWasm.cpp ------------------------------------------------===//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#include "ObjectFileWasm.h"10#include "lldb/Core/Module.h"11#include "lldb/Core/ModuleSpec.h"12#include "lldb/Core/PluginManager.h"13#include "lldb/Core/Section.h"14#include "lldb/Target/Process.h"15#include "lldb/Target/SectionLoadList.h"16#include "lldb/Target/Target.h"17#include "lldb/Utility/DataBufferHeap.h"18#include "lldb/Utility/LLDBLog.h"19#include "lldb/Utility/Log.h"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"28#include <optional>29 30using namespace lldb;31using namespace lldb_private;32using namespace lldb_private::wasm;33 34LLDB_PLUGIN_DEFINE(ObjectFileWasm)35 36static const uint32_t kWasmHeaderSize =37 sizeof(llvm::wasm::WasmMagic) + sizeof(llvm::wasm::WasmVersion);38 39/// Helper to read a 32-bit ULEB using LLDB's DataExtractor.40static inline llvm::Expected<uint32_t> GetULEB32(DataExtractor &data,41 lldb::offset_t &offset) {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");45 return value;46}47 48/// Helper to read a 32-bit ULEB using LLVM's DataExtractor.49static inline llvm::Expected<uint32_t>50GetULEB32(llvm::DataExtractor &data, llvm::DataExtractor::Cursor &c) {51 const uint64_t value = data.getULEB128(c);52 if (!c)53 return c.takeError();54 if (value > std::numeric_limits<uint32_t>::max())55 return llvm::createStringError("ULEB exceeds 32 bits");56 return value;57}58 59/// Helper to read a Wasm string, whcih is encoded as a vector of UTF-8 codes.60static inline llvm::Expected<std::string>61GetWasmString(llvm::DataExtractor &data, llvm::DataExtractor::Cursor &c) {62 llvm::Expected<uint32_t> len = GetULEB32(data, c);63 if (!len)64 return len.takeError();65 66 llvm::SmallVector<uint8_t, 32> str_storage;67 data.getU8(c, str_storage, *len);68 if (!c)69 return c.takeError();70 71 return std::string(toStringRef(llvm::ArrayRef(str_storage)));72}73 74/// An "init expr" refers to a constant expression used to determine the initial75/// value of certain elements within a module during instantiation. These76/// expressions are restricted to operations that can be evaluated at module77/// instantiation time. Currently we only support simple constant opcodes.78static lldb::offset_t GetWasmOffsetFromInitExpr(DataExtractor &data,79 lldb::offset_t &offset) {80 lldb::offset_t init_expr_offset = LLDB_INVALID_OFFSET;81 82 uint8_t opcode = data.GetU8(&offset);83 switch (opcode) {84 case llvm::wasm::WASM_OPCODE_I32_CONST:85 case llvm::wasm::WASM_OPCODE_I64_CONST:86 init_expr_offset = data.GetSLEB128(&offset);87 break;88 case llvm::wasm::WASM_OPCODE_GLOBAL_GET:89 init_expr_offset = data.GetULEB128(&offset);90 break;91 case llvm::wasm::WASM_OPCODE_F32_CONST:92 case llvm::wasm::WASM_OPCODE_F64_CONST:93 // Not a meaningful offset.94 data.GetFloat(&offset);95 break;96 case llvm::wasm::WASM_OPCODE_REF_NULL:97 // Not a meaningful offset.98 data.GetULEB128(&offset);99 break;100 }101 102 // Make sure the opcodes we read aren't part of an extended init expr.103 opcode = data.GetU8(&offset);104 if (opcode == llvm::wasm::WASM_OPCODE_END)105 return init_expr_offset;106 107 // Extended init expressions are not supported, but we still have to parse108 // them to skip over them and read the next segment.109 do {110 opcode = data.GetU8(&offset);111 } while (opcode != llvm::wasm::WASM_OPCODE_END);112 return LLDB_INVALID_OFFSET;113}114 115/// Checks whether the data buffer starts with a valid Wasm module header.116static bool ValidateModuleHeader(const DataBufferSP &data_sp) {117 if (!data_sp || data_sp->GetByteSize() < kWasmHeaderSize)118 return false;119 120 if (llvm::identify_magic(toStringRef(data_sp->GetData())) !=121 llvm::file_magic::wasm_object)122 return false;123 124 const uint8_t *Ptr = data_sp->GetBytes() + sizeof(llvm::wasm::WasmMagic);125 126 uint32_t version = llvm::support::endian::read32le(Ptr);127 return version == llvm::wasm::WasmVersion;128}129 130char ObjectFileWasm::ID;131 132void ObjectFileWasm::Initialize() {133 PluginManager::RegisterPlugin(GetPluginNameStatic(),134 GetPluginDescriptionStatic(), CreateInstance,135 CreateMemoryInstance, GetModuleSpecifications);136}137 138void ObjectFileWasm::Terminate() {139 PluginManager::UnregisterPlugin(CreateInstance);140}141 142ObjectFile *143ObjectFileWasm::CreateInstance(const ModuleSP &module_sp, DataBufferSP data_sp,144 offset_t data_offset, const FileSpec *file,145 offset_t file_offset, offset_t length) {146 Log *log = GetLog(LLDBLog::Object);147 148 if (!data_sp) {149 data_sp = MapFileData(*file, length, file_offset);150 if (!data_sp) {151 LLDB_LOGF(log, "Failed to create ObjectFileWasm instance for file %s",152 file->GetPath().c_str());153 return nullptr;154 }155 data_offset = 0;156 }157 158 assert(data_sp);159 if (!ValidateModuleHeader(data_sp)) {160 LLDB_LOGF(log,161 "Failed to create ObjectFileWasm instance: invalid Wasm header");162 return nullptr;163 }164 165 // Update the data to contain the entire file if it doesn't contain it166 // already.167 if (data_sp->GetByteSize() < length) {168 data_sp = MapFileData(*file, length, file_offset);169 if (!data_sp) {170 LLDB_LOGF(log,171 "Failed to create ObjectFileWasm instance: cannot read file %s",172 file->GetPath().c_str());173 return nullptr;174 }175 data_offset = 0;176 }177 178 std::unique_ptr<ObjectFileWasm> objfile_up(new ObjectFileWasm(179 module_sp, data_sp, data_offset, file, file_offset, length));180 ArchSpec spec = objfile_up->GetArchitecture();181 if (spec && objfile_up->SetModulesArchitecture(spec)) {182 LLDB_LOGF(log,183 "%p ObjectFileWasm::CreateInstance() module = %p (%s), file = %s",184 static_cast<void *>(objfile_up.get()),185 static_cast<void *>(objfile_up->GetModule().get()),186 objfile_up->GetModule()->GetSpecificationDescription().c_str(),187 file ? file->GetPath().c_str() : "<NULL>");188 return objfile_up.release();189 }190 191 LLDB_LOGF(log, "Failed to create ObjectFileWasm instance");192 return nullptr;193}194 195ObjectFile *ObjectFileWasm::CreateMemoryInstance(const ModuleSP &module_sp,196 WritableDataBufferSP data_sp,197 const ProcessSP &process_sp,198 addr_t header_addr) {199 if (!ValidateModuleHeader(data_sp))200 return nullptr;201 202 std::unique_ptr<ObjectFileWasm> objfile_up(203 new ObjectFileWasm(module_sp, data_sp, process_sp, header_addr));204 ArchSpec spec = objfile_up->GetArchitecture();205 if (spec && objfile_up->SetModulesArchitecture(spec))206 return objfile_up.release();207 return nullptr;208}209 210bool ObjectFileWasm::DecodeNextSection(lldb::offset_t *offset_ptr) {211 // Buffer sufficient to read a section header and find the pointer to the next212 // section.213 const uint32_t kBufferSize = 1024;214 DataExtractor section_header_data = ReadImageData(*offset_ptr, kBufferSize);215 216 llvm::DataExtractor data = section_header_data.GetAsLLVM();217 llvm::DataExtractor::Cursor c(0);218 219 // Each section consists of:220 // - a one-byte section id,221 // - the u32 size of the contents, in bytes,222 // - the actual contents.223 uint8_t section_id = data.getU8(c);224 uint64_t payload_len = data.getULEB128(c);225 if (!c)226 return !llvm::errorToBool(c.takeError());227 228 if (payload_len > std::numeric_limits<uint32_t>::max())229 return false;230 231 if (section_id == llvm::wasm::WASM_SEC_CUSTOM) {232 // Custom sections have the id 0. Their contents consist of a name233 // identifying the custom section, followed by an uninterpreted sequence234 // of bytes.235 lldb::offset_t prev_offset = c.tell();236 llvm::Expected<std::string> sect_name = GetWasmString(data, c);237 if (!sect_name) {238 LLDB_LOG_ERROR(GetLog(LLDBLog::Object), sect_name.takeError(),239 "failed to parse section name: {0}");240 return false;241 }242 243 if (payload_len < c.tell() - prev_offset)244 return false;245 246 uint32_t section_length = payload_len - (c.tell() - prev_offset);247 m_sect_infos.push_back(section_info{*offset_ptr + c.tell(), section_length,248 section_id, ConstString(*sect_name)});249 *offset_ptr += (c.tell() + section_length);250 } else if (section_id <= llvm::wasm::WASM_SEC_LAST_KNOWN) {251 m_sect_infos.push_back(section_info{*offset_ptr + c.tell(),252 static_cast<uint32_t>(payload_len),253 section_id, ConstString()});254 *offset_ptr += (c.tell() + payload_len);255 } else {256 // Invalid section id.257 return false;258 }259 return true;260}261 262bool ObjectFileWasm::DecodeSections() {263 lldb::offset_t offset = kWasmHeaderSize;264 if (IsInMemory()) {265 offset += m_memory_addr;266 }267 268 while (DecodeNextSection(&offset))269 ;270 return true;271}272 273size_t ObjectFileWasm::GetModuleSpecifications(274 const FileSpec &file, DataBufferSP &data_sp, offset_t data_offset,275 offset_t file_offset, offset_t length, ModuleSpecList &specs) {276 if (!ValidateModuleHeader(data_sp)) {277 return 0;278 }279 280 ModuleSpec spec(file, ArchSpec("wasm32-unknown-unknown-wasm"));281 specs.Append(spec);282 return 1;283}284 285ObjectFileWasm::ObjectFileWasm(const ModuleSP &module_sp, DataBufferSP data_sp,286 offset_t data_offset, const FileSpec *file,287 offset_t offset, offset_t length)288 : ObjectFile(module_sp, file, offset, length, data_sp, data_offset),289 m_arch("wasm32-unknown-unknown-wasm") {290 m_data.SetAddressByteSize(4);291}292 293ObjectFileWasm::ObjectFileWasm(const lldb::ModuleSP &module_sp,294 lldb::WritableDataBufferSP header_data_sp,295 const lldb::ProcessSP &process_sp,296 lldb::addr_t header_addr)297 : ObjectFile(module_sp, process_sp, header_addr, header_data_sp),298 m_arch("wasm32-unknown-unknown-wasm") {}299 300bool ObjectFileWasm::ParseHeader() {301 // We already parsed the header during initialization.302 return true;303}304 305struct WasmFunction {306 lldb::offset_t section_offset = LLDB_INVALID_OFFSET;307 uint32_t size = 0;308};309 310static llvm::Expected<std::vector<WasmFunction>>311ParseFunctions(DataExtractor &data) {312 lldb::offset_t offset = 0;313 314 llvm::Expected<uint32_t> function_count = GetULEB32(data, offset);315 if (!function_count)316 return function_count.takeError();317 318 std::vector<WasmFunction> functions;319 functions.reserve(*function_count);320 321 for (uint32_t i = 0; i < *function_count; ++i) {322 llvm::Expected<uint32_t> function_size = GetULEB32(data, offset);323 if (!function_size)324 return function_size.takeError();325 // llvm-objdump considers the ULEB with the function size to be part of the326 // function. We can't do that here because that would break symbolic327 // breakpoints, as that address is never executed.328 functions.push_back({offset, *function_size});329 330 std::optional<lldb::offset_t> next_offset =331 llvm::checkedAddUnsigned<lldb::offset_t>(offset, *function_size);332 if (!next_offset)333 return llvm::createStringError("function offset overflows 64 bits");334 offset = *next_offset;335 }336 337 return functions;338}339 340struct WasmSegment {341 enum SegmentType {342 Active,343 Passive,344 };345 346 std::string name;347 SegmentType type = Passive;348 lldb::offset_t section_offset = LLDB_INVALID_OFFSET;349 uint32_t size = 0;350 uint32_t memory_index = 0;351 lldb::offset_t init_expr_offset = 0;352 353 lldb::offset_t GetFileOffset() const { return section_offset & 0xffffffff; }354};355 356static llvm::Expected<std::vector<WasmSegment>> ParseData(DataExtractor &data) {357 lldb::offset_t offset = 0;358 359 llvm::Expected<uint32_t> segment_count = GetULEB32(data, offset);360 if (!segment_count)361 return segment_count.takeError();362 363 std::vector<WasmSegment> segments;364 segments.reserve(*segment_count);365 366 for (uint32_t i = 0; i < *segment_count; ++i) {367 llvm::Expected<uint32_t> flags = GetULEB32(data, offset);368 if (!flags)369 return flags.takeError();370 371 WasmSegment segment;372 373 // Data segments have a mode that identifies them as either passive or374 // active. An active data segment copies its contents into a memory during375 // instantiation, as specified by a memory index and a constant expression376 // defining an offset into that memory.377 segment.type = (*flags & llvm::wasm::WASM_DATA_SEGMENT_IS_PASSIVE)378 ? WasmSegment::Passive379 : WasmSegment::Active;380 381 if (*flags & llvm::wasm::WASM_DATA_SEGMENT_HAS_MEMINDEX) {382 assert(segment.type == WasmSegment::Active);383 llvm::Expected<uint32_t> memidx = GetULEB32(data, offset);384 if (!memidx)385 return memidx.takeError();386 segment.memory_index = *memidx;387 }388 389 if (segment.type == WasmSegment::Active)390 segment.init_expr_offset = GetWasmOffsetFromInitExpr(data, offset);391 392 llvm::Expected<uint32_t> segment_size = GetULEB32(data, offset);393 if (!segment_size)394 return segment_size.takeError();395 396 segment.section_offset = offset;397 segment.size = *segment_size;398 segments.push_back(segment);399 400 std::optional<lldb::offset_t> next_offset =401 llvm::checkedAddUnsigned<lldb::offset_t>(offset, *segment_size);402 if (!next_offset)403 return llvm::createStringError("segment offset overflows 64 bits");404 offset = *next_offset;405 }406 407 return segments;408}409 410static llvm::Expected<std::vector<Symbol>>411ParseNames(SectionSP code_section_sp, DataExtractor &name_data,412 const std::vector<WasmFunction> &functions,413 std::vector<WasmSegment> &segments) {414 415 llvm::DataExtractor data = name_data.GetAsLLVM();416 llvm::DataExtractor::Cursor c(0);417 std::vector<Symbol> symbols;418 while (c && c.tell() < data.size()) {419 const uint8_t type = data.getU8(c);420 llvm::Expected<uint32_t> size = GetULEB32(data, c);421 if (!size)422 return size.takeError();423 424 switch (type) {425 case llvm::wasm::WASM_NAMES_FUNCTION: {426 const uint64_t count = data.getULEB128(c);427 if (count > std::numeric_limits<uint32_t>::max())428 return llvm::createStringError("function count overflows uint32_t");429 430 for (uint64_t i = 0; c && i < count; ++i) {431 llvm::Expected<uint32_t> idx = GetULEB32(data, c);432 if (!idx)433 return idx.takeError();434 llvm::Expected<std::string> name = GetWasmString(data, c);435 if (!name)436 return name.takeError();437 if (*idx >= functions.size())438 continue;439 symbols.emplace_back(440 symbols.size(), *name, lldb::eSymbolTypeCode,441 /*external=*/false, /*is_debug=*/false, /*is_trampoline=*/false,442 /*is_artificial=*/false, code_section_sp,443 functions[i].section_offset, functions[i].size,444 /*size_is_valid=*/true, /*contains_linker_annotations=*/false,445 /*flags=*/0);446 }447 } break;448 case llvm::wasm::WASM_NAMES_DATA_SEGMENT: {449 llvm::Expected<uint32_t> count = GetULEB32(data, c);450 if (!count)451 return count.takeError();452 for (uint32_t i = 0; c && i < *count; ++i) {453 llvm::Expected<uint32_t> idx = GetULEB32(data, c);454 if (!idx)455 return idx.takeError();456 llvm::Expected<std::string> name = GetWasmString(data, c);457 if (!name)458 return name.takeError();459 if (*idx >= segments.size())460 continue;461 // Update the segment name.462 segments[i].name = *name;463 }464 465 } break;466 case llvm::wasm::WASM_NAMES_GLOBAL:467 case llvm::wasm::WASM_NAMES_LOCAL:468 default:469 std::optional<lldb::offset_t> offset =470 llvm::checkedAddUnsigned<lldb::offset_t>(c.tell(), *size);471 if (!offset)472 return llvm::createStringError("offset overflows 64 bits");473 c.seek(*offset);474 }475 }476 477 if (!c)478 return c.takeError();479 480 return symbols;481}482 483void ObjectFileWasm::ParseSymtab(Symtab &symtab) {484 for (const Symbol &symbol : m_symbols)485 symtab.AddSymbol(symbol);486 487 symtab.Finalize();488 m_symbols.clear();489}490 491static SectionType GetSectionTypeFromName(llvm::StringRef Name) {492 if (Name == "name")493 return lldb::eSectionTypeWasmName;494 if (Name.consume_front(".debug_") || Name.consume_front(".zdebug_"))495 return ObjectFile::GetDWARFSectionTypeFromName(Name);496 return eSectionTypeOther;497}498 499std::optional<ObjectFileWasm::section_info>500ObjectFileWasm::GetSectionInfo(uint32_t section_id) {501 for (const section_info §_info : m_sect_infos) {502 if (sect_info.id == section_id)503 return sect_info;504 }505 return std::nullopt;506}507 508std::optional<ObjectFileWasm::section_info>509ObjectFileWasm::GetSectionInfo(llvm::StringRef section_name) {510 for (const section_info §_info : m_sect_infos) {511 if (sect_info.name == section_name)512 return sect_info;513 }514 return std::nullopt;515}516 517void ObjectFileWasm::CreateSections(SectionList &unified_section_list) {518 Log *log = GetLog(LLDBLog::Object);519 520 if (m_sections_up)521 return;522 523 m_sections_up = std::make_unique<SectionList>();524 525 if (m_sect_infos.empty()) {526 DecodeSections();527 }528 529 for (const section_info §_info : m_sect_infos) {530 SectionType section_type = eSectionTypeOther;531 ConstString section_name;532 offset_t file_offset = sect_info.offset & 0xffffffff;533 addr_t vm_addr = sect_info.offset;534 size_t vm_size = sect_info.size;535 536 if (llvm::wasm::WASM_SEC_CODE == sect_info.id) {537 section_type = eSectionTypeCode;538 section_name = ConstString("code");539 540 // A code address in DWARF for WebAssembly is the offset of an541 // instruction relative within the Code section of the WebAssembly file.542 // For this reason Section::GetFileAddress() must return zero for the543 // Code section.544 vm_addr = 0;545 } else {546 section_type = GetSectionTypeFromName(sect_info.name.GetStringRef());547 if (section_type == eSectionTypeOther)548 continue;549 section_name = sect_info.name;550 if (!IsInMemory()) {551 vm_size = 0;552 vm_addr = 0;553 }554 }555 556 SectionSP section_sp = std::make_shared<Section>(557 GetModule(), // Module to which this section belongs.558 this, // ObjectFile to which this section belongs and559 // should read section data from.560 section_type, // Section ID.561 section_name, // Section name.562 section_type, // Section type.563 vm_addr, // VM address.564 vm_size, // VM size in bytes of this section.565 file_offset, // Offset of this section in the file.566 sect_info.size, // Size of the section as found in the file.567 0, // Alignment of the section568 0, // Flags for this section.569 1); // Number of host bytes per target byte570 m_sections_up->AddSection(section_sp);571 unified_section_list.AddSection(section_sp);572 }573 574 // The name section contains names and indexes. First parse the data from the575 // relevant sections so we can access it by its index.576 std::vector<WasmFunction> functions;577 std::vector<WasmSegment> segments;578 579 // Parse the code section.580 if (std::optional<section_info> info =581 GetSectionInfo(llvm::wasm::WASM_SEC_CODE)) {582 DataExtractor code_data = ReadImageData(info->offset, info->size);583 llvm::Expected<std::vector<WasmFunction>> maybe_functions =584 ParseFunctions(code_data);585 if (!maybe_functions) {586 LLDB_LOG_ERROR(log, maybe_functions.takeError(),587 "Failed to parse Wasm code section: {0}");588 } else {589 functions = *maybe_functions;590 }591 }592 593 // Parse the data section.594 std::optional<section_info> data_info =595 GetSectionInfo(llvm::wasm::WASM_SEC_DATA);596 if (data_info) {597 DataExtractor data_data = ReadImageData(data_info->offset, data_info->size);598 llvm::Expected<std::vector<WasmSegment>> maybe_segments =599 ParseData(data_data);600 if (!maybe_segments) {601 LLDB_LOG_ERROR(log, maybe_segments.takeError(),602 "Failed to parse Wasm data section: {0}");603 } else {604 segments = *maybe_segments;605 }606 }607 608 if (std::optional<section_info> info = GetSectionInfo("name")) {609 DataExtractor names_data = ReadImageData(info->offset, info->size);610 llvm::Expected<std::vector<Symbol>> symbols = ParseNames(611 m_sections_up->FindSectionByType(lldb::eSectionTypeCode, false),612 names_data, functions, segments);613 if (!symbols) {614 LLDB_LOG_ERROR(log, symbols.takeError(),615 "Failed to parse Wasm names: {0}");616 } else {617 m_symbols = *symbols;618 }619 }620 621 lldb::user_id_t segment_id = 0;622 for (const WasmSegment &segment : segments) {623 if (segment.type == WasmSegment::Active) {624 // FIXME: Support segments with a memory index.625 if (segment.memory_index != 0) {626 LLDB_LOG(log, "Skipping segment {0}: non-zero memory index is "627 "currently unsupported");628 continue;629 }630 631 if (segment.init_expr_offset == LLDB_INVALID_OFFSET) {632 LLDB_LOG(log, "Skipping segment {0}: unsupported init expression");633 continue;634 }635 }636 637 const lldb::addr_t file_vm_addr =638 segment.type == WasmSegment::Active639 ? segment.init_expr_offset640 : data_info->offset + segment.section_offset;641 const lldb::offset_t file_offset =642 data_info->GetFileOffset() + segment.GetFileOffset();643 SectionSP segment_sp = std::make_shared<Section>(644 GetModule(),645 /*obj_file=*/this,646 ++segment_id << 8, // 1-based segment index, shifted by 8 bits to avoid647 // collision with section IDs.648 ConstString(segment.name), eSectionTypeData,649 /*file_vm_addr=*/file_vm_addr,650 /*vm_size=*/segment.size,651 /*file_offset=*/file_offset,652 /*file_size=*/segment.size,653 /*log2align=*/0, /*flags=*/0);654 m_sections_up->AddSection(segment_sp);655 GetModule()->GetSectionList()->AddSection(segment_sp);656 }657}658 659bool ObjectFileWasm::SetLoadAddress(Target &target, lldb::addr_t load_address,660 bool value_is_offset) {661 /// In WebAssembly, linear memory is disjointed from code space. The VM can662 /// load multiple instances of a module, which logically share the same code.663 /// We represent a wasm32 code address with 64-bits, like:664 /// 63 32 31 0665 /// +---------------+---------------+666 /// + module_id | offset |667 /// +---------------+---------------+668 /// where the lower 32 bits represent a module offset (relative to the module669 /// start not to the beginning of the code section) and the higher 32 bits670 /// uniquely identify the module in the WebAssembly VM.671 /// In other words, we assume that each WebAssembly module is loaded by the672 /// engine at a 64-bit address that starts at the boundary of 4GB pages, like673 /// 0x0000000400000000 for module_id == 4.674 /// These 64-bit addresses will be used to request code ranges for a specific675 /// module from the WebAssembly engine.676 677 assert(m_memory_addr == LLDB_INVALID_ADDRESS ||678 m_memory_addr == load_address);679 680 ModuleSP module_sp = GetModule();681 if (!module_sp)682 return false;683 684 DecodeSections();685 686 size_t num_loaded_sections = 0;687 SectionList *section_list = GetSectionList();688 if (!section_list)689 return false;690 691 const size_t num_sections = section_list->GetSize();692 for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) {693 SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx));694 if (target.SetSectionLoadAddress(695 section_sp, load_address | section_sp->GetFileOffset())) {696 ++num_loaded_sections;697 }698 }699 700 return num_loaded_sections > 0;701}702 703DataExtractor ObjectFileWasm::ReadImageData(offset_t offset, uint32_t size) {704 DataExtractor data;705 if (m_file) {706 if (offset < GetByteSize()) {707 size = std::min(static_cast<uint64_t>(size), GetByteSize() - offset);708 auto buffer_sp = MapFileData(m_file, size, offset);709 return DataExtractor(buffer_sp, GetByteOrder(), GetAddressByteSize());710 }711 } else {712 ProcessSP process_sp(m_process_wp.lock());713 if (process_sp) {714 auto data_up = std::make_unique<DataBufferHeap>(size, 0);715 Status readmem_error;716 size_t bytes_read = process_sp->ReadMemory(717 offset, data_up->GetBytes(), data_up->GetByteSize(), readmem_error);718 if (bytes_read > 0) {719 DataBufferSP buffer_sp(data_up.release());720 data.SetData(buffer_sp, 0, buffer_sp->GetByteSize());721 }722 } else if (offset < m_data.GetByteSize()) {723 size =724 std::min(static_cast<uint64_t>(size), m_data.GetByteSize() - offset);725 return DataExtractor(m_data.GetDataStart() + offset, size, GetByteOrder(),726 GetAddressByteSize());727 }728 }729 data.SetByteOrder(GetByteOrder());730 return data;731}732 733std::optional<FileSpec> ObjectFileWasm::GetExternalDebugInfoFileSpec() {734 static ConstString g_sect_name_external_debug_info("external_debug_info");735 736 for (const section_info §_info : m_sect_infos) {737 if (g_sect_name_external_debug_info == sect_info.name) {738 const uint32_t kBufferSize = 1024;739 DataExtractor section_header_data =740 ReadImageData(sect_info.offset, kBufferSize);741 742 llvm::DataExtractor data = section_header_data.GetAsLLVM();743 llvm::DataExtractor::Cursor c(0);744 llvm::Expected<std::string> symbols_url = GetWasmString(data, c);745 if (!symbols_url) {746 llvm::consumeError(symbols_url.takeError());747 return std::nullopt;748 }749 return FileSpec(*symbols_url);750 }751 }752 return std::nullopt;753}754 755void ObjectFileWasm::Dump(Stream *s) {756 ModuleSP module_sp(GetModule());757 if (!module_sp)758 return;759 760 std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());761 762 llvm::raw_ostream &ostream = s->AsRawOstream();763 ostream << static_cast<void *>(this) << ": ";764 s->Indent();765 ostream << "ObjectFileWasm, file = '";766 m_file.Dump(ostream);767 ostream << "', arch = ";768 ostream << GetArchitecture().GetArchitectureName() << "\n";769 770 SectionList *sections = GetSectionList();771 if (sections) {772 sections->Dump(s->AsRawOstream(), s->GetIndentLevel(), nullptr, true,773 UINT32_MAX);774 }775 ostream << "\n";776 DumpSectionHeaders(ostream);777 ostream << "\n";778}779 780void ObjectFileWasm::DumpSectionHeader(llvm::raw_ostream &ostream,781 const section_info &sh) {782 ostream << llvm::left_justify(sh.name.GetStringRef(), 16) << " "783 << llvm::format_hex(sh.offset, 10) << " "784 << llvm::format_hex(sh.size, 10) << " " << llvm::format_hex(sh.id, 6)785 << "\n";786}787 788void ObjectFileWasm::DumpSectionHeaders(llvm::raw_ostream &ostream) {789 ostream << "Section Headers\n";790 ostream << "IDX name addr size id\n";791 ostream << "==== ---------------- ---------- ---------- ------\n";792 793 uint32_t idx = 0;794 for (auto pos = m_sect_infos.begin(); pos != m_sect_infos.end();795 ++pos, ++idx) {796 ostream << "[" << llvm::format_decimal(idx, 2) << "] ";797 ObjectFileWasm::DumpSectionHeader(ostream, *pos);798 }799}800