Skip to content

Commit 9b3254d

Browse files
paolosevMSFTdschuff
authored andcommitted
[LLDB] Add SymbolVendorWasm plugin for WebAssembly debugging
Add plugin class SymbolVendorWasm, with the logic to manage debug symbols for Wasm modules. Reviewers: clayborg, labath, aprantl, sbc100, teemperor Reviewed By: labath Tags: #lldb Differential Revision: https://reviews.llvm.org/D72650
1 parent 3478551 commit 9b3254d

File tree

9 files changed

+328
-0
lines changed

9 files changed

+328
-0
lines changed

lldb/source/API/SystemInitializerFull.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
#include "Plugins/SymbolFile/PDB/SymbolFilePDB.h"
9696
#include "Plugins/SymbolFile/Symtab/SymbolFileSymtab.h"
9797
#include "Plugins/SymbolVendor/ELF/SymbolVendorELF.h"
98+
#include "Plugins/SymbolVendor/wasm/SymbolVendorWasm.h"
9899
#include "Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.h"
99100
#include "Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h"
100101
#include "Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h"
@@ -242,6 +243,7 @@ llvm::Error SystemInitializerFull::Initialize() {
242243
SymbolFileDWARF::Initialize();
243244
SymbolFilePDB::Initialize();
244245
SymbolFileSymtab::Initialize();
246+
wasm::SymbolVendorWasm::Initialize();
245247
UnwindAssemblyInstEmulation::Initialize();
246248
UnwindAssembly_x86::Initialize();
247249

@@ -334,6 +336,7 @@ void SystemInitializerFull::Terminate() {
334336
ThreadSanitizerRuntime::Terminate();
335337
UndefinedBehaviorSanitizerRuntime::Terminate();
336338
MainThreadCheckerRuntime::Terminate();
339+
wasm::SymbolVendorWasm::Terminate();
337340
SymbolVendorELF::Terminate();
338341
breakpad::SymbolFileBreakpad::Terminate();
339342
SymbolFileDWARF::Terminate();

lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ GetWasmString(llvm::DataExtractor &data, llvm::DataExtractor::Cursor &c) {
7272
return ConstString(str);
7373
}
7474

75+
char ObjectFileWasm::ID;
76+
7577
void ObjectFileWasm::Initialize() {
7678
PluginManager::RegisterPlugin(GetPluginNameStatic(),
7779
GetPluginDescriptionStatic(), CreateInstance,
@@ -177,6 +179,9 @@ bool ObjectFileWasm::DecodeNextSection(lldb::offset_t *offset_ptr) {
177179
return false;
178180

179181
if (section_id == llvm::wasm::WASM_SEC_CUSTOM) {
182+
// Custom sections have the id 0. Their contents consist of a name
183+
// identifying the custom section, followed by an uninterpreted sequence
184+
// of bytes.
180185
lldb::offset_t prev_offset = c.tell();
181186
llvm::Optional<ConstString> sect_name = GetWasmString(data, c);
182187
if (!sect_name)
@@ -389,6 +394,24 @@ DataExtractor ObjectFileWasm::ReadImageData(uint64_t offset, size_t size) {
389394
return data;
390395
}
391396

397+
llvm::Optional<FileSpec> ObjectFileWasm::GetExternalDebugInfoFileSpec() {
398+
static ConstString g_sect_name_external_debug_info("external_debug_info");
399+
400+
for (const section_info &sect_info : m_sect_infos) {
401+
if (g_sect_name_external_debug_info == sect_info.name) {
402+
const uint32_t kBufferSize = 1024;
403+
DataExtractor section_header_data =
404+
ReadImageData(sect_info.offset, kBufferSize);
405+
llvm::DataExtractor data = section_header_data.GetAsLLVM();
406+
llvm::DataExtractor::Cursor c(0);
407+
llvm::Optional<ConstString> symbols_url = GetWasmString(data, c);
408+
if (symbols_url)
409+
return FileSpec(symbols_url->GetStringRef());
410+
}
411+
}
412+
return llvm::None;
413+
}
414+
392415
void ObjectFileWasm::Dump(Stream *s) {
393416
ModuleSP module_sp(GetModule());
394417
if (!module_sp)

lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,15 @@ class ObjectFileWasm : public ObjectFile {
5252
uint32_t GetPluginVersion() override { return 1; }
5353
/// \}
5454

55+
/// LLVM RTTI support
56+
/// \{
57+
static char ID;
58+
bool isA(const void *ClassID) const override {
59+
return ClassID == &ID || ObjectFile::isA(ClassID);
60+
}
61+
static bool classof(const ObjectFile *obj) { return obj->isA(&ID); }
62+
/// \}
63+
5564
/// ObjectFile Protocol.
5665
/// \{
5766
bool ParseHeader() override;
@@ -97,6 +106,12 @@ class ObjectFileWasm : public ObjectFile {
97106
}
98107
/// \}
99108

109+
/// A Wasm module that has external DWARF debug information should contain a
110+
/// custom section named "external_debug_info", whose payload is an UTF-8
111+
/// encoded string that points to a Wasm module that contains the debug
112+
/// information for this module.
113+
llvm::Optional<FileSpec> GetExternalDebugInfoFileSpec();
114+
100115
private:
101116
ObjectFileWasm(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp,
102117
lldb::offset_t data_offset, const FileSpec *file,

lldb/source/Plugins/SymbolVendor/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
33
endif()
44

55
add_subdirectory(ELF)
6+
add_subdirectory(wasm)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
add_lldb_library(lldbPluginSymbolVendorWasm PLUGIN
2+
SymbolVendorWasm.cpp
3+
4+
LINK_LIBS
5+
lldbCore
6+
lldbHost
7+
lldbSymbol
8+
lldbPluginObjectFileWasm
9+
)
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
//===-- SymbolVendorWasm.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 "SymbolVendorWasm.h"
10+
11+
#include <string.h>
12+
13+
#include "Plugins/ObjectFile/wasm/ObjectFileWasm.h"
14+
#include "lldb/Core/Module.h"
15+
#include "lldb/Core/ModuleSpec.h"
16+
#include "lldb/Core/PluginManager.h"
17+
#include "lldb/Core/Section.h"
18+
#include "lldb/Host/Host.h"
19+
#include "lldb/Symbol/LocateSymbolFile.h"
20+
#include "lldb/Symbol/ObjectFile.h"
21+
#include "lldb/Target/Target.h"
22+
#include "lldb/Utility/StreamString.h"
23+
#include "lldb/Utility/Timer.h"
24+
25+
using namespace lldb;
26+
using namespace lldb_private;
27+
using namespace lldb_private::wasm;
28+
29+
// SymbolVendorWasm constructor
30+
SymbolVendorWasm::SymbolVendorWasm(const lldb::ModuleSP &module_sp)
31+
: SymbolVendor(module_sp) {}
32+
33+
void SymbolVendorWasm::Initialize() {
34+
PluginManager::RegisterPlugin(GetPluginNameStatic(),
35+
GetPluginDescriptionStatic(), CreateInstance);
36+
}
37+
38+
void SymbolVendorWasm::Terminate() {
39+
PluginManager::UnregisterPlugin(CreateInstance);
40+
}
41+
42+
lldb_private::ConstString SymbolVendorWasm::GetPluginNameStatic() {
43+
static ConstString g_name("WASM");
44+
return g_name;
45+
}
46+
47+
const char *SymbolVendorWasm::GetPluginDescriptionStatic() {
48+
return "Symbol vendor for WASM that looks for dwo files that match "
49+
"executables.";
50+
}
51+
52+
// CreateInstance
53+
//
54+
// Platforms can register a callback to use when creating symbol vendors to
55+
// allow for complex debug information file setups, and to also allow for
56+
// finding separate debug information files.
57+
SymbolVendor *
58+
SymbolVendorWasm::CreateInstance(const lldb::ModuleSP &module_sp,
59+
lldb_private::Stream *feedback_strm) {
60+
if (!module_sp)
61+
return nullptr;
62+
63+
ObjectFileWasm *obj_file =
64+
llvm::dyn_cast_or_null<ObjectFileWasm>(module_sp->GetObjectFile());
65+
if (!obj_file)
66+
return nullptr;
67+
68+
// If the main object file already contains debug info, then we are done.
69+
if (obj_file->GetSectionList()->FindSectionByType(
70+
lldb::eSectionTypeDWARFDebugInfo, true))
71+
return nullptr;
72+
73+
static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
74+
Timer scoped_timer(func_cat, "SymbolVendorWasm::CreateInstance (module = %s)",
75+
module_sp->GetFileSpec().GetPath().c_str());
76+
77+
ModuleSpec module_spec;
78+
module_spec.GetFileSpec() = obj_file->GetFileSpec();
79+
FileSystem::Instance().Resolve(module_spec.GetFileSpec());
80+
module_spec.GetUUID() = obj_file->GetUUID();
81+
82+
// A Wasm module may have a custom section named "external_debug_info" whose
83+
// content is the absolute or relative path of the Wasm module that contains
84+
// debug symbols for this module.
85+
llvm::Optional<FileSpec> symbol_file_spec =
86+
obj_file->GetExternalDebugInfoFileSpec();
87+
if (!symbol_file_spec)
88+
return nullptr;
89+
module_spec.GetSymbolFileSpec() = *symbol_file_spec;
90+
91+
FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths();
92+
FileSpec sym_fspec =
93+
Symbols::LocateExecutableSymbolFile(module_spec, search_paths);
94+
if (!sym_fspec)
95+
return nullptr;
96+
97+
DataBufferSP sym_file_data_sp;
98+
lldb::offset_t sym_file_data_offset = 0;
99+
ObjectFileSP sym_objfile_sp = ObjectFile::FindPlugin(
100+
module_sp, &sym_fspec, 0, FileSystem::Instance().GetByteSize(sym_fspec),
101+
sym_file_data_sp, sym_file_data_offset);
102+
if (!sym_objfile_sp)
103+
return nullptr;
104+
105+
// This objfile is for debugging purposes.
106+
sym_objfile_sp->SetType(ObjectFile::eTypeDebugInfo);
107+
108+
SymbolVendorWasm *symbol_vendor = new SymbolVendorWasm(module_sp);
109+
110+
// Get the module unified section list and add our debug sections to
111+
// that.
112+
SectionList *module_section_list = module_sp->GetSectionList();
113+
SectionList *objfile_section_list = sym_objfile_sp->GetSectionList();
114+
115+
static const SectionType g_sections[] = {
116+
eSectionTypeDWARFDebugAbbrev, eSectionTypeDWARFDebugAddr,
117+
eSectionTypeDWARFDebugAranges, eSectionTypeDWARFDebugCuIndex,
118+
eSectionTypeDWARFDebugFrame, eSectionTypeDWARFDebugInfo,
119+
eSectionTypeDWARFDebugLine, eSectionTypeDWARFDebugLineStr,
120+
eSectionTypeDWARFDebugLoc, eSectionTypeDWARFDebugLocLists,
121+
eSectionTypeDWARFDebugMacInfo, eSectionTypeDWARFDebugMacro,
122+
eSectionTypeDWARFDebugPubNames, eSectionTypeDWARFDebugPubTypes,
123+
eSectionTypeDWARFDebugRanges, eSectionTypeDWARFDebugRngLists,
124+
eSectionTypeDWARFDebugStr, eSectionTypeDWARFDebugStrOffsets,
125+
eSectionTypeDWARFDebugTypes};
126+
for (SectionType section_type : g_sections) {
127+
if (SectionSP section_sp =
128+
objfile_section_list->FindSectionByType(section_type, true)) {
129+
if (SectionSP module_section_sp =
130+
module_section_list->FindSectionByType(section_type, true))
131+
module_section_list->ReplaceSection(module_section_sp->GetID(),
132+
section_sp);
133+
else
134+
module_section_list->AddSection(section_sp);
135+
}
136+
}
137+
138+
symbol_vendor->AddSymbolFileRepresentation(sym_objfile_sp);
139+
return symbol_vendor;
140+
}
141+
142+
// PluginInterface protocol
143+
ConstString SymbolVendorWasm::GetPluginName() { return GetPluginNameStatic(); }
144+
145+
uint32_t SymbolVendorWasm::GetPluginVersion() { return 1; }
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//===-- SymbolVendorWasm.h --------------------------------------*- C++ -*-===//
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+
#ifndef liblldb_SymbolVendorWasm_h_
10+
#define liblldb_SymbolVendorWasm_h_
11+
12+
#include "lldb/Symbol/SymbolVendor.h"
13+
#include "lldb/lldb-private.h"
14+
15+
namespace lldb_private {
16+
namespace wasm {
17+
18+
class SymbolVendorWasm : public lldb_private::SymbolVendor {
19+
public:
20+
SymbolVendorWasm(const lldb::ModuleSP &module_sp);
21+
22+
static void Initialize();
23+
static void Terminate();
24+
static lldb_private::ConstString GetPluginNameStatic();
25+
static const char *GetPluginDescriptionStatic();
26+
27+
static lldb_private::SymbolVendor *
28+
CreateInstance(const lldb::ModuleSP &module_sp,
29+
lldb_private::Stream *feedback_strm);
30+
31+
/// PluginInterface protocol.
32+
/// \{
33+
lldb_private::ConstString GetPluginName() override;
34+
uint32_t GetPluginVersion() override;
35+
/// \}
36+
37+
private:
38+
DISALLOW_COPY_AND_ASSIGN(SymbolVendorWasm);
39+
};
40+
41+
} // namespace wasm
42+
} // namespace lldb_private
43+
44+
#endif // liblldb_SymbolVendorWasm_h_
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# RUN: yaml2obj -docnum=1 %s > test.wasm
2+
# RUN: yaml2obj -docnum=2 %s > test_sym.wasm
3+
# RUN: lldb-test object-file test.wasm | FileCheck %s
4+
5+
# This test checks that SymbolVendorWasm correctly loads DWARF debug sections
6+
# that have been stripped out into a separated Wasm module. The original Wasm
7+
# module contains a "external_debug_info" custom section with the absolute or
8+
# relative path of the debug module.
9+
10+
# CHECK: Plugin name: wasm
11+
# CHECK: Architecture: wasm32-unknown-unknown-wasm
12+
# CHECK: UUID:
13+
# CHECK: Executable: true
14+
# CHECK: Stripped: true
15+
# CHECK: Type: executable
16+
# CHECK: Strata: user
17+
# CHECK: Base VM address: 0xa
18+
19+
# CHECK: Name: code
20+
# CHECK: Type: code
21+
# CHECK: VM address: 0x0
22+
# CHECK: VM size: 56
23+
# CHECK: File size: 56
24+
25+
# CHECK: Name: .debug_info
26+
# CHECK: Type: dwarf-info
27+
# CHECK: VM address: 0x0
28+
# CHECK: VM size: 0
29+
# CHECK: File size: 2
30+
31+
# CHECK: Name: .debug_abbrev
32+
# CHECK: Type: dwarf-abbrev
33+
# CHECK: VM address: 0x0
34+
# CHECK: VM size: 0
35+
# CHECK: File size: 2
36+
37+
# CHECK: Name: .debug_line
38+
# CHECK: Type: dwarf-line
39+
# CHECK: VM address: 0x0
40+
# CHECK: VM size: 0
41+
# CHECK: File size: 2
42+
43+
# CHECK: Name: .debug_str
44+
# CHECK: Type: dwarf-str
45+
# CHECK: VM address: 0x0
46+
# CHECK: VM size: 0
47+
# CHECK: File size: 3
48+
49+
--- !WASM
50+
FileHeader:
51+
Version: 0x00000001
52+
Sections:
53+
- Type: CODE
54+
Functions:
55+
- Index: 0
56+
Locals:
57+
- Type: I32
58+
Count: 6
59+
Body: 238080808000210141102102200120026B21032003200036020C200328020C2104200328020C2105200420056C210620060F0B
60+
- Type: CUSTOM
61+
Name: external_debug_info
62+
Payload: 0D746573745F73796D2E7761736D # test_sym.wasm
63+
64+
...
65+
66+
67+
--- !WASM
68+
FileHeader:
69+
Version: 0x00000001
70+
Sections:
71+
72+
- Type: CUSTOM
73+
Name: .debug_info
74+
Payload: 4C00
75+
- Type: CUSTOM
76+
Name: .debug_abbrev
77+
Payload: 0111
78+
- Type: CUSTOM
79+
Name: .debug_line
80+
Payload: 5100
81+
- Type: CUSTOM
82+
Name: .debug_str
83+
Payload: 636CFF
84+
85+
...

0 commit comments

Comments
 (0)