diff options
author | Yonghong Song <yhs@fb.com> | 2018-10-12 17:01:46 +0000 |
---|---|---|
committer | Yonghong Song <yhs@fb.com> | 2018-10-12 17:01:46 +0000 |
commit | 6c2327a09e2820302a3a2be0f0dbe24530ca5e7c (patch) | |
tree | db78847cffe39e7e33fa9d72d663cb79524d2542 /llvm/lib/CodeGen/AsmPrinter | |
parent | e67b68f341e655b2fd4beff337fe261fd12cb52a (diff) | |
download | bcm5719-llvm-6c2327a09e2820302a3a2be0f0dbe24530ca5e7c.tar.gz bcm5719-llvm-6c2327a09e2820302a3a2be0f0dbe24530ca5e7c.zip |
[BPF] Add BTF generation for BPF target
BTF is the debug format for BPF, a kernel virtual machine
and widely used for tracing, networking and security, etc ([1]).
Currently only instruction streams are passed to kernel,
the kernel verifier verifies them before execution. In order to
provide better visibility of bpf programs to user space
tools, some debug information, e.g., function names and
debug line information are desirable for kernel so tools
can get such information with better annotation
for jited instructions for performance or other reasons.
The dwarf is too complicated in kernel and for BPF.
Hence, BTF is designed to be the debug format for BPF ([2]).
Right now, pahole supports BTF for types, which
are generated based on dwarf sections in the ELF file.
In order to annotate performance metrics for jited bpf insns,
it is necessary to pass debug line info to the kernel.
Furthermore, we want to pass the actual code to the
kernel because of the following reasons:
. bpf program typically is small so storage overhead
should be small.
. in bpf land, it is totally possible that
an application loads the bpf program into the
kernel and then that application quits, so
holding debug info by the user space application
is not practical.
. having source codes directly kept by kernel
would ease deployment since the original source
code does not need ship on every hosts and
kernel-devel package does not need to be
deployed even if kernel headers are used.
The only reliable time to get the source code is
during compilation time. This will result in both more
accurate information and easier deployment as
stated in the above.
Another consideration is for JIT. The project like bcc
use MCJIT to compile a C program into bpf insns and
load them to the kernel ([3]). The generated BTF sections
will be readily available for such cases as well.
This patch implemented generation of BTF info in llvm
compiler. The BTF related sections will be generated
when both -target bpf and -g are specified. Two sections
are generated:
.BTF contains all the type and string information, and
.BTF.ext contains the func_info and line_info.
The separation is related to how two sections are used
differently in bpf loader, e.g., linux libbpf ([4]).
The .BTF section can be loaded into the kernel directly
while .BTF.ext needs loader manipulation before loading
to the kernel. The format of the each section is roughly
defined in llvm:include/llvm/MC/MCBTFContext.h and
from the implementation in llvm:lib/MC/MCBTFContext.cpp.
A later example also shows the contents in each section.
The type and func_info are gathered during CodeGen/AsmPrinter
by traversing dwarf debug_info. The line_info is
gathered in MCObjectStreamer before writing to
the object file. After all the information is gathered,
the two sections are emitted in MCObjectStreamer::finishImpl.
With cmake CMAKE_BUILD_TYPE=Debug, the compiler can
dump out all the tables except insn offset, which
will be resolved later as relocation records.
The debug type "btf" is used for BTFContext dump.
Dwarf tests the debug info generation with
llvm-dwarfdump to decode the binary sections and
check whether the result is expected. Currently
we do not have such a tool yet. We will implement
btf dump functionality in bpftool ([5]) as the bpftool is
considered the recommended tool for bpf introspection.
The implementation for type and func_info is tested
with linux kernel test cases. The line_info is visually
checked with dump from linux kernel libbpf ([4]) and
checked with readelf dumping section raw data.
Note that the .BTF and .BTF.ext information will not
be emitted to assembly code and there is no assembler
support for BTF either.
In the below, with a clang/llvm built with CMAKE_BUILD_TYPE=Debug,
Each table contents are shown for a simple C program.
-bash-4.2$ cat -n test.c
1 struct A {
2 int a;
3 char b;
4 };
5
6 int test(struct A *t) {
7 return t->a;
8 }
-bash-4.2$ clang -O2 -target bpf -g -mllvm -debug-only=btf -c test.c
Type Table:
[1] FUNC name_off=1 info=0x0c000001 size/type=2
param_type=3
[2] INT name_off=12 info=0x01000000 size/type=4
desc=0x01000020
[3] PTR name_off=0 info=0x02000000 size/type=4
[4] STRUCT name_off=16 info=0x04000002 size/type=8
name_off=18 type=2 bit_offset=0
name_off=20 type=5 bit_offset=32
[5] INT name_off=22 info=0x01000000 size/type=1
desc=0x02000008
String Table:
0 :
1 : test
6 : .text
12 : int
16 : A
18 : a
20 : b
22 : char
27 : test.c
34 : int test(struct A *t) {
58 : return t->a;
FuncInfo Table:
sec_name_off=6
insn_offset=<Omitted> type_id=1
LineInfo Table:
sec_name_off=6
insn_offset=<Omitted> file_name_off=27 line_off=34 line_num=6 column_num=0
insn_offset=<Omitted> file_name_off=27 line_off=58 line_num=7 column_num=3
-bash-4.2$ readelf -S test.o
......
[12] .BTF PROGBITS 0000000000000000 0000028d
00000000000000c1 0000000000000000 0 0 1
[13] .BTF.ext PROGBITS 0000000000000000 0000034e
0000000000000050 0000000000000000 0 0 1
[14] .rel.BTF.ext REL 0000000000000000 00000648
0000000000000030 0000000000000010 16 13 8
......
-bash-4.2$
The latest linux kernel ([6]) can already support .BTF with type information.
The [7] has the reference implementation in linux kernel side
to support .BTF.ext func_info. The .BTF.ext line_info support is not
implemented yet. If you have difficulty accessing [6], you can
manually do the following to access the code:
git clone https://github.com/yonghong-song/bpf-next-linux.git
cd bpf-next-linux
git checkout btf
The change will push to linux kernel soon once this patch is landed.
References:
[1]. https://www.kernel.org/doc/Documentation/networking/filter.txt
[2]. https://lwn.net/Articles/750695/
[3]. https://github.com/iovisor/bcc
[4]. https://github.com/torvalds/linux/tree/master/tools/lib/bpf
[5]. https://github.com/torvalds/linux/tree/master/tools/bpf/bpftool
[6]. https://github.com/torvalds/linux
[7]. https://github.com/yonghong-song/bpf-next-linux/tree/btf
Signed-off-by: Song Liu <songliubraving@fb.com>
Signed-off-by: Yonghong Song <yhs@fb.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Differential Revision: https://reviews.llvm.org/D52950
llvm-svn: 344366
Diffstat (limited to 'llvm/lib/CodeGen/AsmPrinter')
-rw-r--r-- | llvm/lib/CodeGen/AsmPrinter/CMakeLists.txt | 1 | ||||
-rw-r--r-- | llvm/lib/CodeGen/AsmPrinter/Dwarf2BTF.cpp | 501 | ||||
-rw-r--r-- | llvm/lib/CodeGen/AsmPrinter/Dwarf2BTF.h | 134 | ||||
-rw-r--r-- | llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 10 | ||||
-rw-r--r-- | llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h | 3 | ||||
-rw-r--r-- | llvm/lib/CodeGen/AsmPrinter/DwarfFile.cpp | 10 | ||||
-rw-r--r-- | llvm/lib/CodeGen/AsmPrinter/DwarfFile.h | 3 |
7 files changed, 662 insertions, 0 deletions
diff --git a/llvm/lib/CodeGen/AsmPrinter/CMakeLists.txt b/llvm/lib/CodeGen/AsmPrinter/CMakeLists.txt index 6cba4a0d4b8..14c895a9c82 100644 --- a/llvm/lib/CodeGen/AsmPrinter/CMakeLists.txt +++ b/llvm/lib/CodeGen/AsmPrinter/CMakeLists.txt @@ -17,6 +17,7 @@ add_llvm_library(LLVMAsmPrinter DwarfFile.cpp DwarfStringPool.cpp DwarfUnit.cpp + Dwarf2BTF.cpp EHStreamer.cpp ErlangGCPrinter.cpp OcamlGCPrinter.cpp diff --git a/llvm/lib/CodeGen/AsmPrinter/Dwarf2BTF.cpp b/llvm/lib/CodeGen/AsmPrinter/Dwarf2BTF.cpp new file mode 100644 index 00000000000..20eab4d1fb8 --- /dev/null +++ b/llvm/lib/CodeGen/AsmPrinter/Dwarf2BTF.cpp @@ -0,0 +1,501 @@ +//===- Dwarf2BTF.cpp ------------------------------------------ *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DwarfUnit.h" +#include "Dwarf2BTF.h" +#include "llvm/MC/MCBTFContext.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCSectionELF.h" + +namespace llvm { + +unsigned char Die2BTFEntry::getDieKind(const DIE & Die) { + auto Tag = Die.getTag(); + + switch (Tag) { + case dwarf::DW_TAG_base_type: + if (getBaseTypeEncoding(Die) == BTF_INVALID_ENCODING) + return BTF_KIND_UNKN; + return BTF_KIND_INT; + case dwarf::DW_TAG_const_type: + return BTF_KIND_CONST; + case dwarf::DW_TAG_pointer_type: + return BTF_KIND_PTR; + case dwarf::DW_TAG_restrict_type: + return BTF_KIND_RESTRICT; + case dwarf::DW_TAG_volatile_type: + return BTF_KIND_VOLATILE; + case dwarf::DW_TAG_typedef: + return BTF_KIND_TYPEDEF; + case dwarf::DW_TAG_structure_type: + case dwarf::DW_TAG_class_type: + if (Die.findAttribute(dwarf::DW_AT_declaration).getType() + != DIEValue::isNone) + return BTF_KIND_FWD; + else + return BTF_KIND_STRUCT; + case dwarf::DW_TAG_union_type: + if (Die.findAttribute(dwarf::DW_AT_declaration).getType() + != DIEValue::isNone) + return BTF_KIND_FWD; + else + return BTF_KIND_UNION; + case dwarf::DW_TAG_enumeration_type: + return BTF_KIND_ENUM; + case dwarf::DW_TAG_array_type: + return BTF_KIND_ARRAY; + case dwarf::DW_TAG_subprogram: + return BTF_KIND_FUNC; + case dwarf::DW_TAG_subroutine_type: + return BTF_KIND_FUNC_PROTO; + default: + break; + } + + return BTF_KIND_UNKN; +} + +std::unique_ptr<Die2BTFEntry> Die2BTFEntry::dieToBTFTypeEntry(const DIE &Die) { + unsigned char Kind = getDieKind(Die); + + switch (Kind) { + case BTF_KIND_INT: + return make_unique<Die2BTFEntryInt>(Die); + case BTF_KIND_PTR: + case BTF_KIND_TYPEDEF: + case BTF_KIND_VOLATILE: + case BTF_KIND_CONST: + case BTF_KIND_RESTRICT: + case BTF_KIND_FWD: + return make_unique<Die2BTFEntry>(Die); + case BTF_KIND_ARRAY: + return make_unique<Die2BTFEntryArray>(Die); + case BTF_KIND_STRUCT: + case BTF_KIND_UNION: + return make_unique<Die2BTFEntryStruct>(Die); + case BTF_KIND_ENUM: + return make_unique<Die2BTFEntryEnum>(Die); + case BTF_KIND_FUNC: + case BTF_KIND_FUNC_PROTO: + return make_unique<Die2BTFEntryFunc>(Die); + default: + break; + } + return nullptr; +} + +bool Die2BTFEntry::shouldSkipDie(const DIE &Die) { + auto Tag = Die.getTag(); + + switch (Tag) { + case dwarf::DW_TAG_const_type: + case dwarf::DW_TAG_pointer_type: + case dwarf::DW_TAG_restrict_type: + case dwarf::DW_TAG_typedef: + case dwarf::DW_TAG_volatile_type: + { + auto TypeV = Die.findAttribute(dwarf::DW_AT_type); + if (TypeV.getType() == DIEValue::isNone) + return false; + auto &TypeDie = TypeV.getDIEEntry().getEntry(); + return Die2BTFEntry::shouldSkipDie(TypeDie); + } + default: + return getDieKind(Die) == BTF_KIND_UNKN; + } + return true; +} +unsigned char Die2BTFEntry::getBaseTypeEncoding(const DIE &Die) { + auto V = Die.findAttribute(dwarf::DW_AT_encoding); + + if (V.getType() != DIEValue::isInteger) + return BTF_INVALID_ENCODING; + + switch (V.getDIEInteger().getValue()) { + case dwarf::DW_ATE_boolean: + return BTF_INT_BOOL; + case dwarf::DW_ATE_signed: + return BTF_INT_SIGNED; + case dwarf::DW_ATE_signed_char: + return BTF_INT_CHAR; + case dwarf::DW_ATE_unsigned: + return 0; + case dwarf::DW_ATE_unsigned_char: + return BTF_INT_CHAR; + case dwarf::DW_ATE_imaginary_float: + case dwarf::DW_ATE_packed_decimal: + case dwarf::DW_ATE_numeric_string: + case dwarf::DW_ATE_edited: + case dwarf::DW_ATE_signed_fixed: + case dwarf::DW_ATE_address: + case dwarf::DW_ATE_complex_float: + case dwarf::DW_ATE_float: + default: + break; + } + return BTF_INVALID_ENCODING; +} + +Die2BTFEntry::Die2BTFEntry(const DIE &Die) : Die(Die) { + unsigned char Kind = getDieKind(Die); + + switch (Kind) { + case BTF_KIND_CONST: + case BTF_KIND_FWD: + case BTF_KIND_PTR: + case BTF_KIND_RESTRICT: + case BTF_KIND_TYPEDEF: + case BTF_KIND_VOLATILE: + break; + default: + assert("Invalid Die passed into BTFTypeEntry()"); + break; + } + + BTFType.info = (Kind & 0xf) << 24; +} + +void Die2BTFEntry::completeData(class Dwarf2BTF &Dwarf2BTF) { + auto TypeV = Die.findAttribute(dwarf::DW_AT_type); + if (TypeV.getType() == DIEValue::isNone) { + BTFType.type = 0; + } else { + auto &TypeDie = TypeV.getDIEEntry().getEntry(); + auto Type = Dwarf2BTF.getTypeIndex(TypeDie); + BTFType.type = Type; + } + + unsigned char Kind = getDieKind(Die); + if (Kind != BTF_KIND_FWD) { + BTFType.name_off = 0; + } else { + auto NameV = Die.findAttribute(dwarf::DW_AT_name); + auto Str = NameV.getDIEString().getString(); + BTFType.name_off = Dwarf2BTF.addBTFString(Str); + } + + auto typeEntry = make_unique<BTFTypeEntry>(Id, BTFType); + Dwarf2BTF.addBTFTypeEntry(std::move(typeEntry)); +} + +Die2BTFEntryInt::Die2BTFEntryInt(const DIE &Die) : Die2BTFEntry(Die) { + unsigned char Kind = getDieKind(Die); + + switch (Kind) { + case BTF_KIND_INT: + break; + default: + assert("Invalid Die passed into BTFTypeEntryInt()"); + break; + } + + // handle BTF_INT_ENCODING in IntVal + auto Encoding = Die2BTFEntry::getBaseTypeEncoding(Die); + assert((Encoding != BTF_INVALID_ENCODING) && + "Invalid Die passed to BTFTypeEntryInt()"); + __u32 IntVal = (Encoding & 0xf) << 24; + + // handle BTF_INT_OFFSET in IntVal + auto V = Die.findAttribute(dwarf::DW_AT_bit_offset); + if (V.getType() == DIEValue::isInteger) + IntVal |= (V.getDIEInteger().getValue() & 0xff) << 16; + + // get btf_type.size + V = Die.findAttribute(dwarf::DW_AT_byte_size); + __u32 Size = V.getDIEInteger().getValue() & 0xffffffff; + +// handle BTF_INT_BITS in IntVal + V = Die.findAttribute(dwarf::DW_AT_bit_size); + if (V.getType() == DIEValue::isInteger) + IntVal |= V.getDIEInteger().getValue() & 0xff; + else + IntVal |= (Size << 3) & 0xff; + + BTFType.info = BTF_KIND_INT << 24; + BTFType.size = Size; + this->IntVal = IntVal; +} + +void Die2BTFEntryInt::completeData(class Dwarf2BTF &Dwarf2BTF) { + auto NameV = Die.findAttribute(dwarf::DW_AT_name); + auto TypeV = Die.findAttribute(dwarf::DW_AT_type); + auto Str = NameV.getDIEString().getString(); + + BTFType.name_off = Dwarf2BTF.addBTFString(Str); + + auto typeEntry = make_unique<BTFTypeEntryInt>(Id, BTFType, IntVal); + Dwarf2BTF.addBTFTypeEntry(std::move(typeEntry)); +} + +Die2BTFEntryEnum::Die2BTFEntryEnum(const DIE &Die) : Die2BTFEntry(Die) { + // get btf_type.size + auto V = Die.findAttribute(dwarf::DW_AT_byte_size); + __u32 Size = V.getDIEInteger().getValue() & 0xffffffff; + + int Vlen = 0; + for (auto &ChildDie : Die.children()) + if (ChildDie.getTag() == dwarf::DW_TAG_enumerator) + Vlen++; + + BTFType.info = (BTF_KIND_ENUM << 24) | (Vlen & BTF_MAX_VLEN); + BTFType.type = Size; +} + +void Die2BTFEntryEnum::completeData(class Dwarf2BTF &Dwarf2BTF) { + auto TypeV = Die.findAttribute(dwarf::DW_AT_type); + auto NameV = Die.findAttribute(dwarf::DW_AT_name); + + if (NameV.getType() != DIEValue::isNone) { + auto Str = NameV.getDIEString().getString(); + BTFType.name_off = Dwarf2BTF.addBTFString(Str); + } else + BTFType.name_off = 0; + + for (auto &ChildDie : Die.children()) { + struct btf_enum BTFEnum; + auto ChildNameV = ChildDie.findAttribute(dwarf::DW_AT_name); + auto Str = ChildNameV.getDIEString().getString(); + + BTFEnum.name_off = Dwarf2BTF.addBTFString(Str); + auto ChildValueV = ChildDie.findAttribute(dwarf::DW_AT_const_value); + BTFEnum.val = (__s32)(ChildValueV.getDIEInteger().getValue()); + + EnumValues.push_back(BTFEnum); + } + + auto typeEntry = make_unique<BTFTypeEntryEnum>(Id, BTFType, EnumValues); + Dwarf2BTF.addBTFTypeEntry(std::move(typeEntry)); +} + +Die2BTFEntryArray::Die2BTFEntryArray(const DIE &Die) : + Die2BTFEntry(Die) { + BTFType.info = (BTF_KIND_ARRAY << 24); + BTFType.size = 0; +} + +void Die2BTFEntryArray::completeData(class Dwarf2BTF &Dwarf2BTF) { + auto NameV = Die.findAttribute(dwarf::DW_AT_name); + + std::string Str; + if (NameV.getType() != DIEValue::isNone) + Str = NameV.getDIEString().getString(); + BTFType.name_off = Dwarf2BTF.addBTFString(Str); + + auto &ArrayTypeDie = Die.findAttribute(dwarf::DW_AT_type).getDIEEntry().getEntry(); + ArrayInfo.type = Dwarf2BTF.getTypeIndex(ArrayTypeDie); + + // The number of elements should count all subranges + unsigned Nelems = 1; + bool IsFirstSubrange = true; + for (auto &ChildDie : Die.children()) { + if (ChildDie.getTag() == dwarf::DW_TAG_subrange_type) { + if (IsFirstSubrange) { + auto TypeV = ChildDie.findAttribute(dwarf::DW_AT_type); + auto &TypeDie = TypeV.getDIEEntry().getEntry(); + ArrayInfo.index_type = Dwarf2BTF.getTypeIndex(TypeDie); + IsFirstSubrange = false; + } + auto CountV = ChildDie.findAttribute(dwarf::DW_AT_count); + if (CountV.getType() == DIEValue::isNone) { + // array like a[] which essentially a pointer + Nelems = 0; + break; + } + Nelems *= (__u32)(CountV.getDIEInteger().getValue()); + } + } + ArrayInfo.nelems = Nelems; + + auto TypeEntry = make_unique<BTFTypeEntryArray>(Id, BTFType, ArrayInfo); + Dwarf2BTF.addBTFTypeEntry(std::move(TypeEntry)); +} + +Die2BTFEntryStruct::Die2BTFEntryStruct(const DIE &Die) : Die2BTFEntry(Die) { + // get btf_type.size + auto V = Die.findAttribute(dwarf::DW_AT_byte_size); + __u32 Size = V.getDIEInteger().getValue() & 0xffffffff; + auto Kind = Die2BTFEntry::getDieKind(Die); + + int Vlen = 0; + for (auto &ChildDie : Die.children()) + if (ChildDie.getTag() == dwarf::DW_TAG_member) + Vlen++; + + BTFType.size = Size; + BTFType.info = (Kind << 24) | (Vlen & BTF_MAX_VLEN); +} + +void Die2BTFEntryStruct::completeData(class Dwarf2BTF &Dwarf2BTF) { + auto NameV = Die.findAttribute(dwarf::DW_AT_name); + + if (NameV.getType() != DIEValue::isNone) { + auto Str = NameV.getDIEString().getString(); + BTFType.name_off = Dwarf2BTF.addBTFString(Str); + } else + BTFType.name_off = 0; + + + for (auto &ChildDie : Die.children()) { + if (ChildDie.getTag() != dwarf::DW_TAG_member) + continue; + + struct btf_member BTFMember; + auto ChildNameV = ChildDie.findAttribute(dwarf::DW_AT_name); + + if (ChildNameV.getType() != DIEValue::isNone) { + auto Str = ChildNameV.getDIEString().getString(); + BTFMember.name_off = Dwarf2BTF.addBTFString(Str); + } else + BTFMember.name_off = 0; + + auto TypeV = ChildDie.findAttribute(dwarf::DW_AT_type); + auto &TypeDie = TypeV.getDIEEntry().getEntry(); + BTFMember.type = Dwarf2BTF.getTypeIndex(TypeDie); + + auto MemLocV = ChildDie.findAttribute(dwarf::DW_AT_data_member_location); + unsigned MemLoc = MemLocV.getDIEInteger().getValue() * 8; + + auto ByteSizeV = ChildDie.findAttribute(dwarf::DW_AT_byte_size); + if (ByteSizeV.getType() != DIEValue::isNone) { + unsigned ByteSize = ByteSizeV.getDIEInteger().getValue(); + auto BitOffsetV = ChildDie.findAttribute(dwarf::DW_AT_bit_offset); + unsigned BitOffset = BitOffsetV.getDIEInteger().getValue(); + auto BitSizeV = ChildDie.findAttribute(dwarf::DW_AT_bit_size); + unsigned BitSize = BitSizeV.getDIEInteger().getValue(); + if (Dwarf2BTF.isLittleEndian()) + MemLoc += ByteSize * 8 - BitSize - BitOffset; + else + MemLoc += BitOffset; + } + BTFMember.offset = MemLoc; + + Members.push_back(BTFMember); + } + + auto typeEntry = make_unique<BTFTypeEntryStruct>(Id, BTFType, Members); + Dwarf2BTF.addBTFTypeEntry(std::move(typeEntry)); +} + +Die2BTFEntryFunc::Die2BTFEntryFunc(const DIE &Die) : Die2BTFEntry(Die) { + auto Kind = Die2BTFEntry::getDieKind(Die); + + int Vlen = 0; + for (auto &ChildDie : Die.children()) + if (ChildDie.getTag() == dwarf::DW_TAG_formal_parameter) + Vlen++; + + BTFType.size = 0; + BTFType.info = (Kind << 24) | (Vlen & BTF_MAX_VLEN); +} + +void Die2BTFEntryFunc::completeData(class Dwarf2BTF &Dwarf2BTF) { + auto NameV = Die.findAttribute(dwarf::DW_AT_name); + if (NameV.getType() == DIEValue::isNone) { + BTFType.name_off = 0; + } else { + auto Str = NameV.getDIEString().getString(); + BTFType.name_off = Dwarf2BTF.addBTFString(Str); + } + + auto RetTypeV = Die.findAttribute(dwarf::DW_AT_type); + if (RetTypeV.getType() != DIEValue::isNone) { + auto &TypeDie = RetTypeV.getDIEEntry().getEntry(); + BTFType.type = Dwarf2BTF.getTypeIndex(TypeDie); + } else { + BTFType.type = 0; + } + + for (auto &ChildDie : Die.children()) { + if (ChildDie.getTag() == dwarf::DW_TAG_formal_parameter) { + auto TypeV = ChildDie.findAttribute(dwarf::DW_AT_abstract_origin); + if (TypeV.getType() != DIEValue::isNone) { + auto &AbsOriginDie = TypeV.getDIEEntry().getEntry(); + assert(AbsOriginDie.getTag() == dwarf::DW_TAG_formal_parameter); + TypeV = AbsOriginDie.findAttribute(dwarf::DW_AT_type); + } else { + TypeV = ChildDie.findAttribute(dwarf::DW_AT_type); + } + auto &TypeDie = TypeV.getDIEEntry().getEntry(); + Parameters.push_back(Dwarf2BTF.getTypeIndex(TypeDie)); + } else if (ChildDie.getTag() == dwarf::DW_TAG_unspecified_parameters) { + Parameters.push_back(0); + } + } + + auto typeEntry = make_unique<BTFTypeEntryFunc>(Id, BTFType, Parameters); + Dwarf2BTF.addBTFTypeEntry(std::move(typeEntry)); + + if (BTF_INFO_KIND(BTFType.info) == BTF_KIND_FUNC) { + auto LowPCV = Die.findAttribute(dwarf::DW_AT_low_pc); + if (LowPCV.getType() != DIEValue::isNone) { + const MCSymbol *Label = LowPCV.getDIELabel().getValue(); + BTFFuncInfo FuncInfo; + unsigned SecNameOff; + + FuncInfo.Label = Label; + FuncInfo.TypeId = Id; + if (Label->isInSection()) { + MCSection &Section = Label->getSection(); + MCSectionELF *SectionELF = dyn_cast<MCSectionELF>(&Section); + assert(SectionELF); + SecNameOff = Dwarf2BTF.addBTFString(SectionELF->getSectionName().str()); + } else { + SecNameOff = Dwarf2BTF.addBTFString(".text"); + } + Dwarf2BTF.addBTFFuncInfo(SecNameOff, FuncInfo); + } + } +} + +Dwarf2BTF::Dwarf2BTF(MCContext &Context, bool IsLittleEndian) + : OuterCtx(Context), IsLE(IsLittleEndian) { + BTFContext = make_unique<MCBTFContext>(); +} + +void Dwarf2BTF::addTypeEntry(const DIE &Die) { + for (auto &ChildDie : Die.children()) + addTypeEntry(ChildDie); + if (Die2BTFEntry::shouldSkipDie(Die)) + return; + auto Kind = Die2BTFEntry::getDieKind(Die); + if (Kind != BTF_KIND_UNKN) { + auto TypeEntry = Die2BTFEntry::dieToBTFTypeEntry(Die); + if (TypeEntry != nullptr) { + TypeEntry->setId(TypeEntries.size() + 1); + DieToIdMap[const_cast<DIE*>(&Die)] = TypeEntry->getId(); + TypeEntries.push_back(std::move(TypeEntry)); + } + } +} + +void Dwarf2BTF::addBTFTypeEntry(std::unique_ptr<BTFTypeEntry> Entry) { + BTFContext->addTypeEntry(std::move(Entry)); +} + +void Dwarf2BTF::completeData() { + BTFContext->addString("\0"); + + for (auto &TypeEntry : TypeEntries) + TypeEntry->completeData(*this); +} + +void Dwarf2BTF::addDwarfCU(DwarfUnit *TheU) { + DIE &CuDie = TheU->getUnitDie(); + + assert((CuDie.getTag() == dwarf::DW_TAG_compile_unit) && + "Not a compile unit"); + addTypeEntry(CuDie); +} + +void Dwarf2BTF::finish() { + completeData(); + OuterCtx.setBTFContext(std::move(BTFContext)); +} + +} diff --git a/llvm/lib/CodeGen/AsmPrinter/Dwarf2BTF.h b/llvm/lib/CodeGen/AsmPrinter/Dwarf2BTF.h new file mode 100644 index 00000000000..3df4dd802a7 --- /dev/null +++ b/llvm/lib/CodeGen/AsmPrinter/Dwarf2BTF.h @@ -0,0 +1,134 @@ +//===- Dwarf2BTF.h -------------------------------------------- *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_LIB_CODEGEN_ASMPRINTER_DWARF2BTF_H +#define LLVM_LIB_CODEGEN_ASMPRINTER_DWARF2BTF_H + +#include "DwarfUnit.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/DIE.h" +#include "llvm/MC/MCBTFContext.h" +#include <map> + +namespace llvm { + +class Dwarf2BTF; +class MCBTFContext; + +#define BTF_INVALID_ENCODING 0xff + +class Die2BTFEntry { +protected: + const DIE &Die; + size_t Id; /* type index in the BTF list, started from 1 */ + struct btf_type BTFType; + +public: + // Return desired BTF_KIND for the Die, return BTF_KIND_UNKN for + // invalid/unsupported Die + static unsigned char getDieKind(const DIE &Die); + + // Return proper BTF_INT_ENCODING of a basetype. + // Return BTF_INVALID_ENCODING for unsupported (float, etc.) + static unsigned char getBaseTypeEncoding(const DIE &Die); + + // Return whether this Die should be skipped. + // We currently skip unsupported data type (e.g. float) + // and references to unsupported types + static bool shouldSkipDie(const DIE &Die); + + static std::unique_ptr<Die2BTFEntry> dieToBTFTypeEntry(const DIE &Die); + + Die2BTFEntry(const DIE &Die); + void setId(size_t Id) { this->Id = Id; } + size_t getId() { return Id; } + virtual void completeData(class Dwarf2BTF &Dwarf2BTF); +}; + +// BTF_KIND_INT +class Die2BTFEntryInt : public Die2BTFEntry { + __u32 IntVal; // encoding, offset, bits + +public: + Die2BTFEntryInt(const DIE &Die); + void completeData(class Dwarf2BTF &Dwarf2BTF); +}; + +// BTF_KIND_ENUM +class Die2BTFEntryEnum : public Die2BTFEntry { + std::vector<struct btf_enum> EnumValues; + +public: + Die2BTFEntryEnum(const DIE &Die); + void completeData(class Dwarf2BTF &Dwarf2BTF); +}; + +// BTF_KIND_ARRAY +class Die2BTFEntryArray : public Die2BTFEntry { + struct btf_array ArrayInfo; + +public: + Die2BTFEntryArray(const DIE &Die); + void completeData(class Dwarf2BTF &Dwarf2BTF); +}; + +// BTF_KIND_STRUCT and BTF_KIND_UNION +class Die2BTFEntryStruct : public Die2BTFEntry { + std::vector<struct btf_member> Members; + +public: + Die2BTFEntryStruct(const DIE &Die); + void completeData(class Dwarf2BTF &Dwarf2BTF); +}; + +// BTF_KIND_FUNC and BTF_KIND_FUNC_PROTO +class Die2BTFEntryFunc : public Die2BTFEntry { + std::vector<__u32> Parameters; + +public: + Die2BTFEntryFunc(const DIE &Die); + void completeData(class Dwarf2BTF &Dwarf2BTF); +}; + +class Dwarf2BTF { + std::vector<std::unique_ptr<Die2BTFEntry>> TypeEntries; + std::map<DIE*, size_t> DieToIdMap; + std::unique_ptr<MCBTFContext> BTFContext; + MCContext &OuterCtx; + bool IsLE; + +public: + Dwarf2BTF(MCContext &Context, bool IsLittleEndian); + bool isLittleEndian() { return IsLE; } + void addDwarfCU(DwarfUnit *TheU); + void finish(); + __u32 getTypeIndex(DIE &Die) { + DIE *DiePtr = const_cast<DIE*>(&Die); + assert((DieToIdMap.find(DiePtr) != DieToIdMap.end()) && + "Die not added to in the BTFContext"); + return DieToIdMap[DiePtr]; + } + size_t addBTFString(std::string S) { + return BTFContext->addString(S); + } + void addBTFTypeEntry(std::unique_ptr<BTFTypeEntry> Entry); + void addBTFFuncInfo(unsigned SecNameOff, BTFFuncInfo FuncInfo) { + BTFContext->addFuncInfo(SecNameOff, FuncInfo); + } + +private: + void addTypeEntry(const DIE &Die); + bool alreadyAdded(DIE &Die) { + return DieToIdMap.find(const_cast<DIE*>(&Die)) != DieToIdMap.end(); + } + void completeData(); +}; + +} +#endif diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp index 94e12658cfe..184ec4dabe9 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -971,6 +971,10 @@ void DwarfDebug::endModule() { // Emit the pubnames and pubtypes sections if requested. emitDebugPubSections(); + const Triple &TT = Asm->TM.getTargetTriple(); + if (TT.getArch() == Triple::bpfel || TT.getArch() == Triple::bpfeb) + emitBTFSection(TT.getArch() == Triple::bpfel); + // clean up. // FIXME: AbstractVariables.clear(); } @@ -2455,6 +2459,12 @@ MCDwarfDwoLineTable *DwarfDebug::getDwoLineTable(const DwarfCompileUnit &CU) { return &SplitTypeUnitFileTable; } +void DwarfDebug::emitBTFSection(bool IsLittleEndian) { + DwarfFile &Holder = useSplitDwarf() ? SkeletonHolder : InfoHolder; + + Holder.emitBTFSection(IsLittleEndian); +} + uint64_t DwarfDebug::makeTypeSignature(StringRef Identifier) { MD5 Hash; Hash.update(Identifier); diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h index fecf8056765..1350317db02 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h @@ -567,6 +567,9 @@ class DwarfDebug : public DebugHandlerBase { /// Emit the reference to the section. void emitSectionReference(const DwarfCompileUnit &CU); + // Emit the BTF sections + void emitBTFSection(bool IsLittleEndian); + protected: /// Gather pre-function debug information. void beginFunctionImpl(const MachineFunction *MF) override; diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfFile.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfFile.cpp index 0ab9ea87c23..7ac16b34c4c 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfFile.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfFile.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "Dwarf2BTF.h" #include "DwarfFile.h" #include "DwarfCompileUnit.h" #include "DwarfDebug.h" @@ -15,6 +16,8 @@ #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/DIE.h" #include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/MC/MCBTFContext.h" +#include "llvm/MC/MCContext.h" #include "llvm/MC/MCStreamer.h" #include <algorithm> #include <cstdint> @@ -88,6 +91,13 @@ void DwarfFile::emitStrings(MCSection *StrSection, MCSection *OffsetSection, StrPool.emit(*Asm, StrSection, OffsetSection, UseRelativeOffsets); } +void DwarfFile::emitBTFSection(bool IsLittleEndian) { + Dwarf2BTF Dwarf2BTF(Asm->OutContext, IsLittleEndian); + for (auto &TheU : CUs) + Dwarf2BTF.addDwarfCU(TheU.get()); + Dwarf2BTF.finish(); +} + bool DwarfFile::addScopeVariable(LexicalScope *LS, DbgVariable *Var) { auto &ScopeVars = ScopeVariables[LS]; const DILocalVariable *DV = Var->getVariable(); diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfFile.h b/llvm/lib/CodeGen/AsmPrinter/DwarfFile.h index c315f44a8d8..9aafe2613f6 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfFile.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfFile.h @@ -114,6 +114,9 @@ public: void emitStrings(MCSection *StrSection, MCSection *OffsetSection = nullptr, bool UseRelativeOffsets = false); + // Emit all data for the BTF section + void emitBTFSection(bool IsLittleEndian); + /// Returns the string pool. DwarfStringPool &getStringPool() { return StrPool; } |