Coverage Report

Created: 2022-01-18 06:27

/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp
Line
Count
Source (jump to first uncovered line)
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"
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/Log.h"
19
#include "llvm/ADT/ArrayRef.h"
20
#include "llvm/ADT/SmallVector.h"
21
#include "llvm/ADT/StringRef.h"
22
#include "llvm/BinaryFormat/Magic.h"
23
#include "llvm/BinaryFormat/Wasm.h"
24
#include "llvm/Support/Endian.h"
25
#include "llvm/Support/Format.h"
26
27
using namespace lldb;
28
using namespace lldb_private;
29
using namespace lldb_private::wasm;
30
31
LLDB_PLUGIN_DEFINE(ObjectFileWasm)
32
33
static const uint32_t kWasmHeaderSize =
34
    sizeof(llvm::wasm::WasmMagic) + sizeof(llvm::wasm::WasmVersion);
35
36
/// Checks whether the data buffer starts with a valid Wasm module header.
37
2.89k
static bool ValidateModuleHeader(const DataBufferSP &data_sp) {
38
2.89k
  if (!data_sp || data_sp->GetByteSize() < kWasmHeaderSize)
39
0
    return false;
40
41
2.89k
  if (llvm::identify_magic(toStringRef(data_sp->GetData())) !=
42
2.89k
      llvm::file_magic::wasm_object)
43
2.88k
    return false;
44
45
16
  uint8_t *Ptr = data_sp->GetBytes() + sizeof(llvm::wasm::WasmMagic);
46
47
16
  uint32_t version = llvm::support::endian::read32le(Ptr);
48
16
  return version == llvm::wasm::WasmVersion;
49
2.89k
}
50
51
static llvm::Optional<ConstString>
52
44
GetWasmString(llvm::DataExtractor &data, llvm::DataExtractor::Cursor &c) {
53
  // A Wasm string is encoded as a vector of UTF-8 codes.
54
  // Vectors are encoded with their u32 length followed by the element
55
  // sequence.
56
44
  uint64_t len = data.getULEB128(c);
57
44
  if (!c) {
58
0
    consumeError(c.takeError());
59
0
    return llvm::None;
60
0
  }
61
62
44
  if (len >= (uint64_t(1) << 32)) {
63
0
    return llvm::None;
64
0
  }
65
66
44
  llvm::SmallVector<uint8_t, 32> str_storage;
67
44
  data.getU8(c, str_storage, len);
68
44
  if (!c) {
69
0
    consumeError(c.takeError());
70
0
    return llvm::None;
71
0
  }
72
73
44
  llvm::StringRef str = toStringRef(makeArrayRef(str_storage));
74
44
  return ConstString(str);
75
44
}
76
77
char ObjectFileWasm::ID;
78
79
3.44k
void ObjectFileWasm::Initialize() {
80
3.44k
  PluginManager::RegisterPlugin(GetPluginNameStatic(),
81
3.44k
                                GetPluginDescriptionStatic(), CreateInstance,
82
3.44k
                                CreateMemoryInstance, GetModuleSpecifications);
83
3.44k
}
84
85
3.43k
void ObjectFileWasm::Terminate() {
86
3.43k
  PluginManager::UnregisterPlugin(CreateInstance);
87
3.43k
}
88
89
ObjectFile *
90
ObjectFileWasm::CreateInstance(const ModuleSP &module_sp, DataBufferSP &data_sp,
91
                               offset_t data_offset, const FileSpec *file,
92
213
                               offset_t file_offset, offset_t length) {
93
213
  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
94
95
213
  if (!data_sp) {
96
0
    data_sp = MapFileData(*file, length, file_offset);
97
0
    if (!data_sp) {
98
0
      LLDB_LOGF(log, "Failed to create ObjectFileWasm instance for file %s",
99
0
                file->GetPath().c_str());
100
0
      return nullptr;
101
0
    }
102
0
    data_offset = 0;
103
0
  }
104
105
213
  assert(data_sp);
106
213
  if (!ValidateModuleHeader(data_sp)) {
107
206
    LLDB_LOGF(log,
108
206
              "Failed to create ObjectFileWasm instance: invalid Wasm header");
109
206
    return nullptr;
110
206
  }
111
112
  // Update the data to contain the entire file if it doesn't contain it
113
  // already.
114
7
  if (data_sp->GetByteSize() < length) {
115
0
    data_sp = MapFileData(*file, length, file_offset);
116
0
    if (!data_sp) {
117
0
      LLDB_LOGF(log,
118
0
                "Failed to create ObjectFileWasm instance: cannot read file %s",
119
0
                file->GetPath().c_str());
120
0
      return nullptr;
121
0
    }
122
0
    data_offset = 0;
123
0
  }
124
125
7
  std::unique_ptr<ObjectFileWasm> objfile_up(new ObjectFileWasm(
126
7
      module_sp, data_sp, data_offset, file, file_offset, length));
127
7
  ArchSpec spec = objfile_up->GetArchitecture();
128
7
  if (spec && objfile_up->SetModulesArchitecture(spec)) {
129
7
    LLDB_LOGF(log,
130
7
              "%p ObjectFileWasm::CreateInstance() module = %p (%s), file = %s",
131
7
              static_cast<void *>(objfile_up.get()),
132
7
              static_cast<void *>(objfile_up->GetModule().get()),
133
7
              objfile_up->GetModule()->GetSpecificationDescription().c_str(),
134
7
              file ? file->GetPath().c_str() : "<NULL>");
135
7
    return objfile_up.release();
136
7
  }
137
138
0
  LLDB_LOGF(log, "Failed to create ObjectFileWasm instance");
139
0
  return nullptr;
140
7
}
141
142
ObjectFile *ObjectFileWasm::CreateMemoryInstance(const ModuleSP &module_sp,
143
                                                 DataBufferSP &data_sp,
144
                                                 const ProcessSP &process_sp,
145
2
                                                 addr_t header_addr) {
146
2
  if (!ValidateModuleHeader(data_sp))
147
0
    return nullptr;
148
149
2
  std::unique_ptr<ObjectFileWasm> objfile_up(
150
2
      new ObjectFileWasm(module_sp, data_sp, process_sp, header_addr));
151
2
  ArchSpec spec = objfile_up->GetArchitecture();
152
2
  if (spec && objfile_up->SetModulesArchitecture(spec))
153
2
    return objfile_up.release();
154
0
  return nullptr;
155
2
}
156
157
58
bool ObjectFileWasm::DecodeNextSection(lldb::offset_t *offset_ptr) {
158
  // Buffer sufficient to read a section header and find the pointer to the next
159
  // section.
160
58
  const uint32_t kBufferSize = 1024;
161
58
  DataExtractor section_header_data = ReadImageData(*offset_ptr, kBufferSize);
162
163
58
  llvm::DataExtractor data = section_header_data.GetAsLLVM();
164
58
  llvm::DataExtractor::Cursor c(0);
165
166
  // Each section consists of:
167
  // - a one-byte section id,
168
  // - the u32 size of the contents, in bytes,
169
  // - the actual contents.
170
58
  uint8_t section_id = data.getU8(c);
171
58
  uint64_t payload_len = data.getULEB128(c);
172
58
  if (!c)
173
10
    return !llvm::errorToBool(c.takeError());
174
175
48
  if (payload_len >= (uint64_t(1) << 32))
176
0
    return false;
177
178
48
  if (section_id == llvm::wasm::WASM_SEC_CUSTOM) {
179
    // Custom sections have the id 0. Their contents consist of a name
180
    // identifying the custom section, followed by an uninterpreted sequence
181
    // of bytes.
182
41
    lldb::offset_t prev_offset = c.tell();
183
41
    llvm::Optional<ConstString> sect_name = GetWasmString(data, c);
184
41
    if (!sect_name)
185
0
      return false;
186
187
41
    if (payload_len < c.tell() - prev_offset)
188
0
      return false;
189
190
41
    uint32_t section_length = payload_len - (c.tell() - prev_offset);
191
41
    m_sect_infos.push_back(section_info{*offset_ptr + c.tell(), section_length,
192
41
                                        section_id, *sect_name});
193
41
    *offset_ptr += (c.tell() + section_length);
194
41
  } else 
if (7
section_id <= llvm::wasm::WASM_SEC_TAG7
) {
195
7
    m_sect_infos.push_back(section_info{*offset_ptr + c.tell(),
196
7
                                        static_cast<uint32_t>(payload_len),
197
7
                                        section_id, ConstString()});
198
7
    *offset_ptr += (c.tell() + payload_len);
199
7
  } else {
200
    // Invalid section id.
201
0
    return false;
202
0
  }
203
48
  return true;
204
48
}
205
206
10
bool ObjectFileWasm::DecodeSections() {
207
10
  lldb::offset_t offset = kWasmHeaderSize;
208
10
  if (IsInMemory()) {
209
2
    offset += m_memory_addr;
210
2
  }
211
212
58
  while (DecodeNextSection(&offset))
213
48
    ;
214
10
  return true;
215
10
}
216
217
size_t ObjectFileWasm::GetModuleSpecifications(
218
    const FileSpec &file, DataBufferSP &data_sp, offset_t data_offset,
219
2.68k
    offset_t file_offset, offset_t length, ModuleSpecList &specs) {
220
2.68k
  if (!ValidateModuleHeader(data_sp)) {
221
2.67k
    return 0;
222
2.67k
  }
223
224
7
  ModuleSpec spec(file, ArchSpec("wasm32-unknown-unknown-wasm"));
225
7
  specs.Append(spec);
226
7
  return 1;
227
2.68k
}
228
229
ObjectFileWasm::ObjectFileWasm(const ModuleSP &module_sp, DataBufferSP &data_sp,
230
                               offset_t data_offset, const FileSpec *file,
231
                               offset_t offset, offset_t length)
