brintos

brintos / llvm-project-archived public Read only

0
0
Text · 27.3 KiB · 492b441 Raw
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 &sect_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 &sect_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 &sect_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 &sect_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