Skip to content

Commit

Permalink
[WebAssembly] Add support for debug (DWARF) sections
Browse files Browse the repository at this point in the history
Specifically add support for custom sections that contain
relocations, and for the two new relocation types needed
by DWARF sections.

See: https://reviews.llvm.org/D44184

Patch by Yury Delendik!

Differential Revision: https://reviews.llvm.org/D44184

llvm-svn: 331566
  • Loading branch information
sbc100 committed May 4, 2018
1 parent e0fb481 commit d177ab2
Show file tree
Hide file tree
Showing 12 changed files with 391 additions and 19 deletions.
68 changes: 68 additions & 0 deletions lld/test/wasm/Inputs/debuginfo1.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
; ModuleID = 'hi.c'
source_filename = "hi.c"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown-wasm"

; // hi.c:
; extern void foo(int);
;
; int test(int t) {
; return t * t;
; }
;
; int _start() {
; foo(test(10));
; return 0;
; }

; Function Attrs: nounwind readnone
define hidden i32 @test(i32 %t) local_unnamed_addr #0 !dbg !7 {
entry:
call void @llvm.dbg.value(metadata i32 %t, metadata !12, metadata !DIExpression()), !dbg !13
%mul = mul nsw i32 %t, %t, !dbg !14
ret i32 %mul, !dbg !15
}

; Function Attrs: nounwind
define hidden i32 @_start() local_unnamed_addr #1 !dbg !16 {
entry:
tail call void @foo(i32 100) #4, !dbg !19
ret i32 0, !dbg !20
}

declare void @foo(i32) local_unnamed_addr #2

; Function Attrs: nounwind readnone speculatable
declare void @llvm.dbg.value(metadata, metadata, metadata) #3

attributes #0 = { nounwind readnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #2 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #3 = { nounwind readnone speculatable }
attributes #4 = { nounwind }

!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!3, !4, !5}
!llvm.ident = !{!6}

!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 7.0.0 (trunk 331321)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
!1 = !DIFile(filename: "hi.c", directory: "/Users/yury/llvmwasm")
!2 = !{}
!3 = !{i32 2, !"Dwarf Version", i32 4}
!4 = !{i32 2, !"Debug Info Version", i32 3}
!5 = !{i32 1, !"wchar_size", i32 4}
!6 = !{!"clang version 7.0.0 (trunk 331321)"}
!7 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 3, type: !8, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !11)
!8 = !DISubroutineType(types: !9)
!9 = !{!10, !10}
!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!11 = !{!12}
!12 = !DILocalVariable(name: "t", arg: 1, scope: !7, file: !1, line: 3, type: !10)
!13 = !DILocation(line: 3, column: 14, scope: !7)
!14 = !DILocation(line: 4, column: 12, scope: !7)
!15 = !DILocation(line: 4, column: 3, scope: !7)
!16 = distinct !DISubprogram(name: "_start", scope: !1, file: !1, line: 7, type: !17, isLocal: false, isDefinition: true, scopeLine: 7, isOptimized: true, unit: !0, variables: !2)
!17 = !DISubroutineType(types: !18)
!18 = !{!10}
!19 = !DILocation(line: 8, column: 3, scope: !16)
!20 = !DILocation(line: 9, column: 3, scope: !16)
64 changes: 64 additions & 0 deletions lld/test/wasm/Inputs/debuginfo2.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
; ModuleID = 'hi_foo.c'
source_filename = "hi_foo.c"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown-wasm"

; // hi_foo.c:
; int y[2] = {23, 41};
;
; void foo(int p) {
; y[p & 1]++;
; }

@y = hidden local_unnamed_addr global [2 x i32] [i32 23, i32 41], align 4, !dbg !0

; Function Attrs: nounwind
define hidden void @foo(i32 %p) local_unnamed_addr #0 !dbg !14 {
entry:
call void @llvm.dbg.value(metadata i32 %p, metadata !18, metadata !DIExpression()), !dbg !19
%and = and i32 %p, 1, !dbg !20
%arrayidx = getelementptr inbounds [2 x i32], [2 x i32]* @y, i32 0, i32 %and, !dbg !21
%0 = load i32, i32* %arrayidx, align 4, !dbg !22, !tbaa !23
%inc = add nsw i32 %0, 1, !dbg !22
store i32 %inc, i32* %arrayidx, align 4, !dbg !22, !tbaa !23
ret void, !dbg !27
}

; Function Attrs: nounwind readnone speculatable
declare void @llvm.dbg.value(metadata, metadata, metadata) #1

attributes #0 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { nounwind readnone speculatable }

!llvm.dbg.cu = !{!2}
!llvm.module.flags = !{!10, !11, !12}
!llvm.ident = !{!13}

