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/DenseSet.h"
22#include "llvm/ADT/SmallVector.h"
23#include "llvm/ADT/StringRef.h"
24#include "llvm/ADT/StringSwitch.h"
25#include "llvm/BinaryFormat/Magic.h"
26#include "llvm/BinaryFormat/Wasm.h"
27#include "llvm/Support/CheckedArithmetic.h"
28#include "llvm/Support/Endian.h"
29#include "llvm/Support/Format.h"
30#include <cstring>
31#include <optional>
32
33using namespace lldb;
34using namespace lldb_private;
35using namespace lldb_private::wasm;
36
38
39static const uint32_t kWasmHeaderSize =
40 sizeof(llvm::wasm::WasmMagic) + sizeof(llvm::wasm::WasmVersion);
41
42/// File address the synthetic global section is based at. Code and linear
43/// memory are both addressed from zero, so the globals need a range of their
44/// own to keep a global index from naming a code or data address as well.
47
48/// Helper to read a 32-bit ULEB using LLDB's DataExtractor.
49static inline llvm::Expected<uint32_t> GetULEB32(DataExtractor &data,
50 lldb::offset_t &offset) {
51 const uint64_t value = data.GetULEB128(&offset);
52 if (value > std::numeric_limits<uint32_t>::max())
53 return llvm::createStringError("ULEB exceeds 32 bits");
54 return value;
55}
56
57/// Helper to read a 32-bit ULEB using LLVM's DataExtractor.
58static inline llvm::Expected<uint32_t>
59GetULEB32(llvm::DataExtractor &data, llvm::DataExtractor::Cursor &c) {
60 const uint64_t value = data.getULEB128(c);
61 if (!c)
62 return c.takeError();
63 if (value > std::numeric_limits<uint32_t>::max())
64 return llvm::createStringError("ULEB exceeds 32 bits");
65 return value;
66}
67
68/// Helper to read a Wasm string, whcih is encoded as a vector of UTF-8 codes.
69static inline llvm::Expected<std::string>
70GetWasmString(llvm::DataExtractor &data, llvm::DataExtractor::Cursor &c) {
71 llvm::Expected<uint32_t> len = GetULEB32(data, c);
72 if (!len)
73 return len.takeError();
74
75 llvm::SmallVector<uint8_t, 32> str_storage;
76 data.getU8(c, str_storage, *len);
77 if (!c)
78 return c.takeError();
79
80 return std::string(toStringRef(llvm::ArrayRef(str_storage)));
81}
82
83/// An "init expr" refers to a constant expression used to determine the initial
84/// value of certain elements within a module during instantiation. These
85/// expressions are restricted to operations that can be evaluated at module
86/// instantiation time. Currently we only support simple constant opcodes.
88 lldb::offset_t &offset) {
89 lldb::offset_t init_expr_offset = LLDB_INVALID_OFFSET;
90
91 uint8_t opcode = data.GetU8(&offset);
92 switch (opcode) {
93 case llvm::wasm::WASM_OPCODE_I32_CONST:
94 case llvm::wasm::WASM_OPCODE_I64_CONST:
95 init_expr_offset = data.GetSLEB128(&offset);
96 break;
97 case llvm::wasm::WASM_OPCODE_GLOBAL_GET:
98 init_expr_offset = data.GetULEB128(&offset);
99 break;
100 case llvm::wasm::WASM_OPCODE_F32_CONST:
101 case llvm::wasm::WASM_OPCODE_F64_CONST:
102 // Not a meaningful offset.
103 data.GetFloat(&offset);
104 break;
105 case llvm::wasm::WASM_OPCODE_REF_NULL:
106 // Not a meaningful offset.
107 data.GetULEB128(&offset);
108 break;
109 }
110
111 // Make sure the opcodes we read aren't part of an extended init expr.
112 opcode = data.GetU8(&offset);
113 if (opcode == llvm::wasm::WASM_OPCODE_END)
114 return init_expr_offset;
115
116 // Extended init expressions are not supported, but we still have to parse
117 // them to skip over them and read the next segment. A truncated expression
118 // never reaches the end opcode, so the scan is bounded by the data.
119 while (opcode != llvm::wasm::WASM_OPCODE_END && data.ValidOffset(offset))
120 opcode = data.GetU8(&offset);
121 return LLDB_INVALID_OFFSET;
122}
123
124/// Checks whether the data buffer starts with a valid Wasm module header.
125static bool ValidateModuleHeader(llvm::ArrayRef<uint8_t> data) {
126 if (data.size() < kWasmHeaderSize)
127 return false;
128
129 if (llvm::identify_magic(toStringRef(data)) != llvm::file_magic::wasm_object)
130 return false;
131
132 const uint8_t *Ptr = data.data() + sizeof(llvm::wasm::WasmMagic);
133
134 uint32_t version = llvm::support::endian::read32le(Ptr);
135 return version == llvm::wasm::WasmVersion;
136}
137
139
145
149
151 DataExtractorSP extractor_sp,
152 offset_t data_offset,
153 const FileSpec *file,
154 offset_t file_offset,
155 offset_t length) {
156 Log *log = GetLog(LLDBLog::Object);
157
158 if (!extractor_sp || !extractor_sp->HasData()) {
159 DataBufferSP data_sp = MapFileData(*file, length, file_offset);
160 if (!data_sp) {
161 LLDB_LOGF(log, "Failed to create ObjectFileWasm instance for file %s",
162 file->GetPath().c_str());
163 return nullptr;
164 }
165 extractor_sp = std::make_shared<DataExtractor>(data_sp);
166 data_offset = 0;
167 }
168
169 assert(extractor_sp);
170 if (!ValidateModuleHeader(extractor_sp->GetData())) {
171 LLDB_LOGF(log,
172 "Failed to create ObjectFileWasm instance: invalid Wasm header");
173 return nullptr;
174 }
175
176 // Update the data to contain the entire file if it doesn't contain it
177 // already.
178 if (extractor_sp->GetByteSize() < length) {
179 DataBufferSP data_sp = MapFileData(*file, length, file_offset);
180 if (!data_sp) {
181 LLDB_LOGF(log,
182 "Failed to create ObjectFileWasm instance: cannot read file %s",
183 file->GetPath().c_str());
184 return nullptr;
185 }
186 extractor_sp = std::make_shared<DataExtractor>(data_sp);
187 data_offset = 0;
188 }
189
190 std::unique_ptr<ObjectFileWasm> objfile_up(new ObjectFileWasm(
191 module_sp, extractor_sp, data_offset, file, file_offset, length));
192 ArchSpec spec = objfile_up->GetArchitecture();
193 if (spec && objfile_up->SetModulesArchitecture(spec)) {
194 LLDB_LOGF(log,
195 "%p ObjectFileWasm::CreateInstance() module = %p (%s), file = %s",
196 static_cast<void *>(objfile_up.get()),
197 static_cast<void *>(objfile_up->GetModule().get()),
198 objfile_up->GetModule()->GetSpecificationDescription().c_str(),
199 file ? file->GetPath().c_str() : "<NULL>");
200 return objfile_up.release();
201 }
202
203 LLDB_LOGF(log, "Failed to create ObjectFileWasm instance");
204 return nullptr;
205}
206
208 WritableDataBufferSP data_sp,
209 const ProcessSP &process_sp,
210 addr_t header_addr) {
211 if (!ValidateModuleHeader(data_sp->GetData()))
212 return nullptr;
213
214 std::unique_ptr<ObjectFileWasm> objfile_up(
215 new ObjectFileWasm(module_sp, data_sp, process_sp, header_addr));
216 ArchSpec spec = objfile_up->GetArchitecture();
217 if (spec && objfile_up->SetModulesArchitecture(spec))
218 return objfile_up.release();
219 return nullptr;
220}
221
223 // Buffer sufficient to read a section header and find the pointer to the next
224 // section.
225 const uint32_t kBufferSize = 1024;
226 DataExtractor section_header_data = ReadImageData(*offset_ptr, kBufferSize);
227
228 llvm::DataExtractor data = section_header_data.GetAsLLVM();
229 llvm::DataExtractor::Cursor c(0);
230
231 // Each section consists of:
232 // - a one-byte section id,
233 // - the u32 size of the contents, in bytes,
234 // - the actual contents.
235 uint8_t section_id = data.getU8(c);
236 uint64_t payload_len = data.getULEB128(c);
237 if (!c)
238 return !llvm::errorToBool(c.takeError());
239
240 if (payload_len > std::numeric_limits<uint32_t>::max())
241 return false;
242
243 if (section_id == llvm::wasm::WASM_SEC_CUSTOM) {
244 // Custom sections have the id 0. Their contents consist of a name
245 // identifying the custom section, followed by an uninterpreted sequence
246 // of bytes.
247 lldb::offset_t prev_offset = c.tell();
248 llvm::Expected<std::string> sect_name = GetWasmString(data, c);
249 if (!sect_name) {
250 LLDB_LOG_ERROR(GetLog(LLDBLog::Object), sect_name.takeError(),
251 "failed to parse section name: {0}");
252 return false;
253 }
254
255 if (payload_len < c.tell() - prev_offset)
256 return false;
257
258 uint32_t section_length = payload_len - (c.tell() - prev_offset);
259 m_sect_infos.push_back(section_info{*offset_ptr + c.tell(), section_length,
260 section_id, std::move(*sect_name)});
261 *offset_ptr += (c.tell() + section_length);
262 } else if (section_id <= llvm::wasm::WASM_SEC_LAST_KNOWN) {
263 m_sect_infos.push_back(section_info{*offset_ptr + c.tell(),
264 static_cast<uint32_t>(payload_len),
265 section_id,
266 {}});
267 *offset_ptr += (c.tell() + payload_len);
268 } else {
269 // Invalid section id.
270 return false;
271 }
272 return true;
273}
274
277 if (IsInMemory()) {
278 offset += m_memory_addr;
279 }
280
281 while (DecodeNextSection(&offset))
282 ;
283 return true;
284}
285
288 DataExtractorSP &extractor_sp,
289 offset_t file_offset, offset_t length) {
290 if (!ValidateModuleHeader(extractor_sp->GetData()))
291 return {};
292
293 ModuleSpecList specs;
294 specs.Append(ModuleSpec(file, ArchSpec("wasm32")));
295 return specs;
296}
297
299 DataExtractorSP extractor_sp,
300 offset_t data_offset, const FileSpec *file,
301 offset_t offset, offset_t length)
302 : ObjectFile(module_sp, file, offset, length, extractor_sp, data_offset),
303 m_arch("wasm32") {
304 m_data_nsp->SetAddressByteSize(4);
305}
306
308 lldb::WritableDataBufferSP header_data_sp,
309 const lldb::ProcessSP &process_sp,
310 lldb::addr_t header_addr)
311 : ObjectFile(module_sp, process_sp, header_addr,
312 std::make_shared<DataExtractor>(header_data_sp)),
313 m_arch("wasm32") {}
314
316 // We already parsed the header during initialization.
317 return true;
318}
319
321 /// Offset from the section to the start of the function. This points past the
322 /// function size, which some other tools consider part of the function.
324
325 /// Function size, which includes the function header, but not the size ULEB
326 /// that proceeds it.
327 uint32_t size = 0;
328
329 /// Offset from section_offset to the first instruction in the function, past
330 /// the local variable declarations.
331 uint32_t code_offset = 0;
332};
333
334/// The number of imports of each kind. Imports occupy the low indices of the
335/// index space of their kind.
337 uint32_t functions = 0;
338 uint32_t globals = 0;
339};
340
341static llvm::Expected<WasmImports> ParseImports(DataExtractor &import_data) {
342 llvm::DataExtractor data = import_data.GetAsLLVM();
343 llvm::DataExtractor::Cursor c(0);
344
345 llvm::Expected<uint32_t> count = GetULEB32(data, c);
346 if (!count)
347 return count.takeError();
348
349 WasmImports imports;
350 for (uint32_t i = 0; c && i < *count; ++i) {
351 // We don't need module and field names, so we can just get them as raw
352 // strings and discard.
353 llvm::Expected<std::string> module_name = GetWasmString(data, c);
354 if (!module_name)
355 return llvm::joinErrors(
356 llvm::createStringError("failed to parse module name"),
357 module_name.takeError());
358 llvm::Expected<std::string> field_name = GetWasmString(data, c);
359 if (!field_name)
360 return llvm::joinErrors(
361 llvm::createStringError("failed to parse field name"),
362 field_name.takeError());
363
364 // The descriptor differs per kind, so each has to be parsed to find where
365 // the next import starts.
366 const uint8_t kind = data.getU8(c);
367 switch (kind) {
368 case llvm::wasm::WASM_EXTERNAL_FUNCTION:
369 imports.functions++;
370 data.getULEB128(c); // type index
371 break;
372 case llvm::wasm::WASM_EXTERNAL_GLOBAL:
373 imports.globals++;
374 data.getU8(c); // value type
375 data.getU8(c); // mutability
376 break;
377 case llvm::wasm::WASM_EXTERNAL_TAG:
378 data.getU8(c); // attribute
379 data.getULEB128(c); // type index
380 break;
381 case llvm::wasm::WASM_EXTERNAL_TABLE:
382 data.getU8(c); // element type
383 [[fallthrough]];
384 case llvm::wasm::WASM_EXTERNAL_MEMORY: {
385 // Tables and memories are both described by limits.
386 const uint8_t flags = data.getU8(c);
387 data.getULEB128(c); // minimum
388 if (flags & llvm::wasm::WASM_LIMITS_FLAG_HAS_MAX)
389 data.getULEB128(c);
390 break;
391 }
392 default:
393 // The cursor's error has to be consumed before it goes out of scope.
394 return llvm::joinErrors(
395 c.takeError(),
396 llvm::createStringError("unknown import kind %u", kind));
397 }
398 }
399
400 if (!c)
401 return c.takeError();
402
403 return imports;
404}
405
406/// Get the offset in the function to the first instruction.
407static llvm::Expected<uint32_t> GetFunctionCodeOffset(DataExtractor &data,
408 lldb::offset_t offset) {
409 // Wasm function bodies start with:
410 // [local_count: ULEB128]
411 // [local_decl: {count: ULEB128, type: byte}] × local_count
412 // [instructions...]
413 const lldb::offset_t locals_start = offset;
414 const uint32_t local_count = data.GetULEB128(&offset);
415 for (uint32_t i = 0; i < local_count; ++i) {
416 data.GetULEB128(&offset); // count
417 data.GetU8(&offset); // valtype
418 }
419 return offset - locals_start;
420}
421
422static llvm::Expected<std::vector<WasmFunction>>
424 lldb::offset_t offset = 0;
425
426 llvm::Expected<uint32_t> function_count = GetULEB32(data, offset);
427 if (!function_count)
428 return function_count.takeError();
429
430 std::vector<WasmFunction> functions;
431 functions.reserve(*function_count);
432
433 for (uint32_t i = 0; i < *function_count; ++i) {
434 // llvm-objdump considers the ULEB with the function size to be part of the
435 // function. We can't do that here because that would not match the DWARF,
436 // which considers the function to start with the local variable
437 // declarations (the header).
438 llvm::Expected<uint32_t> function_size = GetULEB32(data, offset);
439 if (!function_size)
440 return function_size.takeError();
441
442 // Functions start with with a number of local variable declarations.
443 // They're part of the function but they're not instructions.
444 llvm::Expected<uint32_t> code_offset = GetFunctionCodeOffset(data, offset);
445 if (!code_offset)
446 return code_offset.takeError();
447
448 functions.push_back({offset, *function_size, *code_offset});
449
450 std::optional<lldb::offset_t> next_offset =
451 llvm::checkedAddUnsigned<lldb::offset_t>(offset, *function_size);
452 if (!next_offset)
453 return llvm::createStringError("function offset overflows 64 bits");
454 offset = *next_offset;
455 }
456
457 return functions;
458}
459
475
476static llvm::Expected<std::vector<WasmSegment>> ParseData(DataExtractor &data) {
477 lldb::offset_t offset = 0;
478
479 llvm::Expected<uint32_t> segment_count = GetULEB32(data, offset);
480 if (!segment_count)
481 return segment_count.takeError();
482
483 std::vector<WasmSegment> segments;
484 segments.reserve(*segment_count);
485
486 for (uint32_t i = 0; i < *segment_count; ++i) {
487 llvm::Expected<uint32_t> flags = GetULEB32(data, offset);
488 if (!flags)
489 return flags.takeError();
490
491 WasmSegment segment;
492
493 // Data segments have a mode that identifies them as either passive or
494 // active. An active data segment copies its contents into a memory during
495 // instantiation, as specified by a memory index and a constant expression
496 // defining an offset into that memory.
497 segment.type = (*flags & llvm::wasm::WASM_DATA_SEGMENT_IS_PASSIVE)
500
501 if (*flags & llvm::wasm::WASM_DATA_SEGMENT_HAS_MEMINDEX) {
502 assert(segment.type == WasmSegment::Active);
503 llvm::Expected<uint32_t> memidx = GetULEB32(data, offset);
504 if (!memidx)
505 return memidx.takeError();
506 segment.memory_index = *memidx;
507 }
508
509 if (segment.type == WasmSegment::Active)
510 segment.init_expr_offset = GetWasmOffsetFromInitExpr(data, offset);
511
512 llvm::Expected<uint32_t> segment_size = GetULEB32(data, offset);
513 if (!segment_size)
514 return segment_size.takeError();
515
516 segment.section_offset = offset;
517 segment.size = *segment_size;
518 segments.push_back(segment);
519
520 std::optional<lldb::offset_t> next_offset =
521 llvm::checkedAddUnsigned<lldb::offset_t>(offset, *segment_size);
522 if (!next_offset)
523 return llvm::createStringError("segment offset overflows 64 bits");
524 offset = *next_offset;
525 }
526
527 return segments;
528}
529
530/// Parse the minimum size in bytes of the module's first linear memory. This is
531/// the memory guaranteed to exist at instantiation, and so the upper bound of
532/// the static data region.
533static llvm::Expected<uint64_t> ParseMemoryMinSize(DataExtractor &data) {
534 lldb::offset_t offset = 0;
535
536 llvm::Expected<uint32_t> memory_count = GetULEB32(data, offset);
537 if (!memory_count)
538 return memory_count.takeError();
539 if (*memory_count == 0)
540 return llvm::createStringError("module declares no linear memory");
541
542 // The limits of a memory are a flags byte followed by the minimum, and
543 // optionally the maximum, page count.
544 data.GetU8(&offset);
545 llvm::Expected<uint32_t> min_pages = GetULEB32(data, offset);
546 if (!min_pages)
547 return min_pages.takeError();
548
549 return static_cast<uint64_t>(*min_pages) * llvm::wasm::WasmDefaultPageSize;
550}
551
552/// Size in bytes of a WebAssembly value type, or nothing for the types whose
553/// values cannot be read.
554static std::optional<uint32_t> GetWasmValueTypeSize(uint8_t type) {
555 switch (type) {
556 case llvm::wasm::WASM_TYPE_I32:
557 case llvm::wasm::WASM_TYPE_F32:
558 return 4;
559 case llvm::wasm::WASM_TYPE_I64:
560 case llvm::wasm::WASM_TYPE_F64:
561 return 8;
562 default:
563 return std::nullopt;
564 }
565}
566
567/// Parse the init expr that gives a global its initial value. The result is the
568/// bit pattern of the value, so an operand that has to be evaluated against
569/// module state yields nothing.
570static std::optional<uint64_t> ParseGlobalInitValue(DataExtractor &data,
571 lldb::offset_t &offset) {
572 std::optional<uint64_t> value;
573
574 switch (data.GetU8(&offset)) {
575 case llvm::wasm::WASM_OPCODE_I32_CONST:
576 value = static_cast<uint32_t>(data.GetSLEB128(&offset));
577 break;
578 case llvm::wasm::WASM_OPCODE_I64_CONST:
579 value = static_cast<uint64_t>(data.GetSLEB128(&offset));
580 break;
581 case llvm::wasm::WASM_OPCODE_F32_CONST:
582 value = data.GetU32(&offset);
583 break;
584 case llvm::wasm::WASM_OPCODE_F64_CONST:
585 value = data.GetU64(&offset);
586 break;
587 case llvm::wasm::WASM_OPCODE_GLOBAL_GET:
588 case llvm::wasm::WASM_OPCODE_REF_NULL:
589 // The operand still has to be consumed to find the end of the expression.
590 data.GetULEB128(&offset);
591 break;
592 }
593
594 // An expression this parser does not understand can only be skipped to its
595 // end opcode. If that end never comes the parse is out of step, and there is
596 // no value to report.
597 uint8_t opcode = data.GetU8(&offset);
598 while (opcode != llvm::wasm::WASM_OPCODE_END && data.ValidOffset(offset))
599 opcode = data.GetU8(&offset);
600 if (opcode != llvm::wasm::WASM_OPCODE_END)
601 return std::nullopt;
602
603 return value;
604}
605
606/// Parse the module's own globals, which start at the number of imported ones.
607static llvm::Expected<std::vector<WasmGlobal>>
609 lldb::offset_t offset = 0;
610
611 llvm::Expected<uint32_t> count = GetULEB32(data, offset);
612 if (!count)
613 return count.takeError();
614
615 // The count comes from the file, so it is not a size to allocate up front.
616 std::vector<WasmGlobal> globals;
617
618 for (uint32_t i = 0; i < *count; ++i) {
619 if (!data.ValidOffset(offset))
620 return llvm::createStringError(
621 "global section holds %zu of its %u globals", globals.size(), *count);
622
623 WasmGlobal global;
624 global.value_type = data.GetU8(&offset);
625 global.size = GetWasmValueTypeSize(global.value_type);
626 global.is_mutable = data.GetU8(&offset) != 0;
627 global.init_expr_value = ParseGlobalInitValue(data, offset);
628 globals.push_back(global);
629 }
630
631 return globals;
632}
633
634static llvm::Expected<std::vector<Symbol>>
635ParseNames(SectionSP code_section_sp, SectionSP global_section_sp,
636 DataExtractor &name_data, const std::vector<WasmFunction> &functions,
637 std::vector<WasmSegment> &segments,
638 const std::vector<WasmGlobal> &globals,
639 uint32_t num_imported_functions, uint32_t num_imported_globals) {
640
641 llvm::DataExtractor data = name_data.GetAsLLVM();
642 llvm::DataExtractor::Cursor c(0);
643 std::vector<Symbol> symbols;
644 while (c && c.tell() < data.size()) {
645 const uint8_t type = data.getU8(c);
646 llvm::Expected<uint32_t> size = GetULEB32(data, c);
647 if (!size)
648 return size.takeError();
649
650 switch (type) {
651 case llvm::wasm::WASM_NAMES_FUNCTION: {
652 const uint64_t count = data.getULEB128(c);
653 if (count > std::numeric_limits<uint32_t>::max())
654 return llvm::joinErrors(
655 c.takeError(),
656 llvm::createStringError("function count overflows uint32_t"));
657
658 for (uint64_t i = 0; c && i < count; ++i) {
659 llvm::Expected<uint32_t> idx = GetULEB32(data, c);
660 if (!idx)
661 return idx.takeError();
662 llvm::Expected<std::string> name = GetWasmString(data, c);
663 if (!name)
664 return name.takeError();
665 if (*idx >= num_imported_functions + functions.size())
666 continue;
667
668 if (*idx < num_imported_functions) {
669 symbols.emplace_back(symbols.size(), *name, lldb::eSymbolTypeCode,
670 /*external=*/true, /*is_debug=*/false,
671 /*is_trampoline=*/false,
672 /*is_artificial=*/false,
673 /*section_sp=*/lldb::SectionSP(),
674 /*value=*/0, /*size=*/0,
675 /*size_is_valid=*/false,
676 /*contains_linker_annotations=*/false,
677 /*flags=*/0);
678 } else {
679 const WasmFunction &func = functions[*idx - num_imported_functions];
680 symbols.emplace_back(symbols.size(), *name, lldb::eSymbolTypeCode,
681 /*external=*/false, /*is_debug=*/false,
682 /*is_trampoline=*/false, /*is_artificial=*/false,
683 code_section_sp, func.section_offset, func.size,
684 /*size_is_valid=*/true,
685 /*contains_linker_annotations=*/false,
686 /*flags=*/0);
687 if (func.code_offset)
688 symbols.back().SetPrologueByteSize(func.code_offset);
689 }
690 }
691 } break;
692 case llvm::wasm::WASM_NAMES_DATA_SEGMENT: {
693 llvm::Expected<uint32_t> count = GetULEB32(data, c);
694 if (!count)
695 return count.takeError();
696 for (uint32_t i = 0; c && i < *count; ++i) {
697 llvm::Expected<uint32_t> idx = GetULEB32(data, c);
698 if (!idx)
699 return idx.takeError();
700 llvm::Expected<std::string> name = GetWasmString(data, c);
701 if (!name)
702 return name.takeError();
703 if (*idx >= segments.size())
704 continue;
705 // Update the segment name.
706 segments[*idx].name = *name;
707 }
708
709 } break;
710 case llvm::wasm::WASM_NAMES_GLOBAL: {
711 llvm::Expected<uint32_t> count = GetULEB32(data, c);
712 if (!count)
713 return count.takeError();
714 for (uint32_t i = 0; c && i < *count; ++i) {
715 llvm::Expected<uint32_t> idx = GetULEB32(data, c);
716 if (!idx)
717 return idx.takeError();
718 llvm::Expected<std::string> name = GetWasmString(data, c);
719 if (!name)
720 return name.takeError();
721
722 // An imported global has no entry in the global section, and so no
723 // value to bound a read with.
724 if (*idx < num_imported_globals)
725 continue;
726 const uint32_t global_idx = *idx - num_imported_globals;
727 if (global_idx >= globals.size())
728 continue;
729
730 // The section is indexed rather than byte addressed, so a global spans
731 // the one index it occupies. Bounding a read by the size of the value
732 // is the section's business.
733 symbols.emplace_back(symbols.size(), *name, lldb::eSymbolTypeData,
734 /*external=*/true, /*is_debug=*/false,
735 /*is_trampoline=*/false, /*is_artificial=*/false,
736 global_section_sp, /*offset=*/*idx,
737 /*size=*/1,
738 /*size_is_valid=*/true,
739 /*contains_linker_annotations=*/false,
740 /*flags=*/0);
741 }
742 } break;
743 case llvm::wasm::WASM_NAMES_LOCAL:
744 default:
745 std::optional<lldb::offset_t> offset =
746 llvm::checkedAddUnsigned<lldb::offset_t>(c.tell(), *size);
747 if (!offset)
748 return llvm::joinErrors(
749 c.takeError(), llvm::createStringError("offset overflows 64 bits"));
750 c.seek(*offset);
751 }
752 }
753
754 if (!c)
755 return c.takeError();
756
757 return symbols;
758}
759
761 std::string name;
762 uint8_t kind = 0;
763 uint32_t index = 0;
764};
765
766static llvm::Expected<std::vector<WasmExport>>
768 llvm::DataExtractor data = export_data.GetAsLLVM();
769 llvm::DataExtractor::Cursor c(0);
770
771 llvm::Expected<uint32_t> count = GetULEB32(data, c);
772 if (!count)
773 return count.takeError();
774
775 std::vector<WasmExport> exports;
776 for (uint32_t i = 0; c && i < *count; ++i) {
777 llvm::Expected<std::string> name = GetWasmString(data, c);
778 if (!name)
779 return name.takeError();
780 const uint8_t kind = data.getU8(c);
781 llvm::Expected<uint32_t> index = GetULEB32(data, c);
782 if (!index)
783 return index.takeError();
784 exports.push_back({std::move(*name), kind, *index});
785 }
786
787 if (!c)
788 return c.takeError();
789
790 return exports;
791}
792
793/// Adds to \p symbols a data symbol for each export that stands for a variable
794/// in linear memory. Such an export refers to an immutable Wasm global that
795/// holds the variable's address, not the variable's value. Wasm globals listed
796/// in the name section already have a symbol and are skipped.
797static void AddExportedDataSymbols(llvm::ArrayRef<WasmExport> exports,
798 llvm::ArrayRef<WasmGlobal> globals,
799 uint32_t num_imported_globals,
800 const SectionSP &global_section_sp,
801 llvm::ArrayRef<SectionSP> memory_sections,
802 std::vector<Symbol> &symbols) {
803 llvm::DenseSet<uint32_t> named_globals;
804 if (global_section_sp)
805 for (const Symbol &symbol : symbols)
806 if (symbol.GetAddressRef().GetSection() == global_section_sp)
807 named_globals.insert(symbol.GetAddressRef().GetOffset());
808
809 for (const WasmExport &exp : exports) {
810 if (exp.kind != llvm::wasm::WASM_EXTERNAL_GLOBAL ||
811 exp.index < num_imported_globals || named_globals.contains(exp.index))
812 continue;
813 const uint32_t global_idx = exp.index - num_imported_globals;
814 if (global_idx >= globals.size())
815 continue;
816
817 const WasmGlobal &global = globals[global_idx];
818 if (global.is_mutable || !global.init_expr_value ||
819 (global.value_type != llvm::wasm::WASM_TYPE_I32 &&
820 global.value_type != llvm::wasm::WASM_TYPE_I64))
821 continue;
822
823 const addr_t addr = *global.init_expr_value;
824 const SectionSP *section_sp =
825 llvm::find_if(memory_sections, [addr](const SectionSP &section) {
826 return section->ContainsFileAddress(addr);
827 });
828 if (section_sp == memory_sections.end())
829 continue;
830
831 // The module does not record the variable's size. Symtab would size a
832 // symbol without one up to the next symbol, across unnamed variables.
833 symbols.emplace_back(symbols.size(), exp.name, lldb::eSymbolTypeData,
834 /*external=*/true, /*is_debug=*/false,
835 /*is_trampoline=*/false, /*is_artificial=*/false,
836 *section_sp, addr - (*section_sp)->GetFileAddress(),
837 /*size=*/1, /*size_is_valid=*/true,
838 /*contains_linker_annotations=*/false, /*flags=*/0);
839 }
840}
841
843 for (const Symbol &symbol : m_symbols)
844 symtab.AddSymbol(symbol);
845
846 symtab.Finalize();
847 m_symbols.clear();
848}
849
850static SectionType GetSectionTypeFromName(llvm::StringRef Name) {
851 if (Name == "name")
853 if (Name.consume_front(".debug_") || Name.consume_front(".zdebug_"))
855 return eSectionTypeOther;
856}
857
858/// A `section` attribute on a data variable lands in a named data segment on
859/// wasm, not a top-level custom section, so formatter sections appear as
860/// segment names rather than section names.
861static SectionType GetSegmentTypeFromName(llvm::StringRef Name) {
862 return llvm::StringSwitch<SectionType>(Name)
863 .Case(".lldbsummaries", eSectionTypeLLDBTypeSummaries)
864 .Case(".lldbformatters", eSectionTypeLLDBFormatters)
865 .Default(eSectionTypeData);
866}
867
868std::optional<ObjectFileWasm::section_info>
869ObjectFileWasm::GetSectionInfo(uint32_t section_id) {
870 for (const section_info &sect_info : m_sect_infos) {
871 if (sect_info.id == section_id)
872 return sect_info;
873 }
874 return std::nullopt;
875}
876
877std::optional<ObjectFileWasm::section_info>
878ObjectFileWasm::GetSectionInfo(llvm::StringRef section_name) {
879 for (const section_info &sect_info : m_sect_infos) {
880 if (sect_info.name == section_name)
881 return sect_info;
882 }
883 return std::nullopt;
884}
885
886void ObjectFileWasm::CreateSections(SectionList &unified_section_list) {
887 Log *log = GetLog(LLDBLog::Object);
888
889 if (m_sections_up)
890 return;
891
892 m_sections_up = std::make_unique<SectionList>();
893
894 if (m_sect_infos.empty()) {
896 }
897
898 for (const section_info &sect_info : m_sect_infos) {
899 SectionType section_type = eSectionTypeOther;
900 std::string section_name;
901 offset_t file_offset = sect_info.offset & 0xffffffff;
902 addr_t vm_addr = sect_info.offset;
903 size_t vm_size = sect_info.size;
904
905 if (llvm::wasm::WASM_SEC_CODE == sect_info.id) {
906 section_type = eSectionTypeCode;
907 section_name = "code";
908
909 // A code address in DWARF for WebAssembly is the offset of an
910 // instruction relative within the Code section of the WebAssembly file.
911 // For this reason Section::GetFileAddress() must return zero for the
912 // Code section.
913 vm_addr = 0;
914 } else {
915 section_type = GetSectionTypeFromName(sect_info.name);
916 if (section_type == eSectionTypeOther)
917 continue;
918 section_name = sect_info.name;
919 if (!IsInMemory()) {
920 vm_size = 0;
921 vm_addr = 0;
922 }
923 }
924
925 SectionSP section_sp = std::make_shared<Section>(
926 GetModule(), // Module to which this section belongs.
927 this, // ObjectFile to which this section belongs and
928 // should read section data from.
929 section_type, // Section ID.
930 section_name, // Section name.
931 section_type, // Section type.
932 vm_addr, // VM address.
933 vm_size, // VM size in bytes of this section.
934 file_offset, // Offset of this section in the file.
935 sect_info.size, // Size of the section as found in the file.
936 0, // Alignment of the section
937 0); // Flags for this section.
938 m_sections_up->AddSection(section_sp);
939 unified_section_list.AddSection(section_sp);
940 }
941
942 // The name section contains names and indexes. First parse the data from the
943 // relevant sections so we can access it by its index.
944 std::vector<WasmFunction> functions;
945 std::vector<WasmSegment> segments;
946
947 // Parse the code section.
948 if (std::optional<section_info> info =
949 GetSectionInfo(llvm::wasm::WASM_SEC_CODE)) {
950 DataExtractor code_data = ReadImageData(info->offset, info->size);
951 llvm::Expected<std::vector<WasmFunction>> maybe_functions =
952 ParseFunctions(code_data);
953 if (!maybe_functions) {
954 LLDB_LOG_ERROR(log, maybe_functions.takeError(),
955 "Failed to parse Wasm code section: {0}");
956 } else {
957 functions = *maybe_functions;
958 }
959 }
960
961 // Parse the import section. The counts are needed because the function and
962 // global index spaces used in the name section include imports.
963 bool imports_parsed = true;
964 if (std::optional<section_info> info =
965 GetSectionInfo(llvm::wasm::WASM_SEC_IMPORT)) {
966 DataExtractor import_data = ReadImageData(info->offset, info->size);
967 llvm::Expected<WasmImports> imports = ParseImports(import_data);
968 if (!imports) {
969 LLDB_LOG_ERROR(log, imports.takeError(),
970 "Failed to parse Wasm import section: {0}");
971 imports_parsed = false;
972 } else {
973 m_num_imported_functions = imports->functions;
974 m_num_imported_globals = imports->globals;
975 }
976 }
977
978 // Parse the global section.
979 if (std::optional<section_info> info =
980 GetSectionInfo(llvm::wasm::WASM_SEC_GLOBAL)) {
981 DataExtractor global_data = ReadImageData(info->offset, info->size);
982 llvm::Expected<std::vector<WasmGlobal>> globals = ParseGlobals(global_data);
983 if (!globals) {
984 LLDB_LOG_ERROR(log, globals.takeError(),
985 "Failed to parse Wasm global section: {0}");
986 } else {
987 m_globals = *globals;
988 }
989 }
990
991 // Parse the data section.
992 std::optional<section_info> data_info =
993 GetSectionInfo(llvm::wasm::WASM_SEC_DATA);
994 if (data_info) {
995 DataExtractor data_data = ReadImageData(data_info->offset, data_info->size);
996 llvm::Expected<std::vector<WasmSegment>> maybe_segments =
997 ParseData(data_data);
998 if (!maybe_segments) {
999 LLDB_LOG_ERROR(log, maybe_segments.takeError(),
1000 "Failed to parse Wasm data section: {0}");
1001 } else {
1002 segments = *maybe_segments;
1003 }
1004 }
1005
1006 // The section maps nothing: it exists to give globals an address, which is
1007 // what lets one be named and read. Its size counts globals rather than bytes,
1008 // imported ones included, because they share the index space.
1009 SectionSP global_section_sp;
1010 if (!m_globals.empty()) {
1011 global_section_sp = std::make_shared<Section>(
1012 GetModule(),
1013 /*obj_file=*/this, eSectionTypeWasmGlobal, "global",
1015 /*file_vm_addr=*/kWasmGlobalFileAddress,
1016 /*vm_size=*/m_num_imported_globals + m_globals.size(),
1017 /*file_offset=*/0, /*file_size=*/0,
1018 /*log2align=*/0, /*flags=*/0);
1019 m_sections_up->AddSection(global_section_sp);
1020 unified_section_list.AddSection(global_section_sp);
1021 }
1022
1023 if (std::optional<section_info> info = GetSectionInfo("name")) {
1024 DataExtractor names_data = ReadImageData(info->offset, info->size);
1025 llvm::Expected<std::vector<Symbol>> symbols = ParseNames(
1026 m_sections_up->FindSectionByType(lldb::eSectionTypeCode, false),
1027 global_section_sp, names_data, functions, segments, m_globals,
1029 if (!symbols) {
1030 LLDB_LOG_ERROR(log, symbols.takeError(),
1031 "Failed to parse Wasm names: {0}");
1032 } else {
1033 m_symbols = *symbols;
1034 }
1035 }
1036
1037 lldb::user_id_t segment_id = 0;
1038 lldb::addr_t static_data_end = 0;
1039 std::vector<SectionSP> memory_sections;
1040 for (const WasmSegment &segment : segments) {
1041 if (segment.type == WasmSegment::Active) {
1042 // FIXME: Support segments with a memory index.
1043 if (segment.memory_index != 0) {
1044 LLDB_LOG(log,
1045 "Skipping segment {}: non-zero memory index is "
1046 "currently unsupported",
1047 segment.name);
1048 continue;
1049 }
1050
1051 if (segment.init_expr_offset == LLDB_INVALID_OFFSET) {
1052 LLDB_LOG(log, "Skipping segment {}: unsupported init expression",
1053 segment.name);
1054 continue;
1055 }
1056 }
1057
1058 const lldb::addr_t file_vm_addr =
1059 segment.type == WasmSegment::Active
1060 ? segment.init_expr_offset
1061 : data_info->offset + segment.section_offset;
1062 const lldb::offset_t file_offset =
1063 data_info->GetFileOffset() + segment.GetFileOffset();
1064 SectionSP segment_sp = std::make_shared<Section>(
1065 GetModule(),
1066 /*obj_file=*/this,
1067 ++segment_id << 8, // 1-based segment index, shifted by 8 bits to avoid
1068 // collision with section IDs.
1069 segment.name, GetSegmentTypeFromName(segment.name),
1070 /*file_vm_addr=*/file_vm_addr,
1071 /*vm_size=*/segment.size,
1072 /*file_offset=*/file_offset,
1073 /*file_size=*/segment.size,
1074 /*log2align=*/0, /*flags=*/0);
1075 m_sections_up->AddSection(segment_sp);
1076 GetModule()->GetSectionList()->AddSection(segment_sp);
1077
1078 if (segment.type == WasmSegment::Active) {
1079 memory_sections.push_back(segment_sp);
1080 static_data_end = std::max(static_data_end, file_vm_addr + segment.size);
1081 }
1082 }
1083
1084 // Zero-initialized globals (BSS) have no data segment, so the loop above
1085 // leaves their linear-memory addresses uncovered by any section, and a static
1086 // read of one can't be resolved. Cover the rest of linear memory with a
1087 // zero-fill section. SetLoadAddress maps it like a data segment so live reads
1088 // still go through process memory.
1089 if (std::optional<section_info> mem_info =
1090 GetSectionInfo(llvm::wasm::WASM_SEC_MEMORY)) {
1091 DataExtractor mem_data = ReadImageData(mem_info->offset, mem_info->size);
1092 llvm::Expected<uint64_t> memory_size = ParseMemoryMinSize(mem_data);
1093 if (!memory_size) {
1094 LLDB_LOG_ERROR(log, memory_size.takeError(),
1095 "Failed to parse Wasm memory section: {0}");
1096 } else if (*memory_size > static_data_end) {
1097 SectionSP bss_sp = std::make_shared<Section>(
1098 GetModule(),
1099 /*obj_file=*/this, ++segment_id << 8, ".bss", eSectionTypeZeroFill,
1100 /*file_vm_addr=*/static_data_end,
1101 /*vm_size=*/*memory_size - static_data_end,
1102 /*file_offset=*/0,
1103 /*file_size=*/0,
1104 /*log2align=*/0, /*flags=*/0);
1105 m_sections_up->AddSection(bss_sp);
1106 GetModule()->GetSectionList()->AddSection(bss_sp);
1107 memory_sections.push_back(bss_sp);
1108 }
1109 }
1110
1111 // The global index of an export counts imported globals, so it can be mapped
1112 // to a global of this module only if the import section parsed.
1113 if (!imports_parsed)
1114 return;
1115
1116 if (std::optional<section_info> info =
1117 GetSectionInfo(llvm::wasm::WASM_SEC_EXPORT)) {
1118 DataExtractor export_data = ReadImageData(info->offset, info->size);
1119 llvm::Expected<std::vector<WasmExport>> exports = ParseExports(export_data);
1120 if (!exports) {
1121 LLDB_LOG_ERROR(log, exports.takeError(),
1122 "Failed to parse Wasm export section: {0}");
1123 } else {
1125 global_section_sp, memory_sections, m_symbols);
1126 }
1127 }
1128}
1129
1131 lldb::offset_t section_offset, void *dst,
1132 size_t dst_len) {
1133 if (!section || section->GetType() != eSectionTypeWasmGlobal)
1134 return ObjectFile::ReadSectionData(section, section_offset, dst, dst_len);
1135
1136 // The low indices belong to imported globals, which the module does not
1137 // declare and so has no initializer for.
1138 if (section_offset < m_num_imported_globals)
1139 return 0;
1140 const lldb::offset_t index = section_offset - m_num_imported_globals;
1141 if (index >= m_globals.size())
1142 return 0;
1143
1144 const WasmGlobal &global = m_globals[index];
1145 if (!global.init_expr_value || !global.size || dst_len > *global.size)
1146 return 0;
1147
1148 // A global holds a value rather than bytes, and WebAssembly is little-endian.
1149 uint8_t bytes[sizeof(uint64_t)];
1150 llvm::support::endian::write64le(bytes, *global.init_expr_value);
1151 std::memcpy(dst, bytes, dst_len);
1152 return dst_len;
1153}
1154
1156 bool value_is_offset) {
1157 /// In WebAssembly, linear memory is disjointed from code space. The VM can
1158 /// load multiple instances of a module, which logically share the same code.
1159 /// We represent a wasm32 code address with 64-bits, like:
1160 /// 63 32 31 0
1161 /// +---------------+---------------+
1162 /// + module_id | offset |
1163 /// +---------------+---------------+
1164 /// where the lower 32 bits represent a module offset (relative to the module
1165 /// start not to the beginning of the code section) and the higher 32 bits
1166 /// uniquely identify the module in the WebAssembly VM.
1167 /// In other words, we assume that each WebAssembly module is loaded by the
1168 /// engine at a 64-bit address that starts at the boundary of 4GB pages, like
1169 /// 0x0000000400000000 for module_id == 4.
1170 /// These 64-bit addresses will be used to request code ranges for a specific
1171 /// module from the WebAssembly engine.
1172
1174 m_memory_addr == load_address);
1175
1176 ModuleSP module_sp = GetModule();
1177 if (!module_sp)
1178 return false;
1179
1181
1182 size_t num_loaded_sections = 0;
1183 SectionList *section_list = GetSectionList();
1184 if (!section_list)
1185 return false;
1186
1187 const size_t num_sections = section_list->GetSize();
1188 for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) {
1189 SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx));
1190 lldb::addr_t section_load_addr;
1191 switch (section_sp->GetType()) {
1192 case eSectionTypeData:
1197 // These live in linear memory, and the globals in an index space of their
1198 // own, both separate from code. A section's file address already carries
1199 // the space it belongs to, so only the module id comes from the load
1200 // address.
1201 section_load_addr =
1202 (load_address & ~kWasmAddressTypeMask) | section_sp->GetFileAddress();
1203 break;
1204 default:
1205 // Code (and other) sections are addressed by their offset within the
1206 // module in the Object address space.
1207 section_load_addr = load_address | section_sp->GetFileOffset();
1208 break;
1209 }
1210 if (target.SetSectionLoadAddress(section_sp, section_load_addr))
1211 ++num_loaded_sections;
1212 }
1213
1214 return num_loaded_sections > 0;
1215}
1216
1218 DataExtractor data;
1219 if (m_file) {
1220 if (offset < GetByteSize()) {
1221 size = std::min(static_cast<uint64_t>(size), GetByteSize() - offset);
1222 auto buffer_sp = MapFileData(m_file, size, offset);
1223 return DataExtractor(buffer_sp, GetByteOrder(), GetAddressByteSize());
1224 }
1225 } else {
1226 ProcessSP process_sp(m_process_wp.lock());
1227 if (process_sp) {
1228 auto data_up = std::make_unique<DataBufferHeap>(size, 0);
1229 Status readmem_error;
1230 size_t bytes_read = process_sp->ReadMemory(
1231 offset, data_up->GetBytes(), data_up->GetByteSize(), readmem_error);
1232 if (bytes_read > 0) {
1233 DataBufferSP buffer_sp(data_up.release());
1234 data.SetData(buffer_sp);
1235 }
1236 } else if (offset < m_data_nsp->GetByteSize()) {
1237 size = std::min(static_cast<uint64_t>(size),
1238 m_data_nsp->GetByteSize() - offset);
1239 return DataExtractor(m_data_nsp->GetDataStart() + offset, size,
1241 }
1242 }
1243 data.SetByteOrder(GetByteOrder());
1244 return data;
1245}
1246
1248 if (m_uuid)
1249 return m_uuid;
1250
1251 // A Wasm module carries the identifier a linker gave it in a custom section,
1252 // as a vector of bytes. It is the only thing that tells one build of a module
1253 // from another, so a module linked without one cannot be identified at all.
1254 static constexpr llvm::StringLiteral g_sect_name_build_id("build_id");
1255 for (const section_info &sect_info : m_sect_infos) {
1256 if (g_sect_name_build_id != sect_info.name)
1257 continue;
1258
1259 DataExtractor section_data =
1260 ReadImageData(sect_info.offset, sect_info.size);
1261 llvm::DataExtractor data = section_data.GetAsLLVM();
1262 llvm::DataExtractor::Cursor c(0);
1263 llvm::Expected<uint32_t> length = GetULEB32(data, c);
1264 if (!length) {
1265 LLDB_LOG_ERROR(GetLog(LLDBLog::Object), length.takeError(),
1266 "failed to parse the build id length: {0}");
1267 return m_uuid;
1268 }
1269 llvm::SmallVector<uint8_t, 32> id(*length, 0);
1270 data.getU8(c, id.data(), id.size());
1271 if (!c) {
1272 LLDB_LOG_ERROR(GetLog(LLDBLog::Object), c.takeError(),
1273 "failed to parse the build id: {0}");
1274 return m_uuid;
1275 }
1276 m_uuid = UUID(id);
1277 break;
1278 }
1279
1280 return m_uuid;
1281}
1282
1284 static constexpr llvm::StringLiteral g_sect_name_external_debug_info(
1285 "external_debug_info");
1286
1287 for (const section_info &sect_info : m_sect_infos) {
1288 if (g_sect_name_external_debug_info == sect_info.name) {
1289 const uint32_t kBufferSize = 1024;
1290 DataExtractor section_header_data =
1291 ReadImageData(sect_info.offset, kBufferSize);
1292
1293 llvm::DataExtractor data = section_header_data.GetAsLLVM();
1294 llvm::DataExtractor::Cursor c(0);
1295 llvm::Expected<std::string> symbols_url = GetWasmString(data, c);
1296 if (!symbols_url) {
1297 llvm::consumeError(symbols_url.takeError());
1298 return std::nullopt;
1299 }
1300 return FileSpec(*symbols_url);
1301 }
1302 }
1303 return std::nullopt;
1304}
1305
1307 ModuleSP module_sp(GetModule());
1308 if (!module_sp)
1309 return;
1310
1311 std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
1312
1313 llvm::raw_ostream &ostream = s->AsRawOstream();
1314 ostream << static_cast<void *>(this) << ": ";
1315 s->Indent();
1316 ostream << "ObjectFileWasm, file = '";
1317 m_file.Dump(ostream);
1318 ostream << "', arch = ";
1319 ostream << GetArchitecture().GetArchitectureName() << "\n";
1320
1321 SectionList *sections = GetSectionList();
1322 if (sections) {
1323 sections->Dump(s->AsRawOstream(), s->GetIndentLevel(), nullptr, true,
1324 UINT32_MAX);
1325 }
1326 ostream << "\n";
1327 DumpSectionHeaders(ostream);
1328 ostream << "\n";
1329}
1330
1331void ObjectFileWasm::DumpSectionHeader(llvm::raw_ostream &ostream,
1332 const section_info &sh) {
1333 ostream << llvm::left_justify(sh.name, 16) << " "
1334 << llvm::format_hex(sh.offset, 10) << " "
1335 << llvm::format_hex(sh.size, 10) << " " << llvm::format_hex(sh.id, 6)
1336 << "\n";
1337}
1338
1339void ObjectFileWasm::DumpSectionHeaders(llvm::raw_ostream &ostream) {
1340 ostream << "Section Headers\n";
1341 ostream << "IDX name addr size id\n";
1342 ostream << "==== ---------------- ---------- ---------- ------\n";
1343
1344 uint32_t idx = 0;
1345 for (auto pos = m_sect_infos.begin(); pos != m_sect_infos.end();
1346 ++pos, ++idx) {
1347 ostream << "[" << llvm::format_decimal(idx, 2) << "] ";
1348 ObjectFileWasm::DumpSectionHeader(ostream, *pos);
1349 }
1350}
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
Definition Log.h:375
#define LLDB_LOGF(log,...)
Definition Log.h:389
#define LLDB_LOG_ERROR(log, error,...)
Definition Log.h:405
static SectionType GetSectionTypeFromName(llvm::StringRef Name)
static void AddExportedDataSymbols(llvm::ArrayRef< WasmExport > exports, llvm::ArrayRef< WasmGlobal > globals, uint32_t num_imported_globals, const SectionSP &global_section_sp, llvm::ArrayRef< SectionSP > memory_sections, std::vector< Symbol > &symbols)
Adds to symbols a data symbol for each export that stands for a variable in linear memory.
static std::optional< uint32_t > GetWasmValueTypeSize(uint8_t type)
Size in bytes of a WebAssembly value type, or nothing for the types whose values cannot be read.
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::vector< WasmGlobal > > ParseGlobals(DataExtractor &data)
Parse the module's own globals, which start at the number of imported ones.
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< WasmImports > ParseImports(DataExtractor &import_data)
static llvm::Expected< std::vector< WasmExport > > ParseExports(DataExtractor &export_data)
static llvm::Expected< uint32_t > GetFunctionCodeOffset(DataExtractor &data, lldb::offset_t offset)
Get the offset in the function to the first instruction.
static llvm::Expected< std::vector< WasmFunction > > ParseFunctions(DataExtractor &data)
static llvm::Expected< std::vector< Symbol > > ParseNames(SectionSP code_section_sp, SectionSP global_section_sp, DataExtractor &name_data, const std::vector< WasmFunction > &functions, std::vector< WasmSegment > &segments, const std::vector< WasmGlobal > &globals, uint32_t num_imported_functions, uint32_t num_imported_globals)
static bool ValidateModuleHeader(llvm::ArrayRef< uint8_t > data)
Checks whether the data buffer starts with a valid Wasm module header.
static SectionType GetSegmentTypeFromName(llvm::StringRef Name)
A section attribute on a data variable lands in a named data segment on wasm, not a top-level custom ...
static const uint32_t kWasmHeaderSize
static llvm::Expected< uint64_t > ParseMemoryMinSize(DataExtractor &data)
Parse the minimum size in bytes of the module's first linear memory.
static constexpr lldb::addr_t kWasmGlobalFileAddress
File address the synthetic global section is based at.
static std::optional< uint64_t > ParseGlobalInitValue(DataExtractor &data, lldb::offset_t &offset)
Parse the init expr that gives a global its initial value.
static llvm::Expected< std::vector< WasmSegment > > ParseData(DataExtractor &data)
#define LLDB_PLUGIN_DEFINE(PluginName)
An architecture specification class.
Definition ArchSpec.h:32
const char * GetArchitectureName() const
Returns a static string representing the current architecture.
Definition ArchSpec.cpp:745
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.
uint64_t GetU64(lldb::offset_t *offset_ptr) const
Extract a uint64_t value from *offset_ptr.
llvm::DataExtractor GetAsLLVM() const
void SetByteOrder(lldb::ByteOrder byte_order)
Set the byte_order value.
uint32_t GetU32(lldb::offset_t *offset_ptr) const
Extract a uint32_t value from *offset_ptr.
bool ValidOffset(lldb::offset_t offset) const
Test the validity of offset.
virtual 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:56
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition FileSpec.cpp:380
lldb::ModuleSP GetModule() const
Get const accessor for the module pointer.
void Append(const ModuleSpec &spec)
Definition ModuleSpec.h:371
std::unique_ptr< lldb_private::SectionList > m_sections_up
Definition ObjectFile.h:789
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.
Definition ObjectFile.h:787
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.
Definition ObjectFile.h:781
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.
Definition ObjectFile.h:695
lldb::ProcessWP m_process_wp
Definition ObjectFile.h:785
virtual size_t ReadSectionData(Section *section, lldb::offset_t section_offset, void *dst, size_t dst_len)
virtual lldb::addr_t GetByteSize() const
Definition ObjectFile.h:277
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:76
size_t AddSection(const lldb::SectionSP &section_sp)
Definition Section.cpp:483
void Dump(llvm::raw_ostream &s, unsigned indent, Target *target, bool show_header, uint32_t depth) const
Definition Section.cpp:648
lldb::SectionSP GetSectionAtIndex(size_t idx) const
Definition Section.cpp:555
lldb::SectionType GetType() const
Definition Section.h:214
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:405
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:193
uint32_t AddSymbol(const Symbol &symbol)
Definition Symtab.cpp:62
bool SetSectionLoadAddress(const lldb::SectionSP &section, lldb::addr_t load_addr, bool warn_multiple=false)
Definition Target.cpp:3536
Represents UUID's of various sizes.
Definition UUID.h:27
Generic Wasm object file reader.
ArchSpec GetArchitecture() override
Get the ArchSpec for this object file.
std::optional< FileSpec > GetExternalDebugInfoFileSpec()
A Wasm module that has external DWARF debug information should contain a custom section named "extern...
size_t ReadSectionData(Section *section, lldb::offset_t section_offset, void *dst, size_t dst_len) override
A global has no bytes in the module to read, so serve its initial value when there is no process to a...
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
UUID GetUUID() override
Gets the UUID for this object file.
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.
void ParseSymtab(lldb_private::Symtab &symtab) override
Parse the symbol table into the provides symbol table object.
static ModuleSpecList GetModuleSpecifications(const FileSpec &file, lldb::DataExtractorSP &extractor_sp, lldb::offset_t file_offset, lldb::offset_t length)
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)
std::vector< WasmGlobal > m_globals
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
static constexpr uint64_t kWasmAddressTypeMask
Definition WasmAddress.h:57
WasmAddressType
Each WebAssembly module has separate address spaces for Code and Memory.
Definition WasmAddress.h:29
static constexpr uint32_t kWasmAddressTypeShift
Definition WasmAddress.h:47
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:338
uint64_t offset_t
Definition lldb-types.h:86
std::shared_ptr< lldb_private::Process > ProcessSP
uint64_t user_id_t
Definition lldb-types.h:83
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
@ eSectionTypeZeroFill
@ eSectionTypeLLDBFormatters
@ eSectionTypeWasmGlobal
@ eSectionTypeLLDBTypeSummaries
@ eSectionTypeWasmName
std::shared_ptr< lldb_private::DataExtractor > DataExtractorSP
std::shared_ptr< lldb_private::Module > ModuleSP
std::string name
lldb::offset_t section_offset
Offset from the section to the start of the function.
uint32_t code_offset
Offset from section_offset to the first instruction in the function, past the local variable declarat...
uint32_t size
Function size, which includes the function header, but not the size ULEB that proceeds it.
The number of imports of each kind.
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
A global declared by the module itself.
std::optional< uint32_t > size
Size of the global's value type, which bounds what a read can produce.
std::optional< uint64_t > init_expr_value
Value the global is initialized with, when the initializer is a constant.