232
    : ObjectFile(module_sp, file, offset, length, data_sp, data_offset),
233
7
      m_arch("wasm32-unknown-unknown-wasm") {
234
7
  m_data.SetAddressByteSize(4);
235
7
}
236
237
ObjectFileWasm::ObjectFileWasm(const lldb::ModuleSP &module_sp,
238
                               lldb::DataBufferSP &header_data_sp,
239
                               const lldb::ProcessSP &process_sp,
240
                               lldb::addr_t header_addr)
241
    : ObjectFile(module_sp, process_sp, header_addr, header_data_sp),
242
2
      m_arch("wasm32-unknown-unknown-wasm") {}
243
244
0
bool ObjectFileWasm::ParseHeader() {
245
  // We already parsed the header during initialization.
246
0
  return true;
247
0
}
248
249
4
void ObjectFileWasm::ParseSymtab(Symtab &symtab) {}
250
251
37
static SectionType GetSectionTypeFromName(llvm::StringRef Name) {
252
37
  if (Name.consume_front(".debug_") || 
Name.consume_front(".zdebug_")2
) {
253
35
    return llvm::StringSwitch<SectionType>(Name)
254
35
        .Case("abbrev", eSectionTypeDWARFDebugAbbrev)
255
35
        .Case("abbrev.dwo", eSectionTypeDWARFDebugAbbrevDwo)
256
35
        .Case("addr", eSectionTypeDWARFDebugAddr)
257
35
        .Case("aranges", eSectionTypeDWARFDebugAranges)
258
35
        .Case("cu_index", eSectionTypeDWARFDebugCuIndex)
259
35
        .Case("frame", eSectionTypeDWARFDebugFrame)
260
35
        .Case("info", eSectionTypeDWARFDebugInfo)
261
35
        .Case("info.dwo", eSectionTypeDWARFDebugInfoDwo)
262
35
        .Cases("line", "line.dwo", eSectionTypeDWARFDebugLine)
263
35
        .Cases("line_str", "line_str.dwo", eSectionTypeDWARFDebugLineStr)
264
35
        .Case("loc", eSectionTypeDWARFDebugLoc)
265
35
        .Case("loc.dwo", eSectionTypeDWARFDebugLocDwo)
266
35
        .Case("loclists", eSectionTypeDWARFDebugLocLists)
267
35
        .Case("loclists.dwo", eSectionTypeDWARFDebugLocListsDwo)
268
35
        .Case("macinfo", eSectionTypeDWARFDebugMacInfo)
269
35
        .Cases("macro", "macro.dwo", eSectionTypeDWARFDebugMacro)
270
35
        .Case("names", eSectionTypeDWARFDebugNames)
271
35
        .Case("pubnames", eSectionTypeDWARFDebugPubNames)
272
35
        .Case("pubtypes", eSectionTypeDWARFDebugPubTypes)
273
35
        .Case("ranges", eSectionTypeDWARFDebugRanges)
274
35
        .Case("rnglists", eSectionTypeDWARFDebugRngLists)
275
35
        .Case("rnglists.dwo", eSectionTypeDWARFDebugRngListsDwo)
276
35
        .Case("str", eSectionTypeDWARFDebugStr)
277
35
        .Case("str.dwo", eSectionTypeDWARFDebugStrDwo)
278
35
        .Case("str_offsets", eSectionTypeDWARFDebugStrOffsets)
279
35
        .Case("str_offsets.dwo", eSectionTypeDWARFDebugStrOffsetsDwo)
280
35
        .Case("tu_index", eSectionTypeDWARFDebugTuIndex)
281
35
        .Case("types", eSectionTypeDWARFDebugTypes)
282
35
        .Case("types.dwo", eSectionTypeDWARFDebugTypesDwo)
283
35
        .Default(eSectionTypeOther);
284
35
  }
285
2
  return eSectionTypeOther;
286
37
}
287
288
11
void ObjectFileWasm::CreateSections(SectionList &unified_section_list) {
289
11
  if (m_sections_up)
290
2
    return;
291
292
9
  m_sections_up = std::make_unique<SectionList>();
293
294
9
  if (m_sect_infos.empty()) {
295
7
    DecodeSections();
296
7
  }
297
298
43
  for (const section_info &sect_info : m_sect_infos) {
299
43
    SectionType section_type = eSectionTypeOther;
300
43
    ConstString section_name;
301
43
    offset_t file_offset = sect_info.offset & 0xffffffff;
302
43
    addr_t vm_addr = file_offset;
303
43
    size_t vm_size = sect_info.size;
304
305
43
    if (llvm::wasm::WASM_SEC_CODE == sect_info.id) {
306
6
      section_type = eSectionTypeCode;
307
6
      section_name = ConstString("code");
308
309
      // A code address in DWARF for WebAssembly is the offset of an
310
      // instruction relative within the Code section of the WebAssembly file.
311
      // For this reason Section::GetFileAddress() must return zero for the
312
      // Code section.
313
6
      vm_addr = 0;
314
37
    } else {
315
37
      section_type = GetSectionTypeFromName(sect_info.name.GetStringRef());
316
37
      if (section_type == eSectionTypeOther)
317
2
        continue;
318
35
      section_name = sect_info.name;
319
35
      if (!IsInMemory()) {
320
31
        vm_size = 0;
321
31
        vm_addr = 0;
322
31
      }
323
35
    }
324
325
41
    SectionSP section_sp(
326
41
        new Section(GetModule(), // Module to which this section belongs.
327
41
                    this,        // ObjectFile to which this section belongs and
328
                                 // should read section data from.
329
41
                    section_type,   // Section ID.
330
41
                    section_name,   // Section name.
331
41
                    section_type,   // Section type.
332
41
                    vm_addr,        // VM address.
333
41
                    vm_size,        // VM size in bytes of this section.
334
41
                    file_offset,    // Offset of this section in the file.
335
41
                    sect_info.size, // Size of the section as found in the file.
336
41
                    0,              // Alignment of the section
337
41
                    0,              // Flags for this section.
338
41
                    1));            // Number of host bytes per target byte
339
41
    m_sections_up->AddSection(section_sp);
340
41
    unified_section_list.AddSection(section_sp);
341
41
  }
342
9
}
343
344
bool ObjectFileWasm::SetLoadAddress(Target &target, lldb::addr_t load_address,
345
3
                                    bool value_is_offset) {
346
  /// In WebAssembly, linear memory is disjointed from code space. The VM can
347
  /// load multiple instances of a module, which logically share the same code.
348
  /// We represent a wasm32 code address with 64-bits, like:
349
  /// 63            32 31             0
350
  /// +---------------+---------------+
351
  /// +   module_id   |     offset    |
352
  /// +---------------+---------------+
353
  /// where the lower 32 bits represent a module offset (relative to the module
354
  /// start not to the beginning of the code section) and the higher 32 bits
355
  /// uniquely identify the module in the WebAssembly VM.
356
  /// In other words, we assume that each WebAssembly module is loaded by the
357
  /// engine at a 64-bit address that starts at the boundary of 4GB pages, like
358
  /// 0x0000000400000000 for module_id == 4.
359
  /// These 64-bit addresses will be used to request code ranges for a specific
360
  /// module from the WebAssembly engine.
361
362
3
  assert(m_memory_addr == LLDB_INVALID_ADDRESS ||
363
3
         m_memory_addr == load_address);
364
365
0
  ModuleSP module_sp = GetModule();
366
3
  if (!module_sp)
367
0
    return false;
368
369
3
  DecodeSections();
370
371
3
  size_t num_loaded_sections = 0;
372
3
  SectionList *section_list = GetSectionList();
373
3
  if (!section_list)
374
0
    return false;
375
376
3
  const size_t num_sections = section_list->GetSize();
377
14
  for (size_t sect_idx = 0; sect_idx < num_sections; 
++sect_idx11
) {
378
11
    SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx));