!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
!1 = distinct !DIGlobalVariable(name: "y", scope: !2, file: !3, line: 1, type: !6, isLocal: false, isDefinition: true)
!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 7.0.0 (trunk 331321)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5)
!3 = !DIFile(filename: "hi_foo.c", directory: "/Users/yury/llvmwasm")
!4 = !{}
!5 = !{!0}
!6 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 64, elements: !8)
!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!8 = !{!9}
!9 = !DISubrange(count: 2)
!10 = !{i32 2, !"Dwarf Version", i32 4}
!11 = !{i32 2, !"Debug Info Version", i32 3}
!12 = !{i32 1, !"wchar_size", i32 4}
!13 = !{!"clang version 7.0.0 (trunk 331321)"}
!14 = distinct !DISubprogram(name: "foo", scope: !3, file: !3, line: 3, type: !15, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: true, unit: !2, variables: !17)
!15 = !DISubroutineType(types: !16)
!16 = !{null, !7}
!17 = !{!18}
!18 = !DILocalVariable(name: "p", arg: 1, scope: !14, file: !3, line: 3, type: !7)
!19 = !DILocation(line: 3, column: 14, scope: !14)
!20 = !DILocation(line: 4, column: 7, scope: !14)
!21 = !DILocation(line: 4, column: 3, scope: !14)
!22 = !DILocation(line: 4, column: 11, scope: !14)
!23 = !{!24, !24, i64 0}
!24 = !{!"int", !25, i64 0}
!25 = !{!"omnipotent char", !26, i64 0}
!26 = !{!"Simple C/C++ TBAA"}
!27 = !DILocation(line: 5, column: 1, scope: !14)
73 changes: 73 additions & 0 deletions lld/test/wasm/debuginfo.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
RUN: llc -filetype=obj %p/Inputs/debuginfo1.ll -o %t.debuginfo1.o
RUN: llc -filetype=obj %p/Inputs/debuginfo2.ll -o %t.debuginfo2.o
RUN: wasm-ld -r -o %t.wasm %t.debuginfo1.o %t.debuginfo2.o
RUN: llvm-dwarfdump %t.wasm | FileCheck %s

CHECK: file format WASM

CHECK: .debug_info contents:
CHECK: DW_TAG_compile_unit
CHECK-NEXT: DW_AT_producer ("clang version 7.0.0 (trunk 331321)")
CHECK-NEXT: DW_AT_language (DW_LANG_C99)
CHECK-NEXT: DW_AT_name ("hi.c")

CHECK: DW_TAG_subprogram
CHECK-NEXT: DW_AT_low_pc
CHECK-NEXT: DW_AT_high_pc
CHECK-NEXT: DW_AT_name ("test")
CHECK-NEXT: DW_AT_decl_file ("/Users/yury/llvmwasm/hi.c")
CHECK-NEXT: DW_AT_decl_line (3)
CHECK-NEXT: DW_AT_prototyped (true)

CHECK: DW_TAG_formal_parameter
CHECK-NEXT: DW_AT_name ("t")
CHECK-NEXT: DW_AT_decl_file ("/Users/yury/llvmwasm/hi.c")
CHECK-NEXT: DW_AT_decl_line (3)

CHECK: DW_TAG_subprogram
CHECK-NEXT: DW_AT_low_pc
CHECK-NEXT: DW_AT_high_pc
CHECK-NEXT: DW_AT_name ("_start")
CHECK-NEXT: DW_AT_decl_file ("/Users/yury/llvmwasm/hi.c")
CHECK-NEXT: DW_AT_decl_line (7)

CHECK: DW_TAG_base_type
CHECK-NEXT: DW_AT_name ("int")
CHECK-NEXT: DW_AT_encoding (DW_ATE_signed)
CHECK-NEXT: DW_AT_byte_size (0x04)

CHECK: DW_TAG_compile_unit
CHECK-NEXT: DW_AT_producer ("clang version 7.0.0 (trunk 331321)")
CHECK-NEXT: DW_AT_language (DW_LANG_C99)
CHECK-NEXT: DW_AT_name ("hi_foo.c")

CHECK: DW_TAG_variable
CHECK-NEXT: DW_AT_name ("y")
CHECK-NEXT: "int[]"

CHECK: DW_TAG_array_type

CHECK: DW_TAG_subrange_type

CHECK: DW_TAG_base_type
CHECK-NEXT: DW_AT_name ("int")
CHECK-NEXT: DW_AT_encoding (DW_ATE_signed)
CHECK-NEXT: DW_AT_byte_size (0x04)

CHECK: DW_TAG_base_type
CHECK-NEXT: DW_AT_name ("__ARRAY_SIZE_TYPE__")
CHECK-NEXT: DW_AT_byte_size (0x08)
CHECK-NEXT: DW_AT_encoding (DW_ATE_unsigned)

