Skip to content

Commit

Permalink
Augment LabeledInstruction with source/file/line info from BTF if ava…
Browse files Browse the repository at this point in the history
…ilable

Signed-off-by: Alan Jowett <alanjo@microsoft.com>
  • Loading branch information
Alan-Jowett authored and elazarg committed Feb 21, 2022
1 parent 13f4b00 commit 5639d4c
Show file tree
Hide file tree
Showing 15 changed files with 416 additions and 12 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ file(GLOB ALL_TEST
"./src/test/test.cpp"
"./src/test/test_loop.cpp"
"./src/test/test_marshal.cpp"
"./src/test/test_print.cpp"
"./src/test/test_termination.cpp"
"./src/test/test_verify.cpp"
"./src/test/test_wto.cpp"
Expand Down
2 changes: 1 addition & 1 deletion src/asm_cfg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ static cfg_t instruction_seq_to_cfg(const InstructionSeq& insts, bool must_have_
cfg_t cfg;
std::optional<label_t> falling_from = {};
bool first = true;
for (const auto& [label, inst] : insts) {
for (const auto& [label, inst, _] : insts) {

if (std::holds_alternative<Undefined>(inst))
continue;
Expand Down
36 changes: 36 additions & 0 deletions src/asm_files.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
// SPDX-License-Identifier: MIT
#include <cassert>
#include <iostream>
#include <map>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <sys/stat.h>

#include "asm_files.hpp"
#include "btf_parser.h"
#include "platform.hpp"

#include "elfio/elfio.hpp"
Expand Down Expand Up @@ -190,8 +192,42 @@ vector<raw_program> read_elf(const std::string& path, const std::string& desired
}
}
}
prog.line_info.resize(prog.prog.size());
res.push_back(prog);
}

auto btf = reader.sections[string(".BTF")];
auto btf_ext = reader.sections[string(".BTF.ext")];
if (btf != nullptr && btf_ext != nullptr) {
std::map<std::string, raw_program&> segment_to_program;
for (auto& program : res) {
segment_to_program.insert({program.section, program});
}

auto visitor = [&](const std::string& section, uint32_t instruction_offset, const std::string& file_name,
const std::string& source, uint32_t line_number, uint32_t column_number) {
auto program_iter = segment_to_program.find(section);
if (program_iter == segment_to_program.end()) {
return;
}
auto& program = program_iter->second;
program.line_info[instruction_offset / sizeof(ebpf_inst)] = {file_name, source, line_number, column_number};
};

btf_parse_line_information(vector_of<uint8_t>(*btf), vector_of<uint8_t>(*btf_ext), visitor);

// BTF doesn't include line info for every instruction, instead sets it only on the first instruction.
for (auto& [name, program] : segment_to_program) {
for (size_t i = 1; i < program.line_info.size(); i++) {
// If the previous PC has line info, copy it.
if ((std::get<2>(program.line_info[i]) == 0) &&
(std::get<2>(program.line_info[i - 1]) != 0)) {
program.line_info[i] = program.line_info[i - 1];
}
}
}
}

if (res.empty()) {
if (desired_section.empty()) {
throw std::runtime_error(string("Can't find any non-empty TEXT sections in file ") + path);
Expand Down
4 changes: 2 additions & 2 deletions src/asm_marshal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ static int size(Instruction inst) {
static auto get_labels(const InstructionSeq& insts) {
pc_t pc = 0;
std::map<label_t, pc_t> pc_of_label;
for (auto [label, inst] : insts) {
for (auto [label, inst, _] : insts) {
pc_of_label[label] = pc;
pc += size(inst);
}
Expand All @@ -270,7 +270,7 @@ vector<ebpf_inst> marshal(const InstructionSeq& insts) {
vector<ebpf_inst> res;
auto pc_of_label = get_labels(insts);
pc_t pc = 0;
for (auto [label, ins] : insts) {
for (auto [label, ins, _] : insts) {
(void)label; // unused
if (std::holds_alternative<Jmp>(ins)) {
Jmp& jmp = std::get<Jmp>(ins);
Expand Down
15 changes: 13 additions & 2 deletions src/asm_ostream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ int size(Instruction inst) {
auto get_labels(const InstructionSeq& insts) {
pc_t pc = 0;
std::map<label_t, pc_t> pc_of_label;
for (auto [label, inst] : insts) {
for (auto [label, inst, _] : insts) {
pc_of_label[label] = pc;
pc += size(inst);
}
Expand All @@ -360,10 +360,21 @@ auto get_labels(const InstructionSeq& insts) {
void print(const InstructionSeq& insts, std::ostream& out, std::optional<const label_t> label_to_print) {
auto pc_of_label = get_labels(insts);
pc_t pc = 0;
std::string previous_source;
InstructionPrinterVisitor visitor{out};
for (const LabeledInstruction& labeled_inst : insts) {
const auto& [label, ins] = labeled_inst;
const auto& [label, ins, line_info] = labeled_inst;
if (!label_to_print.has_value() || (label == label_to_print)) {
if (line_info.has_value()) {
auto& [file, source, line, column] = line_info.value();
// Only decorate the first instruction associated with a source line.
if (source != previous_source)
{
out << "; " << file.c_str() << ":" << line << "\n";
out << "; " << source.c_str() << "\n";
previous_source = source;
}
}
if (label.isjump()) {
out << "\n";
out << label << ":\n";
Expand Down
2 changes: 1 addition & 1 deletion src/asm_parse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ static InstructionSeq parse_program(std::istream& is) {

if (!next_label)
next_label = label_t(static_cast<int>(labeled_insts.size()));
labeled_insts.emplace_back(*next_label, ins);
labeled_insts.emplace_back(*next_label, ins, std::optional<btf_line_info>());
next_label = {};
}
return labeled_insts;
Expand Down
8 changes: 7 additions & 1 deletion src/asm_syntax.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,13 @@ struct Assert {

using Instruction = std::variant<Undefined, Bin, Un, LoadMapFd, Call, Exit, Jmp, Mem, Packet, LockAdd, Assume, Assert>;

using LabeledInstruction = std::tuple<label_t, Instruction>;
using btf_line_info = std::tuple<
std::string /* File Name */,
std::string /* Source Line */,
uint32_t /* Line Number */,
uint32_t /* Column Number */>;

using LabeledInstruction = std::tuple<label_t, Instruction, std::optional<btf_line_info>>;
using InstructionSeq = std::vector<LabeledInstruction>;

using pc_t = uint16_t;
Expand Down
11 changes: 8 additions & 3 deletions src/asm_unmarshal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ struct Unmarshaller {
}
}

vector<LabeledInstruction> unmarshal(vector<ebpf_inst> const& insts) {
vector<LabeledInstruction> unmarshal(vector<ebpf_inst> const& insts, vector<btf_line_info> const& line_info) {
vector<LabeledInstruction> prog;
int exit_count = 0;
if (insts.empty()) {
Expand Down Expand Up @@ -425,14 +425,19 @@ struct Unmarshaller {
*/
if (pc == insts.size() - 1 && fallthrough)
note("fallthrough in last instruction");
prog.emplace_back(label_t(static_cast<int>(pc)), new_ins);

prog.emplace_back(label_t(static_cast<int>(pc)), new_ins, std::optional<btf_line_info>());

pc++;
note_next_pc();
if (lddw) {
pc++;
note_next_pc();
}
}
for (size_t i = 0; i < prog.size(); i++) {
std::get<2>(prog[i]) = line_info[i];
}
if (exit_count == 0)
note("no exit instruction");
return prog;
Expand All @@ -442,7 +447,7 @@ struct Unmarshaller {
std::variant<InstructionSeq, std::string> unmarshal(const raw_program& raw_prog, vector<vector<string>>& notes) {
global_program_info = raw_prog.info;
try {
return Unmarshaller{notes, raw_prog.info}.unmarshal(raw_prog.prog);
return Unmarshaller{notes, raw_prog.info}.unmarshal(raw_prog.prog, raw_prog.line_info);
} catch (InvalidInstruction& arg) {
std::ostringstream ss;
ss << arg.pc << ": " << arg.what() << "\n";
Expand Down
151 changes: 151 additions & 0 deletions src/btf.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// Copyright (c) Prevail Verifier contributors.
// SPDX-License-Identifier: MIT

#pragma once
#include <stdint.h>
// Format of .BTF and .BTF.ext is documented here:
// https://www.kernel.org/doc/html/latest/bpf/btf.html

#define BTF_KIND_INT 1 /* Integer */
#define BTF_KIND_PTR 2 /* Pointer */
#define BTF_KIND_ARRAY 3 /* Array */
#define BTF_KIND_STRUCT 4 /* Struct */
#define BTF_KIND_UNION 5 /* Union */
#define BTF_KIND_ENUM 6 /* Enumeration */
#define BTF_KIND_FWD 7 /* Forward */
#define BTF_KIND_TYPEDEF 8 /* Typedef */
#define BTF_KIND_VOLATILE 9 /* Volatile */
#define BTF_KIND_CONST 10 /* Const */
#define BTF_KIND_RESTRICT 11 /* Restrict */
#define BTF_KIND_FUNC 12 /* Function */
#define BTF_KIND_FUNC_PROTO 13 /* Function Proto */
#define BTF_KIND_VAR 14 /* Variable */
#define BTF_KIND_DATASEC 15 /* Section */
#define BTF_KIND_FLOAT 16 /* Floating point */
#define BTF_KIND_DECL_TAG 17 /* Decl Tag */
#define BTF_KIND_TYPE_TAG 18 /* Type Tag */

#define BTF_INT_ENCODING(VAL) (((VAL)&0x0f000000) >> 24)
#define BTF_INT_OFFSET(VAL) (((VAL)&0x00ff0000) >> 16)
#define BTF_INT_BITS(VAL) ((VAL)&0x000000ff)

#define BTF_MEMBER_BITFIELD_SIZE(val) ((val) >> 24)
#define BTF_MEMBER_BIT_OFFSET(val) ((val)&0xffffff)

#define BTF_MEMBER_BITFIELD_SIZE(val) ((val) >> 24)
#define BTF_MEMBER_BIT_OFFSET(val) ((val)&0xffffff)

#define BTF_HEADER_MAGIC 0xeB9F
#define BTF_HEADER_VERSION 1

#define BPF_LINE_INFO_LINE_NUM(line_col) ((line_col) >> 10)
#define BPF_LINE_INFO_LINE_COL(line_col) ((line_col)&0x3ff)

typedef struct _btf_header
{
uint16_t magic;
uint8_t version;
uint8_t flags;
uint32_t hdr_len;

/* All offsets are in bytes relative to the end of this header */
uint32_t type_off; /* offset of type section */
uint32_t type_len; /* length of type section */
uint32_t str_off; /* offset of string section */
uint32_t str_len; /* length of string section */
} btf_header_t;

typedef struct _btf_type
{
uint32_t name_off;
/* "info" bits arrangement
* bits 0-15: vlen (e.g. # of struct's members)
* bits 16-23: unused
* bits 24-28: kind (e.g. int, ptr, array...etc)
* bits 29-30: unused
* bit 31: kind_flag, currently used by
* struct, union and fwd
*/
uint32_t info;
/* "size" is used by INT, ENUM, STRUCT and UNION.
* "size" tells the size of the type it is describing.
*
* "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
* FUNC, FUNC_PROTO, DECL_TAG and TYPE_TAG.
* "type" is a type_id referring to another type.
*/
union
{
uint32_t size;
uint32_t type;
};
} btf_type_t;

typedef struct _btf_array
{
uint32_t type;
uint32_t index_type;
uint32_t nelems;
} btf_array_t;

typedef struct _btf_param
{
uint32_t name_off;
uint32_t type;
} btf_param_t;

typedef struct _btf_var
{
uint32_t linkage;
} btf_var_t;

typedef struct _btf_var_secinfo
{
uint32_t type;
uint32_t offset;
uint32_t size;
} btf_var_secinfo_t;

typedef struct _btf_decl_tag
{
uint32_t component_idx;
} btf_decl_tag_t;

typedef struct _btf_ext_header
{
uint16_t magic;
uint8_t version;
uint8_t flags;
uint32_t hdr_len;

/* All offsets are in bytes relative to the end of this header */
uint32_t func_info_off;
uint32_t func_info_len;
uint32_t line_info_off;
uint32_t line_info_len;
} btf_ext_header_t;

#pragma warning(push)
#pragma warning(disable : 4200) // nonstandard extension used: zero-sized array in struct/union
typedef struct _btf_ext_info_sec
{
uint32_t sec_name_off; /* offset to section name */
uint32_t num_info;
/* Followed by num_info * record_size number of bytes */
uint8_t data[0];
} btf_ext_info_sec_t;
#pragma warning(pop)

typedef struct _bpf_func_info
{
uint32_t insn_off; /* [0, insn_cnt - 1] */
uint32_t type_id; /* pointing to a BTF_KIND_FUNC type */
} bpf_func_info_t;

typedef struct _bpf_line_info
{
uint32_t insn_off; /* [0, insn_cnt - 1] */
uint32_t file_name_off; /* offset to string table for the filename */
uint32_t line_off; /* offset to string table for the source line */
uint32_t line_col; /* line number and column number */
} bpf_line_info_t;
Loading

0 comments on commit 5639d4c

Please sign in to comment.