379
11
    if (target.SetSectionLoadAddress(
380
11
            section_sp, load_address | section_sp->GetFileOffset())) {
381
7
      ++num_loaded_sections;
382
7
    }
383
11
  }
384
385
3
  return num_loaded_sections > 0;
386
3
}
387
388
61
DataExtractor ObjectFileWasm::ReadImageData(offset_t offset, uint32_t size) {
389
61
  DataExtractor data;
390
61
  if (m_file) {
391
51
    if (offset < GetByteSize()) {
392
43
      size = std::min(static_cast<uint64_t>(size), GetByteSize() - offset);
393
43
      auto buffer_sp = MapFileData(m_file, size, offset);
394
43
      return DataExtractor(buffer_sp, GetByteOrder(), GetAddressByteSize());
395
43
    }
396
51
  } else {
397
10
    ProcessSP process_sp(m_process_wp.lock());
398
10
    if (process_sp) {
399
10
      auto data_up = std::make_unique<DataBufferHeap>(size, 0);
400
10
      Status readmem_error;
401
10
      size_t bytes_read = process_sp->ReadMemory(
402
10
          offset, data_up->GetBytes(), data_up->GetByteSize(), readmem_error);
403
10
      if (bytes_read > 0) {
404
8
        DataBufferSP buffer_sp(data_up.release());
405
8
        data.SetData(buffer_sp, 0, buffer_sp->GetByteSize());
406
8
      }
407
10
    }
408
10
  }
409
410
18
  data.SetByteOrder(GetByteOrder());
411
18
  return data;
412
61
}
413
414
8
llvm::Optional<FileSpec> ObjectFileWasm::GetExternalDebugInfoFileSpec() {
415
8
  static ConstString g_sect_name_external_debug_info("external_debug_info");
416
417
29
  for (const section_info &sect_info : m_sect_infos) {
418
29
    if (g_sect_name_external_debug_info == sect_info.name) {
419
3
      const uint32_t kBufferSize = 1024;
420
3
      DataExtractor section_header_data =
421
3
          ReadImageData(sect_info.offset, kBufferSize);
422
3
      llvm::DataExtractor data = section_header_data.GetAsLLVM();
423
3
      llvm::DataExtractor::Cursor c(0);
424
3
      llvm::Optional<ConstString> symbols_url = GetWasmString(data, c);
425
3
      if (symbols_url)
426
3
        return FileSpec(symbols_url->GetStringRef());
427
3
    }
428
29
  }
429
5
  return llvm::None;
430
8
}
431
432
0
void ObjectFileWasm::Dump(Stream *s) {
433
0
  ModuleSP module_sp(GetModule());
434
0
  if (!module_sp)
435
0
    return;
436
437
0
  std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
438
439
0
  llvm::raw_ostream &ostream = s->AsRawOstream();
440
0
  ostream << static_cast<void *>(this) << ": ";
441
0
  s->Indent();
442
0
  ostream << "ObjectFileWasm, file = '";
443
0
  m_file.Dump(ostream);
444
0
  ostream << "', arch = ";
445
0
  ostream << GetArchitecture().GetArchitectureName() << "\n";
446
447
0
  SectionList *sections = GetSectionList();
448
0
  if (sections) {
449
0
    sections->Dump(s->AsRawOstream(), s->GetIndentLevel(), nullptr, true,
450
0
                   UINT32_MAX);
451
0
  }
452
0
  ostream << "\n";
453
0
  DumpSectionHeaders(ostream);
454
0
  ostream << "\n";
455
0
}
456
457
void ObjectFileWasm::DumpSectionHeader(llvm::raw_ostream &ostream,
458
0
                                       const section_info_t &sh) {
459
0
  ostream << llvm::left_justify(sh.name.GetStringRef(), 16) << " "
460
0
          << llvm::format_hex(sh.offset, 10) << " "
461
0
          << llvm::format_hex(sh.size, 10) << " " << llvm::format_hex(sh.id, 6)
462
0
          << "\n";
463
0
}
464
465
0
void ObjectFileWasm::DumpSectionHeaders(llvm::raw_ostream &ostream) {
466
0
  ostream << "Section Headers\n";
467
0
  ostream << "IDX  name             addr       size       id\n";
468
0
  ostream << "==== ---------------- ---------- ---------- ------\n";
469
470
0
  uint32_t idx = 0;
471
0
  for (auto pos = m_sect_infos.begin(); pos != m_sect_infos.end();
472
0
       ++pos, ++idx) {
473
0
    ostream << "[" << llvm::format_decimal(idx, 2) << "] ";
474
0
    ObjectFileWasm::DumpSectionHeader(ostream, *pos);
475
0
  }
476
0
}