CHECK: DW_TAG_subprogram
CHECK-NEXT: DW_AT_low_pc
CHECK-NEXT: DW_AT_high_pc
CHECK-NEXT: DW_AT_name ("foo")
CHECK-NEXT: DW_AT_decl_file ("/Users/yury/llvmwasm/hi_foo.c")
CHECK-NEXT: DW_AT_decl_line (3)

CHECK: DW_TAG_formal_parameter
CHECK-NEXT: DW_AT_name ("p")
CHECK-NEXT: DW_AT_decl_file ("/Users/yury/llvmwasm/hi_foo.c")
CHECK-NEXT: DW_AT_decl_line (3)

6 changes: 5 additions & 1 deletion lld/wasm/InputChunks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ void InputChunk::writeTo(uint8_t *Buf) const {
break;
case R_WEBASSEMBLY_TABLE_INDEX_I32:
case R_WEBASSEMBLY_MEMORY_ADDR_I32:
case R_WEBASSEMBLY_FUNCTION_OFFSET_I32:
case R_WEBASSEMBLY_SECTION_OFFSET_I32:
ExistingValue = static_cast<uint32_t>(read32le(Loc));
write32le(Loc, Value);
break;
Expand Down Expand Up @@ -124,7 +126,9 @@ void InputChunk::writeRelocations(raw_ostream &OS) const {
case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
case R_WEBASSEMBLY_MEMORY_ADDR_I32:
writeSleb128(OS, Rel.Addend, "reloc addend");
case R_WEBASSEMBLY_FUNCTION_OFFSET_I32:
case R_WEBASSEMBLY_SECTION_OFFSET_I32:
writeSleb128(OS, File->calcNewAddend(Rel), "reloc addend");
break;
}
}
Expand Down
2 changes: 2 additions & 0 deletions lld/wasm/InputChunks.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ class InputFunction : public InputChunk {
StringRef getName() const override { return Function->SymbolName; }
StringRef getDebugName() const override { return Function->DebugName; }
uint32_t getComdat() const override { return Function->Comdat; }
const ArrayRef<uint8_t> getFunctionBody() const { return Function->Body; }
uint32_t getFunctionInputOffset() const { return getInputSectionOffset(); }
uint32_t getFunctionIndex() const { return FunctionIndex.getValue(); }
bool hasFunctionIndex() const { return FunctionIndex.hasValue(); }
void setFunctionIndex(uint32_t Index);
Expand Down
71 changes: 65 additions & 6 deletions lld/wasm/InputFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "lld/Common/Memory.h"
#include "llvm/Object/Binary.h"
#include "llvm/Object/Wasm.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/raw_ostream.h"

#define DEBUG_TYPE "lld"
Expand All @@ -42,6 +43,12 @@ Optional<MemoryBufferRef> lld::wasm::readFile(StringRef Path) {
return MBRef;
}

static size_t getFunctionCodeOffset(ArrayRef<uint8_t> FunctionBody) {
unsigned Count;
llvm::decodeULEB128(FunctionBody.data(), &Count);
return Count;
}

void ObjFile::dumpInfo() const {
log("info for: " + getName() +
"\n Symbols : " + Twine(Symbols.size()) +
Expand All @@ -60,6 +67,22 @@ uint32_t ObjFile::calcNewIndex(const WasmRelocation &Reloc) const {
return Symbols[Reloc.Index]->getOutputSymbolIndex();
}

// Relocations can contain addend for combined sections. This function takes a
// relocation and returns updated addend by offset in the output section.
uint32_t ObjFile::calcNewAddend(const WasmRelocation &Reloc) const {
switch (Reloc.Type) {
case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
case R_WEBASSEMBLY_MEMORY_ADDR_I32:
case R_WEBASSEMBLY_FUNCTION_OFFSET_I32:
return Reloc.Addend;
case R_WEBASSEMBLY_SECTION_OFFSET_I32:
return getSectionSymbol(Reloc.Index)->Section->OutputOffset + Reloc.Addend;
default:
llvm_unreachable("unexpected relocation type");
}
}

// Calculate the value we expect to find at the relocation location.
// This is used as a sanity check before applying a relocation to a given
// location. It is useful for catching bugs in the compiler and linker.
Expand All @@ -80,6 +103,16 @@ uint32_t ObjFile::calcExpectedValue(const WasmRelocation &Reloc) const {
return Segment.Data.Offset.Value.Int32 + Sym.Info.DataRef.Offset +
Reloc.Addend;
}
case R_WEBASSEMBLY_FUNCTION_OFFSET_I32:
if (auto *Sym = dyn_cast<DefinedFunction>(getFunctionSymbol(Reloc.Index))) {
size_t FunctionCodeOffset =
getFunctionCodeOffset(Sym->Function->getFunctionBody());
return Sym->Function->getFunctionInputOffset() + FunctionCodeOffset +
Reloc.Addend;
}
return 0;
case R_WEBASSEMBLY_SECTION_OFFSET_I32:
return Reloc.Addend;
case R_WEBASSEMBLY_TYPE_INDEX_LEB:
return Reloc.Index;
case R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
Expand Down Expand Up @@ -110,6 +143,15 @@ uint32_t ObjFile::calcNewValue(const WasmRelocation &Reloc) const {
return getFunctionSymbol(Reloc.Index)->getFunctionIndex();
case R_WEBASSEMBLY_GLOBAL_INDEX_LEB:
return getGlobalSymbol(Reloc.Index)->getGlobalIndex();
case R_WEBASSEMBLY_FUNCTION_OFFSET_I32:
if (auto *Sym = dyn_cast<DefinedFunction>(getFunctionSymbol(Reloc.Index))) {
size_t FunctionCodeOffset =
getFunctionCodeOffset(Sym->Function->getFunctionBody());
return Sym->Function->OutputOffset + FunctionCodeOffset + Reloc.Addend;
}
return 0;
case R_WEBASSEMBLY_SECTION_OFFSET_I32:
return getSectionSymbol(Reloc.Index)->Section->OutputOffset + Reloc.Addend;
default:
llvm_unreachable("unknown relocation type");
}
Expand Down Expand Up @@ -147,14 +189,19 @@ void ObjFile::parse() {

// Find the code and data sections. Wasm objects can have at most one code
// and one data section.
uint32_t SectionIndex = 0;
for (const SectionRef &Sec : WasmObj->sections()) {
const WasmSection &Section = WasmObj->getWasmSection(Sec);
if (Section.Type == WASM_SEC_CODE)
if (Section.Type == WASM_SEC_CODE) {
CodeSection = &Section;
else if (Section.Type == WASM_SEC_DATA)
} else if (Section.Type == WASM_SEC_DATA) {
DataSection = &Section;
else if (Section.Type == WASM_SEC_CUSTOM)
} else if (Section.Type == WASM_SEC_CUSTOM) {
CustomSections.emplace_back(make<InputSection>(Section, this));
CustomSections.back()->copyRelocations(Section);
CustomSectionsByIndex[SectionIndex] = CustomSections.back();
}
SectionIndex++;
}

TypeMap.resize(getWasmObj()->types().size());
Expand Down Expand Up @@ -215,6 +262,10 @@ GlobalSymbol *ObjFile::getGlobalSymbol(uint32_t Index) const {
return cast<GlobalSymbol>(Symbols[Index]);
}

SectionSymbol *ObjFile::getSectionSymbol(uint32_t Index) const {
return cast<SectionSymbol>(Symbols[Index]);
}

DataSymbol *ObjFile::getDataSymbol(uint32_t Index) const {
return cast<DataSymbol>(Symbols[Index]);
}
Expand Down Expand Up @@ -253,14 +304,20 @@ Symbol *ObjFile::createDefined(const WasmSymbol &Sym) {
return make<DefinedData>(Name, Flags, this, Seg, Offset, Size);
return Symtab->addDefinedData(Name, Flags, this, Seg, Offset, Size);
}
case WASM_SYMBOL_TYPE_GLOBAL:
case WASM_SYMBOL_TYPE_GLOBAL: {
InputGlobal *Global =
Globals[Sym.Info.ElementIndex - WasmObj->getNumImportedGlobals()];
if (Sym.isBindingLocal())
return make<DefinedGlobal>(Name, Flags, this, Global);
return Symtab->addDefinedGlobal(Name, Flags, this, Global);
}
llvm_unreachable("unkown symbol kind");
case WASM_SYMBOL_TYPE_SECTION: {
InputSection *Section = CustomSectionsByIndex[Sym.Info.ElementIndex];
assert(Sym.isBindingLocal());
return make<SectionSymbol>(Name, Flags, Section, this);
}
}
llvm_unreachable("unknown symbol kind");
}

Symbol *ObjFile::createUndefined(const WasmSymbol &Sym) {
Expand All @@ -274,8 +331,10 @@ Symbol *ObjFile::createUndefined(const WasmSymbol &Sym) {
return Symtab->addUndefinedData(Name, Flags, this);
case WASM_SYMBOL_TYPE_GLOBAL:
return Symtab->addUndefinedGlobal(Name, Flags, this, Sym.GlobalType);
case WASM_SYMBOL_TYPE_SECTION:
llvm_unreachable("section symbols cannot be undefined");
}
llvm_unreachable("unkown symbol kind");
llvm_unreachable("unknown symbol kind");
}

void ArchiveFile::parse() {
Expand Down
Loading

0 comments on commit d177ab2

Please sign in to comment.