LLDB mainline
ObjectFileWasm.cpp
Go to the documentation of this file.
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-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "ObjectFileWasm.h"
10#include "lldb/Core/Module.h"
13#include "lldb/Core/Section.h"
14#include "lldb/Target/Process.h"
16#include "lldb/Target/Target.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
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 initial
75/// value of certain elements within a module during instantiation. These
76/// expressions are restricted to operations that can be evaluated at module
77/// instantiation time. Currently we only support simple constant opcodes.
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 parse
108 // 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
131
137
141
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 it
166 // 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
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
211 // Buffer sufficient to read a section header and find the pointer to the next
212 // 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 name
233 // identifying the custom section, followed by an uninterpreted sequence
234 // 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
264 if (IsInMemory()) {
265 offset += m_memory_addr;
266 }
267
268 while (DecodeNextSection(&offset))
269 ;
270 return true;
271}
272
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
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
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
301 // We already parsed the header during initialization.
302 return true;
303}
304
309
310static llvm::Expected<std::vector<WasmFunction>>
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 the
326 // function. We can't do that here because that would break symbolic
327 // 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
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 or
374 // active. An active data segment copies its contents into a memory during
375 // instantiation, as specified by a memory index and a constant expression
376 // defining an offset into that memory.
377 segment.type = (*flags & llvm::wasm::WASM_DATA_SEGMENT_IS_PASSIVE)
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
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")
494 if (Name.consume_front(".debug_") || Name.consume_front(".zdebug_"))
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()) {
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 an
541 // instruction relative within the Code section of the WebAssembly file.
542 // For this reason Section::GetFileAddress() must return zero for the
543 // 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 and
559 // 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 section
568 0, // Flags for this section.
569 1); // Number of host bytes per target byte
570 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 the
575 // 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::Active
639 ? segment.init_expr_offset
640 : 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 avoid
647 // 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
660 bool value_is_offset) {
661 /// In WebAssembly, linear memory is disjointed from code space. The VM can
662 /// 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 0
665 /// +---------------+---------------+
666 /// + module_id | offset |
667 /// +---------------+---------------+
668 /// where the lower 32 bits represent a module offset (relative to the module
669 /// start not to the beginning of the code section) and the higher 32 bits
670 /// uniquely identify the module in the WebAssembly VM.
671 /// In other words, we assume that each WebAssembly module is loaded by the
672 /// engine at a 64-bit address that starts at the boundary of 4GB pages, like
673 /// 0x0000000400000000 for module_id == 4.
674 /// These 64-bit addresses will be used to request code ranges for a specific
675 /// module from the WebAssembly engine.
676
678 m_memory_addr == load_address);
679
680 ModuleSP module_sp = GetModule();
681 if (!module_sp)
682 return false;
683
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
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(),
727 }
728 }
730 return data;
731}
732
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
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) << "] ";
798 }
799}
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
Definition Log.h:369
#define LLDB_LOGF(log,...)
Definition Log.h:376
#define LLDB_LOG_ERROR(log, error,...)
Definition Log.h:392
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< std::vector< WasmFunction > > ParseFunctions(DataExtractor &data)
static const uint32_t kWasmHeaderSize
static llvm::Expected< std::vector< Symbol > > ParseNames(SectionSP code_section_sp, DataExtractor &name_data, const std::vector< WasmFunction > &functions, std::vector< WasmSegment > &segments)
static llvm::Expected< std::vector< WasmSegment > > ParseData(DataExtractor &data)
#define LLDB_PLUGIN_DEFINE(PluginName)
An architecture specification class.
Definition ArchSpec.h:31
const char * GetArchitectureName() const
Returns a static string representing the current architecture.
Definition ArchSpec.cpp:548
A uniqued constant string class.
Definition ConstString.h:40
llvm::StringRef GetStringRef() const
Get the string value as a llvm::StringRef.
An data extractor class.
uint64_t GetULEB128(lldb::offset_t *offset_ptr) const
Extract a unsigned LEB128 value from *offset_ptr.
float GetFloat(lldb::offset_t *offset_ptr) const
Extract a float from *offset_ptr.
llvm::DataExtractor GetAsLLVM() const
void SetByteOrder(lldb::ByteOrder byte_order)
Set the byte_order value.
lldb::offset_t SetData(const void *bytes, lldb::offset_t length, lldb::ByteOrder byte_order)
Set data with a buffer that is caller owned.
int64_t GetSLEB128(lldb::offset_t *offset_ptr) const
Extract a signed LEB128 value from *offset_ptr.
uint8_t GetU8(lldb::offset_t *offset_ptr) const
Extract a uint8_t value from *offset_ptr.
A file utility class.
Definition FileSpec.h:57
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition FileSpec.cpp:374
lldb::ModuleSP GetModule() const
Get const accessor for the module pointer.
void Append(const ModuleSpec &spec)
Definition ModuleSpec.h:308
A plug-in interface definition class for object file parsers.
Definition ObjectFile.h:45
DataExtractor m_data
The data for this object file so things can be parsed lazily.
Definition ObjectFile.h:784
std::unique_ptr< lldb_private::SectionList > m_sections_up
Definition ObjectFile.h:788
static lldb::DataBufferSP MapFileData(const FileSpec &file, uint64_t Size, uint64_t Offset)
ObjectFile(const lldb::ModuleSP &module_sp, const FileSpec *file_spec_ptr, lldb::offset_t file_offset, lldb::offset_t length, lldb::DataBufferSP data_sp, lldb::offset_t data_offset)
Construct with a parent module, offset, and header data.
const lldb::addr_t m_memory_addr
Set if the object file only exists in memory.
Definition ObjectFile.h:787
static lldb::SectionType GetDWARFSectionTypeFromName(llvm::StringRef name)
Parses the section type from a section name for DWARF sections.
virtual SectionList * GetSectionList(bool update_module_section_list=true)
Gets the section list for the currently selected architecture (and object for archives).
bool IsInMemory() const
Returns true if the object file exists only in memory.
Definition ObjectFile.h:709
lldb::ProcessWP m_process_wp
Definition ObjectFile.h:785
virtual lldb::addr_t GetByteSize() const
Definition ObjectFile.h:274
static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description, ABICreateInstance create_callback)
static bool UnregisterPlugin(ABICreateInstance create_callback)
size_t GetSize() const
Definition Section.h:75
size_t AddSection(const lldb::SectionSP &section_sp)
Definition Section.cpp:482
void Dump(llvm::raw_ostream &s, unsigned indent, Target *target, bool show_header, uint32_t depth) const
Definition Section.cpp:644
lldb::SectionSP GetSectionAtIndex(size_t idx) const
Definition Section.cpp:551
An error handling class.
Definition Status.h:118
A stream class that can stream formatted output to a file.
Definition Stream.h:28
llvm::raw_ostream & AsRawOstream()
Returns a raw_ostream that forwards the data to this Stream object.
Definition Stream.h:400
size_t Indent(llvm::StringRef s="")
Indent the current line in the stream.
Definition Stream.cpp:157
unsigned GetIndentLevel() const
Get the current indentation level.
Definition Stream.cpp:187
uint32_t AddSymbol(const Symbol &symbol)
Definition Symtab.cpp:64
bool SetSectionLoadAddress(const lldb::SectionSP &section, lldb::addr_t load_addr, bool warn_multiple=false)
Definition Target.cpp:3296
Generic Wasm object file reader.
ObjectFileWasm(const lldb::ModuleSP &module_sp, lldb::DataBufferSP data_sp, lldb::offset_t data_offset, const FileSpec *file, lldb::offset_t offset, lldb::offset_t length)
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
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::DataBufferSP data_sp, lldb::offset_t data_offset, const FileSpec *file, lldb::offset_t file_offset, lldb::offset_t length)
static char ID
LLVM RTTI support.
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.
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
#define UINT32_MAX
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.
Definition Log.h:332
uint64_t offset_t
Definition lldb-types.h:85
std::shared_ptr< lldb_private::Process > ProcessSP
uint64_t user_id_t
Definition lldb-types.h:82
std::shared_ptr< lldb_private::DataBuffer > DataBufferSP
std::shared_ptr< lldb_private::Section > SectionSP
std::shared_ptr< lldb_private::WritableDataBuffer > WritableDataBufferSP
uint64_t addr_t
Definition lldb-types.h:80
@ eSectionTypeWasmName
std::shared_ptr< lldb_private::Module > ModuleSP
lldb::offset_t section_offset
SegmentType type
std::string name
uint32_t memory_index
lldb::offset_t section_offset
lldb::offset_t GetFileOffset() const
lldb::offset_t init_expr_offset