diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e49c437 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/systemtap-4.0.tar.gz diff --git a/.systemtap.metadata b/.systemtap.metadata new file mode 100644 index 0000000..eb6d659 --- /dev/null +++ b/.systemtap.metadata @@ -0,0 +1 @@ +40a21d71b0d42bc216f75befd3fca82701821211 SOURCES/systemtap-4.0.tar.gz diff --git a/SOURCES/rhbz1643997.0001-testsuite-systemtap.bpf-diagnose-a-bug-in-print_form.patch b/SOURCES/rhbz1643997.0001-testsuite-systemtap.bpf-diagnose-a-bug-in-print_form.patch new file mode 100644 index 0000000..d0af424 --- /dev/null +++ b/SOURCES/rhbz1643997.0001-testsuite-systemtap.bpf-diagnose-a-bug-in-print_form.patch @@ -0,0 +1,91 @@ +From 16e9cfa909a183d8e61142d80575189408a2a244 Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Wed, 24 Oct 2018 15:56:44 -0400 +Subject: [PATCH 01/32] testsuite/systemtap.bpf :: diagnose a bug in + print_format("%s%s", ...) + +--- + testsuite/systemtap.bpf/asm_tests/string-basic.stp | 19 ++++++++++ + testsuite/systemtap.bpf/bpf_tests/string3.stp | 44 ++++++++++++++++++++++ + 2 files changed, 63 insertions(+) + create mode 100644 testsuite/systemtap.bpf/asm_tests/string-basic.stp + create mode 100644 testsuite/systemtap.bpf/bpf_tests/string3.stp + +diff --git a/testsuite/systemtap.bpf/asm_tests/string-basic.stp b/testsuite/systemtap.bpf/asm_tests/string-basic.stp +new file mode 100644 +index 000000000..7377e4399 +--- /dev/null ++++ b/testsuite/systemtap.bpf/asm_tests/string-basic.stp +@@ -0,0 +1,19 @@ ++/* narrowing down a bug that turned out unrelated to assembly */ ++function foo:string() %{ /* bpf */ /* pure */ ++ 0xbf, $$, "test", -, -; ++%} ++ ++probe begin { ++ printf("U %s %s\n", foo(), "test"/*bar (5)*/) ++} ++ ++probe kernel.function("vfs_read") { ++ printf("K 1 %s\n", foo()) // <- this worked ++ printf("K 2 %s\n", "test") // <- this worked ++ printf("K 3 %s %s\n", foo(), "test") // <- this didn't ++ printf("K 4 %s %s\n", "test", "test") // <- this didn't ++ printf("K 5 %s %s\n", foo(), foo()) // <- this didn't ++ printf("K 6 %s", "test") printf(" %s\n", "test") // <- this did ++ printf("K %d %s\n", 7, "test") // <- this did ++ exit() ++} +diff --git a/testsuite/systemtap.bpf/bpf_tests/string3.stp b/testsuite/systemtap.bpf/bpf_tests/string3.stp +new file mode 100644 +index 000000000..cf6ec071d +--- /dev/null ++++ b/testsuite/systemtap.bpf/bpf_tests/string3.stp +@@ -0,0 +1,44 @@ ++// stapbpf string manipulation -- store string in global from kernel space ++// XXX: the 'locking' scheme here is kind of dumb but it seems to work ++ ++global counter = 0 ++global var ++global tab1 ++global tab2 ++ ++@define test_print ++%( ++ /* Test multiple %s in one printf */ ++ printf("[") ++ printf("%s%s%s", "str0", var, tab1[17]) ++ printf("%s]", tab2["key"]) ++%) ++ ++probe begin { ++ printf("BEGIN") ++} ++ ++probe kernel.function("vfs_read") { ++ if (counter == 0) { ++ var = "str1" ++ tab1[17] = "str2" ++ tab2["key"] = "str3" ++ printf("probe0") ++ @test_print ++ counter = 1 ++ } ++} ++ ++probe kernel.function("vfs_read") { ++ if (counter == 1) { ++ printf("probe1") ++ @test_print ++ exit() ++ } ++} ++ ++probe end { ++ printf("end") ++ @test_print ++ printf("END\n") ++} +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0002-stapbpf-assembler-WIP-1-basic-parser-and-control-flo.patch b/SOURCES/rhbz1643997.0002-stapbpf-assembler-WIP-1-basic-parser-and-control-flo.patch new file mode 100644 index 0000000..ed6c8bd --- /dev/null +++ b/SOURCES/rhbz1643997.0002-stapbpf-assembler-WIP-1-basic-parser-and-control-flo.patch @@ -0,0 +1,945 @@ +From 17d4495bef5c3878bb38730ff0d849415b52641a Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Mon, 1 Oct 2018 15:38:16 -0400 +Subject: [PATCH 02/32] stapbpf assembler WIP #1 :: basic parser and control + flow + +--- + bpf-internal.h | 7 +- + bpf-opt.cxx | 2 +- + bpf-translate.cxx | 745 +++++++++++++++++++++++++++++++++++++++++++----------- + parse.h | 14 + + 4 files changed, 619 insertions(+), 149 deletions(-) + +diff --git a/bpf-internal.h b/bpf-internal.h +index 17a033533..719446db8 100644 +--- a/bpf-internal.h ++++ b/bpf-internal.h +@@ -261,9 +261,10 @@ struct program + void print(std::ostream &) const; + }; + +-// ??? Properly belongs to bpf_unparser but must be accessible from bpf-opt.cxx: +-value *emit_literal_str(program &this_prog, insn_inserter &this_ins, +- value *dest, int ofs, std::string &src, bool zero_pad = false); ++// ??? Properly belongs to bpf_unparser but must be visible from bpf-opt.cxx: ++value *emit_simple_literal_str(program &this_prog, insn_inserter &this_ins, ++ value *dest, int ofs, std::string &src, ++ bool zero_pad = false); + + inline std::ostream& + operator<< (std::ostream &o, const program &c) +diff --git a/bpf-opt.cxx b/bpf-opt.cxx +index 0f64d826d..c2e30a690 100644 +--- a/bpf-opt.cxx ++++ b/bpf-opt.cxx +@@ -41,7 +41,7 @@ alloc_literal_str(program &p, insn_inserter &ins, std::string &str) + int ofs = -tmp_space; + + value *frame = p.lookup_reg(BPF_REG_10); +- value *out = emit_literal_str(p, ins, frame, ofs, str, false /* don't zero pad */); ++ value *out = emit_simple_literal_str(p, ins, frame, ofs, str, false /* don't zero pad */); + return out; + } + +diff --git a/bpf-translate.cxx b/bpf-translate.cxx +index d848c9f16..023ac6ce7 100644 +--- a/bpf-translate.cxx ++++ b/bpf-translate.cxx +@@ -8,6 +8,7 @@ + + #include "config.h" + #include "bpf-internal.h" ++#include "parse.h" + #include "staptree.h" + #include "elaborate.h" + #include "session.h" +@@ -134,6 +135,9 @@ has_side_effects (expression *e) + return t.side_effects; + } + ++/* forward declaration */ ++struct asm_stmt; ++ + struct bpf_unparser : public throwing_visitor + { + // The visitor class isn't as helpful as it might be. As a consequence, +@@ -233,10 +237,19 @@ struct bpf_unparser : public throwing_visitor + value *emit_expr(expression *e); + value *emit_bool(expression *e); + value *emit_context_var(bpf_context_vardecl *v); +- value *parse_reg(const std::string &str, embeddedcode *s); + +- // Used for copying string data: +- value *emit_copied_str(value *dest, int ofs, value *src, bool zero_pad = false); ++ // Used for the embedded-code assembler: ++ size_t parse_asm_stmt (embeddedcode *s, size_t start, ++ /*OUT*/asm_stmt &stmt); ++ value *emit_asm_arg(const asm_stmt &stmt, const std::string ®, ++ bool allow_imm = true); ++ value *emit_asm_reg(const asm_stmt &stmt, const std::string ®); ++ void emit_asm_opcode(const asm_stmt &stmt, ++ std::map label_map); ++ ++ // Used for string data: ++ value *emit_literal_string(const std::string &str, const token *tok); ++ value *emit_string_copy(value *dest, int ofs, value *src, bool zero_pad = false); + + // Used for passing long and string arguments on the stack where an address is expected: + void emit_long_arg(value *arg, int ofs, value *val); +@@ -552,172 +565,604 @@ bpf_unparser::visit_block (::block *s) + emit_stmt (s->statements[i]); + } + ++/* WORK IN PROGRESS: A simple eBPF assembler. ++ ++ In order to effectively write eBPF tapset functions, we want to use ++ embedded-code assembly rather than compile from SystemTap code. At ++ the same time, we want to hook into stapbpf functionality to ++ reserve stack memory, allocate virtual registers or signal errors. ++ ++ The assembler syntax will probably take a couple of attempts to get ++ just right. This attempt keeps things as close as possible to the ++ first embedded-code assembler, with a few more features and a ++ disgustingly lenient parser that allows things like ++ $ this is all one "**identifier**" believe-it!-or-not ++ ++ Ahh for the days of 1960s FORTRAN. ++ ++ TODO: It might make more sense to implement an assembler based on ++ the syntax used in official eBPF subsystem docs. */ ++ ++/* Possible assembly statement types include: ++ ++ ::= label, ; ++ ::= , , , ++ , ; ++ ++ Possible argument types include: ++ ++ ::= | r | ++ $ | $ | $$ | ++ ::= | BPF_MAXSTRINGLEN ++ ::= | ++ ++*/ ++ ++struct asm_stmt { ++ std::string kind; ++ ++ unsigned code; ++ std::string dest, src1; ++ int64_t off, imm; ++ ++ // metadata for jmp instructions ++ bool has_fallthrough = false; ++ std::string jmp_target, fallthrough; ++ ++ token *tok; ++ bool deallocate_tok = false; ++ ~asm_stmt() { if (deallocate_tok) delete tok; } ++}; ++ ++std::ostream& ++operator << (std::ostream& o, const asm_stmt& stmt) ++{ ++ if (stmt.kind == "label") ++ o << "label, " << stmt.dest << ";"; ++ else if (stmt.kind == "opcode") ++ { ++ o << std::hex << stmt.code << ", " ++ << stmt.dest << ", " ++ << stmt.src1 << ", "; ++ if (stmt.off != 0 || stmt.jmp_target == "") ++ o << stmt.off; ++ else if (stmt.off != 0) // && stmt.jmp_target != "" ++ o << stmt.off << "/"; ++ if (stmt.jmp_target != "") ++ o << "label:" << stmt.jmp_target; ++ o << ", " ++ << stmt.imm << ";" ++ << (stmt.has_fallthrough ? " +FALLTHROUGH " + stmt.fallthrough : ""); ++ } ++ else ++ o << ""; ++ return o; ++} ++ ++bool ++is_numeric (const std::string &str) ++{ ++ size_t pos = 0; ++ try { ++ stol(str, &pos, 0); ++ } catch (std::invalid_argument &e) { ++ return false; ++ } ++ return (pos == str.size()); ++} ++ ++/* Parse an assembly statement starting from position start in code, ++ then write the output in stmt. Returns a position immediately after ++ the parsed statement. */ ++size_t ++bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start, ++ /*OUT*/asm_stmt &stmt) ++{ ++ const interned_string &code = s->code; ++ ++ retry: ++ std::vector args; ++ unsigned n = code.size(); ++ bool in_comment = false; ++ bool in_string = false; ++ ++ // compute token with adjusted source location for diagnostics ++ source_loc adjusted_loc; // TODO: ought to create a proper copy constructor for source_loc ++ adjusted_loc.file = s->tok->location.file; ++ adjusted_loc.line = s->tok->location.line; ++ adjusted_loc.column = s->tok->location.column; ++ for (size_t pos = 0; pos < start && pos < n; pos++) ++ { ++ // TODO: should save adjusted_loc state between parse_asm_stmt invocations; add field? ++ char c = code[pos]; ++ if (c == '\n') ++ { ++ adjusted_loc.line++; ++ adjusted_loc.column = 1; ++ } ++ else ++ adjusted_loc.column++; ++ } ++ ++ // TODO: As before, parser is extremely non-rigorous and could do ++ // with some tightening in terms of the inputs it accepts. ++ size_t pos; ++ std::string arg = ""; ++ for (pos = start; pos < n; pos++) ++ { ++ char c = code[pos]; ++ char c2 = pos + 1 < n ? code [pos + 1] : 0; ++ if (isspace(c)) ++ continue; // skip ++ else if (in_comment) ++ { ++ if (c == '*' && c2 == '/') ++ ++pos, in_comment = false; ++ // else skip ++ } ++ else if (in_string) ++ { ++ // resulting string will be processed by translate_escapes() ++ if (c == '"') ++ arg.push_back(c), in_string = false; // include quote ++ else if (c == '\\' && c2 == '"') ++ ++pos, arg.push_back(c), arg.push_back(c2); ++ else // accept any char, including whitespace ++ arg.push_back(c); ++ } ++ else if (c == '/' && c2 == '*') ++ ++pos, in_comment = true; ++ else if (c == '"') // found a literal string ++ { ++ // XXX: This allows '"' inside an arg and will treat the ++ // string as a sequence of weird identifier characters. A ++ // more rigorous parser would error on mixing strings and ++ // regular chars. ++ arg.push_back(c); // include quote ++ in_string = true; ++ } ++ else if (c == ',') // reached end of argument ++ { ++ // XXX: This strips out empty args. A more rigorous parser would error. ++ if (arg != "") ++ args.push_back(arg); ++ arg = ""; ++ } ++ else if (c == ';') // reached end of statement ++ { ++ // XXX: This strips out empty args. A more rigorous parser would error. ++ if (arg != "") ++ args.push_back(arg); ++ arg = ""; ++ pos++; break; ++ } ++ else // found (we assume) a regular char ++ { ++ // XXX: As before, this strips whitespace within args ++ // (so '$ab', '$ a b' and '$a b' are equivalent). ++ // ++ // A more rigorous parser would track in_arg ++ // and after_arg states and error on whitespace within args. ++ arg.push_back(c); ++ } ++ } ++ // final ';' is optional, so we watch for a trailing arg: ++ if (arg != "") args.push_back(arg); ++ ++ // handle the case with no args ++ if (args.empty() && pos >= n) ++ return std::string::npos; // finished parsing ++ else if (args.empty()) ++ { ++ // XXX: This skips an empty statement. ++ // A more rigorous parser would error. ++ start = pos; ++ goto retry; ++ } ++ ++ // set token with adjusted source location ++ //stmt.tok = (token *)s->tok; ++ // TODO this segfaults for some reason, some data not copied? ++ stmt.tok = s->tok->adjust_location(adjusted_loc); ++ stmt.deallocate_tok = false; // TODO must avoid destroy-on-copy ++ ++ std::cerr << "DEBUG GOT stmt "; // TODO ++ for (unsigned k = 0; k < args.size(); k++) std::cerr << args[k] << " / "; ++ std::cerr << std::endl; // TODO ++ if (args[0] == "label") ++ { ++ if (args.size() != 2) ++ throw SEMANTIC_ERROR (_("invalid bpf embeddedcode syntax"), stmt.tok); ++ stmt.kind = args[0]; ++ stmt.dest = args[1]; ++ } ++ else if (is_numeric(args[0])) ++ { ++ if (args.size() != 5) // TODO change to 4 to test err+tok ++ throw SEMANTIC_ERROR (_("invalid bpf embeddedcode syntax"), stmt.tok); ++ stmt.kind = "opcode"; ++ stmt.code = stoul(args[0], 0, 0); // TODO signal error ++ stmt.dest = args[1]; ++ stmt.src1 = args[2]; ++ ++ bool has_jmp_target = ++ BPF_CLASS(stmt.code) == BPF_JMP ++ && BPF_OP(stmt.code) != BPF_EXIT ++ && BPF_OP(stmt.code) != BPF_CALL; ++ stmt.has_fallthrough = // only for jcond ++ has_jmp_target ++ && BPF_OP(stmt.code) != BPF_JA; ++ // XXX: stmt.fallthrough is computed by visit_embeddedcode ++ ++ if (has_jmp_target) ++ { ++ stmt.off = 0; ++ stmt.jmp_target = args[3]; ++ } ++ else if (args[3] == "BPF_MAXSTRINGLEN") ++ stmt.off = BPF_MAXSTRINGLEN; ++ else if (args[3] == "-") ++ stmt.off = 0; ++ else ++ stmt.off = stol(args[3]); // TODO signal error ++ ++ if (args[4] == "BPF_MAXSTRINGLEN") ++ stmt.imm = BPF_MAXSTRINGLEN; ++ else if (args[4] == "-") ++ stmt.imm = 0; ++ else ++ stmt.imm = stol(args[4]); // TODO signal error ++ } ++ else ++ throw SEMANTIC_ERROR (_F("unknown bpf embeddedcode operator '%s'", ++ args[0].c_str()), stmt.tok); ++ ++ // we returned a statement, so there's more parsing to be done ++ return pos; ++} ++ ++/* forward declaration */ ++std::string translate_escapes (const interned_string &str); ++ ++/* Convert a or operand to a value. ++ May emit code to store a string constant on the stack. */ + value * +-bpf_unparser::parse_reg(const std::string &str, embeddedcode *s) ++bpf_unparser::emit_asm_arg (const asm_stmt &stmt, const std::string &arg, ++ bool allow_imm) + { +- if (str == "$$") ++ if (arg == "$$") + { +- if (func_return.empty ()) +- throw SEMANTIC_ERROR (_("no return value outside function"), s->tok); ++ /* arg is a return value */ ++ if (func_return.empty()) ++ throw SEMANTIC_ERROR (_("no return value outside function"), stmt.tok); + return func_return_val.back(); + } +- else if (str[0] == '$') ++ else if (arg[0] == '$') + { +- std::string var = str.substr(1); ++ /* assume arg is a variable */ ++ std::string var = arg.substr(1); + for (auto i = this_locals->begin(); i != this_locals->end(); ++i) + { + vardecl *v = i->first; + if (var == v->unmangled_name) + return i->second; + } +- throw SEMANTIC_ERROR (_("unknown variable"), s->tok); ++ ++ /* if it's an unknown variable, allocate a temporary */ ++ struct vardecl *vd = new vardecl; ++ vd->name = "__bpfasm__local_" + var; ++ vd->unmangled_name = var; ++ vd->type = pe_long; ++ vd->arity = 0; ++ value *reg = this_prog.new_reg(); ++ const locals_map::value_type v (vd, reg); ++ auto ok = this_locals->insert (v); ++ assert (ok.second); ++ return reg; ++ // TODO write a testcase + } +- else ++ else if (is_numeric(arg) && allow_imm) + { +- unsigned long num = stoul(str, 0, 0); ++ /* arg is an immediate constant */ ++ long imm = stol(arg, 0, 0); ++ return this_prog.new_imm(imm); ++ } ++ else if (is_numeric(arg) || arg[0] == 'r') ++ { ++ /* arg is a register number */ ++ std::string reg = arg[0] == 'r' ? arg.substr(1) : arg; ++ unsigned long num = stoul(reg, 0, 0); + if (num > 10) +- throw SEMANTIC_ERROR (_("invalid bpf register"), s->tok); ++ throw SEMANTIC_ERROR (_F("invalid bpf register '%s'", ++ arg.c_str()), stmt.tok); + return this_prog.lookup_reg(num); + } ++ else if (arg[0] == '"') ++ { ++ // TODO verify correctness ++ /* arg is a string constant */ ++ if (arg[arg.size() - 1] != '"') ++ throw SEMANTIC_ERROR (_F("BUG: improper string %s", ++ arg.c_str()), stmt.tok); ++ std::string escaped_str = arg.substr(1,arg.size()-2); /* strip quotes */ ++ std::string str = translate_escapes(escaped_str); // TODO interned_str? ++ return emit_literal_string(str, stmt.tok); ++ } ++ else if (arg == "BPF_MAXSTRINGLEN") ++ { ++ /* arg is BPF_MAXSTRINGLEN */ ++ if (!allow_imm) ++ throw SEMANTIC_ERROR (_F("invalid bpf register '%s'", ++ arg.c_str()), stmt.tok); ++ return this_prog.new_imm(BPF_MAXSTRINGLEN); ++ } ++ else if (arg == "-") ++ { ++ /* arg is null a.k.a '0' */ ++ if (!allow_imm) ++ throw SEMANTIC_ERROR (_F("invalid bpf register '%s'", ++ arg.c_str()), stmt.tok); ++ return this_prog.new_imm(0); ++ } ++ else if (allow_imm) ++ throw SEMANTIC_ERROR (_F("invalid bpf argument '%s'", ++ arg.c_str()), stmt.tok); ++ else ++ throw SEMANTIC_ERROR (_F("invalid bpf register '%s'", ++ arg.c_str()), stmt.tok); ++ ++} ++ ++value * ++bpf_unparser::emit_asm_reg (const asm_stmt &stmt, const std::string ®) ++{ ++ return emit_asm_arg(stmt, reg, /*allow_imm=*/false); + } + + void +-bpf_unparser::visit_embeddedcode (embeddedcode *s) ++bpf_unparser::emit_asm_opcode (const asm_stmt &stmt, ++ std::map label_map) + { +- std::string strip; +- { +- const interned_string &code = s->code; +- unsigned n = code.size(); +- bool in_comment = false; ++ if (stmt.code > 0xff && stmt.code != BPF_LD_MAP) ++ throw SEMANTIC_ERROR (_("invalid bpf code"), stmt.tok); + +- for (unsigned i = 0; i < n; ++i) +- { +- char c = code[i]; +- if (isspace(c)) +- continue; +- if (in_comment) +- { +- if (c == '*' && code[i + 1] == '/') +- ++i, in_comment = false; +- } +- else if (c == '/' && code[i + 1] == '*') +- ++i, in_comment = true; +- else +- strip += c; +- } +- } ++ bool r_dest = false, r_src0 = false, r_src1 = false, i_src1 = false; ++ bool op_jmp = false, op_jcond = false; condition c; ++ switch (BPF_CLASS (stmt.code)) ++ { ++ case BPF_LDX: ++ r_dest = r_src1 = true; ++ break; ++ case BPF_STX: ++ r_src0 = r_src1 = true; ++ break; ++ case BPF_ST: ++ r_src0 = i_src1 = true; ++ break; ++ ++ case BPF_ALU: ++ case BPF_ALU64: ++ r_dest = true; ++ if (stmt.code & BPF_X) ++ r_src1 = true; ++ else ++ i_src1 = true; ++ switch (BPF_OP (stmt.code)) ++ { ++ case BPF_NEG: ++ case BPF_MOV: ++ break; ++ case BPF_END: ++ /* X/K bit repurposed as LE/BE. */ ++ i_src1 = false, r_src1 = true; ++ break; ++ default: ++ r_src0 = true; ++ } ++ break; ++ ++ case BPF_JMP: ++ switch (BPF_OP (stmt.code)) ++ { ++ case BPF_EXIT: ++ // no special treatment needed ++ break; ++ case BPF_CALL: ++ i_src1 = true; ++ break; ++ case BPF_JA: ++ op_jmp = true; ++ break; ++ default: ++ // XXX: assume this is a jcond op ++ op_jcond = true; ++ r_src0 = true; ++ if (stmt.code & BPF_X) ++ r_src1 = true; ++ else ++ i_src1 = true; ++ } ++ ++ // compute jump condition c ++ switch (BPF_OP (stmt.code)) ++ { ++ case BPF_JEQ: c = EQ; break; ++ case BPF_JNE: c = NE; break; ++ case BPF_JGT: c = GTU; break; ++ case BPF_JGE: c = GEU; break; ++ case BPF_JLT: c = LTU; break; ++ case BPF_JLE: c = LEU; break; ++ case BPF_JSGT: c = GT; break; ++ case BPF_JSGE: c = GE; break; ++ case BPF_JSLT: c = LT; break; ++ case BPF_JSLE: c = LE; break; ++ case BPF_JSET: c = TEST; break; ++ default: ++ if (op_jcond) ++ throw SEMANTIC_ERROR (_("invalid branch in bpf code"), stmt.tok); ++ } ++ break; ++ ++ default: ++ if (stmt.code == BPF_LD_MAP) ++ r_dest = true, i_src1 = true; ++ else ++ throw SEMANTIC_ERROR (_F("unknown opcode '%d' in bpf code", ++ stmt.code), stmt.tok); ++ } + +- std::istringstream ii (strip); +- ii >> std::setbase(0); ++ value *v_dest = NULL; ++ if (r_dest || r_src0) ++ v_dest = emit_asm_reg(stmt, stmt.dest); ++ else if (stmt.dest != "0" && stmt.dest != "-") ++ throw SEMANTIC_ERROR (_F("invalid register field '%s' in bpf code", ++ stmt.dest.c_str()), stmt.tok); + +- while (true) ++ value *v_src1 = NULL; ++ if (r_src1) ++ v_src1 = emit_asm_reg(stmt, stmt.src1); ++ else + { +- unsigned code; +- char s1, s2, s3, s4; +- char dest_b[256], src1_b[256]; +- int64_t off, imm; ++ if (stmt.src1 != "0" && stmt.src1 != "-") ++ throw SEMANTIC_ERROR (_F("invalid register field '%s' in bpf code", ++ stmt.src1.c_str()), stmt.tok); ++ if (i_src1) ++ v_src1 = this_prog.new_imm(stmt.imm); ++ else if (stmt.imm != 0) ++ throw SEMANTIC_ERROR (_("invalid immediate field in bpf code"), stmt.tok); ++ } + +- ii >> code >> s1; +- ii.get(dest_b, sizeof(dest_b), ',') >> s2; +- ii.get(src1_b, sizeof(src1_b), ',') >> s3; +- ii >> off >> s4 >> imm; ++ if (stmt.off != (int16_t)stmt.off) ++ throw SEMANTIC_ERROR (_F("offset field '%ld' out of range in bpf code", stmt.off), stmt.tok); + +- if (ii.fail() || s1 != ',' || s2 != ',' || s3 != ',' || s4 != ',') +- throw SEMANTIC_ERROR (_("invalid bpf embeddedcode syntax"), s->tok); ++ if (op_jmp) ++ { ++ block *target = label_map[stmt.jmp_target]; ++ this_prog.mk_jmp(this_ins, target); ++ } ++ else if (op_jcond) ++ { ++ if (label_map.count(stmt.jmp_target) == 0) ++ throw SEMANTIC_ERROR(_F("undefined jump target '%s' in bpf code", ++ stmt.jmp_target.c_str()), stmt.tok); ++ if (label_map.count(stmt.fallthrough) == 0) ++ throw SEMANTIC_ERROR(_F("BUG: undefined fallthrough target '%s'", ++ stmt.fallthrough.c_str()), stmt.tok); ++ block *target = label_map[stmt.jmp_target]; ++ block *fallthrough = label_map[stmt.fallthrough]; ++ this_prog.mk_jcond(this_ins, c, v_dest, v_src1, target, fallthrough); ++ } ++ else // regular opcode ++ { ++ insn *i = this_ins.new_insn(); ++ i->code = stmt.code; ++ i->dest = (r_dest ? v_dest : NULL); ++ i->src0 = (r_src0 ? v_dest : NULL); ++ i->src1 = v_src1; ++ i->off = stmt.off; ++ } ++} + +- if (code > 0xff && code != BPF_LD_MAP) +- throw SEMANTIC_ERROR (_("invalid bpf code"), s->tok); ++void ++bpf_unparser::visit_embeddedcode (embeddedcode *s) ++{ ++ std::vector statements; ++ asm_stmt stmt; + +- bool r_dest = false, r_src0 = false, r_src1 = false, i_src1 = false; +- switch (BPF_CLASS (code)) +- { +- case BPF_LDX: +- r_dest = r_src1 = true; +- break; +- case BPF_STX: +- r_src0 = r_src1 = true; +- break; +- case BPF_ST: +- r_src0 = i_src1 = true; +- break; ++ size_t pos = 0; ++ while ((pos = parse_asm_stmt(s, pos, stmt)) != std::string::npos) ++ { ++ statements.push_back(stmt); ++ } + +- case BPF_ALU: +- case BPF_ALU64: +- r_dest = true; +- if (code & BPF_X) +- r_src1 = true; +- else +- i_src1 = true; +- switch (BPF_OP (code)) +- { +- case BPF_NEG: +- case BPF_MOV: +- break; +- case BPF_END: +- /* X/K bit repurposed as LE/BE. */ +- i_src1 = false, r_src1 = true; +- break; +- default: +- r_src0 = true; +- } +- break; ++ // build basic block table ++ std::map label_map; ++ block *entry_block = this_ins.b; ++ label_map[";;entry"] = entry_block; + +- case BPF_JMP: +- switch (BPF_OP (code)) +- { +- case BPF_EXIT: +- break; +- case BPF_CALL: +- i_src1 = true; +- break; +- default: +- throw SEMANTIC_ERROR (_("invalid branch in bpf code"), s->tok); +- } +- break; ++ bool after_label = true; ++ asm_stmt *after_jump = NULL; ++ unsigned fallthrough_count = 0; ++ for (std::vector::iterator it = statements.begin(); ++ it != statements.end(); it++) ++ { ++ stmt = *it; + +- default: +- if (code == BPF_LD_MAP) +- r_dest = true, i_src1 = true; +- else +- throw SEMANTIC_ERROR (_("unknown opcode in bpf code"), s->tok); +- } ++ if (after_jump != NULL && stmt.kind == "label") ++ { ++ after_jump->fallthrough = stmt.dest; ++ } ++ else if (after_jump != NULL) ++ { ++ block *b = this_prog.new_block(); + +- std::string dest(dest_b); +- value *v_dest = NULL; +- if (r_dest || r_src0) +- v_dest = parse_reg(dest, s); +- else if (dest != "0") +- throw SEMANTIC_ERROR (_("invalid register field in bpf code"), s->tok); +- +- std::string src1(src1_b); +- value *v_src1 = NULL; +- if (r_src1) +- v_src1 = parse_reg(src1, s); +- else +- { +- if (src1 != "0") +- throw SEMANTIC_ERROR (_("invalid register field in bpf code"), s->tok); +- if (i_src1) +- v_src1 = this_prog.new_imm(imm); +- else if (imm != 0) +- throw SEMANTIC_ERROR (_("invalid immediate field in bpf code"), s->tok); +- } ++ // generate unique label for fallthrough edge ++ std::ostringstream oss; ++ oss << "fallthrough;;" << fallthrough_count++; ++ std::string fallthrough_label = oss.str(); ++ // XXX: semicolons prevent collision with programmer-defined labels + +- if (off != (int16_t)off) +- throw SEMANTIC_ERROR (_("offset field out of range in bpf code"), s->tok); ++ label_map[fallthrough_label] = b; ++ set_block(b); + +- insn *i = this_ins.new_insn(); +- i->code = code; +- i->dest = (r_dest ? v_dest : NULL); +- i->src0 = (r_src0 ? v_dest : NULL); +- i->src1 = v_src1; +- i->off = off; ++ after_jump->fallthrough = fallthrough_label; ++ } + +- ii >> s1; +- if (ii.eof()) +- break; +- if (s1 != ';') +- throw SEMANTIC_ERROR (_("invalid bpf embeddedcode syntax"), s->tok); ++ if (stmt.kind == "label" && after_label) ++ { ++ // avoid creating multiple blocks for consecutive labels ++ label_map[stmt.dest] = this_ins.b; ++ after_jump = NULL; ++ } ++ else if (stmt.kind == "label") ++ { ++ block *b = this_prog.new_block(); ++ label_map[stmt.dest] = b; ++ set_block(b); ++ after_label = true; ++ after_jump = NULL; ++ } ++ else if (stmt.has_fallthrough) ++ { ++ after_label = false; ++ after_jump = &*it; // be sure to refer to original, not copied stmt ++ } ++ else ++ { ++ after_label = false; ++ after_jump = NULL; ++ } ++ } ++ if (after_jump != NULL) // TODO: should just fall through to exit ++ throw SEMANTIC_ERROR (_("BUG: bpf embeddedcode doesn't support " ++ "fallthrough on final asm_stmt"), stmt.tok); ++ ++ // emit statements ++ bool jumped_already = true; ++ set_block(entry_block); ++ for (std::vector::iterator it = statements.begin(); ++ it != statements.end(); it++) ++ { ++ stmt = *it; ++ std::cerr << "DEBUG processing " << stmt << std::endl; // TODO ++ if (stmt.kind == "label") ++ { ++ // TODO: be sure there's no gap in the edge ++ if (!jumped_already) ++ emit_jmp (label_map[stmt.dest]); ++ set_block(label_map[stmt.dest]); ++ } ++ else if (stmt.kind == "opcode") ++ { ++ emit_asm_opcode (stmt, label_map); ++ } ++ else ++ throw SEMANTIC_ERROR (_F("BUG: bpf embeddedcode contains unexpected " ++ "asm_stmt kind '%s'", stmt.kind.c_str()), ++ stmt.tok); ++ jumped_already = stmt.has_fallthrough; ++ if (stmt.has_fallthrough) ++ set_block(label_map[stmt.fallthrough]); + } + } + +@@ -1016,8 +1461,13 @@ bpf_unparser::visit_delete_statement (delete_statement *s) + } + + // Translate string escape characters. ++// Accepts strings produced by parse.cxx lexer::scan and ++// by the eBPF embedded-code assembler. ++// ++// PR23559: This is currently an eBPF-only version of the function ++// that does not translate octal escapes. + std::string +-translate_escapes (interned_string &str) ++translate_escapes (const interned_string &str) + { + std::string result; + bool saw_esc = false; +@@ -1045,16 +1495,21 @@ translate_escapes (interned_string &str) + return result; + } + ++value * ++bpf_unparser::emit_literal_string (const std::string &str, const token *tok) ++{ ++ size_t str_bytes = str.size() + 1; ++ if (str_bytes > BPF_MAXSTRINGLEN) ++ throw SEMANTIC_ERROR(_("string literal too long"), tok); ++ return this_prog.new_str(str); // will be lowered to a pointer by bpf-opt.cxx ++} ++ + void + bpf_unparser::visit_literal_string (literal_string* e) + { + interned_string v = e->value; + std::string str = translate_escapes(v); +- +- size_t str_bytes = str.size() + 1; +- if (str_bytes > BPF_MAXSTRINGLEN) +- throw SEMANTIC_ERROR(_("String literal too long"), e->tok); +- result = this_prog.new_str(str); // will be lowered to a pointer by bpf-opt.cxx ++ result = emit_literal_string(str, e->tok); + } + + void +@@ -1783,7 +2238,7 @@ bpf_unparser::visit_target_register (target_register* e) + // ??? Could use 8-byte chunks if we're starved for instruction count. + // ??? Endianness of the target comes into play here. + value * +-emit_literal_str(program &this_prog, insn_inserter &this_ins, ++emit_simple_literal_str(program &this_prog, insn_inserter &this_ins, + value *dest, int ofs, std::string &src, bool zero_pad) + { + size_t str_bytes = src.size() + 1; +@@ -1835,15 +2290,15 @@ emit_literal_str(program &this_prog, insn_inserter &this_ins, + // ??? Could use 8-byte chunks if we're starved for instruction count. + // ??? Endianness of the target may come into play here. + value * +-bpf_unparser::emit_copied_str(value *dest, int ofs, value *src, bool zero_pad) ++bpf_unparser::emit_string_copy(value *dest, int ofs, value *src, bool zero_pad) + { + if (src->is_str()) + { + /* If src is a string literal, its exact length is known and + we can emit simpler, unconditional string copying code. */ + std::string str = src->str(); +- return emit_literal_str(this_prog, this_ins, +- dest, ofs, str, zero_pad); ++ return emit_simple_literal_str(this_prog, this_ins, ++ dest, ofs, str, zero_pad); + } + + size_t str_bytes = BPF_MAXSTRINGLEN; +@@ -1931,7 +2386,7 @@ bpf_unparser::emit_copied_str(value *dest, int ofs, value *src, bool zero_pad) + } + + // XXX: Zero-padding is only used under specific circumstances; +- // see the corresponding comment in emit_literal_str(). ++ // see the corresponding comment in emit_simple_literal_str(). + if (zero_pad) + { + for (unsigned i = 0; i < str_words; ++i) +@@ -1977,7 +2432,7 @@ void + bpf_unparser::emit_str_arg(value *arg, int ofs, value *str) + { + value *frame = this_prog.lookup_reg(BPF_REG_10); +- value *out = emit_copied_str(frame, ofs, str, true /* zero pad */); ++ value *out = emit_string_copy(frame, ofs, str, true /* zero pad */); + emit_mov(arg, out); + } + +diff --git a/parse.h b/parse.h +index 42b0bc5fd..96aef0394 100644 +--- a/parse.h ++++ b/parse.h +@@ -65,11 +65,25 @@ struct token + token_junk_type junk_type; + + std::string junk_message(systemtap_session& session) const; ++ ++ // Creates a new token with the same content but different coordinates. ++ // Can be used for exact error reporting *within* a token e.g. embedded-code. ++ token *adjust_location(const source_loc &adjusted_loc) const ++ { // TODO split from header ++ token *new_tok = new token; ++ new_tok->location = adjusted_loc; ++ new_tok->content = content; ++ new_tok->chain = chain; ++ new_tok->type = type; ++ new_tok->junk_type = junk_type; ++ return new_tok; ++ } + + friend class parser; + friend class lexer; + private: + void make_junk (token_junk_type); ++ + token(): chain(0), type(tok_junk), junk_type(tok_junk_unknown) {} + token(const token& other): + location(other.location), content(other.content), +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0003-stapbpf-assembler-WIP-2-testcases-no-driver-so-far.patch b/SOURCES/rhbz1643997.0003-stapbpf-assembler-WIP-2-testcases-no-driver-so-far.patch new file mode 100644 index 0000000..bc386aa --- /dev/null +++ b/SOURCES/rhbz1643997.0003-stapbpf-assembler-WIP-2-testcases-no-driver-so-far.patch @@ -0,0 +1,115 @@ +From df2e44cd4a35c0cbba528e1d5821d5cd14cb7a87 Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Mon, 1 Oct 2018 15:39:00 -0400 +Subject: [PATCH 03/32] stapbpf assembler WIP #2 :: testcases (no driver so + far) + +--- + testsuite/systemtap.bpf/asm_tests/branch.stp | 22 ++++++++++++++++++++++ + testsuite/systemtap.bpf/asm_tests/err_token.stp | 21 +++++++++++++++++++++ + testsuite/systemtap.bpf/asm_tests/leniency.stp | 17 +++++++++++++++++ + testsuite/systemtap.bpf/asm_tests/simple.stp | 11 +++++++++++ + 4 files changed, 71 insertions(+) + create mode 100644 testsuite/systemtap.bpf/asm_tests/branch.stp + create mode 100644 testsuite/systemtap.bpf/asm_tests/err_token.stp + create mode 100644 testsuite/systemtap.bpf/asm_tests/leniency.stp + create mode 100644 testsuite/systemtap.bpf/asm_tests/simple.stp + +diff --git a/testsuite/systemtap.bpf/asm_tests/branch.stp b/testsuite/systemtap.bpf/asm_tests/branch.stp +new file mode 100644 +index 000000000..aa22bf195 +--- /dev/null ++++ b/testsuite/systemtap.bpf/asm_tests/branch.stp +@@ -0,0 +1,22 @@ ++function foo:long (x:long) %{ /* bpf */ /* pure */ ++ /* if x <= 10 then 50 else 100 */ ++ 0xd5, $x, -, _bar, 10; /* jsle $x, 10, _bar */ ++ 0xb7, $$, -, -, 100; /* mov $$, 100 */ ++ 0x05, -, -, _done, -; /* ja _done; */ ++ label, _bar; ++ 0xb7, $$, -, -, 50; /* mov $$, 50 */ ++ label, _done; ++ /* 0xbf, $$, $$, -, -; /* dummy op */ ++%} ++ ++function bar:long (x:long) { ++ if (x <= 10) return 50 else return 100 ++} ++ ++probe begin { ++ printf("foo(1)=%d should be %d\n", foo(1), bar(1)) ++ printf("foo(8)=%d should be %d\n", foo(8), bar(8)) ++ printf("foo(15)=%d should be %d\n", foo(15), bar(15)) ++ exit() ++} ++ +diff --git a/testsuite/systemtap.bpf/asm_tests/err_token.stp b/testsuite/systemtap.bpf/asm_tests/err_token.stp +new file mode 100644 +index 000000000..b9f8bd866 +--- /dev/null ++++ b/testsuite/systemtap.bpf/asm_tests/err_token.stp +@@ -0,0 +1,21 @@ ++function foo:long (x:long) %{ /* bpf */ /* pure */ ++ /* if x <= 10 then 50 else 100 */ ++ 0xd5, $x, -, _bar, 10; /* jsle $x, 10, _bar */ ++ 0xb7, $$, -, -, 100; /* mov $$, 100 */ ++ florb, -, -, _done, -; /* TRIGGER ERROR */ ++ label, _bar; ++ 0xb7, $$, -, -, 50; /* mov $$, 50 */ ++ label, _done; ++ /* 0xbf, $$, $$, -, -; /* dummy op */ ++%} ++ ++function bar:long (x:long) { ++ if (x <= 10) return 50 else return 100 ++} ++ ++probe begin { ++ printf("foo(1)=%d should be %d\n", foo(1), bar(1)) ++ printf("foo(8)=%d should be %d\n", foo(8), bar(8)) ++ printf("foo(15)=%d should be %d\n", foo(15), bar(15)) ++ exit() ++} +diff --git a/testsuite/systemtap.bpf/asm_tests/leniency.stp b/testsuite/systemtap.bpf/asm_tests/leniency.stp +new file mode 100644 +index 000000000..939061158 +--- /dev/null ++++ b/testsuite/systemtap.bpf/asm_tests/leniency.stp +@@ -0,0 +1,17 @@ ++function foo:long (x:long) %{ /* bpf */ /* pure */ ++ /* if x < 10 then 17 else 16 */ ++ 0xa5, $x, -, _bar, 10; /* jlt $x, 10, _bar */ ++ 0xb7, $this is an "!!ide\nt!!" believe it or not, -, -, 16; /* mov $t, 0 */ ++ 0xbf, $$, $thisisan"!!ide\nt!!"believeitornot, -, -; /* mov $$, $t */ ++ 0x05, -, -, _done, -; /* ja _done; */ ++ label, _bar; ++ 0xb7, $$, -, -, 17; /* mov $$, 1 */ ++ label, _done; ++%} ++ ++probe begin { ++ printf("foo(1)=%d\n", foo(1)) ++ printf("foo(15)=%d\n", foo(15)) ++ exit() ++} ++ +diff --git a/testsuite/systemtap.bpf/asm_tests/simple.stp b/testsuite/systemtap.bpf/asm_tests/simple.stp +new file mode 100644 +index 000000000..693219d15 +--- /dev/null ++++ b/testsuite/systemtap.bpf/asm_tests/simple.stp +@@ -0,0 +1,11 @@ ++function foo:long (x:long) %{ /* bpf */ /* pure */ ++ /* compute 100-x */ ++ 0xb7, $$, -, -, 100; /* mov $$, ee */ ++ 0x1f, $$, $x, -, -; /* sub $$, $x */ ++%} ++ ++probe begin { ++ printf("foo(1)=%d\n", foo(1)) ++ printf("foo(15)=%d\n", foo(15)) ++ exit() ++} +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0004-stapbpf-assembler-WIP-3-additional-assembly-test-cas.patch b/SOURCES/rhbz1643997.0004-stapbpf-assembler-WIP-3-additional-assembly-test-cas.patch new file mode 100644 index 0000000..358d800 --- /dev/null +++ b/SOURCES/rhbz1643997.0004-stapbpf-assembler-WIP-3-additional-assembly-test-cas.patch @@ -0,0 +1,128 @@ +From 6411e53fe729a987a300274f6b15fd88b737367d Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Tue, 16 Oct 2018 18:13:47 -0400 +Subject: [PATCH 04/32] stapbpf assembler WIP #3 :: additional assembly test + cases + +--- + testsuite/systemtap.bpf/asm_tests/err_alloc.stp | 10 ++++++++++ + testsuite/systemtap.bpf/asm_tests/err_numeric.stp | 18 ++++++++++++++++++ + testsuite/systemtap.bpf/asm_tests/simple.stp | 6 +++--- + testsuite/systemtap.bpf/asm_tests/string.stp | 22 ++++++++++++++++++++++ + testsuite/systemtap.bpf/asm_tests/temporary.stp | 14 ++++++++++++++ + 5 files changed, 67 insertions(+), 3 deletions(-) + create mode 100644 testsuite/systemtap.bpf/asm_tests/err_alloc.stp + create mode 100644 testsuite/systemtap.bpf/asm_tests/err_numeric.stp + create mode 100644 testsuite/systemtap.bpf/asm_tests/string.stp + create mode 100644 testsuite/systemtap.bpf/asm_tests/temporary.stp + +diff --git a/testsuite/systemtap.bpf/asm_tests/err_alloc.stp b/testsuite/systemtap.bpf/asm_tests/err_alloc.stp +new file mode 100644 +index 000000000..6778a52e2 +--- /dev/null ++++ b/testsuite/systemtap.bpf/asm_tests/err_alloc.stp +@@ -0,0 +1,10 @@ ++function foo:long (x:long) %{ /* bpf */ /* pure */ ++ alloc "not a register", BPF_MAXSTRINGLEN; /* SHOULD ERROR */ ++ 0xbf, $$, "fifty", -, -; /* mov $$, "fifty" */ ++%} ++ ++ ++probe begin { ++ printf("foo(1)=%d should be fifty\n", foo(1)) ++ exit() ++} +diff --git a/testsuite/systemtap.bpf/asm_tests/err_numeric.stp b/testsuite/systemtap.bpf/asm_tests/err_numeric.stp +new file mode 100644 +index 000000000..9428e5704 +--- /dev/null ++++ b/testsuite/systemtap.bpf/asm_tests/err_numeric.stp +@@ -0,0 +1,18 @@ ++function foo:long (x:long) %{ /* bpf */ /* pure */ ++ /* verify refusal to accept gibberish */ ++ 0xd33333333333333333333333333333333333333333333333333333333333333333333333333334db33f, $x, -, _bar, 10; /* XTREAM opcode */ ++ /* 0xb7, $$, -, huirgishvirguwishgiburg, 100; /* XTREAM 3rd arg */ ++ /* 0xb7, $$, -, -, borkborkborkborkbork; /* XTREAM 4th arg */ ++%} ++ ++function bar:long (x:long) { ++ if (x <= 10) return 50 else return 100 ++} ++ ++probe begin { ++ printf("foo(1)=%d should be %d\n", foo(1), bar(1)) ++ printf("foo(8)=%d should be %d\n", foo(8), bar(8)) ++ printf("foo(15)=%d should be %d\n", foo(15), bar(15)) ++ exit() ++} ++ +diff --git a/testsuite/systemtap.bpf/asm_tests/simple.stp b/testsuite/systemtap.bpf/asm_tests/simple.stp +index 693219d15..17184a139 100644 +--- a/testsuite/systemtap.bpf/asm_tests/simple.stp ++++ b/testsuite/systemtap.bpf/asm_tests/simple.stp +@@ -1,11 +1,11 @@ + function foo:long (x:long) %{ /* bpf */ /* pure */ + /* compute 100-x */ +- 0xb7, $$, -, -, 100; /* mov $$, ee */ ++ 0xb7, $$, -, -, 100; /* mov $$, 100 */ + 0x1f, $$, $x, -, -; /* sub $$, $x */ + %} + + probe begin { +- printf("foo(1)=%d\n", foo(1)) +- printf("foo(15)=%d\n", foo(15)) ++ printf("foo(1)=%d, should be 99\n", foo(1)) ++ printf("foo(15)=%d, should be 85\n", foo(15)) + exit() + } +diff --git a/testsuite/systemtap.bpf/asm_tests/string.stp b/testsuite/systemtap.bpf/asm_tests/string.stp +new file mode 100644 +index 000000000..dce665c14 +--- /dev/null ++++ b/testsuite/systemtap.bpf/asm_tests/string.stp +@@ -0,0 +1,22 @@ ++function foo:long (x:long) %{ /* bpf */ /* pure */ ++ /* if x <= 10 then "fifty" else "one-hundred" */ ++ 0xd5, $x, -, _bar, 10; /* jsle $x, 10, _bar */ ++ 0xbf, $$, "one-hundred", -, -; /* mov $$, "one-hundred" */ ++ 0x05, -, -, _done, -; /* ja _done; */ ++ label, _bar; ++ 0xbf, $$, "fifty", -, -; /* mov $$, "fifty" */ ++ label, _done; ++ /* 0xbf, $$, $$, -, -; /* dummy op */ ++%} ++ ++function bar:long (x:long) { ++ if (x <= 10) return 50 else return 100 ++} ++ ++probe begin { ++ printf("foo(1)=%d should be %d\n", foo(1), bar(1)) ++ printf("foo(8)=%d should be %d\n", foo(8), bar(8)) ++ printf("foo(15)=%d should be %d\n", foo(15), bar(15)) ++ exit() ++} ++ +diff --git a/testsuite/systemtap.bpf/asm_tests/temporary.stp b/testsuite/systemtap.bpf/asm_tests/temporary.stp +new file mode 100644 +index 000000000..153c759ba +--- /dev/null ++++ b/testsuite/systemtap.bpf/asm_tests/temporary.stp +@@ -0,0 +1,14 @@ ++function foo:long (x:long) %{ /* bpf */ /* pure */ ++ /* compute (100-x)*(x+2) */ ++ 0xb7, $$, -, -, 100; /* mov $$, 100 */ ++ 0x1f, $$, $x, -, -; /* sub $$, $x */ ++ 0xbf, $tmp, $x, -, -; /* mov $tmp, $x */ ++ 0x07, $tmp, -, -, 2; /* add $tmp, 2 */ ++ 0x2f, $$, $tmp, -, -; /* mul $$, $tmp */ ++%} ++ ++probe begin { ++ printf("foo(1)=%d, should be 99*3=297\n", foo(1)) ++ printf("foo(15)=%d, should be 85*18=1530\n", foo(15)) ++ exit() ++} +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0005-stapbpf-assembler-WIP-4-alloc-and-helper-call-operat.patch b/SOURCES/rhbz1643997.0005-stapbpf-assembler-WIP-4-alloc-and-helper-call-operat.patch new file mode 100644 index 0000000..c4c1c97 --- /dev/null +++ b/SOURCES/rhbz1643997.0005-stapbpf-assembler-WIP-4-alloc-and-helper-call-operat.patch @@ -0,0 +1,604 @@ +From f2339483ab00eff7a1a45c33b968891a63668c98 Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Tue, 16 Oct 2018 18:16:48 -0400 +Subject: [PATCH 05/32] stapbpf assembler WIP #4 :: alloc and (helper) call + operations + +--- + bpf-base.cxx | 46 ++++++---- + bpf-internal.h | 8 +- + bpf-translate.cxx | 259 ++++++++++++++++++++++++++++++++++++++++------------- + tapset/logging.stp | 2 + + 4 files changed, 237 insertions(+), 78 deletions(-) + +diff --git a/bpf-base.cxx b/bpf-base.cxx +index c3e36efa1..277927b72 100644 +--- a/bpf-base.cxx ++++ b/bpf-base.cxx +@@ -135,31 +135,43 @@ is_commutative(opcode code) + } + } + ++/* Various functions for eBPF helper lookup: */ ++ ++std::map bpf_func_name_map; ++std::map bpf_func_id_map; ++ ++void ++init_bpf_helper_tables () // TODO call before script translation ++{ ++#define __BPF_SET_FUNC_NAME(x) bpf_func_name_map[BPF_FUNC_ ## x] = #x ++#define __BPF_SET_FUNC_ID(x) bpf_func_id_map[#x] = BPF_FUNC_ ## x ++ __BPF_FUNC_MAPPER(__BPF_SET_FUNC_NAME) ++ __STAPBPF_FUNC_MAPPER(__BPF_SET_FUNC_NAME) ++ __BPF_FUNC_MAPPER(__BPF_SET_FUNC_ID) ++ __STAPBPF_FUNC_MAPPER(__BPF_SET_FUNC_ID) ++ (void)0; ++} ++ + const char * + bpf_function_name (unsigned id) + { +- switch (id) +- { +- case BPF_FUNC_map_lookup_elem: return "map_lookup_elem"; +- case BPF_FUNC_map_update_elem: return "map_update_elem"; +- case BPF_FUNC_map_delete_elem: return "map_delete_elem"; +- case BPF_FUNC_probe_read: return "probe_read"; +- case BPF_FUNC_ktime_get_ns: return "ktime_get_ns"; +- case BPF_FUNC_trace_printk: return "trace_printk"; +- case BPF_FUNC_get_prandom_u32: return "get_prandom_u32"; +- case BPF_FUNC_get_smp_processor_id: return "get_smp_processor_id"; +- case BPF_FUNC_get_current_pid_tgid: return "get_current_pid_tgid"; +- case BPF_FUNC_get_current_uid_gid: return "get_current_uid_gid"; +- case BPF_FUNC_get_current_comm: return "get_current_comm"; +- case BPF_FUNC_perf_event_read: return "perf_event_read"; +- case BPF_FUNC_perf_event_output: return "perf_event_output"; +- default: return NULL; +- } ++ if (bpf_func_name_map.count(id) != 0) ++ return bpf_func_name_map[id]; ++ return NULL; ++} ++ ++bpf_func_id ++bpf_function_id (const std::string& name) ++{ ++ if (bpf_func_id_map.count(name) != 0) ++ return bpf_func_id_map[name]; ++ return __BPF_FUNC_MAX_ID; + } + + unsigned + bpf_function_nargs (unsigned id) + { ++ // ??? generalize to all bpf functions + switch (id) + { + case BPF_FUNC_map_lookup_elem: return 2; +diff --git a/bpf-internal.h b/bpf-internal.h +index 719446db8..61514db9f 100644 +--- a/bpf-internal.h ++++ b/bpf-internal.h +@@ -96,14 +96,20 @@ bool is_move(opcode c); + bool is_ldst(opcode c); + bool is_binary(opcode c); + bool is_commutative(opcode c); ++ ++void init_bpf_helper_tables(); + const char *bpf_function_name (unsigned id); ++bpf_func_id bpf_function_id (const std::string &name); + unsigned bpf_function_nargs (unsigned id); + + const opcode BPF_LD_MAP = BPF_LD | BPF_IMM | BPF_DW | (BPF_PSEUDO_MAP_FD << 8); + +-// Not actual BPF helpers, but treating them like one simplifies some of the ++// Not actual BPF helpers, but treating them as such simplifies some of the + // interpreter logic. We give them IDs that shouldn't conflict with IDs of + // real BPF helpers. ++#define __STAPBPF_FUNC_MAPPER(FN) \ ++ FN(map_get_next_key), \ ++ FN(sprintf), + const bpf_func_id BPF_FUNC_map_get_next_key = (bpf_func_id) -1; + const bpf_func_id BPF_FUNC_sprintf = (bpf_func_id) -2; + +diff --git a/bpf-translate.cxx b/bpf-translate.cxx +index 023ac6ce7..af3f54b50 100644 +--- a/bpf-translate.cxx ++++ b/bpf-translate.cxx +@@ -179,7 +179,7 @@ struct bpf_unparser : public throwing_visitor + // TODO General triage of bpf-possible functionality: + virtual void visit_block (::block *s); + // TODO visit_try_block -> UNHANDLED +- virtual void visit_embeddedcode (embeddedcode *s); // TODO need to find testcase/example for this ++ virtual void visit_embeddedcode (embeddedcode *s); + virtual void visit_null_statement (null_statement *s); + virtual void visit_expr_statement (expr_statement *s); + virtual void visit_if_statement (if_statement* s); +@@ -192,7 +192,7 @@ struct bpf_unparser : public throwing_visitor + virtual void visit_continue_statement (continue_statement* s); + virtual void visit_literal_string (literal_string *e); + virtual void visit_literal_number (literal_number* e); +- // TODO visit_embedded_expr -> UNHANDLED, could be handled like embedded_code with a return value? ++ // TODO visit_embedded_expr -> UNHANDLED, could treat as embedded_code + virtual void visit_binary_expression (binary_expression* e); + virtual void visit_unary_expression (unary_expression* e); + virtual void visit_pre_crement (pre_crement* e); +@@ -200,7 +200,7 @@ struct bpf_unparser : public throwing_visitor + virtual void visit_logical_or_expr (logical_or_expr* e); + virtual void visit_logical_and_expr (logical_and_expr* e); + virtual void visit_array_in (array_in* e); +- // ??? visit_regex_query is UNHANDLED, requires adding new kernel functionality. ++ // ??? visit_regex_query -> UNHANDLED, requires new kernel functionality + virtual void visit_compound_expression (compound_expression *e); + virtual void visit_comparison (comparison* e); + // TODO visit_concatenation -> (2) pseudo-LOOP: copy the strings while concatenating +@@ -239,14 +239,21 @@ struct bpf_unparser : public throwing_visitor + value *emit_context_var(bpf_context_vardecl *v); + + // Used for the embedded-code assembler: ++ int64_t parse_imm (const asm_stmt &stmt, const std::string &str); + size_t parse_asm_stmt (embeddedcode *s, size_t start, + /*OUT*/asm_stmt &stmt); +- value *emit_asm_arg(const asm_stmt &stmt, const std::string ®, +- bool allow_imm = true); ++ value *emit_asm_arg(const asm_stmt &stmt, const std::string &arg, ++ bool allow_imm = true, bool allow_emit = true); + value *emit_asm_reg(const asm_stmt &stmt, const std::string ®); ++ value *get_asm_reg(const asm_stmt &stmt, const std::string ®); + void emit_asm_opcode(const asm_stmt &stmt, + std::map label_map); + ++ // Used for the embedded-code assembler's diagnostics: ++ source_loc adjusted_loc; ++ size_t adjust_pos; ++ std::vector adjusted_toks; // track for deallocation ++ + // Used for string data: + value *emit_literal_string(const std::string &str, const token *tok); + value *emit_string_copy(value *dest, int ofs, value *src, bool zero_pad = false); +@@ -580,17 +587,22 @@ bpf_unparser::visit_block (::block *s) + + Ahh for the days of 1960s FORTRAN. + +- TODO: It might make more sense to implement an assembler based on ++ ??? It might make more sense to implement an assembler based on + the syntax used in official eBPF subsystem docs. */ + +-/* Possible assembly statement types include: ++/* Supported assembly statement types include: + + ::= label, ; ++ ::= alloc, , ; ++ ::= call, , , , ...; ++ ::= printf, , , ...; ++ ::= error, , , ...; + ::= , , , + , ; + +- Possible argument types include: ++ Supported argument types include: + ++ ::= | + ::= | r | + $ | $ | $$ | + ::= | BPF_MAXSTRINGLEN +@@ -598,6 +610,9 @@ bpf_unparser::visit_block (::block *s) + + */ + ++// TODO ++#define BPF_ASM_DEBUG ++ + struct asm_stmt { + std::string kind; + +@@ -609,9 +624,10 @@ struct asm_stmt { + bool has_fallthrough = false; + std::string jmp_target, fallthrough; + ++ // metadata for call, error instructions ++ std::vector params; ++ + token *tok; +- bool deallocate_tok = false; +- ~asm_stmt() { if (deallocate_tok) delete tok; } + }; + + std::ostream& +@@ -647,10 +663,30 @@ is_numeric (const std::string &str) + stol(str, &pos, 0); + } catch (std::invalid_argument &e) { + return false; ++ } catch (std::out_of_range &e) { ++ /* XXX: probably numeric but not valid; give up */ ++ return false; + } + return (pos == str.size()); + } + ++int64_t ++bpf_unparser::parse_imm (const asm_stmt &stmt, const std::string &str) ++{ ++ int64_t val; ++ if (str == "BPF_MAXSTRINGLEN") ++ val = BPF_MAXSTRINGLEN; ++ else if (str == "-") ++ val = 0; ++ else try { ++ val = stol(str); ++ } catch (std::exception &e) { // XXX: invalid_argument, out_of_range ++ throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode operand '%s'", ++ str.c_str()), stmt.tok); ++ } ++ return val; ++} ++ + /* Parse an assembly statement starting from position start in code, + then write the output in stmt. Returns a position immediately after + the parsed statement. */ +@@ -663,31 +699,14 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start, + retry: + std::vector args; + unsigned n = code.size(); ++ size_t pos; + bool in_comment = false; + bool in_string = false; + +- // compute token with adjusted source location for diagnostics +- source_loc adjusted_loc; // TODO: ought to create a proper copy constructor for source_loc +- adjusted_loc.file = s->tok->location.file; +- adjusted_loc.line = s->tok->location.line; +- adjusted_loc.column = s->tok->location.column; +- for (size_t pos = 0; pos < start && pos < n; pos++) +- { +- // TODO: should save adjusted_loc state between parse_asm_stmt invocations; add field? +- char c = code[pos]; +- if (c == '\n') +- { +- adjusted_loc.line++; +- adjusted_loc.column = 1; +- } +- else +- adjusted_loc.column++; +- } +- +- // TODO: As before, parser is extremely non-rigorous and could do ++ // ??? As before, parser is extremely non-rigorous and could do + // with some tightening in terms of the inputs it accepts. +- size_t pos; + std::string arg = ""; ++ size_t save_start = start; // -- position for diagnostics + for (pos = start; pos < n; pos++) + { + char c = code[pos]; +@@ -714,6 +733,9 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start, + ++pos, in_comment = true; + else if (c == '"') // found a literal string + { ++ if (arg.empty() && args.empty()) ++ save_start = pos; // start of first argument ++ + // XXX: This allows '"' inside an arg and will treat the + // string as a sequence of weird identifier characters. A + // more rigorous parser would error on mixing strings and +@@ -738,6 +760,9 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start, + } + else // found (we assume) a regular char + { ++ if (arg.empty() && args.empty()) ++ save_start = pos; // start of first argument ++ + // XXX: As before, this strips whitespace within args + // (so '$ab', '$ a b' and '$a b' are equivalent). + // +@@ -760,28 +785,73 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start, + goto retry; + } + ++ // compute token with adjusted source location for diagnostics ++ // TODO: needs some attention to how multiline tokens are printed in error reporting -- with this code, caret aligns incorrectly ++ for (/* use saved adjust_pos */; adjust_pos < save_start && adjust_pos < n; adjust_pos++) ++ { ++ char c = code[adjust_pos]; ++ if (c == '\n') ++ { ++ adjusted_loc.line++; ++ adjusted_loc.column = 1; ++ } ++ else ++ adjusted_loc.column++; ++ } ++ + // set token with adjusted source location +- //stmt.tok = (token *)s->tok; +- // TODO this segfaults for some reason, some data not copied? + stmt.tok = s->tok->adjust_location(adjusted_loc); +- stmt.deallocate_tok = false; // TODO must avoid destroy-on-copy ++ adjusted_toks.push_back(stmt.tok); + +- std::cerr << "DEBUG GOT stmt "; // TODO +- for (unsigned k = 0; k < args.size(); k++) std::cerr << args[k] << " / "; +- std::cerr << std::endl; // TODO ++#ifdef BPF_ASM_DEBUG ++ std::cerr << "bpf_asm parse_asm_stmt: tokenizer got "; ++ for (unsigned k = 0; k < args.size(); k++) ++ std::cerr << args[k] << ", "; ++ std::cerr << std::endl; ++#endif + if (args[0] == "label") + { + if (args.size() != 2) +- throw SEMANTIC_ERROR (_("invalid bpf embeddedcode syntax"), stmt.tok); ++ throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode syntax (label expects 1 arg, found %lu)", args.size()-1), stmt.tok); ++ stmt.kind = args[0]; ++ stmt.dest = args[1]; ++ } ++ else if (args[0] == "alloc") ++ { ++ if (args.size() != 3) ++ throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode syntax (alloc expects 2 args, found %lu)", args.size()-1), stmt.tok); + stmt.kind = args[0]; + stmt.dest = args[1]; ++ stmt.imm = parse_imm(stmt, args[2]); ++ } ++ else if (args[0] == "call") ++ { ++ if (args.size() < 3) ++ throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode syntax (call expects at least 2 args, found %lu)", args.size()-1), stmt.tok); ++ stmt.kind = args[0]; ++ stmt.dest = args[1]; ++ for (unsigned k = 2; k < args.size(); k++) ++ stmt.params.push_back(args[k]); ++ } ++ else if (args[0] == "printf" || args[0] == "error") ++ { ++ if (args.size() < 2) ++ throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode syntax (%s expects at least 2 args, found %lu)", args[0].c_str(), args.size()-1), stmt.tok); ++ stmt.kind = args[0]; ++ for (unsigned k = 2; k < args.size(); k++) ++ stmt.params.push_back(args[k]); + } + else if (is_numeric(args[0])) + { +- if (args.size() != 5) // TODO change to 4 to test err+tok +- throw SEMANTIC_ERROR (_("invalid bpf embeddedcode syntax"), stmt.tok); ++ if (args.size() != 5) ++ throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode syntax (opcode expects 4 args, found %lu)", args.size()-1), stmt.tok); + stmt.kind = "opcode"; +- stmt.code = stoul(args[0], 0, 0); // TODO signal error ++ try { ++ stmt.code = stoul(args[0], 0, 0); ++ } catch (std::exception &e) { // XXX: invalid_argument, out_of_range ++ throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode opcode '%s'", ++ args[0].c_str()), stmt.tok); ++ } + stmt.dest = args[1]; + stmt.src1 = args[2]; + +@@ -799,25 +869,16 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start, + stmt.off = 0; + stmt.jmp_target = args[3]; + } +- else if (args[3] == "BPF_MAXSTRINGLEN") +- stmt.off = BPF_MAXSTRINGLEN; +- else if (args[3] == "-") +- stmt.off = 0; + else +- stmt.off = stol(args[3]); // TODO signal error ++ stmt.off = parse_imm(stmt, args[3]); + +- if (args[4] == "BPF_MAXSTRINGLEN") +- stmt.imm = BPF_MAXSTRINGLEN; +- else if (args[4] == "-") +- stmt.imm = 0; +- else +- stmt.imm = stol(args[4]); // TODO signal error ++ stmt.imm = parse_imm(stmt, args[4]); + } + else + throw SEMANTIC_ERROR (_F("unknown bpf embeddedcode operator '%s'", + args[0].c_str()), stmt.tok); + +- // we returned a statement, so there's more parsing to be done ++ // we returned one statement, there may be more parsing to be done + return pos; + } + +@@ -828,7 +889,7 @@ std::string translate_escapes (const interned_string &str); + May emit code to store a string constant on the stack. */ + value * + bpf_unparser::emit_asm_arg (const asm_stmt &stmt, const std::string &arg, +- bool allow_imm) ++ bool allow_imm, bool allow_emit) + { + if (arg == "$$") + { +@@ -859,7 +920,6 @@ bpf_unparser::emit_asm_arg (const asm_stmt &stmt, const std::string &arg, + auto ok = this_locals->insert (v); + assert (ok.second); + return reg; +- // TODO write a testcase + } + else if (is_numeric(arg) && allow_imm) + { +@@ -879,13 +939,17 @@ bpf_unparser::emit_asm_arg (const asm_stmt &stmt, const std::string &arg, + } + else if (arg[0] == '"') + { +- // TODO verify correctness ++ if (!allow_emit) ++ throw SEMANTIC_ERROR (_F("invalid bpf argument %s " ++ "(string literal not allowed here)", ++ arg.c_str()), stmt.tok); ++ + /* arg is a string constant */ + if (arg[arg.size() - 1] != '"') + throw SEMANTIC_ERROR (_F("BUG: improper string %s", + arg.c_str()), stmt.tok); + std::string escaped_str = arg.substr(1,arg.size()-2); /* strip quotes */ +- std::string str = translate_escapes(escaped_str); // TODO interned_str? ++ std::string str = translate_escapes(escaped_str); + return emit_literal_string(str, stmt.tok); + } + else if (arg == "BPF_MAXSTRINGLEN") +@@ -913,12 +977,22 @@ bpf_unparser::emit_asm_arg (const asm_stmt &stmt, const std::string &arg, + + } + ++/* As above, but don't accept immediate values. ++ Do accept string constants (since they're stored in a register). */ + value * + bpf_unparser::emit_asm_reg (const asm_stmt &stmt, const std::string ®) + { + return emit_asm_arg(stmt, reg, /*allow_imm=*/false); + } + ++/* As above, but don't allow string constants or anything that emits code. ++ Useful if the context requires an lvalue. */ ++value * ++bpf_unparser::get_asm_reg (const asm_stmt &stmt, const std::string ®) ++{ ++ return emit_asm_arg(stmt, reg, /*allow_imm=*/false, /*allow_emit=*/false); ++} ++ + void + bpf_unparser::emit_asm_opcode (const asm_stmt &stmt, + std::map label_map) +@@ -1013,7 +1087,7 @@ bpf_unparser::emit_asm_opcode (const asm_stmt &stmt, + + value *v_dest = NULL; + if (r_dest || r_src0) +- v_dest = emit_asm_reg(stmt, stmt.dest); ++ v_dest = get_asm_reg(stmt, stmt.dest); + else if (stmt.dest != "0" && stmt.dest != "-") + throw SEMANTIC_ERROR (_F("invalid register field '%s' in bpf code", + stmt.dest.c_str()), stmt.tok); +@@ -1069,6 +1143,10 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s) + std::vector statements; + asm_stmt stmt; + ++ // track adjusted source location for each stmt ++ adjusted_loc = s->tok->location; ++ adjust_pos = 0; ++ + size_t pos = 0; + while ((pos = parse_asm_stmt(s, pos, stmt)) != std::string::npos) + { +@@ -1133,7 +1211,7 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s) + after_jump = NULL; + } + } +- if (after_jump != NULL) // TODO: should just fall through to exit ++ if (after_jump != NULL) // ??? should just fall through to exit + throw SEMANTIC_ERROR (_("BUG: bpf embeddedcode doesn't support " + "fallthrough on final asm_stmt"), stmt.tok); + +@@ -1144,14 +1222,67 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s) + it != statements.end(); it++) + { + stmt = *it; +- std::cerr << "DEBUG processing " << stmt << std::endl; // TODO ++#ifdef BPF_ASM_DEBUG ++ std::cerr << "bpf_asm visit_embeddedcode: " << stmt << std::endl; ++#endif + if (stmt.kind == "label") + { +- // TODO: be sure there's no gap in the edge + if (!jumped_already) + emit_jmp (label_map[stmt.dest]); + set_block(label_map[stmt.dest]); + } ++ else if (stmt.kind == "alloc") ++ { ++ /* Reserve stack space and store its address in dest. */ ++ int ofs = this_prog.max_tmp_space + stmt.imm; ++ value *dest = get_asm_reg(stmt, stmt.dest); ++ this_prog.use_tmp_space(-ofs); ++ this_prog.mk_binary(this_ins, BPF_ADD, dest, ++ this_prog.lookup_reg(BPF_REG_10) /*frame*/, ++ this_prog.new_imm(ofs)); ++ } ++ else if (stmt.kind == "call") ++ { ++ std::string func_name = stmt.params[0]; ++ bpf_func_id hid = bpf_function_id(func_name); ++ if (hid != __BPF_FUNC_MAX_ID) ++ { ++ // TODO diagnostic: check if the number of arguments is correct ++ regno r = BPF_REG_1; unsigned nargs = 0; ++ for (unsigned k = 1; k < stmt.params.size(); k++) ++ { ++ // ??? Could make params optional to avoid this part, ++ // ??? since the calling convention is well-known. ++ value *from_reg = emit_asm_arg(stmt, stmt.params[k]); ++ value *to_reg = this_prog.lookup_reg(r); ++ this_prog.mk_mov(this_ins, to_reg, from_reg); ++ nargs++; r++; ++ } ++ this_prog.mk_call(this_ins, hid, nargs); ++ this_prog.mk_mov(this_ins, get_asm_reg(stmt, stmt.dest), ++ this_prog.lookup_reg(BPF_REG_0) /* returnval */); ++ // ??? Could make stmt.dest optional to avoid this extra mov, ++ // ??? since the BPF_REG_0 convention is well-known. ++ } ++ else ++ { ++ // TODO function_name = params[0]; ++ // TODO args = parse_reg(params[1]), parse_reg(params[2]), ... ++ // TODO emit_functioncall() with good bits from visit_functioncall() ++ throw SEMANTIC_ERROR (_("BUG: bpf embeddedcode non-helper 'call' not yet supported"), ++ stmt.tok); ++ } ++ } ++ else if (stmt.kind == "printf" || stmt.kind == "error") ++ { ++ // TODO Note that error() should be modeled on the tapset function in tapset/logging.stp ++ // TODO format = params[0]; ++ // TODO args = parse_reg(params[1]), parse_reg(params[2]), ... ++ // TODO emit_print_format() with good bits from visit_print_format() ++ // TODO if (stmt.kind == "error") emit functioncall to exit() ++ throw SEMANTIC_ERROR (_("BUG: bpf embeddedcode 'printf/error' not yet supported"), ++ stmt.tok); ++ } + else if (stmt.kind == "opcode") + { + emit_asm_opcode (stmt, label_map); +@@ -1164,6 +1295,12 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s) + if (stmt.has_fallthrough) + set_block(label_map[stmt.fallthrough]); + } ++ ++ // housekeeping -- deallocate adjusted_toks along with statements ++ for (std::vector::iterator it = adjusted_toks.begin(); ++ it != adjusted_toks.end(); it++) ++ delete *it; ++ adjusted_toks.clear(); + } + + void +@@ -3260,6 +3397,8 @@ translate_bpf_pass (systemtap_session& s) + { + using namespace bpf; + ++ init_bpf_helper_tables(); ++ + if (elf_version(EV_CURRENT) == EV_NONE) + return 1; + +diff --git a/tapset/logging.stp b/tapset/logging.stp +index 59edce3c8..839239b8f 100644 +--- a/tapset/logging.stp ++++ b/tapset/logging.stp +@@ -128,6 +128,8 @@ function error (msg:string) + exit() // TODO: should support MAXERRORS, probe error {} + } + %) ++// NOTE: The 'error' statement in the eBPF assembler in bpf-translate.cxx ++// should be kept up-to-date with the behaviour of this function. + + /** + * sfunction assert - evaluate assertion +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0006-stapbpf-assembler-WIP-5-basic-kernel_string-implemen.patch b/SOURCES/rhbz1643997.0006-stapbpf-assembler-WIP-5-basic-kernel_string-implemen.patch new file mode 100644 index 0000000..c9959f7 --- /dev/null +++ b/SOURCES/rhbz1643997.0006-stapbpf-assembler-WIP-5-basic-kernel_string-implemen.patch @@ -0,0 +1,128 @@ +From f614583a605f4c23d2f6e1a2ad31e089d10f8d8e Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Tue, 16 Oct 2018 18:17:25 -0400 +Subject: [PATCH 06/32] stapbpf assembler WIP #5 :: basic kernel_string() + implementation + +--- + tapset/bpf/conversions.stp | 108 +++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 108 insertions(+) + create mode 100644 tapset/bpf/conversions.stp + +diff --git a/tapset/bpf/conversions.stp b/tapset/bpf/conversions.stp +new file mode 100644 +index 000000000..d741ec584 +--- /dev/null ++++ b/tapset/bpf/conversions.stp +@@ -0,0 +1,108 @@ ++// conversions tapset -- eBPF version ++// Copyright (C) 2018 Red Hat Inc. ++// ++// This file is part of systemtap, and is free software. You can ++// redistribute it and/or modify it under the terms of the GNU General ++// Public License (GPL); either version 2, or (at your option) any ++// later version. ++ ++/** ++ * sfunction kernel_string - Retrieves string from kernel memory ++ * @addr: The kernel address to retrieve the string from ++ * ++ * Description: This function returns the null terminated C string ++ * from a given kernel memory address. Reports an error on string ++ * copy fault. ++ */ ++function kernel_string:string (addr:long) { ++ return kernel_string_n(addr, 64 /*TODO: define BPF_MAXSTRINGLEN*/) ++} ++ ++// TODO kernel_string:string(addr:long, err_msg:string) ++// TODO kernel_string2:string(addr:long, err_msg:string) ++ ++/** ++ * sfunction kernel_string - Retrieves string from kernel memory with alternative error string ++ * @addr: The kernel address to retrieve the string from ++ * @err_msg: The error message to return when data isn't available ++ * ++ * Description: This function returns the null terminated C string ++ * from a given kernel memory address. Reports the given error message ++ * on string copy fault. ++ */ ++function kernel_string:string (addr:long, err_msg:string) ++%{ /* bpf */ /* pure */ ++ /* buf = bpf_stk_alloc(BPF_MAXSTRINGLEN); ++ buf[0] = 0x0; // guarantee NUL byte ++ rc = bpf_probe_read_str(buf, n, addr); */ ++ alloc, $buf, BPF_MAXSTRINGLEN; ++ 0x62, $buf, -, -, 0x0; /* stw [$buf+0], 0x0 -- guarantee NUL byte */ ++ call, $rc, bpf_probe_read_str, $buf, BPF_MAXSTRINGLEN, $addr; ++ ++ /* if (rc < 0) return err_msg; ++ return buf; */ ++ 0xa5, rc, 0, _err, -; /* jlt $rc, 0, _err */ ++ 0xbf, $$, $buf, -, -; /* mov $$, $buf */ ++ 0x05, -, -, _done, -; /* ja _done; */ ++ label, _err; ++ 0xbf, $$, $err_msg, -, -; /* mov $$, $err_msg */ ++ label, _done; ++%} ++function kernel_string2:string (addr:long, err_msg:string) { ++ return kernel_string(addr, err_msg); ++} ++ ++// TODO kernel_string_quoted:string(addr:long) -- requires pseudo-loop to quote unprintable chars ++ ++/** ++ * sfunction kernel_string_n - Retrieves string of given length from kernel memory ++ * @addr: The kernel address to retrieve the string from ++ * @n: The maximum length of the string (if not null terminated) ++ * ++ * Description: Returns the C string of a maximum given length from a ++ * given kernel memory address. Reports an error on string copy fault. ++ */ ++function kernel_string_n:string (addr:long, n:long) ++%{ /* bpf */ /* pure */ ++ /* if (n > BPF_MAXSTRINGLEN) n = BPF_MAXSTRINGLEN; */ ++ 0xb5, $n, -, _skip, BPF_MAXSTRINGLEN; /* jle n, BPF_MAXSTRINGLEN, _skip */ ++ 0xb7, $n, -, -, BPF_MAXSTRINGLEN; /* mov $n, BPF_MAXSTRINGLEN */ ++ label, _skip; ++ ++ /* buf = bpf_stk_alloc(BPF_MAXSTRINGLEN); ++ buf[0] = 0x0; // guarantee NUL byte ++ rc = bpf_probe_read_str(buf, n, addr); */ ++ alloc, $buf, BPF_MAXSTRINGLEN; ++ 0x62, $buf, -, -, 0x0; /* stw [buf+0], 0 -- guarantee NUL byte */ ++ call, $rc, probe_read_str, $buf, $n, $addr; /* TODO: should work with bpf_probe_read_str too */ ++ ++ /* TODO pending implementation of error */ ++ /* if (rc < 0) error("...", addr); */ ++ /*0x35, $rc, 0, _done, -; /* jge rc, 0, _done */ ++ /*error, "kernel string copy fault at 0x%p [man error::fault]", $addr; /* TODO document bpf version of error::fault */ ++ /*label, _done;*/ ++ ++ /* return buf; */ ++ 0xbf, $$, $buf, -, -; /* mov $$, buf */ ++%} ++ ++// TODO kernel_string_utf32:string(addr:long) ++// TODO kernel_string_utf32:string(addr:long,err_msg:string) ++// TODO kernel_string2_utf32:string(addr:long,err_msg:string) ++// TODO kernel_string_quoted_utf32:string(addr:long) ++ ++// TODO kernel_string_utf16:string(addr:long) ++// TODO kernel_string_utf16:string(addr:long,err_msg:string) ++// TODO kernel_string2_utf16:string(addr:long,err_msg:string) ++// TODO kernel_string_quoted_utf16:string(addr:long) ++ ++// TODO kernel_long:long(addr:long) ++// TODO kernel_int:long(addr:long) ++// TODO kernel_short:long(addr:long) ++// TODO kernel_char:long(addr:long) ++ ++// TODO kernel_pointer:long(addr:long) ++ ++// TODO kernel_buffer_quoted:string(addr:long,inlen:long) ++// TODO kernel_buffer_quoted:string(addr:long,inlen:long,outlen:long) ++// TODO kernel_buffer_quoted_error:string(addr:long,inlen:long,outlen:long) +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0007-stapbpf-assembler-WIP-6-other-call-functions-s-print.patch b/SOURCES/rhbz1643997.0007-stapbpf-assembler-WIP-6-other-call-functions-s-print.patch new file mode 100644 index 0000000..1fc397e --- /dev/null +++ b/SOURCES/rhbz1643997.0007-stapbpf-assembler-WIP-6-other-call-functions-s-print.patch @@ -0,0 +1,623 @@ +From 1d21523d10628fd14c4a16d5f85d98add9b5c0a6 Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Tue, 23 Oct 2018 13:35:08 -0400 +Subject: [PATCH 07/32] stapbpf assembler WIP #6 :: other call functions + ({s}printf and tapset) + +Only very limited support for tapset functions (restricted to exit() +for now) due to the difficulty of resolving symbols after the semantic +pass is already completed. Could address this in the future. + +* bpf_internal.h (program::use_tmp_space): check for overflow. +(globals::session): new field for systemtap_session (used by function lookup). + +* bpf_translate.cxx (asm_stmt::has_jmp_target): new field. +(operator <<): printing rules for alloc, call. +(bpf_unparser::parse_asm_stmt): remove printf/error, BUGFIX alloc, call, string literal. +Also calculate has_jmp_target in the resulting stmt. +(bpf_unparser::visit_embeddedcode): handle printf, sprintf and exit(). +Also fix the way fallthrough fields are populated to avoid spurious extra jump. +(bpf_unparser::emit_functioncall): new function. Factors out non-staptree code. +(bpf_unparser::visit_functioncall): use new emit_functioncall(). +(print_format_add_tag): new function on std::string. Factors out string operations. +(bpf_unparser::emit_print_format): new function. Factors out non-staptree code. +(bpf_unparser::visit_print_format): use new emit_print_format(). +(translate_bpf_pass): store session in globals. +--- + bpf-internal.h | 5 + + bpf-translate.cxx | 346 +++++++++++++++++++++++++++++++++++++++--------------- + 2 files changed, 255 insertions(+), 96 deletions(-) + +diff --git a/bpf-internal.h b/bpf-internal.h +index 61514db9f..3041bbdf5 100644 +--- a/bpf-internal.h ++++ b/bpf-internal.h +@@ -30,6 +30,7 @@ namespace bpf { + #define MAX_BPF_STACK 512 + #define BPF_REG_SIZE 8 + #define BPF_MAXSTRINGLEN 64 ++// #define BPF_MAXSTRINGLEN 128 // TODO: Longer strings require storage allocator & better printf(). + #define BPF_MAXFORMATLEN 256 + #define BPF_MAXMAPENTRIES 2048 + // TODO: add BPF_MAXSPRINTFLEN +@@ -245,6 +246,7 @@ struct program + { + if (max_tmp_space < bytes) + max_tmp_space = bytes; ++ assert(max_tmp_space <= MAX_BPF_STACK); + } + + void mk_ld(insn_inserter &ins, int sz, value *dest, value *base, int off); +@@ -318,6 +320,9 @@ struct globals + EXIT = 0, + NUM_INTERNALS, // non-ABI + }; ++ ++ // Used to resolve function symbols in embedded code. ++ systemtap_session *session; + }; + } // namespace bpf + +diff --git a/bpf-translate.cxx b/bpf-translate.cxx +index af3f54b50..df22401ad 100644 +--- a/bpf-translate.cxx ++++ b/bpf-translate.cxx +@@ -135,8 +135,10 @@ has_side_effects (expression *e) + return t.side_effects; + } + +-/* forward declaration */ ++/* forward declarations */ + struct asm_stmt; ++static void print_format_add_tag(std::string&); ++static void print_format_add_tag(print_format*); + + struct bpf_unparser : public throwing_visitor + { +@@ -238,6 +240,11 @@ struct bpf_unparser : public throwing_visitor + value *emit_bool(expression *e); + value *emit_context_var(bpf_context_vardecl *v); + ++ value *emit_functioncall(functiondecl *f, const std::vector &args); ++ value *emit_print_format(const std::string &format, ++ const std::vector &actual, ++ bool print_to_stream = true); ++ + // Used for the embedded-code assembler: + int64_t parse_imm (const asm_stmt &stmt, const std::string &str); + size_t parse_asm_stmt (embeddedcode *s, size_t start, +@@ -594,24 +601,22 @@ bpf_unparser::visit_block (::block *s) + + ::= label, ; + ::= alloc, , ; +- ::= call, , , , ...; +- ::= printf, , , ...; +- ::= error, , , ...; ++ ::= call, , , , ...; + ::= , , , + , ; + + Supported argument types include: + +- ::= | +- ::= | r | +- $ | $ | $$ | +- ::= | BPF_MAXSTRINGLEN +- ::= | ++ ::= | ++ ::= | - ++ ::= | r | ++ $ | $ | $$ | ++ ::= | BPF_MAXSTRINGLEN | - ++ ::= | + + */ + +-// TODO +-#define BPF_ASM_DEBUG ++// #define BPF_ASM_DEBUG + + struct asm_stmt { + std::string kind; +@@ -621,6 +626,8 @@ struct asm_stmt { + int64_t off, imm; + + // metadata for jmp instructions ++ // ??? The logic around these flags could be pruned a bit. ++ bool has_jmp_target = false; + bool has_fallthrough = false; + std::string jmp_target, fallthrough; + +@@ -650,6 +657,19 @@ operator << (std::ostream& o, const asm_stmt& stmt) + << stmt.imm << ";" + << (stmt.has_fallthrough ? " +FALLTHROUGH " + stmt.fallthrough : ""); + } ++ else if (stmt.kind == "alloc") ++ { ++ o << "alloc, " << stmt.dest << ", " << stmt.imm << ";"; ++ } ++ else if (stmt.kind == "call") ++ { ++ o << "call, " << stmt.dest << ", "; ++ for (unsigned k = 0; k < stmt.params.size(); k++) ++ { ++ o << stmt.params[k]; ++ o << (k >= stmt.params.size() - 1 ? ";" : ", "); ++ } ++ } + else + o << ""; + return o; +@@ -711,7 +731,7 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start, + { + char c = code[pos]; + char c2 = pos + 1 < n ? code [pos + 1] : 0; +- if (isspace(c)) ++ if (isspace(c) && !in_string) + continue; // skip + else if (in_comment) + { +@@ -799,6 +819,10 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start, + adjusted_loc.column++; + } + ++ // Now populate the statement data. ++ ++ stmt = asm_stmt(); // clear pre-existing data ++ + // set token with adjusted source location + stmt.tok = s->tok->adjust_location(adjusted_loc); + adjusted_toks.push_back(stmt.tok); +@@ -830,14 +854,7 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start, + throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode syntax (call expects at least 2 args, found %lu)", args.size()-1), stmt.tok); + stmt.kind = args[0]; + stmt.dest = args[1]; +- for (unsigned k = 2; k < args.size(); k++) +- stmt.params.push_back(args[k]); +- } +- else if (args[0] == "printf" || args[0] == "error") +- { +- if (args.size() < 2) +- throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode syntax (%s expects at least 2 args, found %lu)", args[0].c_str(), args.size()-1), stmt.tok); +- stmt.kind = args[0]; ++ assert(stmt.params.empty()); + for (unsigned k = 2; k < args.size(); k++) + stmt.params.push_back(args[k]); + } +@@ -855,16 +872,16 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start, + stmt.dest = args[1]; + stmt.src1 = args[2]; + +- bool has_jmp_target = ++ stmt.has_jmp_target = + BPF_CLASS(stmt.code) == BPF_JMP + && BPF_OP(stmt.code) != BPF_EXIT + && BPF_OP(stmt.code) != BPF_CALL; + stmt.has_fallthrough = // only for jcond +- has_jmp_target ++ stmt.has_jmp_target + && BPF_OP(stmt.code) != BPF_JA; + // XXX: stmt.fallthrough is computed by visit_embeddedcode + +- if (has_jmp_target) ++ if (stmt.has_jmp_target) + { + stmt.off = 0; + stmt.jmp_target = args[3]; +@@ -1168,6 +1185,7 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s) + + if (after_jump != NULL && stmt.kind == "label") + { ++ after_jump->has_fallthrough = true; + after_jump->fallthrough = stmt.dest; + } + else if (after_jump != NULL) +@@ -1183,6 +1201,7 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s) + label_map[fallthrough_label] = b; + set_block(b); + ++ after_jump->has_fallthrough = true; + after_jump->fallthrough = fallthrough_label; + } + +@@ -1205,6 +1224,12 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s) + after_label = false; + after_jump = &*it; // be sure to refer to original, not copied stmt + } ++ else if (stmt.kind == "opcode" && BPF_CLASS(stmt.code) == BPF_JMP ++ && BPF_OP(stmt.code) != BPF_CALL /* CALL stays in the same block */) ++ { ++ after_label = false; ++ after_jump = &*it; // be sure to refer to original, not copied stmt ++ } + else + { + after_label = false; +@@ -1216,7 +1241,7 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s) + "fallthrough on final asm_stmt"), stmt.tok); + + // emit statements +- bool jumped_already = true; ++ bool jumped_already = false; + set_block(entry_block); + for (std::vector::iterator it = statements.begin(); + it != statements.end(); it++) +@@ -1234,24 +1259,27 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s) + else if (stmt.kind == "alloc") + { + /* Reserve stack space and store its address in dest. */ +- int ofs = this_prog.max_tmp_space + stmt.imm; +- value *dest = get_asm_reg(stmt, stmt.dest); ++ int ofs = -this_prog.max_tmp_space - stmt.imm; + this_prog.use_tmp_space(-ofs); ++ // ??? Consider using a storage allocator and this_prog.new_obj(). ++ ++ value *dest = get_asm_reg(stmt, stmt.dest); + this_prog.mk_binary(this_ins, BPF_ADD, dest, + this_prog.lookup_reg(BPF_REG_10) /*frame*/, + this_prog.new_imm(ofs)); + } + else if (stmt.kind == "call") + { ++ assert (!stmt.params.empty()); + std::string func_name = stmt.params[0]; + bpf_func_id hid = bpf_function_id(func_name); + if (hid != __BPF_FUNC_MAX_ID) + { +- // TODO diagnostic: check if the number of arguments is correct ++ // ??? For diagnostics: check if the number of arguments is correct. + regno r = BPF_REG_1; unsigned nargs = 0; + for (unsigned k = 1; k < stmt.params.size(); k++) + { +- // ??? Could make params optional to avoid this part, ++ // ??? Could make params optional to avoid the MOVs, + // ??? since the calling convention is well-known. + value *from_reg = emit_asm_arg(stmt, stmt.params[k]); + value *to_reg = this_prog.lookup_reg(r); +@@ -1259,30 +1287,117 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s) + nargs++; r++; + } + this_prog.mk_call(this_ins, hid, nargs); +- this_prog.mk_mov(this_ins, get_asm_reg(stmt, stmt.dest), +- this_prog.lookup_reg(BPF_REG_0) /* returnval */); +- // ??? Could make stmt.dest optional to avoid this extra mov, +- // ??? since the BPF_REG_0 convention is well-known. ++ if (stmt.dest != "-") ++ { ++ value *dest = get_asm_reg(stmt, stmt.dest); ++ this_prog.mk_mov(this_ins, dest, ++ this_prog.lookup_reg(BPF_REG_0) /* returnval */); ++ } ++ // ??? For diagnostics: check other cases with stmt.dest. ++ } ++ else if (func_name == "printf" || func_name == "sprintf") ++ { ++ if (stmt.params.size() < 2) ++ throw SEMANTIC_ERROR (_F("bpf embeddedcode '%s' expects format string, " ++ "none provided", func_name.c_str()), ++ stmt.tok); ++ std::string format = stmt.params[1]; ++ if (format.size() < 2 || format[0] != '"' ++ || format[format.size()-1] != '"') ++ throw SEMANTIC_ERROR (_F("bpf embeddedcode '%s' expects format string, " ++ "but first parameter is not a string literal", ++ func_name.c_str()), stmt.tok); ++ format = format.substr(1,format.size()-2); /* strip quotes */ ++ format = translate_escapes(format); ++ ++ bool print_to_stream = (func_name == "printf"); ++ if (print_to_stream) ++ print_format_add_tag(format); ++ ++ size_t format_bytes = format.size() + 1; ++ if (format_bytes > BPF_MAXFORMATLEN) ++ throw SEMANTIC_ERROR(_("Format string for print too long"), stmt.tok); ++ ++ std::vector args; ++ for (unsigned k = 2; k < stmt.params.size(); k++) ++ args.push_back(emit_asm_arg(stmt, stmt.params[k])); ++ if (args.size() > 3) ++ throw SEMANTIC_ERROR(_NF("additional argument to print", ++ "too many arguments to print (%zu)", ++ args.size(), args.size()), stmt.tok); ++ ++ value *retval = emit_print_format(format, args, print_to_stream); ++ if (retval != NULL && stmt.dest != "-") ++ { ++ value *dest = get_asm_reg(stmt, stmt.dest); ++ this_prog.mk_mov(this_ins, dest, retval); ++ ++ } ++ // ??? For diagnostics: check other cases with retval and stmt.dest. + } + else + { +- // TODO function_name = params[0]; +- // TODO args = parse_reg(params[1]), parse_reg(params[2]), ... +- // TODO emit_functioncall() with good bits from visit_functioncall() +- throw SEMANTIC_ERROR (_("BUG: bpf embeddedcode non-helper 'call' not yet supported"), +- stmt.tok); ++ // TODO: Experimental code for supporting basic functioncalls. ++ // Needs improvement and simplification to work with full generality. ++ // But thus far, it is sufficient for calling exit(). ++#if 1 ++ if (func_name != "exit") ++ throw SEMANTIC_ERROR(_("BUG: bpf embeddedcode non-helper 'call' operation only supports printf(),sprintf(),exit() for now"), stmt.tok); ++#elif 1 ++ throw SEMANTIC_ERROR(_("BUG: bpf embeddedcode non-helper 'call' operation only supports printf(),sprintf() for now"), stmt.tok); ++#endif ++#if 1 ++ // ???: Passing systemtap_session through all the way to here ++ // seems intrusive, but less intrusive than moving ++ // embedded-code assembly to the translate_globals() pass. ++ symresolution_info sym (*glob.session); ++ functioncall *call = new functioncall; ++ call->tok = stmt.tok; ++ unsigned nargs = stmt.params.size() - 1; ++ std::vector fds ++ = sym.find_functions (call, func_name, nargs, stmt.tok); ++ delete call; ++ ++ if (fds.empty()) ++ // ??? Could call levenshtein_suggest() as in ++ // symresolution_info::visit_functioncall(). ++ throw SEMANTIC_ERROR(_("bpf embeddedcode unresolved function call"), stmt.tok); ++ if (fds.size() > 1) ++ throw SEMANTIC_ERROR(_("bpf embeddedcode unhandled function overloading"), stmt.tok); ++ functiondecl *f = fds[0]; ++ // TODO: Imitation of semantic_pass_symbols, does not ++ // cover full generality of the lookup process. ++ update_visitor_loop (*glob.session, glob.session->code_filters, f->body); ++ sym.current_function = f; sym.current_probe = 0; ++ f->body->visit (&sym); ++ ++ // ??? For now, always inline the function call. ++ for (auto i = func_calls.begin(); i != func_calls.end(); ++i) ++ if (f == *i) ++ throw SEMANTIC_ERROR (_("unhandled function recursion"), stmt.tok); ++ ++ // Collect the function arguments. ++ std::vector args; ++ for (unsigned k = 1; k < stmt.params.size(); k++) ++ args.push_back(emit_asm_arg(stmt, stmt.params[k])); ++ ++ if (args.size () != f->formal_args.size()) ++ throw SEMANTIC_ERROR(_F("bpf embeddedcode call to function '%s' " ++ "expected %zu arguments, got %zu", ++ func_name.c_str(), ++ f->formal_args.size(), args.size()), ++ stmt.tok); ++ ++ value *retval = emit_functioncall(f, args); ++ if (stmt.dest != "-") ++ { ++ value *dest = get_asm_reg(stmt, stmt.dest); ++ this_prog.mk_mov(this_ins, dest, retval); ++ } ++ // ??? For diagnostics: check other cases with retval and stmt.dest. ++#endif + } + } +- else if (stmt.kind == "printf" || stmt.kind == "error") +- { +- // TODO Note that error() should be modeled on the tapset function in tapset/logging.stp +- // TODO format = params[0]; +- // TODO args = parse_reg(params[1]), parse_reg(params[2]), ... +- // TODO emit_print_format() with good bits from visit_print_format() +- // TODO if (stmt.kind == "error") emit functioncall to exit() +- throw SEMANTIC_ERROR (_("BUG: bpf embeddedcode 'printf/error' not yet supported"), +- stmt.tok); +- } + else if (stmt.kind == "opcode") + { + emit_asm_opcode (stmt, label_map); +@@ -1291,9 +1406,13 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s) + throw SEMANTIC_ERROR (_F("BUG: bpf embeddedcode contains unexpected " + "asm_stmt kind '%s'", stmt.kind.c_str()), + stmt.tok); +- jumped_already = stmt.has_fallthrough; + if (stmt.has_fallthrough) +- set_block(label_map[stmt.fallthrough]); ++ { ++ jumped_already = true; ++ set_block(label_map[stmt.fallthrough]); ++ } ++ else ++ jumped_already = false; + } + + // housekeeping -- deallocate adjusted_toks along with statements +@@ -1779,7 +1898,8 @@ bpf_unparser::visit_logical_and_expr (logical_and_expr* e) + result = emit_bool (e); + } + +-// TODO: This matches translate.cxx, but it looks like the functionality is disabled in the parser. ++// ??? This matches the code in translate.cxx, but it looks like the ++// functionality has been disabled in the SystemTap parser. + void + bpf_unparser::visit_compound_expression (compound_expression* e) + { +@@ -2573,30 +2693,17 @@ bpf_unparser::emit_str_arg(value *arg, int ofs, value *str) + emit_mov(arg, out); + } + +-void +-bpf_unparser::visit_functioncall (functioncall *e) ++value * ++bpf_unparser::emit_functioncall (functiondecl *f, const std::vector& args) + { +- // ??? For now, always inline the function call. +- // ??? Function overloading isn't handled. +- if (e->referents.size () != 1) +- throw SEMANTIC_ERROR (_("unhandled function overloading"), e->tok); +- functiondecl *f = e->referents[0]; +- +- for (auto i = func_calls.begin(); i != func_calls.end(); ++i) +- if (f == *i) +- throw SEMANTIC_ERROR (_("unhandled function recursion"), e->tok); +- +- assert (e->args.size () == f->formal_args.size ()); +- + // Create a new map for the function's local variables. + locals_map *locals = new_locals(f->locals); + +- // Evaluate the function arguments and install in the map. +- for (unsigned n = e->args.size (), i = 0; i < n; ++i) ++ // Install locals in the map. ++ unsigned n = args.size(); ++ for (unsigned i = 0; i < n; ++i) + { +- value *r = this_prog.new_reg (); +- emit_mov (r, emit_expr (e->args[i])); +- const locals_map::value_type v (f->formal_args[i], r); ++ const locals_map::value_type v (f->formal_args[i], args[i]); + auto ok = locals->insert (v); + assert (ok.second); + } +@@ -2622,7 +2729,47 @@ bpf_unparser::visit_functioncall (functioncall *e) + this_locals = old_locals; + delete locals; + +- result = retval; ++ return retval; ++} ++ ++void ++bpf_unparser::visit_functioncall (functioncall *e) ++{ ++ // ??? Function overloading isn't handled. ++ if (e->referents.size () != 1) ++ throw SEMANTIC_ERROR (_("unhandled function overloading"), e->tok); ++ functiondecl *f = e->referents[0]; ++ ++ // ??? For now, always inline the function call. ++ for (auto i = func_calls.begin(); i != func_calls.end(); ++i) ++ if (f == *i) ++ throw SEMANTIC_ERROR (_("unhandled function recursion"), e->tok); ++ ++ // XXX: Should have been checked in earlier pass. ++ assert (e->args.size () == f->formal_args.size ()); ++ ++ // Evaluate and collect the function arguments. ++ std::vector args; ++ for (unsigned n = e->args.size (), i = 0; i < n; ++i) ++ { ++ value *r = this_prog.new_reg (); ++ emit_mov (r, emit_expr (e->args[i])); ++ args.push_back(r); ++ } ++ ++ result = emit_functioncall(f, args); ++} ++ ++static void ++print_format_add_tag(std::string& format) ++{ ++ // surround the string with ... to facilitate ++ // stapbpf recovering it from debugfs. ++ std::string start_tag = module_name; ++ start_tag = "<" + start_tag.erase(4,1) + ">"; ++ std::string end_tag = start_tag + "\n"; ++ end_tag.insert(1, "/"); ++ format = start_tag + format + end_tag; + } + + static void +@@ -2677,6 +2824,32 @@ print_format_add_tag(print_format *e) + } + } + ++value * ++bpf_unparser::emit_print_format (const std::string& format, ++ const std::vector& actual, ++ bool print_to_stream) ++{ ++ size_t nargs = actual.size(); ++ ++ // The bpf verifier requires that the format string be stored on the ++ // bpf program stack. This is handled by bpf-opt.cxx lowering STR values. ++ size_t format_bytes = format.size() + 1; ++ this_prog.mk_mov(this_ins, this_prog.lookup_reg(BPF_REG_1), ++ this_prog.new_str(format)); ++ emit_mov(this_prog.lookup_reg(BPF_REG_2), this_prog.new_imm(format_bytes)); ++ for (size_t i = 0; i < nargs; ++i) ++ emit_mov(this_prog.lookup_reg(BPF_REG_3 + i), actual[i]); ++ ++ if (print_to_stream) ++ this_prog.mk_call(this_ins, BPF_FUNC_trace_printk, nargs + 2); ++ else ++ { ++ this_prog.mk_call(this_ins, BPF_FUNC_sprintf, nargs + 2); ++ return this_prog.lookup_reg(BPF_REG_0); ++ } ++ return NULL; ++} ++ + void + bpf_unparser::visit_print_format (print_format *e) + { +@@ -2696,9 +2869,9 @@ bpf_unparser::visit_print_format (print_format *e) + "too many arguments to print (%zu)", + e->args.size(), e->args.size()), e->tok); + +- value *actual[3] = { NULL, NULL, NULL }; ++ std::vector actual; + for (i = 0; i < nargs; ++i) +- actual[i] = emit_expr(e->args[i]); ++ actual.push_back(emit_expr(e->args[i])); + + std::string format; + if (e->print_with_format) +@@ -2750,36 +2923,17 @@ bpf_unparser::visit_print_format (print_format *e) + if (e->print_with_newline) + format += '\n'; + +- // surround the string with ... to facilitate +- // stapbpf recovering it from debugfs. + if (e->print_to_stream) +- { +- std::string start_tag = module_name; +- start_tag = "<" + start_tag.erase(4,1) + ">"; +- std::string end_tag = start_tag + "\n"; +- end_tag.insert(1, "/"); +- format = start_tag + format + end_tag; +- } ++ print_format_add_tag(format); + } + +- // The bpf verifier requires that the format string be stored on the +- // bpf program stack. This is handled by bpf-opt.cxx lowering STR values. + size_t format_bytes = format.size() + 1; + if (format_bytes > BPF_MAXFORMATLEN) + throw SEMANTIC_ERROR(_("Format string for print too long"), e->tok); +- this_prog.mk_mov(this_ins, this_prog.lookup_reg(BPF_REG_1), +- this_prog.new_str(format)); +- emit_mov(this_prog.lookup_reg(BPF_REG_2), this_prog.new_imm(format_bytes)); +- for (i = 0; i < nargs; ++i) +- emit_mov(this_prog.lookup_reg(BPF_REG_3 + i), actual[i]); + +- if (e->print_to_stream) +- this_prog.mk_call(this_ins, BPF_FUNC_trace_printk, nargs + 2); +- else +- { +- this_prog.mk_call(this_ins, BPF_FUNC_sprintf, nargs + 2); +- result = this_prog.lookup_reg(BPF_REG_0); +- } ++ value *retval = emit_print_format(format, actual, e->print_to_stream); ++ if (retval != NULL) ++ result = retval; + } + + // } // anon namespace +@@ -3409,7 +3563,7 @@ translate_bpf_pass (systemtap_session& s) + return 1; + + BPF_Output eo(fd); +- globals glob; ++ globals glob; glob.session = &s; + int ret = 0; + const token* t = 0; + try +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0008-stapbpf-assembler-WIP-7-fixed-kernel_string-tapset-a.patch b/SOURCES/rhbz1643997.0008-stapbpf-assembler-WIP-7-fixed-kernel_string-tapset-a.patch new file mode 100644 index 0000000..b8c3069 --- /dev/null +++ b/SOURCES/rhbz1643997.0008-stapbpf-assembler-WIP-7-fixed-kernel_string-tapset-a.patch @@ -0,0 +1,98 @@ +From 06835f4435a706d2495245116417a48178435c4c Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Wed, 24 Oct 2018 12:46:55 -0400 +Subject: [PATCH 08/32] stapbpf assembler WIP #7 :: fixed kernel_string() + tapset and testcase + +* tapset/bpf/conversions.stp (kernel_string_n): enable error path. +* tapset/logging.stp (abort): note future work. +* testsuite/systemtap.bpf/bpf_tests/context_vars3.stp: new testcase. +--- + tapset/bpf/conversions.stp | 14 ++++++++------ + tapset/logging.stp | 4 ++-- + testsuite/systemtap.bpf/bpf_tests/context_vars3.stp | 15 +++++++++++++++ + 3 files changed, 25 insertions(+), 8 deletions(-) + create mode 100644 testsuite/systemtap.bpf/bpf_tests/context_vars3.stp + +diff --git a/tapset/bpf/conversions.stp b/tapset/bpf/conversions.stp +index d741ec584..1140a6875 100644 +--- a/tapset/bpf/conversions.stp ++++ b/tapset/bpf/conversions.stp +@@ -44,8 +44,10 @@ function kernel_string:string (addr:long, err_msg:string) + 0xa5, rc, 0, _err, -; /* jlt $rc, 0, _err */ + 0xbf, $$, $buf, -, -; /* mov $$, $buf */ + 0x05, -, -, _done, -; /* ja _done; */ ++ + label, _err; + 0xbf, $$, $err_msg, -, -; /* mov $$, $err_msg */ ++ + label, _done; + %} + function kernel_string2:string (addr:long, err_msg:string) { +@@ -67,21 +69,21 @@ function kernel_string_n:string (addr:long, n:long) + /* if (n > BPF_MAXSTRINGLEN) n = BPF_MAXSTRINGLEN; */ + 0xb5, $n, -, _skip, BPF_MAXSTRINGLEN; /* jle n, BPF_MAXSTRINGLEN, _skip */ + 0xb7, $n, -, -, BPF_MAXSTRINGLEN; /* mov $n, BPF_MAXSTRINGLEN */ +- label, _skip; + ++ label, _skip; + /* buf = bpf_stk_alloc(BPF_MAXSTRINGLEN); + buf[0] = 0x0; // guarantee NUL byte + rc = bpf_probe_read_str(buf, n, addr); */ + alloc, $buf, BPF_MAXSTRINGLEN; + 0x62, $buf, -, -, 0x0; /* stw [buf+0], 0 -- guarantee NUL byte */ +- call, $rc, probe_read_str, $buf, $n, $addr; /* TODO: should work with bpf_probe_read_str too */ ++ call, $rc, probe_read_str, $buf, $n, $addr; /* TODO: should work if the helper is named bpf_probe_read_str() too */ + +- /* TODO pending implementation of error */ + /* if (rc < 0) error("...", addr); */ +- /*0x35, $rc, 0, _done, -; /* jge rc, 0, _done */ +- /*error, "kernel string copy fault at 0x%p [man error::fault]", $addr; /* TODO document bpf version of error::fault */ +- /*label, _done;*/ ++ 0x35, $rc, 0, _done, -; /* jge rc, 0, _done */ ++ call, -, printf, "ERROR: kernel string copy fault at 0x%p [man error::fault]", $addr; /* TODO document stapbpf version of error::fault */ ++ call, -, exit; + ++ label, _done; + /* return buf; */ + 0xbf, $$, $buf, -, -; /* mov $$, buf */ + %} +diff --git a/tapset/logging.stp b/tapset/logging.stp +index 839239b8f..441ad2c21 100644 +--- a/tapset/logging.stp ++++ b/tapset/logging.stp +@@ -95,8 +95,8 @@ function abort () + %: + { /* unprivileged */ /* bpf */ + _set_exit_status() +- printf("ERROR: abort() not supported yet\n") +- exit() /* TODO: need to abort the execution flow immediately */ ++ printf("ERROR: abort() not supported in eBPF backend\n") ++ exit() /* TODO: need to abort the execution flow immediately -- could be handled with a special assembly operation */ + } + %) + %) +diff --git a/testsuite/systemtap.bpf/bpf_tests/context_vars3.stp b/testsuite/systemtap.bpf/bpf_tests/context_vars3.stp +new file mode 100644 +index 000000000..97cd338d6 +--- /dev/null ++++ b/testsuite/systemtap.bpf/bpf_tests/context_vars3.stp +@@ -0,0 +1,15 @@ ++probe begin { ++ printf("BEGIN\n") ++} ++ ++probe kernel.function("vfs_read") { ++ if ($file != 0 && $file->f_cred->usage->counter > 0 && $buf != 0) { ++ filename = kernel_string($file->f_path->dentry->d_name->name) ++ printf("found %s\n", filename) ++ exit() ++ } ++} ++ ++probe end { ++ printf("END PASS\n") ++} +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0009-stapbpf-assembler-WIP-8-bpf-asm.exp-driver-and-more-.patch b/SOURCES/rhbz1643997.0009-stapbpf-assembler-WIP-8-bpf-asm.exp-driver-and-more-.patch new file mode 100644 index 0000000..2b0e48b --- /dev/null +++ b/SOURCES/rhbz1643997.0009-stapbpf-assembler-WIP-8-bpf-asm.exp-driver-and-more-.patch @@ -0,0 +1,356 @@ +From 17e0a28b75e8a5961fcfae52b631eebf9e9fd118 Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Wed, 24 Oct 2018 13:30:29 -0400 +Subject: [PATCH 09/32] stapbpf assembler WIP #8 :: bpf-asm.exp driver and more + testcases + +--- + testsuite/systemtap.bpf/asm_tests/branch.stp | 14 ++- + .../systemtap.bpf/asm_tests/err-printf_args.stp | 9 ++ + testsuite/systemtap.bpf/asm_tests/err_numeric.stp | 1 - + testsuite/systemtap.bpf/asm_tests/leniency.stp | 14 ++- + testsuite/systemtap.bpf/asm_tests/printf-basic.stp | 9 ++ + testsuite/systemtap.bpf/asm_tests/printf.stp | 16 ++++ + testsuite/systemtap.bpf/asm_tests/simple.stp | 9 +- + testsuite/systemtap.bpf/asm_tests/string.stp | 21 +++-- + testsuite/systemtap.bpf/asm_tests/temporary.stp | 9 +- + testsuite/systemtap.bpf/asm_tests/unreachable.stp | 17 ++++ + testsuite/systemtap.bpf/bpf-asm.exp | 105 +++++++++++++++++++++ + 11 files changed, 205 insertions(+), 19 deletions(-) + create mode 100644 testsuite/systemtap.bpf/asm_tests/err-printf_args.stp + create mode 100644 testsuite/systemtap.bpf/asm_tests/printf-basic.stp + create mode 100644 testsuite/systemtap.bpf/asm_tests/printf.stp + create mode 100644 testsuite/systemtap.bpf/asm_tests/unreachable.stp + create mode 100644 testsuite/systemtap.bpf/bpf-asm.exp + +diff --git a/testsuite/systemtap.bpf/asm_tests/branch.stp b/testsuite/systemtap.bpf/asm_tests/branch.stp +index aa22bf195..0e49213df 100644 +--- a/testsuite/systemtap.bpf/asm_tests/branch.stp ++++ b/testsuite/systemtap.bpf/asm_tests/branch.stp +@@ -6,7 +6,6 @@ function foo:long (x:long) %{ /* bpf */ /* pure */ + label, _bar; + 0xb7, $$, -, -, 50; /* mov $$, 50 */ + label, _done; +- /* 0xbf, $$, $$, -, -; /* dummy op */ + %} + + function bar:long (x:long) { +@@ -14,9 +13,14 @@ function bar:long (x:long) { + } + + probe begin { +- printf("foo(1)=%d should be %d\n", foo(1), bar(1)) +- printf("foo(8)=%d should be %d\n", foo(8), bar(8)) +- printf("foo(15)=%d should be %d\n", foo(15), bar(15)) +- exit() ++ printf("U foo(1)=%d should be %d\n", foo(1), bar(1)) ++ printf("U foo(8)=%d should be %d\n", foo(8), bar(8)) ++ printf("U foo(15)=%d should be %d\n", foo(15), bar(15)) + } + ++probe kernel.function("vfs_read") { ++ printf("K foo(1)=%d should be %d\n", foo(1), bar(1)) ++ printf("K foo(8)=%d should be %d\n", foo(8), bar(8)) ++ printf("K foo(15)=%d should be %d\n", foo(15), bar(15)) ++ exit() ++} +diff --git a/testsuite/systemtap.bpf/asm_tests/err-printf_args.stp b/testsuite/systemtap.bpf/asm_tests/err-printf_args.stp +new file mode 100644 +index 000000000..eb4adc00f +--- /dev/null ++++ b/testsuite/systemtap.bpf/asm_tests/err-printf_args.stp +@@ -0,0 +1,9 @@ ++function foo:long (x:long) %{ /* bpf */ ++ call, -, printf, "x = 0x%p causing exit\n", $x, $x, $x, $x; ++ 0xb7, $$, -, -, 0x0; /* mov $$, 0 */ ++%} ++ ++probe begin { ++ printf("U x = 10 should print:\n"); foo(10) ++ exit() ++} +diff --git a/testsuite/systemtap.bpf/asm_tests/err_numeric.stp b/testsuite/systemtap.bpf/asm_tests/err_numeric.stp +index 9428e5704..ed82b32a1 100644 +--- a/testsuite/systemtap.bpf/asm_tests/err_numeric.stp ++++ b/testsuite/systemtap.bpf/asm_tests/err_numeric.stp +@@ -15,4 +15,3 @@ probe begin { + printf("foo(15)=%d should be %d\n", foo(15), bar(15)) + exit() + } +- +diff --git a/testsuite/systemtap.bpf/asm_tests/leniency.stp b/testsuite/systemtap.bpf/asm_tests/leniency.stp +index 939061158..1db1ec7fd 100644 +--- a/testsuite/systemtap.bpf/asm_tests/leniency.stp ++++ b/testsuite/systemtap.bpf/asm_tests/leniency.stp +@@ -9,9 +9,17 @@ function foo:long (x:long) %{ /* bpf */ /* pure */ + label, _done; + %} + ++function bar:long (x:long) { ++ if (x < 10) return 17 else return 16 ++} ++ + probe begin { +- printf("foo(1)=%d\n", foo(1)) +- printf("foo(15)=%d\n", foo(15)) +- exit() ++ printf("U foo(1)=%d should be %d\n", foo(1), bar(1)) ++ printf("U foo(15)=%d should be %d\n", foo(15), bar(15)) + } + ++probe kernel.function("vfs_read") { ++ printf("K foo(1)=%d should be %d\n", foo(1), bar(1)) ++ printf("K foo(15)=%d should be %d\n", foo(15), bar(15)) ++ exit() ++} +diff --git a/testsuite/systemtap.bpf/asm_tests/printf-basic.stp b/testsuite/systemtap.bpf/asm_tests/printf-basic.stp +new file mode 100644 +index 000000000..ffa8e01eb +--- /dev/null ++++ b/testsuite/systemtap.bpf/asm_tests/printf-basic.stp +@@ -0,0 +1,9 @@ ++function foo:long (x:long) %{ /* bpf */ ++ call, -, printf, "x = %p \n", $x; ++ 0xb7, $$, -, -, 0x0; /* mov $$, 0 */ ++%} ++ ++probe begin { ++ printf("U 'x = 0xa' should print:\n"); foo(10) ++ exit() ++} +diff --git a/testsuite/systemtap.bpf/asm_tests/printf.stp b/testsuite/systemtap.bpf/asm_tests/printf.stp +new file mode 100644 +index 000000000..4bfa34648 +--- /dev/null ++++ b/testsuite/systemtap.bpf/asm_tests/printf.stp +@@ -0,0 +1,16 @@ ++function foo:long (x:long) %{ /* bpf */ /* calls:exit */ ++ 0xb5, $x, -, _skip, 20; /* jle n, 20, _skip */ ++ call, -, printf, "x = %d causing exit\n", $x; /* like error() */ ++ call, -, exit; ++ label, _skip; ++ call, -, printf, "x = %d not causing exit\n", $x; ++ 0xb7, $$, -, -, 0x0; /* mov $$, 0 */ ++%} ++ ++probe begin { ++ printf("U 'x = 10' should print:\n"); foo(10) ++} ++ ++probe kernel.function("vfs_read") { ++ printf("K 'x = 25' should print:\n"); foo(25) ++} +diff --git a/testsuite/systemtap.bpf/asm_tests/simple.stp b/testsuite/systemtap.bpf/asm_tests/simple.stp +index 17184a139..f8dd693c5 100644 +--- a/testsuite/systemtap.bpf/asm_tests/simple.stp ++++ b/testsuite/systemtap.bpf/asm_tests/simple.stp +@@ -5,7 +5,12 @@ function foo:long (x:long) %{ /* bpf */ /* pure */ + %} + + probe begin { +- printf("foo(1)=%d, should be 99\n", foo(1)) +- printf("foo(15)=%d, should be 85\n", foo(15)) ++ printf("U foo(1)=%d, should be 99\n", foo(1)) ++ printf("U foo(15)=%d, should be 85\n", foo(15)) ++} ++ ++probe kernel.function("vfs_read") { ++ printf("K foo(1)=%d, should be 99\n", foo(1)) ++ printf("K foo(15)=%d, should be 85\n", foo(15)) + exit() + } +diff --git a/testsuite/systemtap.bpf/asm_tests/string.stp b/testsuite/systemtap.bpf/asm_tests/string.stp +index dce665c14..6ecbe08da 100644 +--- a/testsuite/systemtap.bpf/asm_tests/string.stp ++++ b/testsuite/systemtap.bpf/asm_tests/string.stp +@@ -1,4 +1,4 @@ +-function foo:long (x:long) %{ /* bpf */ /* pure */ ++function foo:string (x:long) %{ /* bpf */ /* pure */ + /* if x <= 10 then "fifty" else "one-hundred" */ + 0xd5, $x, -, _bar, 10; /* jsle $x, 10, _bar */ + 0xbf, $$, "one-hundred", -, -; /* mov $$, "one-hundred" */ +@@ -9,14 +9,23 @@ function foo:long (x:long) %{ /* bpf */ /* pure */ + /* 0xbf, $$, $$, -, -; /* dummy op */ + %} + +-function bar:long (x:long) { +- if (x <= 10) return 50 else return 100 ++function bar:string (x:long) { ++ if (x <= 10) return "fifty" else return "one-hundred" + } + + probe begin { +- printf("foo(1)=%d should be %d\n", foo(1), bar(1)) +- printf("foo(8)=%d should be %d\n", foo(8), bar(8)) +- printf("foo(15)=%d should be %d\n", foo(15), bar(15)) ++ printf("U foo(1)=%s should be %s\n", foo(1), bar(1)) ++ printf("U foo(8)=%s should be %s\n", foo(8), bar(8)) ++ printf("U foo(15)=%s should be %s\n", foo(15), bar(15)) ++} ++ ++probe kernel.function("vfs_read") { ++ printf("K foo(1)=%s should be %s\n", bar(1), bar(1)) ++ printf("K foo(8)=%s should be %s\n", bar(8), bar(8)) ++ printf("K foo(15)=%s should be %s\n", bar(15), bar(15)) ++ # printf("K foo(1)=%s should be %s\n", foo(1), bar(1)) ++ # printf("K foo(8)=%s should be %s\n", foo(8), bar(8)) ++ # printf("K foo(15)=%s should be %s\n", foo(15), bar(15)) + exit() + } + +diff --git a/testsuite/systemtap.bpf/asm_tests/temporary.stp b/testsuite/systemtap.bpf/asm_tests/temporary.stp +index 153c759ba..7cec89bb3 100644 +--- a/testsuite/systemtap.bpf/asm_tests/temporary.stp ++++ b/testsuite/systemtap.bpf/asm_tests/temporary.stp +@@ -8,7 +8,12 @@ function foo:long (x:long) %{ /* bpf */ /* pure */ + %} + + probe begin { +- printf("foo(1)=%d, should be 99*3=297\n", foo(1)) +- printf("foo(15)=%d, should be 85*18=1530\n", foo(15)) ++ printf("U foo(1)=%d, should be 99*3=297\n", foo(1)) ++ printf("U foo(15)=%d, should be 85*18=1530\n", foo(15)) ++} ++ ++probe kernel.function("vfs_read") { ++ printf("K foo(1)=%d, should be 99*3=297\n", foo(1)) ++ printf("K foo(15)=%d, should be 85*18=1530\n", foo(15)) + exit() + } +diff --git a/testsuite/systemtap.bpf/asm_tests/unreachable.stp b/testsuite/systemtap.bpf/asm_tests/unreachable.stp +new file mode 100644 +index 000000000..b629fd5cb +--- /dev/null ++++ b/testsuite/systemtap.bpf/asm_tests/unreachable.stp +@@ -0,0 +1,17 @@ ++/* testcase for an early bug that would generate duplicate jump */ ++function foo:long (x:long) %{ /* bpf */ ++ /* the code in the middle is unreachable */ ++ 0xd5, $x, -, _bar, 20; /* jsle $x, 20, _done */ ++ 0xb7, $$, -, -, 100; /* mov $$, 100 */ ++ 0x05, -, -, _done, -; ++ label, _bar; ++ 0xb7, $$, -, -, 50; /* mov $$, 50 */ ++ label, _done; ++%} ++ ++probe kernel.function("vfs_read") { ++ x = 10 ++ if (x > 12) printf("unreachable\n") ++ printf ("got %d (should be 50)\n", foo(x)) ++ exit() ++} +diff --git a/testsuite/systemtap.bpf/bpf-asm.exp b/testsuite/systemtap.bpf/bpf-asm.exp +new file mode 100644 +index 000000000..a53c7bcc4 +--- /dev/null ++++ b/testsuite/systemtap.bpf/bpf-asm.exp +@@ -0,0 +1,105 @@ ++# bpf-asm.exp ++# ++# TODO: Very basic test driver. Need to work out a way of signaling correctness. ++ ++set testdir "$srcdir/$subdir/asm_tests" ++ ++proc stapbpf_run { TEST_NAME args } { ++ global rc ++ set rc -1 ++ ++ # return codes ++ set pass 0 ++ #set fail 1 ++ #set bad_output 2 ++ set eof_start 3 ++ set eof_end 4 ++ set timeout_start 5 ++ set timeout_end 6 ++ set invalid_prog 7 ++ set comp_err 8 ++ ++ set cmd [concat stap -vg --runtime=bpf $args] ++ send_log "executing: $cmd\n" ++ eval spawn $cmd ++ set mypid [exp_pid -i $spawn_id] ++ expect { ++ -timeout 30 ++ -re {Pass 5: starting run} { ++ expect { ++ -timeout 20 ++ -re {Pass 5: run completed} { ++ set rc $pass ++ } ++ -re "bpf program load failed:" { set rc $invalid_prog } ++ default { ++ set rc $bad_output ++ } ++ timeout { ++ set rc $timeout_end ++ kill -INT -$mypid ++ } ++ eof { ++ set rc $eof_end ++ } ++ } ++ } ++ -re "semantic error:" { set rc $comp_err } ++ -re "bpf program load failed:" { set rc $invalid_prog } ++ timeout { ++ set rc $timeout_start ++ kill -INT -$mypid ++ } ++ eof { ++ set rc $eof_start ++ } ++ } ++ # again for good measure with KILL after 3s ++ kill -INT -$mypid 3 ++ catch close ++ wait ++ return $rc ++} ++ ++set stap_files [lsort [glob -nocomplain $testdir/*.stp]] ++ ++foreach file $stap_files { ++ global mypid ++ set mypid 0 ++ set test [file tail $file] ++ if {! [installtest_p]} { untested $test; continue } ++ if {! [bpf_p]} { untested $test; continue } ++ ++ set errtest_p [regexp {^err} [file tail $file]] ++ verbose -log "Running $file" ++ switch [stapbpf_run $test $file] { ++ 0 { ++ if $errtest_p { ++ fail "$test unexpected success" ++ } else { ++ pass $test ++ } ++ } ++ 1 { fail "$test incorrect result" } ++ 2 { fail "$test unexpected output" } ++ 3 { fail "$test eof (startup)" } ++ 4 { fail "$test eof (shutdown)" } ++ 5 { fail "$test timeout (startup)" } ++ 6 { fail "$test timeout (startup)" } ++ 7 { fail "$test invalid bpf program" } ++ 8 { ++ if $errtest_p { ++ pass $test ++ } else { ++ fail "$test compilation" ++ } ++ } ++ default { fail "$test unknown return value" } ++ } ++ ++ if { $mypid > 0 } { ++ kill -INT -$mypid 3 ++ catch close ++ wait ++ } ++} +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0010-bpf-translate.cxx-plug-an-exception-gap-in-is_numeri.patch b/SOURCES/rhbz1643997.0010-bpf-translate.cxx-plug-an-exception-gap-in-is_numeri.patch new file mode 100644 index 0000000..bb9127c --- /dev/null +++ b/SOURCES/rhbz1643997.0010-bpf-translate.cxx-plug-an-exception-gap-in-is_numeri.patch @@ -0,0 +1,35 @@ +From baabce812704826c5f7b20ed4afcca816cab7967 Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Thu, 25 Oct 2018 12:19:53 -0400 +Subject: [PATCH 10/32] bpf-translate.cxx :: plug an exception gap in + is_numeric() + +--- + bpf-translate.cxx | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/bpf-translate.cxx b/bpf-translate.cxx +index df22401ad..bf55c56b4 100644 +--- a/bpf-translate.cxx ++++ b/bpf-translate.cxx +@@ -681,11 +681,15 @@ is_numeric (const std::string &str) + size_t pos = 0; + try { + stol(str, &pos, 0); +- } catch (std::invalid_argument &e) { ++ } catch (const std::invalid_argument &e) { + return false; +- } catch (std::out_of_range &e) { ++ } catch (const std::out_of_range &e) { + /* XXX: probably numeric but not valid; give up */ + return false; ++ } catch (...) { ++ /* XXX: handle other errors the same way */ ++ std::cerr << "BUG: bpf assembler -- is_numeric() saw unexpected exception" << std::endl; ++ return false; + } + return (pos == str.size()); + } +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0011-session.cxx-enable-caching-for-bpf-backend.patch b/SOURCES/rhbz1643997.0011-session.cxx-enable-caching-for-bpf-backend.patch new file mode 100644 index 0000000..874d110 --- /dev/null +++ b/SOURCES/rhbz1643997.0011-session.cxx-enable-caching-for-bpf-backend.patch @@ -0,0 +1,25 @@ +From 1f892756af4de555fb1dc70bd1c7437eecde0d31 Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Fri, 26 Oct 2018 11:15:13 -0400 +Subject: [PATCH 11/32] session.cxx :: enable caching for bpf backend + +--- + session.cxx | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/session.cxx b/session.cxx +index eed28bf7e..ffad3b2b5 100644 +--- a/session.cxx ++++ b/session.cxx +@@ -1701,7 +1701,7 @@ systemtap_session::parse_cmdline_runtime (const string& opt_runtime) + return false; + #else + runtime_mode = bpf_runtime; +- use_cache = use_script_cache = false; ++ // use_cache = use_script_cache = false; // XXX: From early BPF development. Delete after making sure the cache doesn't break anything. + #endif + } + else if (opt_runtime == string("dyninst")) +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0012-Adjust-the-BPF-translate-error-report-formatting-to-.patch b/SOURCES/rhbz1643997.0012-Adjust-the-BPF-translate-error-report-formatting-to-.patch new file mode 100644 index 0000000..f094685 --- /dev/null +++ b/SOURCES/rhbz1643997.0012-Adjust-the-BPF-translate-error-report-formatting-to-.patch @@ -0,0 +1,65 @@ +From 311eea74540d2ebbf61f4c795a180aec79dc2b00 Mon Sep 17 00:00:00 2001 +From: William Cohen +Date: Tue, 30 Oct 2018 14:20:46 -0400 +Subject: [PATCH 12/32] Adjust the BPF translate error report formatting to + work on 32-bit architectures + +The 32-bit architectures such as arm and i686 had arguments in the +error reporting that did not match up with the %lu or %ld formatting. +Used type casting and %llu and %lld to avoid variation between 32-bit +and 64-bit architectures. +--- + bpf-translate.cxx | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/bpf-translate.cxx b/bpf-translate.cxx +index bf55c56b4..bb133bae5 100644 +--- a/bpf-translate.cxx ++++ b/bpf-translate.cxx +@@ -840,14 +840,14 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start, + if (args[0] == "label") + { + if (args.size() != 2) +- throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode syntax (label expects 1 arg, found %lu)", args.size()-1), stmt.tok); ++ throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode syntax (label expects 1 arg, found %llu)", (long long) args.size()-1), stmt.tok); + stmt.kind = args[0]; + stmt.dest = args[1]; + } + else if (args[0] == "alloc") + { + if (args.size() != 3) +- throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode syntax (alloc expects 2 args, found %lu)", args.size()-1), stmt.tok); ++ throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode syntax (alloc expects 2 args, found %llu)", (long long) args.size()-1), stmt.tok); + stmt.kind = args[0]; + stmt.dest = args[1]; + stmt.imm = parse_imm(stmt, args[2]); +@@ -855,7 +855,7 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start, + else if (args[0] == "call") + { + if (args.size() < 3) +- throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode syntax (call expects at least 2 args, found %lu)", args.size()-1), stmt.tok); ++ throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode syntax (call expects at least 2 args, found %llu)", (long long) args.size()-1), stmt.tok); + stmt.kind = args[0]; + stmt.dest = args[1]; + assert(stmt.params.empty()); +@@ -865,7 +865,7 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start, + else if (is_numeric(args[0])) + { + if (args.size() != 5) +- throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode syntax (opcode expects 4 args, found %lu)", args.size()-1), stmt.tok); ++ throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode syntax (opcode expects 4 args, found %llu)", (long long) args.size()-1), stmt.tok); + stmt.kind = "opcode"; + try { + stmt.code = stoul(args[0], 0, 0); +@@ -1128,7 +1128,7 @@ bpf_unparser::emit_asm_opcode (const asm_stmt &stmt, + } + + if (stmt.off != (int16_t)stmt.off) +- throw SEMANTIC_ERROR (_F("offset field '%ld' out of range in bpf code", stmt.off), stmt.tok); ++ throw SEMANTIC_ERROR (_F("offset field '%lld' out of range in bpf code", (long long) stmt.off), stmt.tok); + + if (op_jmp) + { +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0013-bpf-translate.cxx-fix-segfault-with-malformed-regist.patch b/SOURCES/rhbz1643997.0013-bpf-translate.cxx-fix-segfault-with-malformed-regist.patch new file mode 100644 index 0000000..b7bc8fd --- /dev/null +++ b/SOURCES/rhbz1643997.0013-bpf-translate.cxx-fix-segfault-with-malformed-regist.patch @@ -0,0 +1,50 @@ +From 99ee8b19901f4908e2a2942731c34e03aadd9549 Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Tue, 30 Oct 2018 17:10:53 -0400 +Subject: [PATCH 13/32] bpf-translate.cxx :: fix segfault with malformed + register + +--- + bpf-translate.cxx | 9 +++++++-- + testsuite/systemtap.bpf/asm_tests/err-regparse.stp | 9 +++++++++ + 2 files changed, 16 insertions(+), 2 deletions(-) + create mode 100644 testsuite/systemtap.bpf/asm_tests/err-regparse.stp + +diff --git a/bpf-translate.cxx b/bpf-translate.cxx +index bb133bae5..d46dae44a 100644 +--- a/bpf-translate.cxx ++++ b/bpf-translate.cxx +@@ -952,8 +952,13 @@ bpf_unparser::emit_asm_arg (const asm_stmt &stmt, const std::string &arg, + { + /* arg is a register number */ + std::string reg = arg[0] == 'r' ? arg.substr(1) : arg; +- unsigned long num = stoul(reg, 0, 0); +- if (num > 10) ++ unsigned long num; ++ bool parsed = false; ++ try { ++ num = stoul(reg, 0, 0); ++ parsed = true; ++ } catch (std::exception &e) {} // XXX: invalid_argument, out_of_range ++ if (!parsed || num > 10) + throw SEMANTIC_ERROR (_F("invalid bpf register '%s'", + arg.c_str()), stmt.tok); + return this_prog.lookup_reg(num); +diff --git a/testsuite/systemtap.bpf/asm_tests/err-regparse.stp b/testsuite/systemtap.bpf/asm_tests/err-regparse.stp +new file mode 100644 +index 000000000..ba66800e6 +--- /dev/null ++++ b/testsuite/systemtap.bpf/asm_tests/err-regparse.stp +@@ -0,0 +1,9 @@ ++function foo:long () %{ /* bpf */ /* pure */ ++ 0xb7, $rc, -, -, 50; /* mov $rc, 50 */ ++ 0xbf, $$, rc, -, -; /* mov $$, $rc -- TYPO */ ++%} ++ ++probe begin { ++ printf("foo()=%d should be fifty\n", foo()) ++ exit() ++} +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0014-bpf-asm.exp-bugfix-bad_output-does-occur.patch b/SOURCES/rhbz1643997.0014-bpf-asm.exp-bugfix-bad_output-does-occur.patch new file mode 100644 index 0000000..eff101c --- /dev/null +++ b/SOURCES/rhbz1643997.0014-bpf-asm.exp-bugfix-bad_output-does-occur.patch @@ -0,0 +1,25 @@ +From 0e59ff192856462702191f1e6d87e5fdbfd158a3 Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Tue, 30 Oct 2018 17:11:45 -0400 +Subject: [PATCH 14/32] bpf-asm.exp bugfix :: bad_output does occur + +--- + testsuite/systemtap.bpf/bpf-asm.exp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/testsuite/systemtap.bpf/bpf-asm.exp b/testsuite/systemtap.bpf/bpf-asm.exp +index a53c7bcc4..8925cb049 100644 +--- a/testsuite/systemtap.bpf/bpf-asm.exp ++++ b/testsuite/systemtap.bpf/bpf-asm.exp +@@ -11,7 +11,7 @@ proc stapbpf_run { TEST_NAME args } { + # return codes + set pass 0 + #set fail 1 +- #set bad_output 2 ++ set bad_output 2 + set eof_start 3 + set eof_end 4 + set timeout_start 5 +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0015-tapset-bpf-conversions.stp-bugfix-helper-name-in-ker.patch b/SOURCES/rhbz1643997.0015-tapset-bpf-conversions.stp-bugfix-helper-name-in-ker.patch new file mode 100644 index 0000000..80ecc02 --- /dev/null +++ b/SOURCES/rhbz1643997.0015-tapset-bpf-conversions.stp-bugfix-helper-name-in-ker.patch @@ -0,0 +1,30 @@ +From 4e9775b55b5ddd7447c100894ab816a849c2c8cc Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Tue, 30 Oct 2018 17:13:28 -0400 +Subject: [PATCH 15/32] tapset/bpf/conversions.stp bugfix :: helper name in + kernel_string(addr, err_msg) + +--- + tapset/bpf/conversions.stp | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/tapset/bpf/conversions.stp b/tapset/bpf/conversions.stp +index 1140a6875..88d63b70a 100644 +--- a/tapset/bpf/conversions.stp ++++ b/tapset/bpf/conversions.stp +@@ -34,10 +34,10 @@ function kernel_string:string (addr:long, err_msg:string) + %{ /* bpf */ /* pure */ + /* buf = bpf_stk_alloc(BPF_MAXSTRINGLEN); + buf[0] = 0x0; // guarantee NUL byte +- rc = bpf_probe_read_str(buf, n, addr); */ ++ rc = bpf_probe_read_str(buf, BPF_MAXSTRINGLEN, addr); */ + alloc, $buf, BPF_MAXSTRINGLEN; + 0x62, $buf, -, -, 0x0; /* stw [$buf+0], 0x0 -- guarantee NUL byte */ +- call, $rc, bpf_probe_read_str, $buf, BPF_MAXSTRINGLEN, $addr; ++ call, $rc, probe_read_str, $buf, BPF_MAXSTRINGLEN, $addr; + + /* if (rc < 0) return err_msg; + return buf; */ +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0016-tapset-bpf-context.stp-add-execname-triage-other-fun.patch b/SOURCES/rhbz1643997.0016-tapset-bpf-context.stp-add-execname-triage-other-fun.patch new file mode 100644 index 0000000..23cd7d7 --- /dev/null +++ b/SOURCES/rhbz1643997.0016-tapset-bpf-context.stp-add-execname-triage-other-fun.patch @@ -0,0 +1,175 @@ +From deb88d0b1fef10177ab197b066f434c720253f8d Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Tue, 30 Oct 2018 17:29:46 -0400 +Subject: [PATCH 16/32] tapset/bpf/context.stp :: add execname(), triage other + functions + +* tapset/bpf/context.stp: Notes on other functions that could be added. +(execname): New tapset function. + +* tapset/linux/context.stp: Move pexecname() to a more logical location. +--- + tapset/bpf/context.stp | 66 ++++++++++++++++++++++++++++++++++++++++++++++++ + tapset/linux/context.stp | 30 +++++++++++----------- + 2 files changed, 81 insertions(+), 15 deletions(-) + +diff --git a/tapset/bpf/context.stp b/tapset/bpf/context.stp +index 45dcd4d71..55e0f871b 100644 +--- a/tapset/bpf/context.stp ++++ b/tapset/bpf/context.stp +@@ -6,6 +6,34 @@ + // Public License (GPL); either version 2, or (at your option) any + // later version. + ++/** ++ * sfunction execname - Returns the execname of a target process (or group of processes) ++ * ++ * Description: Returns the execname of a target process (or group of processes). ++ */ ++function execname:string () ++%{ /* bpf */ /* pure */ /* unprivileged */ /* stable */ ++ /* buf = bpf_stk_alloc(BPF_MAXSTRINGLEN); ++ buf[0] = 0x0; // guarantee NUL byte ++ rc = get_current_comm(buf, BPF_MAXSTRINGLEN); */ ++ alloc, $buf, BPF_MAXSTRINGLEN; ++ 0x62, $buf, -, -, 0x0; /* stw [$buf+0], 0x0 -- guarantee NUL byte */ ++ call, $rc, get_current_comm, $buf, BPF_MAXSTRINGLEN; ++ ++ /* if (rc < 0) return err_msg; ++ return buf; */ ++ 0xa5, $rc, 0, _err, -; /* jlt $rc, 0, _err */ ++ 0xbf, $$, $buf, -, -; /* mov $$, $buf */ ++ 0x05, -, -, _done, -; /* ja _done */ ++ ++ label, _err; ++ 0xbf, $$, "", -, -; /* mov $$, */ ++ ++ label, _done; ++%} ++ ++// TODO: pexecname () ++ + /** + * sfunction pid - Returns the ID of a thread group + * +@@ -20,6 +48,8 @@ function pid:long () + 0x77, $$, 0, 0, 32 /* rshk $$, 32 */ + %} + ++// TODO: ns_pid:long () ++ + /** + * sfunction tid - Returns the thread ID of a target process + * +@@ -33,6 +63,14 @@ function tid:long () + 0xbc, $$, 0, 0, 0 /* movwx $$, r0 */ + %} + ++// TODO: ns_tid:long () ++// TODO: ppid:long () ++// TODO: ns_ppid:long () ++// TODO: pgrp:long () ++// TODO: ns_pgrp:long () ++// TODO: sid:long () ++// TODO: ns_sid:long () ++ + /** + * sfunction gid - Returns the group ID of a target process + * +@@ -46,6 +84,10 @@ function gid:long () + 0x77, $$, 0, 0, 32 /* rshk $$, 32 */ + %} + ++// TODO: ns_gid:long () ++// TODO: egid:long () ++// TODO: ns_egid:long () ++ + /** + * sfunction uid - Returns the user ID of a target process + * +@@ -58,6 +100,12 @@ function uid:long () + 0xbc, $$, 0, 0, 0 /* movwx $$, r0 */ + %} + ++// TODO: ns_uid:long () ++// TODO: euid:long () ++// TODO: ns_euid:long () ++// XXX: is_myproc () is only relevant for unprivileged use of eBPF (still theoretical). ++ ++// TODO: Old systemtap-compat scripts should not be running on eBPF backend in the first place? + /** + * sfunction cpuid - Returns the current cpu number + * +@@ -82,3 +130,21 @@ function cpu:long () + 0x85, 0, 0, 0, 8; /* call BPF_FUNC_get_smp_processor_id */ + 0xbf, $$, 0, 0, 0 /* movx $$, r0 */ + %} ++ ++// TODO: registers_valid:long () ++// TODO: user_mode:long () ++// TODO: is_return:long () ++// TODO: target:long () ++// TODO: module_name:string () ++// XXX: module_size:string () -- not clear if this should refer to the entire .bo or to just the current eBPF routine. ++// TODO: stp_pid:long () ++// XXX: remote_id:long (), remote_uri:string() -- pending an evaluation of remote eBPF execution. ++// XXX: stack_size() -- not clear if this should be the eBPF stack size or the kernel stack size. ++// XXX: stack_used(),stack_unused() probably a fairly ill-defined idea with the eBPF stack. ++// TODO: Other context functions for info about things like eBPF maps. ++ ++// TODO: addr:long () ++// TODO: uaddr:long () ++// XXX: cmdline_args:string(n:long, m:long, delim:string) -- requires string concatenation & loops. ++// TODO: cmdline_arg:string(n:long) ++// XXX: cmdline_string:string() -- requires string concatenation & loops. +diff --git a/tapset/linux/context.stp b/tapset/linux/context.stp +index 2bd405186..46b1f6b32 100644 +--- a/tapset/linux/context.stp ++++ b/tapset/linux/context.stp +@@ -19,6 +19,21 @@ function execname:string () + strlcpy (STAP_RETVALUE, current->comm, MAXSTRINGLEN); + %} + ++/** ++ * sfunction pexecname - Returns the execname of a target process's parent process ++ * ++ * Description: This function returns the execname of a target ++ * process's parent procces. ++ */ ++function pexecname:string () ++%{ /* pure */ /* unprivileged */ /* stable */ ++#if defined(STAPCONF_REAL_PARENT) ++ strlcpy (STAP_RETVALUE, current->real_parent->comm, MAXSTRINGLEN); ++#else ++ strlcpy (STAP_RETVALUE, current->parent->comm, MAXSTRINGLEN); ++#endif ++%} ++ + /** + * sfunction pid - Returns the ID of a target process + * +@@ -153,21 +168,6 @@ function ns_sid:long () + else STAP_RETURN (rc); + %} + +-/** +- * sfunction pexecname - Returns the execname of a target process's parent process +- * +- * Description: This function returns the execname of a target +- * process's parent procces. +- */ +-function pexecname:string () +-%{ /* pure */ /* unprivileged */ /* stable */ +-#if defined(STAPCONF_REAL_PARENT) +- strlcpy (STAP_RETVALUE, current->real_parent->comm, MAXSTRINGLEN); +-#else +- strlcpy (STAP_RETVALUE, current->parent->comm, MAXSTRINGLEN); +-#endif +-%} +- + /** + * sfunction gid - Returns the group ID of a target process + * +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0017-PR23849-temporarily-disable-stapbpf-script-caching.patch b/SOURCES/rhbz1643997.0017-PR23849-temporarily-disable-stapbpf-script-caching.patch new file mode 100644 index 0000000..394eab7 --- /dev/null +++ b/SOURCES/rhbz1643997.0017-PR23849-temporarily-disable-stapbpf-script-caching.patch @@ -0,0 +1,31 @@ +From 49f6a9a448b5df9af354897bc6ee2eb68496b3c2 Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Thu, 1 Nov 2018 16:13:07 -0400 +Subject: [PATCH 17/32] PR23849 -- temporarily disable stapbpf script caching + +--- + session.cxx | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/session.cxx b/session.cxx +index ffad3b2b5..f70a7ccfd 100644 +--- a/session.cxx ++++ b/session.cxx +@@ -1701,7 +1701,13 @@ systemtap_session::parse_cmdline_runtime (const string& opt_runtime) + return false; + #else + runtime_mode = bpf_runtime; +- // use_cache = use_script_cache = false; // XXX: From early BPF development. Delete after making sure the cache doesn't break anything. ++ ++ // TODO: From early BPF development. Remove after making sure the ++ // cache doesn't break anything. Currently removal is blocked ++ // by PR22330 (module name encoded in trace_printk() calls, ++ // using up a lot of stack space for the cacheable script ++ // names). ++ use_cache = use_script_cache = false; + #endif + } + else if (opt_runtime == string("dyninst")) +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0018-tapset-bpf-task.stp-rudiment-of-task-tapset.patch b/SOURCES/rhbz1643997.0018-tapset-bpf-task.stp-rudiment-of-task-tapset.patch new file mode 100644 index 0000000..980d42b --- /dev/null +++ b/SOURCES/rhbz1643997.0018-tapset-bpf-task.stp-rudiment-of-task-tapset.patch @@ -0,0 +1,62 @@ +From ba62b58974fd25f7281fa151411c8e32b3951fbb Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Fri, 2 Nov 2018 12:22:01 -0400 +Subject: [PATCH 18/32] tapset/bpf/task.stp :: rudiment of task tapset + +--- + tapset/bpf/task.stp | 20 ++++++++++++++++++++ + testsuite/systemtap.bpf/bpf_tests/task1.stp | 15 +++++++++++++++ + 2 files changed, 35 insertions(+) + create mode 100644 tapset/bpf/task.stp + create mode 100644 testsuite/systemtap.bpf/bpf_tests/task1.stp + +diff --git a/tapset/bpf/task.stp b/tapset/bpf/task.stp +new file mode 100644 +index 000000000..9f558a166 +--- /dev/null ++++ b/tapset/bpf/task.stp +@@ -0,0 +1,20 @@ ++// task information tapset ++// Copyright (C) 2018 Red Hat Inc. ++// ++// This file is part of systemtap, and is free software. You can ++// redistribute it and/or modify it under the terms of the GNU General ++// Public License (GPL); either version 2, or (at your option) any ++// later version. ++ ++@__private30 function _task_cur:long() ++%{ /* bpf */ /* pure */ ++ call, $$, get_current_task; ++%} ++ ++/** ++ * sfunction task_current - The current task_struct of the current task ++ */ ++function task_current:long () { ++ // TODO: return & @task(_task_cur()) ++ return & @cast(_task_cur(), "task_struct") ++} +diff --git a/testsuite/systemtap.bpf/bpf_tests/task1.stp b/testsuite/systemtap.bpf/bpf_tests/task1.stp +new file mode 100644 +index 000000000..b0faab361 +--- /dev/null ++++ b/testsuite/systemtap.bpf/bpf_tests/task1.stp +@@ -0,0 +1,15 @@ ++probe begin { ++ printf("BEGIN\n") ++} ++ ++probe kernel.function("vfs_read") { ++ // TODO: PR23816, task.stp tapset ++ printf("vfs_read by %s", execname()) ++ printf("/%s", kernel_string(@cast(task_current(), "task_struct")->comm)) ++ printf(" PID %d/%d\n", pid(), @cast(task_current(), "task_struct")->tgid) ++ exit() ++} ++ ++probe end { ++ printf("END PASS\n") ++} +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0019-PR23829-fallback-defines-__BPF_FUNC_MAPPER-and-BPF_J.patch b/SOURCES/rhbz1643997.0019-PR23829-fallback-defines-__BPF_FUNC_MAPPER-and-BPF_J.patch new file mode 100644 index 0000000..f4b8241 --- /dev/null +++ b/SOURCES/rhbz1643997.0019-PR23829-fallback-defines-__BPF_FUNC_MAPPER-and-BPF_J.patch @@ -0,0 +1,106 @@ +From 0d45550a184cc5a9f10187a97b9ef8dc7fa13f31 Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Fri, 2 Nov 2018 16:49:23 -0400 +Subject: [PATCH 19/32] PR23829 :: fallback defines __BPF_FUNC_MAPPER and + BPF_J{LT,LE,SLT,SLE} for older kernels + +--- + bpf-base.cxx | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + bpf-internal.h | 11 +++++++++++ + 2 files changed, 70 insertions(+) + +diff --git a/bpf-base.cxx b/bpf-base.cxx +index 277927b72..210efa9aa 100644 +--- a/bpf-base.cxx ++++ b/bpf-base.cxx +@@ -140,6 +140,65 @@ is_commutative(opcode code) + std::map bpf_func_name_map; + std::map bpf_func_id_map; + ++/* PR23829: On older kernels, bpf.h does not define __BPF_FUNC_MAPPER. ++ As a fallback, use the *earliest* __BPF_FUNC_MAPPER, so stapbpf ++ will not try helpers that only exist on subsequent kernels. ++ ++ TODO: This isn't perfect since even older kernels don't have ++ some of these helpers. ++ ++ XXX: Note the build limitation in that SystemTap must be compiled ++ against a recent kernel to be able to use the helpers from that ++ kernel. That's also the case when building against recent bpf.h ++ with __BPF_FUNC_MAPPER, so this workaround is not the source of the ++ problem. */ ++#ifndef __BPF_FUNC_MAPPER ++#define __BPF_FUNC_MAPPER(FN) \ ++ FN(unspec), \ ++ FN(map_lookup_elem), \ ++ FN(map_update_elem), \ ++ FN(map_delete_elem), \ ++ FN(probe_read), \ ++ FN(ktime_get_ns), \ ++ FN(trace_printk), \ ++ FN(get_prandom_u32), \ ++ FN(get_smp_processor_id), \ ++ FN(skb_store_bytes), \ ++ FN(l3_csum_replace), \ ++ FN(l4_csum_replace), \ ++ FN(tail_call), \ ++ FN(clone_redirect), \ ++ FN(get_current_pid_tgid), \ ++ FN(get_current_uid_gid), \ ++ FN(get_current_comm), \ ++ FN(get_cgroup_classid), \ ++ FN(skb_vlan_push), \ ++ FN(skb_vlan_pop), \ ++ FN(skb_get_tunnel_key), \ ++ FN(skb_set_tunnel_key), \ ++ FN(perf_event_read), \ ++ FN(redirect), \ ++ FN(get_route_realm), \ ++ FN(perf_event_output), \ ++ FN(skb_load_bytes), \ ++ FN(get_stackid), \ ++ FN(csum_diff), \ ++ FN(skb_get_tunnel_opt), \ ++ FN(skb_set_tunnel_opt), \ ++ FN(skb_change_proto), \ ++ FN(skb_change_type), \ ++ FN(skb_under_cgroup), \ ++ FN(get_hash_recalc), \ ++ FN(get_current_task), \ ++ FN(probe_write_user), \ ++ FN(current_task_under_cgroup), \ ++ FN(skb_change_tail), \ ++ FN(skb_pull_data), \ ++ FN(csum_update), \ ++ FN(set_hash_invalid), \ ++ ++#endif ++ + void + init_bpf_helper_tables () // TODO call before script translation + { +diff --git a/bpf-internal.h b/bpf-internal.h +index 3041bbdf5..82cba2c79 100644 +--- a/bpf-internal.h ++++ b/bpf-internal.h +@@ -21,6 +21,17 @@ extern "C" { + #include + } + ++/* PR23829: These eBPF opcodes were added in recent kernels, and the ++ following 'ad hoc' defines are only used by the embedded-code ++ assembler. The code generator will convert these opcodes to ++ equivalent operations valid for earlier eBPF. */ ++#ifndef BPF_JLT ++#define BPF_JLT 0xa0 /* LT is unsigned, '<' */ ++#define BPF_JLE 0xb0 /* LE is unsigned, '<=' */ ++#define BPF_JSLT 0xc0 /* SLT is signed, '<' */ ++#define BPF_JSLE 0xd0 /* SLE is signed, '<=' */ ++#endif ++ + struct systemtap_session; + struct derived_probe; + struct vardecl; +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0020-PR23860-partial-fix-fix-BPF_NEG-opcode-generation.patch b/SOURCES/rhbz1643997.0020-PR23860-partial-fix-fix-BPF_NEG-opcode-generation.patch new file mode 100644 index 0000000..4d096e9 --- /dev/null +++ b/SOURCES/rhbz1643997.0020-PR23860-partial-fix-fix-BPF_NEG-opcode-generation.patch @@ -0,0 +1,67 @@ +From 7b8a9050aa8666e29d19f2eea9917afa72fe00ae Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Mon, 5 Nov 2018 16:58:21 -0500 +Subject: [PATCH 20/32] PR23860 partial fix: fix BPF_NEG opcode generation. + +Plus some improved diagnostics on malformed code. + +* bpf-base.cxx (value::print): Don't abort() on unknown operand. +(opcode_name): Don't abort() on unknown opcode. +(insn::print): Don't abort() on malformed insn. +(program::mk_binary): Ensure BPF_NEG src==dest, don't use BPF_X. +--- + bpf-base.cxx | 15 ++++++++++----- + 1 file changed, 10 insertions(+), 5 deletions(-) + +diff --git a/bpf-base.cxx b/bpf-base.cxx +index 210efa9aa..24b610cf3 100644 +--- a/bpf-base.cxx ++++ b/bpf-base.cxx +@@ -32,7 +32,7 @@ value::print(std::ostream &o) const + case TMPREG: + return o << "t" << reg_val; + default: +- abort(); ++ return o << ""; + } + } + +@@ -359,7 +359,7 @@ opcode_name(opcode op) + case BPF_JMP | BPF_JSET | BPF_K: opn = "jsetk"; break; + + default: +- abort(); ++ opn = ""; + } + + return opn; +@@ -457,7 +457,7 @@ insn::print(std::ostream &o) const + return o << opn << "\t" << *src0 << "," << *src1; + + default: +- abort (); ++ return o << ""; + } + } + +@@ -705,10 +705,15 @@ program::mk_binary(insn_inserter &ins, opcode op, value *dest, + void + program::mk_unary(insn_inserter &ins, opcode op, value *dest, value *src) + { ++ assert (op == BPF_NEG); // XXX: BPF_NEG is the only unary operator so far. ++ ++ if (dest != src) // src is not used for BPF_NEG. BPF negates in-place. ++ mk_mov(ins, dest, src); ++ + insn *i = ins.new_insn(); +- i->code = BPF_ALU64 | BPF_X | op; ++ i->code = BPF_ALU64 | op; // BPF_X is not used for BPF_NEG. + i->dest = dest; +- i->src0 = src; ++ i->src0 = dest; // XXX: dest as an ersatz 'source'. + } + + void +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0021-bpf-behind-the-scenes-useful-DEBUG_CODEGEN-diagnosti.patch b/SOURCES/rhbz1643997.0021-bpf-behind-the-scenes-useful-DEBUG_CODEGEN-diagnosti.patch new file mode 100644 index 0000000..71d8dca --- /dev/null +++ b/SOURCES/rhbz1643997.0021-bpf-behind-the-scenes-useful-DEBUG_CODEGEN-diagnosti.patch @@ -0,0 +1,320 @@ +From 31ce3fd0cd21e1a808c417f813b3b5285aeb938a Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Tue, 6 Nov 2018 12:32:38 -0500 +Subject: [PATCH 21/32] bpf behind-the-scenes :: useful DEBUG_CODEGEN + diagnostic + +--- + bpf-base.cxx | 10 ++++++++++ + bpf-internal.h | 21 +++++++++++++++++++++ + bpf-opt.cxx | 28 +++++++++++++++++----------- + bpf-translate.cxx | 26 ++++++++++++++++++++++++-- + 4 files changed, 72 insertions(+), 13 deletions(-) + +diff --git a/bpf-base.cxx b/bpf-base.cxx +index 24b610cf3..5d132bcd1 100644 +--- a/bpf-base.cxx ++++ b/bpf-base.cxx +@@ -368,6 +368,10 @@ opcode_name(opcode op) + std::ostream & + insn::print(std::ostream &o) const + { ++#ifdef DEBUG_CODEGEN ++ if (note != "") ++ o << "{" << note << "} "; ++#endif + const char *opn = opcode_name (code); + + switch (code) +@@ -541,6 +545,12 @@ insn * + insn_inserter::new_insn() + { + insn *n = new insn; ++#ifdef DEBUG_CODEGEN ++ if (!notes.empty()) ++ n->note = notes.top(); ++ else ++ n->note = ""; ++#endif + insert(n); + return n; + } +diff --git a/bpf-internal.h b/bpf-internal.h +index 82cba2c79..75fefb769 100644 +--- a/bpf-internal.h ++++ b/bpf-internal.h +@@ -48,6 +48,8 @@ namespace bpf { + // TODO: BPF_MAX{STRING,FORMAT}LEN,BPF_MAXMAPENTRIES,BPF_MAXSPRINTFLEN should be user-configurable. + // XXX: BPF_MAXMAPENTRIES may depend on kernel version. May need to experiment with rlimit in instantiate_maps(). + ++// #define DEBUG_CODEGEN ++ + typedef unsigned short regno; + static const regno max_regno = BPF_MAXINSNS; + static const regno noreg = -1; +@@ -135,6 +137,9 @@ struct insn + value *src0; // The destination input, pre-allocation + value *src1; // The usual source register operand + insn *prev, *next; // Linked list of insns in the block ++#ifdef DEBUG_CODEGEN ++ std::string note; // For additional diagnostics. ++#endif + + insn(); + +@@ -198,8 +203,18 @@ private: + public: + block *b; + insn *i; ++#ifdef DEBUG_CODEGEN ++ std::stack notes; ++#endif + + insn_inserter(block *bb, insn *ii) : b(bb), i(ii) { } ++ insn_inserter(block *bb, insn *ii, const std::string& note) : b(bb), i(ii) { ++#ifdef DEBUG_CODEGEN ++ notes.push(note); ++#else ++ (void)note; // unused ++#endif ++ } + virtual ~insn_inserter() { } + virtual void insert(insn *i) = 0; + +@@ -214,6 +229,8 @@ struct insn_before_inserter : public insn_inserter + { + insn_before_inserter() : insn_inserter(NULL, NULL) { } + insn_before_inserter(block *b, insn *i) : insn_inserter(b,i) { } ++ insn_before_inserter(block *b, insn *i, const std::string& note) ++ : insn_inserter(b,i,note) { } + virtual void insert(insn *i); + }; + +@@ -221,6 +238,8 @@ struct insn_after_inserter : public insn_inserter + { + insn_after_inserter() : insn_inserter(NULL, NULL) { } + insn_after_inserter(block *b, insn *i) : insn_inserter(b,i) { } ++ insn_after_inserter(block *b, insn *i, const std::string& note) ++ : insn_inserter(b,i,note) { } + virtual void insert(insn *i); + }; + +@@ -228,6 +247,8 @@ struct insn_append_inserter : public insn_after_inserter + { + insn_append_inserter() : insn_after_inserter(NULL, NULL) { } + insn_append_inserter(block *b) : insn_after_inserter(b, NULL) { } ++ insn_append_inserter(block *b, const std::string& note) ++ : insn_after_inserter(b, NULL, note) { } + }; + + struct program +diff --git a/bpf-opt.cxx b/bpf-opt.cxx +index c2e30a690..904b33b46 100644 +--- a/bpf-opt.cxx ++++ b/bpf-opt.cxx +@@ -59,7 +59,7 @@ lower_str_values(program &p) + value *s0 = j->src0; + if (s0 && s0->is_str()) + { +- insn_before_inserter ins(b, j); ++ insn_before_inserter ins(b, j, "str"); + std::string str0 = s0->str(); + value *new_s0 = alloc_literal_str(p, ins, str0); + j->src0 = new_s0; +@@ -68,7 +68,7 @@ lower_str_values(program &p) + value *s1 = j->src1; + if (s1 && s1->is_str()) + { +- insn_before_inserter ins(b, j); ++ insn_before_inserter ins(b, j, "str"); + std::string str1 = s1->str(); + value *new_s1 = alloc_literal_str(p, ins, str1); + j->src1 = new_s1; +@@ -97,7 +97,7 @@ fixup_operands(program &p) + if (s1 && s1->is_imm() && s1->imm() != (int32_t)s1->imm()) + { + value *n = p.new_reg(); +- insn_before_inserter ins(b, j); ++ insn_before_inserter ins(b, j, "opt"); + p.mk_mov(ins, n, s1); + j->src1 = s1 = n; + } +@@ -121,13 +121,13 @@ fixup_operands(program &p) + // Special care for x = y - x + value *n = p.new_reg(); + { +- insn_before_inserter ins(b, j); ++ insn_before_inserter ins(b, j, "opt"); + p.mk_mov(ins, n, s0); + } + j->src0 = n; + j->dest = n; + { +- insn_after_inserter ins(b, j); ++ insn_after_inserter ins(b, j, "opt"); + p.mk_mov(ins, d, n); + } + } +@@ -135,7 +135,7 @@ fixup_operands(program &p) + else + { + // Transform { x = y - z } to { x = y; x -= z; } +- insn_before_inserter ins(b, j); ++ insn_before_inserter ins(b, j, "opt"); + p.mk_mov(ins, d, s0); + j->src0 = d; + } +@@ -144,7 +144,7 @@ fixup_operands(program &p) + { + // Comparisons can't have src0 constant. + value *n = p.new_reg(); +- insn_before_inserter ins(b, j); ++ insn_before_inserter ins(b, j, "opt"); + p.mk_mov(ins, n, s0); + j->src0 = n; + } +@@ -293,7 +293,7 @@ reorder_blocks(program &p) + if (t) + { + block *n = p.new_block (); +- insn_append_inserter ins(n); ++ insn_append_inserter ins(n, "opt"); + p.mk_jmp (ins, o); + ordered.push_back (n); + f->redirect_next (n); +@@ -301,7 +301,7 @@ reorder_blocks(program &p) + else + { + delete f; +- insn_after_inserter ins(b, b->last); ++ insn_after_inserter ins(b, b->last, "opt"); + p.mk_jmp (ins, o); + } + } +@@ -780,7 +780,7 @@ spill(unsigned reg, unsigned num_spills, program &p) + // If reg is a source, insert a load before j + if ((src0 && src0->reg_val == reg) || (src1 && src1->reg_val == reg)) + { +- insn_before_inserter ins(b, j); ++ insn_before_inserter ins(b, j, "regalloc"); + new_tmp = p.new_reg(); + + p.mk_ld (ins, BPF_DW, new_tmp, frame, -off); +@@ -795,7 +795,7 @@ spill(unsigned reg, unsigned num_spills, program &p) + // If reg is the destination, insert a store after j + if (dest && dest->reg_val == reg) + { +- insn_after_inserter ins(b, j); ++ insn_after_inserter ins(b, j, "regalloc"); + new_tmp = new_tmp ?: p.new_reg(); + + p.mk_st (ins, BPF_DW, frame, -off, new_tmp); +@@ -935,6 +935,9 @@ post_alloc_cleanup (program &p) + void + program::generate() + { ++#ifdef DEBUG_CODEGEN ++ std::cerr << "DEBUG BEFORE OPT " << *this << std::endl; ++#endif + lower_str_values(*this); + fixup_operands(*this); + thread_jumps(*this); +@@ -942,6 +945,9 @@ program::generate() + reorder_blocks(*this); + reg_alloc(*this); + post_alloc_cleanup(*this); ++#ifdef DEBUG_CODEGEN ++ std::cerr << "DEBUG AFTER OPT " << *this << std::endl; ++#endif + } + + } // namespace bpf +diff --git a/bpf-translate.cxx b/bpf-translate.cxx +index d46dae44a..57a4cb107 100644 +--- a/bpf-translate.cxx ++++ b/bpf-translate.cxx +@@ -310,7 +310,7 @@ bpf_unparser::get_exit_block() + return exit_block; + + block *b = this_prog.new_block(); +- insn_append_inserter ins(b); ++ insn_append_inserter ins(b, "exit_block"); + + this_prog.mk_exit(ins); + +@@ -325,7 +325,7 @@ bpf_unparser::get_ret0_block() + return ret0_block; + + block *b = this_prog.new_block(); +- insn_append_inserter ins(b); ++ insn_append_inserter ins(b, "ret0_block"); + + this_prog.mk_mov(ins, this_prog.lookup_reg(BPF_REG_0), this_prog.new_imm(0)); + b->fallthru = new edge(b, get_exit_block()); +@@ -1166,6 +1166,9 @@ bpf_unparser::emit_asm_opcode (const asm_stmt &stmt, + void + bpf_unparser::visit_embeddedcode (embeddedcode *s) + { ++#ifdef DEBUG_CODEGEN ++ this_ins.notes.push("asm"); ++#endif + std::vector statements; + asm_stmt stmt; + +@@ -1429,6 +1432,10 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s) + it != adjusted_toks.end(); it++) + delete *it; + adjusted_toks.clear(); ++ ++#ifdef DEBUG_CODEGEN ++ this_ins.notes.pop(); // asm ++#endif + } + + void +@@ -2507,6 +2514,10 @@ value * + emit_simple_literal_str(program &this_prog, insn_inserter &this_ins, + value *dest, int ofs, std::string &src, bool zero_pad) + { ++#ifdef DEBUG_CODEGEN ++ this_ins.notes.push("str"); ++#endif ++ + size_t str_bytes = src.size() + 1; + size_t str_words = (str_bytes + 3) / 4; + +@@ -2546,6 +2557,10 @@ emit_simple_literal_str(program &this_prog, insn_inserter &this_ins, + value *out = this_prog.new_reg(); + this_prog.mk_binary(this_ins, BPF_ADD, out, + dest, this_prog.new_imm(ofs)); ++ ++#ifdef DEBUG_CODEGEN ++ this_ins.notes.pop(); // str ++#endif + return out; + } + +@@ -2567,6 +2582,10 @@ bpf_unparser::emit_string_copy(value *dest, int ofs, value *src, bool zero_pad) + dest, ofs, str, zero_pad); + } + ++#ifdef DEBUG_CODEGEN ++ this_ins.notes.push("strcpy"); ++#endif ++ + size_t str_bytes = BPF_MAXSTRINGLEN; + size_t str_words = (str_bytes + 3) / 4; + +@@ -2674,6 +2693,9 @@ bpf_unparser::emit_string_copy(value *dest, int ofs, value *src, bool zero_pad) + value *out = this_prog.new_reg(); + this_prog.mk_binary(this_ins, BPF_ADD, out, + dest, this_prog.new_imm(ofs)); ++#ifdef DEBUG_CODEGEN ++ this_ins.notes.pop(); // strcpy ++#endif + return out; + } + +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0022-standardize-ktime_get_ns-across-lkm-bpf-runtimes.patch b/SOURCES/rhbz1643997.0022-standardize-ktime_get_ns-across-lkm-bpf-runtimes.patch new file mode 100644 index 0000000..16f658e --- /dev/null +++ b/SOURCES/rhbz1643997.0022-standardize-ktime_get_ns-across-lkm-bpf-runtimes.patch @@ -0,0 +1,284 @@ +From 43f6345c9605c0fad57abfe31dda8951f6630ebf Mon Sep 17 00:00:00 2001 +From: "Frank Ch. Eigler" +Date: Tue, 6 Nov 2018 16:26:57 -0500 +Subject: [PATCH 22/32] standardize ktime_get_ns() across lkm, bpf runtimes + +Make sure ktime_get_ns() is available across runtimes. In the case of +bpf, add a userspace helper to implement the function. Add test case. +Add a systemtap.bpf/nobpf.exp test driver, which runs all the +bpf_tests but specifically without "--bpf", in the hope that all those +scripts should run on the normal backend too. PR23866 blocks some of +that at the moment. +--- + stapbpf/bpfinterp.cxx | 13 ++ + tapset/bpf/time.stp | 2 +- + tapset/linux/timestamp_monotonic.stp | 11 ++ + testsuite/systemtap.bpf/bpf_tests/ktime_get_ns.stp | 33 +++++ + testsuite/systemtap.bpf/nonbpf.exp | 152 +++++++++++++++++++++ + 5 files changed, 210 insertions(+), 1 deletion(-) + create mode 100644 testsuite/systemtap.bpf/bpf_tests/ktime_get_ns.stp + create mode 100644 testsuite/systemtap.bpf/nonbpf.exp + +diff --git a/stapbpf/bpfinterp.cxx b/stapbpf/bpfinterp.cxx +index fe6eacc50..13ac8ee71 100644 +--- a/stapbpf/bpfinterp.cxx ++++ b/stapbpf/bpfinterp.cxx +@@ -162,6 +162,16 @@ bpf_sprintf(std::vector &strings, char *fstr, + return reinterpret_cast(strings.back().c_str()); + } + ++uint64_t ++bpf_ktime_get_ns() ++{ ++ struct timespec t; ++ clock_gettime (CLOCK_BOOTTIME, &t); ++ return (t.tv_sec * 1000000000) + t.tv_nsec; ++} ++ ++ ++ + uint64_t + bpf_interpret(size_t ninsns, const struct bpf_insn insns[], + std::vector &map_fds, FILE *output_f) +@@ -374,6 +384,9 @@ bpf_interpret(size_t ninsns, const struct bpf_insn insns[], + case BPF_FUNC_map_delete_elem: + dr = bpf_delete_elem(map_fds[regs[1]], as_ptr(regs[2])); + break; ++ case BPF_FUNC_ktime_get_ns: ++ dr = bpf_ktime_get_ns(); ++ break; + case BPF_FUNC_trace_printk: + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wformat-nonliteral" +diff --git a/tapset/bpf/time.stp b/tapset/bpf/time.stp +index ee615330c..a1c827e73 100644 +--- a/tapset/bpf/time.stp ++++ b/tapset/bpf/time.stp +@@ -12,7 +12,7 @@ + * Description: This function returns the system ktime. + */ + function ktime_get_ns:long () +-%{ /* bpf */ /* pure */ /* unprivileged */ /* stable */ ++%{ /* bpf */ /* pure */ /* unprivileged */ + 0x85, 0, 0, 0, 5; /* call BPF_FUNC_ktime_get_ns */ + 0xbf, $$, 0, 0, 0 /* movx $$, r0 */ + %} +diff --git a/tapset/linux/timestamp_monotonic.stp b/tapset/linux/timestamp_monotonic.stp +index 6b6d445de..c8d142369 100644 +--- a/tapset/linux/timestamp_monotonic.stp ++++ b/tapset/linux/timestamp_monotonic.stp +@@ -148,3 +148,14 @@ function local_clock_ms:long () { + function local_clock_s:long () { + return local_clock_ns() / 1000000000; + } ++ ++ ++/** ++ * sfunction ktime_get_ns - Number of nanoseconds since boot ++ * ++ * Description: This function returns the system ktime. ++ */ ++function ktime_get_ns:long () %{ ++ /* pure */ /* unprivileged */ ++ STAP_RETURN ((int64_t) ktime_get_ns()); ++%} +diff --git a/testsuite/systemtap.bpf/bpf_tests/ktime_get_ns.stp b/testsuite/systemtap.bpf/bpf_tests/ktime_get_ns.stp +new file mode 100644 +index 000000000..0b1d98e94 +--- /dev/null ++++ b/testsuite/systemtap.bpf/bpf_tests/ktime_get_ns.stp +@@ -0,0 +1,33 @@ ++global t1, t2, t3 ++ ++probe begin { ++ printf("BEGIN\n") ++ t1 = ktime_get_ns() // bpfinterp.cxx ++} ++ ++probe timer.s(1) { ++ t2 = ktime_get_ns() // kernel bpf - XXX watch if timer.s() moves to userspace ++} ++ ++probe perf.type(1).config(0).hz(1) { ++ t3 = ktime_get_ns() // kernel bpf ++} ++ ++ ++/* ++probe kernel.function("SOMETHING") { # to trigger kernel side bpf func ++ t3 = ktime.get.ns() ++} ++*/ ++ ++ ++probe timer.s(5) { ++ exit() ++} ++ ++probe end { ++ if (t1 > 0 && t2 > 0 && t3 > 0) ++ printf("END PASS\n") ++ else ++ printf("END FAIL\n") ++} +diff --git a/testsuite/systemtap.bpf/nonbpf.exp b/testsuite/systemtap.bpf/nonbpf.exp +new file mode 100644 +index 000000000..35f6a7c99 +--- /dev/null ++++ b/testsuite/systemtap.bpf/nonbpf.exp +@@ -0,0 +1,152 @@ ++# bpf.exp ++# ++# To restrict scripts to test, set the CHECK_ONLY environment variable. ++# For example, to only test the printf and uprobes scripts, run: ++# ++# make installcheck RUNTESTFLAGS="nonbpf.exp" CHECK_ONLY="printf uprobes" ++ ++set testdir "$srcdir/$subdir/bpf_tests" ++ ++# All tests should start by printing "BEGIN". If OUTPUT_STR is "", then ++# the test passes if "END PASS" is read and fails if "END FAIL" is read. Otherwise ++# the test passes when "${OUTPUT_STR}END" is read and fails if END is read without ++# OUTPUT_STR preceeding it. ++proc stapnonbpf_run { TEST_NAME OUTPUT_STR args } { ++ global rc ++ set rc -1 ++ set begin_str "BEGIN" ++ set pass_str [expr { $OUTPUT_STR == "" ? "END PASS" : "${OUTPUT_STR}END" }] ++ set fail_str [expr { $OUTPUT_STR == "" ? "END FAIL" : "END" }] ++ ++ # return codes ++ set pass 0 ++ set fail 1 ++ set bad_output 2 ++ set eof_start 3 ++ set eof_end 4 ++ set timeout_start 5 ++ set timeout_end 6 ++ set invalid_prog 7 ++ set comp_err 8 ++ ++ set cmd [concat stap -v $args] ++ # don't the following: ... $test_file_name could be some transient or leftover file ++ # if [file readable $test_file_name] { lappend cmd $test_file_name } ++ ++ send_log "executing: $cmd\n" ++ eval spawn $cmd ++ set mypid [exp_pid -i $spawn_id] ++ expect { ++ -timeout 30 ++ -re {^WARNING: cannot find module [^\r]*DWARF[^\r]*\r\n} {exp_continue} ++ -re {^WARNING: No unwind data for /.+\r\n} {exp_continue} ++ -re {^Pass\ ([1234]):[^\r]*\ in\ ([0-9]+)usr/([0-9]+)sys/([0-9]+)real\ ms\.\r\n} ++ {set pass$expect_out(1,string) "\t$expect_out(2,string)\t$expect_out(3,string)\t$expect_out(4,string)"; exp_continue} ++ -re {^Pass\ ([34]): using cached [^\r]+\r\n} ++ {set pass$expect_out(1,string) "\t0\t0\t0"; exp_continue} ++ -re {^Passes: via server [^\r]* using [^\r]* in [0-9]+usr/[0-9]+sys/[0-9]+real ms\.\r\n} {exp_continue} ++ -re {^Pass 5: starting run.\r\n} {exp_continue} ++ -re $begin_str { ++ # By default, "expect -re" will match up to 2000 chars. ++ # Increase this to 8K worth of data. ++ exp_match_max 8192 ++ ++ # Avoid PR17274 to propagate ++ set origexpinternal 1 ++ if {"[exp_internal -info]" == "0"} {set origexpinternal 0} ++ #exp_internal 0 ++ ++ expect { ++ -timeout 20 ++ -re $pass_str { ++ set rc $pass ++ } ++ -re $fail_str { ++ set rc $fail ++ } ++ default { ++ set rc $bad_output ++ } ++ timeout { ++ set rc $timeout_end ++ kill -INT -$mypid ++ } ++ eof { set rc $eof_end } ++ } ++ exp_internal $origexpinternal ++ } ++ -re "semantic error:" { set rc $comp_err } ++ timeout { ++ set rc $timeout_start ++ kill -INT -$mypid ++ } ++ eof { set rc $eof_start } ++ } ++ # again for good measure with KILL after 3s ++ kill -INT -$mypid 3 ++ catch close ++ wait ++ return $rc ++} ++ ++proc get_output_str { test } { ++ global res ++ switch $test { ++ printf.stp { set res [string repeat "abcd123456" 3] } ++ sprintf.stp { set res [string repeat "0123456789" 2] } ++ string1.stp { set res {begin\[str0str1str2str3\]probe\[str0str1str2str3\]end\[str0str1str2str3\]} } ++ string2.stp { set res {probe0\[str0str1str2str3\]probe1\[str0str1str2str3\]end\[str0str1str2str3\]} } ++ default { set res "" } ++ } ++ return $res ++} ++ ++if {[info exists env(CHECK_ONLY)]} { ++ set all_files [lsort [glob -nocomplain $testdir/*.stp]] ++ set stap_files "" ++ foreach file $env(CHECK_ONLY) { ++ if {[lsearch $all_files $testdir/$file.stp] >= 0} { ++ set stap_files "$stap_files $testdir/$file.stp" ++ } ++ } ++} else { ++ set stap_files [lsort [glob -nocomplain $testdir/*.stp]] ++} ++ ++foreach file $stap_files { ++ global mypid ++ set mypid 0 ++ set test [file tail $file] ++ if {! [installtest_p]} { untested $test; continue } ++ ++ # create a process for the tests that require one ++ switch $test { ++ uprobes.stp { ++ eval spawn /usr/bin/sleep 20 ++ set mypid [exp_pid -i $spawn_id] ++ } ++ } ++ ++ set output_str [get_output_str $test] ++ verbose -log "Running $file" ++ switch [stapnonbpf_run $test $output_str $file] { ++ 0 { pass $test } ++ 1 { fail "$test incorrect result" } ++ 2 { fail "$test unexpected output" } ++ 3 { fail "$test eof (startup)" } ++ 4 { fail "$test eof (shutdown)" } ++ 5 { fail "$test timeout (startup)" } ++ 6 { fail "$test timeout (startup)" } ++ 7 { fail "$test invalid bpf program" } ++ 8 { fail "$test compilation" } ++ default { fail "$test unknown return value" } ++ } ++ ++ if { $mypid > 0 } { ++ kill -INT -$mypid 3 ++ catch close ++ wait ++ } ++} ++ ++ +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0023-pr23860-verifier-workaround-be-sure-to-delete-all-mo.patch b/SOURCES/rhbz1643997.0023-pr23860-verifier-workaround-be-sure-to-delete-all-mo.patch new file mode 100644 index 0000000..79b9a58 --- /dev/null +++ b/SOURCES/rhbz1643997.0023-pr23860-verifier-workaround-be-sure-to-delete-all-mo.patch @@ -0,0 +1,32 @@ +From b43a06011e0a4606504391ffb94762276c95610d Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Wed, 7 Nov 2018 13:07:51 -0500 +Subject: [PATCH 23/32] pr23860 verifier workaround :: be sure to delete all + mov rN,rN + +An apparent bug in the eBPF verifier fails to preserve register state +when MOVing a register to itself, marking rN as 'unknown scalar'. + +Previously bpf-opt.cxx failed to remove spurious MOVs if they were the +final instruction in a basic block. This would fail verification if +the register holds a pointer. +--- + bpf-opt.cxx | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/bpf-opt.cxx b/bpf-opt.cxx +index 904b33b46..8b9a6ea60 100644 +--- a/bpf-opt.cxx ++++ b/bpf-opt.cxx +@@ -909,7 +909,7 @@ post_alloc_cleanup (program &p) + n = j->next; + if (j->is_move() + && j->src1->is_reg() +- && j->dest->reg() == j->src1->reg() && n) ++ && j->dest->reg() == j->src1->reg()) + { + // Delete no-op moves created by partition merging. + insn *p = j->prev; +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0024-PR23860-bugfix-incorrect-comparison-direction-in-str.patch b/SOURCES/rhbz1643997.0024-PR23860-bugfix-incorrect-comparison-direction-in-str.patch new file mode 100644 index 0000000..8ea990f --- /dev/null +++ b/SOURCES/rhbz1643997.0024-PR23860-bugfix-incorrect-comparison-direction-in-str.patch @@ -0,0 +1,30 @@ +From 90b65d0e05faee00c42cd303dd155dd3d228553d Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Fri, 9 Nov 2018 16:19:17 -0500 +Subject: [PATCH 24/32] PR23860 bugfix: incorrect comparison direction in + string_copy() + +(Turns out this branch was flipped and it was a root cause of the havoc.) + +* bpf-translate.cxx (bpf_unparser::emit_string_copy): Correct + direction of JEQ(all_nz,0) jump instruction. +--- + bpf-translate.cxx | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/bpf-translate.cxx b/bpf-translate.cxx +index 57a4cb107..0181380b7 100644 +--- a/bpf-translate.cxx ++++ b/bpf-translate.cxx +@@ -2667,7 +2667,7 @@ bpf_unparser::emit_string_copy(value *dest, int ofs, value *src, bool zero_pad) + } + + this_prog.mk_jcond(this_ins, EQ, all_nz, this_prog.new_imm(0), +- next_block, zero_pad ? block_B[i+1] : join_block); ++ zero_pad ? block_B[i+1] : join_block, next_block); + } + + // XXX: Zero-padding is only used under specific circumstances; +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0025-PR23860-additional-stack-protection-for-strings.patch b/SOURCES/rhbz1643997.0025-PR23860-additional-stack-protection-for-strings.patch new file mode 100644 index 0000000..65d99c7 --- /dev/null +++ b/SOURCES/rhbz1643997.0025-PR23860-additional-stack-protection-for-strings.patch @@ -0,0 +1,117 @@ +From 06351f7f372c670a630e7595fcd77913ed05742d Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Thu, 8 Nov 2018 16:40:40 -0500 +Subject: [PATCH 25/32] PR23860: additional stack protection for strings + +Fixes for verifier rejection of some cases requiring string copy, +since the verifier would reject string copy code extending beyond the +end of the string even if it was not reachable. + +* bpf-opt.cxx (alloc_literal_str): make sure the offset for a short + string is at least BPF_MAXSTRINGLEN. +(zero_stack): New function. +(program::generate): Use zero_stack() to zero temporary area and + prevent verifier complaints. +* testsuite/systemtap.bpf/asm_tests/pr23860.stp: New testcase. +--- + bpf-opt.cxx | 32 ++++++++++++++++++++++++++- + testsuite/systemtap.bpf/asm_tests/pr23860.stp | 24 ++++++++++++++++++++ + 2 files changed, 55 insertions(+), 1 deletion(-) + create mode 100644 testsuite/systemtap.bpf/asm_tests/pr23860.stp + +diff --git a/bpf-opt.cxx b/bpf-opt.cxx +index 8b9a6ea60..089dcde55 100644 +--- a/bpf-opt.cxx ++++ b/bpf-opt.cxx +@@ -36,7 +36,19 @@ alloc_literal_str(program &p, insn_inserter &ins, std::string &str) + if (tmp_space + str_bytes > MAX_BPF_STACK) + throw std::runtime_error("string allocation failed due to lack of room on stack"); + +- tmp_space += str_bytes; // TODO: round up for safety? ++ tmp_space += str_bytes; ++ ++#if 1 ++ // XXX PR23860: Passing a short (non-padded) string constant can fail ++ // the verifier, which is not smart enough to determine that accesses ++ // past the end of the string will never occur. To fix this, make sure ++ // the string offset is at least -BPF_MAXSTRINGLEN. ++ // ++ // Not ideal because an unlucky ordering of allocations may waste space. ++ if (tmp_space < BPF_MAXSTRINGLEN) ++ tmp_space = BPF_MAXSTRINGLEN; ++#endif ++ + p.use_tmp_space(tmp_space); + int ofs = -tmp_space; + +@@ -932,19 +944,37 @@ post_alloc_cleanup (program &p) + } + } + ++// XXX PR23860: Passing a short (non-padded) string constant can fail ++// the verifier, which is not smart enough to determine that accesses ++// past the end of the string will never occur. To fix this, start the ++// program with some code to zero out the temporary stack space. ++void ++zero_stack(program &p) ++{ ++ block *entry_block = p.blocks[0]; ++ insn_before_inserter ins(entry_block, entry_block->first, "zero_stack"); ++ value *frame = p.lookup_reg(BPF_REG_10); ++ for (int32_t ofs = -(int32_t)p.max_tmp_space; ofs < 0; ofs += 4) ++ p.mk_st(ins, BPF_W, frame, (int32_t)ofs, p.new_imm(0)); ++} ++ + void + program::generate() + { + #ifdef DEBUG_CODEGEN + std::cerr << "DEBUG BEFORE OPT " << *this << std::endl; + #endif ++ + lower_str_values(*this); ++ zero_stack(*this); ++ + fixup_operands(*this); + thread_jumps(*this); + fold_jumps(*this); + reorder_blocks(*this); + reg_alloc(*this); + post_alloc_cleanup(*this); ++ + #ifdef DEBUG_CODEGEN + std::cerr << "DEBUG AFTER OPT " << *this << std::endl; + #endif +diff --git a/testsuite/systemtap.bpf/asm_tests/pr23860.stp b/testsuite/systemtap.bpf/asm_tests/pr23860.stp +new file mode 100644 +index 000000000..bd3e05f66 +--- /dev/null ++++ b/testsuite/systemtap.bpf/asm_tests/pr23860.stp +@@ -0,0 +1,24 @@ ++function foo:string () ++%{ /* bpf */ /* pure */ /* unprivileged */ /* stable */ ++ 0xbf, $$, "key", -, -; /* mov $$, "key" */ ++%} ++ ++function bar:string () ++{ ++ return "kez" ++} ++ ++global t ++ ++probe begin { ++ t[foo()] = 4 ++ t[bar()] = 6 ++ printf("U t[key]=%d, t[kez]=%d should be 4,6\n", t["key"], t["kez"]) ++} ++ ++probe kernel.function("vfs_read") { ++ t[foo()] = 5 ++ t[bar()] = 7 ++ printf("K t[key]=%d, t[kez]=%d should be 5,7\n", t["key"], t["kez"]) ++ exit() ++} +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0026-PR23860-additional-ugly-stack-clobber-protection-for.patch b/SOURCES/rhbz1643997.0026-PR23860-additional-ugly-stack-clobber-protection-for.patch new file mode 100644 index 0000000..f654c0c --- /dev/null +++ b/SOURCES/rhbz1643997.0026-PR23860-additional-ugly-stack-clobber-protection-for.patch @@ -0,0 +1,222 @@ +From a7605f298f8d823429b8cfd264bd28c3bd345eb5 Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Fri, 9 Nov 2018 14:36:19 -0500 +Subject: [PATCH 26/32] PR23860: additional ugly stack/clobber protection for + strings + +In addition to prior commit, emit_string_copy() does not work for +overlapping source/destination. So, make sure strings are not +allocated in a way which overlaps map key/value arguments. + +This increases space pressure, inducing a couple of bpf-asm.exp +testcase failures. + +* bpf-internal.h (value::format_str): New flag. +(value::value): Take format_str flag. +(value::mk_str): Take format_str flag. +(program::format_map): New field, caches format_str separately. +(program::new_str): Take format_str flag. +* bpf-base.cxx (program::new_str): Cache format_str separately. +* bpf-opt.cxx (alloc_literal_str): Store non-format str in lower half. +* bpf-translate.cxx (emit_string_copy): Comment -- doesn't support overlap. +(emit_string_copy): DEBUG_CODEGEN -- identify if zero-padding was done. +(emit_print_format): Set format_str flag. +--- + bpf-base.cxx | 13 ++++++++----- + bpf-internal.h | 15 +++++++++++---- + bpf-opt.cxx | 35 ++++++++++++++++++++++++----------- + bpf-translate.cxx | 7 +++++-- + 4 files changed, 48 insertions(+), 22 deletions(-) + +diff --git a/bpf-base.cxx b/bpf-base.cxx +index 5d132bcd1..b19b69133 100644 +--- a/bpf-base.cxx ++++ b/bpf-base.cxx +@@ -657,14 +657,17 @@ program::new_imm(int64_t i) + } + + value * +-program::new_str(std::string str) ++program::new_str(std::string str, bool format_str) + { +- auto old = str_map.find(str); +- if (old != str_map.end()) ++ std::unordered_map& m = str_map; ++ if (format_str) m = format_map; ++ ++ auto old = m.find(str); ++ if (old != m.end()) + return old->second; + +- value *v = new value(value::mk_str(str)); +- auto ok = str_map.insert(std::pair(str, v)); ++ value *v = new value(value::mk_str(str, format_str)); ++ auto ok = m.insert(std::pair(str, v)); + assert(ok.second); + return v; + } +diff --git a/bpf-internal.h b/bpf-internal.h +index 75fefb769..f97501d7d 100644 +--- a/bpf-internal.h ++++ b/bpf-internal.h +@@ -78,18 +78,24 @@ struct value + int64_t imm_val; + std::string str_val; + +- value(value_type t = UNINIT, regno r = noreg, int64_t c = 0, std::string s = "") +- : type(t), reg_val(r), imm_val(c), str_val(s) ++ bool format_str; // for str_val ++ ++ value(value_type t = UNINIT, regno r = noreg, int64_t c = 0, ++ std::string s = "", bool format_str = false) ++ : type(t), reg_val(r), imm_val(c), str_val(s), format_str(format_str) + { } + + static value mk_imm(int64_t i) { return value(IMM, noreg, i); } +- static value mk_str(std::string s) { return value(STR, noreg, 0, s); } ++ static value mk_str(std::string s, bool format_str = false) { ++ return value(STR, noreg, 0, s, format_str); ++ } + static value mk_reg(regno r) { return value(TMPREG, r); } + static value mk_hardreg(regno r) { return value(HARDREG, r); } + + bool is_reg() const { return type >= HARDREG; } + bool is_imm() const { return type == IMM; } + bool is_str() const { return type == STR; } ++ bool is_format() const { assert(is_str()); return format_str; } + + regno reg() const { assert(is_reg()); return reg_val; } + int64_t imm() const { assert(is_imm()); return imm_val; } +@@ -262,12 +268,13 @@ struct program + // Store at most one of each IMM and STR value: + std::unordered_map imm_map; + std::unordered_map str_map; ++ std::unordered_map format_map; + + regno max_reg() const { return reg_vals.size() + MAX_BPF_REG; } + value *lookup_reg(regno r); + value *new_reg(); + value *new_imm(int64_t); +- value *new_str(std::string); ++ value *new_str(std::string, bool format_str = false); + + // The BPF local stack is [0, -512] indexed off BPF_REG_10. + // The translator has dibs on the low bytes, [0, -max_tmp_space], +diff --git a/bpf-opt.cxx b/bpf-opt.cxx +index 089dcde55..f3aa5c462 100644 +--- a/bpf-opt.cxx ++++ b/bpf-opt.cxx +@@ -19,38 +19,51 @@ namespace bpf { + + // Allocate space on the stack and store a string literal in that space: + static value * +-alloc_literal_str(program &p, insn_inserter &ins, std::string &str) ++alloc_literal_str(program &p, insn_inserter &ins, value *s) + { ++ std::string str = s->str(); ++ ++ size_t str_bytes = str.size() + 1; ++ str_bytes += 4 - str_bytes % 4; // write aligned words to avoid garbage data ++ ++ int ofs; size_t tmp_space; ++ + // Append the string to existing temporary data. + // + // TODO: This could produce significant space limitations. + // A better solution would be to integrate with the + // register allocator and reclaim the space after + // the string literal is no longer live. +- size_t tmp_space = p.max_tmp_space; ++ tmp_space = p.max_tmp_space; + tmp_space += 4 - tmp_space % 4; // write aligned words to avoid verifier error + p.use_tmp_space(tmp_space); + +- size_t str_bytes = str.size() + 1; +- str_bytes += 4 - str_bytes % 4; // write aligned words to avoid garbage data + if (tmp_space + str_bytes > MAX_BPF_STACK) + throw std::runtime_error("string allocation failed due to lack of room on stack"); + + tmp_space += str_bytes; + + #if 1 ++ // The following aren't ideal because an unlucky ordering of ++ // allocation requests will waste additional space. ++ + // XXX PR23860: Passing a short (non-padded) string constant can fail + // the verifier, which is not smart enough to determine that accesses + // past the end of the string will never occur. To fix this, make sure + // the string offset is at least -BPF_MAXSTRINGLEN. +- // +- // Not ideal because an unlucky ordering of allocations may waste space. +- if (tmp_space < BPF_MAXSTRINGLEN) +- tmp_space = BPF_MAXSTRINGLEN; ++ //if (!s->is_format() && tmp_space < BPF_MAXSTRINGLEN) ++ // tmp_space = BPF_MAXSTRINGLEN; ++ ++ // TODO PR23860: An even uglier workaround for emit_string_copy() ++ // overlapping source and destination regions. Only do this for ++ // non-format strings, as format strings are not manipulated by the ++ // eBPF program. ++ if (!s->is_format() && tmp_space < BPF_MAXSTRINGLEN * 2 + str_bytes) ++ tmp_space = BPF_MAXSTRINGLEN * 2 + str_bytes; + #endif + + p.use_tmp_space(tmp_space); +- int ofs = -tmp_space; ++ ofs = -tmp_space; + + value *frame = p.lookup_reg(BPF_REG_10); + value *out = emit_simple_literal_str(p, ins, frame, ofs, str, false /* don't zero pad */); +@@ -73,7 +86,7 @@ lower_str_values(program &p) + { + insn_before_inserter ins(b, j, "str"); + std::string str0 = s0->str(); +- value *new_s0 = alloc_literal_str(p, ins, str0); ++ value *new_s0 = alloc_literal_str(p, ins, s0); + j->src0 = new_s0; + } + +@@ -82,7 +95,7 @@ lower_str_values(program &p) + { + insn_before_inserter ins(b, j, "str"); + std::string str1 = s1->str(); +- value *new_s1 = alloc_literal_str(p, ins, str1); ++ value *new_s1 = alloc_literal_str(p, ins, s1); + j->src1 = new_s1; + } + } +diff --git a/bpf-translate.cxx b/bpf-translate.cxx +index 0181380b7..20cd47032 100644 +--- a/bpf-translate.cxx ++++ b/bpf-translate.cxx +@@ -2568,6 +2568,9 @@ emit_simple_literal_str(program &this_prog, insn_inserter &this_ins, + // dest[+ofs] in 4-byte chunks, with optional zero-padding up to + // BPF_MAXSTRINGLEN. + // ++// TODO (PR23860): This code does not work when the source and target ++// regions overlap. ++// + // ??? Could use 8-byte chunks if we're starved for instruction count. + // ??? Endianness of the target may come into play here. + value * +@@ -2583,7 +2586,7 @@ bpf_unparser::emit_string_copy(value *dest, int ofs, value *src, bool zero_pad) + } + + #ifdef DEBUG_CODEGEN +- this_ins.notes.push("strcpy"); ++ this_ins.notes.push(zero_pad ? "strcpy_zero_pad" : "strcpy"); + #endif + + size_t str_bytes = BPF_MAXSTRINGLEN; +@@ -2866,7 +2869,7 @@ bpf_unparser::emit_print_format (const std::string& format, + // bpf program stack. This is handled by bpf-opt.cxx lowering STR values. + size_t format_bytes = format.size() + 1; + this_prog.mk_mov(this_ins, this_prog.lookup_reg(BPF_REG_1), +- this_prog.new_str(format)); ++ this_prog.new_str(format, true /*format_str*/)); + emit_mov(this_prog.lookup_reg(BPF_REG_2), this_prog.new_imm(format_bytes)); + for (size_t i = 0; i < nargs; ++i) + emit_mov(this_prog.lookup_reg(BPF_REG_3 + i), actual[i]); +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0027-PR23860-reduce-stack-pressure-from-format-strings.patch b/SOURCES/rhbz1643997.0027-PR23860-reduce-stack-pressure-from-format-strings.patch new file mode 100644 index 0000000..60beb93 --- /dev/null +++ b/SOURCES/rhbz1643997.0027-PR23860-reduce-stack-pressure-from-format-strings.patch @@ -0,0 +1,48 @@ +From 59ec363d7236d92f896135ef8fe85433d6f1995e Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Fri, 9 Nov 2018 16:24:09 -0500 +Subject: [PATCH 27/32] PR23860: reduce stack pressure from format strings + +Reduce stack pressure created by the earlier commits by allocating +format strings in a predictable location in the top half of the stack +[-BPF_MAXSTRINGLEN*2..0) as long as they fit in there. This works +since only one format string is active at a time and no ordinary +strings are being allocated in that region of the stack now. + +* bpf-opt.cxx (alloc_literal_str): Store format_str in top half. +--- + bpf-opt.cxx | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/bpf-opt.cxx b/bpf-opt.cxx +index f3aa5c462..5c7acb6b9 100644 +--- a/bpf-opt.cxx ++++ b/bpf-opt.cxx +@@ -27,6 +27,16 @@ alloc_literal_str(program &p, insn_inserter &ins, value *s) + str_bytes += 4 - str_bytes % 4; // write aligned words to avoid garbage data + + int ofs; size_t tmp_space; ++ if (s->is_format() && str_bytes <= BPF_MAXSTRINGLEN * 2) ++ { ++ // PR23068 workaround mitigation to reduce stack pressure: ++ // ++ // Store format strings in the top of the stack, since at most ++ // one printf() operation is prepared at a time and other string ++ // values will not be stored in that area now. ++ ofs = -str_bytes; ++ goto write_string; ++ } + + // Append the string to existing temporary data. + // +@@ -65,6 +75,7 @@ alloc_literal_str(program &p, insn_inserter &ins, value *s) + p.use_tmp_space(tmp_space); + ofs = -tmp_space; + ++ write_string: + value *frame = p.lookup_reg(BPF_REG_10); + value *out = emit_simple_literal_str(p, ins, frame, ofs, str, false /* don't zero pad */); + return out; +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0028-testcase-for-PR23875.patch b/SOURCES/rhbz1643997.0028-testcase-for-PR23875.patch new file mode 100644 index 0000000..b2de726 --- /dev/null +++ b/SOURCES/rhbz1643997.0028-testcase-for-PR23875.patch @@ -0,0 +1,40 @@ +From c3023bef537648b282f266878ea8de7a350b3a45 Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Fri, 9 Nov 2018 16:42:21 -0500 +Subject: [PATCH 28/32] testcase for PR23875 + +This triggers a 'stack smashing' error in the userspace interpreter. + +* testsuite/systemtap.bpf/bpf_tests/pr23875.stp: New testcase. +--- + testsuite/systemtap.bpf/bpf_tests/pr23875.stp | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + create mode 100644 testsuite/systemtap.bpf/bpf_tests/pr23875.stp + +diff --git a/testsuite/systemtap.bpf/bpf_tests/pr23875.stp b/testsuite/systemtap.bpf/bpf_tests/pr23875.stp +new file mode 100644 +index 000000000..a31f1d087 +--- /dev/null ++++ b/testsuite/systemtap.bpf/bpf_tests/pr23875.stp +@@ -0,0 +1,18 @@ ++global t ++ ++probe begin { ++ printf("BEGIN\n") ++} ++ ++probe kernel.function("vfs_read") { ++ t["key"] = 6 ++ exit() ++} ++ ++probe end { ++ c = 0 ++ foreach (k in t) ++ c++ ++ printf("%d\n", c) ++ printf("END PASS\n") ++} +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0029-PR23860-bpf_interpret-NEG-should-not-fall-through-to.patch b/SOURCES/rhbz1643997.0029-PR23860-bpf_interpret-NEG-should-not-fall-through-to.patch new file mode 100644 index 0000000..70f91dd --- /dev/null +++ b/SOURCES/rhbz1643997.0029-PR23860-bpf_interpret-NEG-should-not-fall-through-to.patch @@ -0,0 +1,73 @@ +From 6adc7dbeccecff18357751b9eecfa232ee8a8172 Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Tue, 13 Nov 2018 11:42:46 -0500 +Subject: [PATCH 29/32] PR23860 bpf_interpret() :: NEG should not fall through + to DIV + +--- + stapbpf/bpfinterp.cxx | 26 ++++++++++++++++++-------- + 1 file changed, 18 insertions(+), 8 deletions(-) + +diff --git a/stapbpf/bpfinterp.cxx b/stapbpf/bpfinterp.cxx +index 13ac8ee71..2a90c24c9 100644 +--- a/stapbpf/bpfinterp.cxx ++++ b/stapbpf/bpfinterp.cxx +@@ -254,18 +254,23 @@ bpf_interpret(size_t ninsns, const struct bpf_insn insns[], + case BPF_ALU64 | BPF_MOV | BPF_K: dr = s1; break; + case BPF_ALU64 | BPF_ARSH | BPF_X: + case BPF_ALU64 | BPF_ARSH | BPF_K: dr = (int64_t)dr >> s1; break; +- case BPF_ALU64 | BPF_NEG: dr = -sr; +- /* Fallthrough */ ++ case BPF_ALU64 | BPF_NEG: dr = -sr; break; + case BPF_ALU64 | BPF_DIV | BPF_X: + case BPF_ALU64 | BPF_DIV | BPF_K: + if (s1 == 0) +- return 0; ++ { ++ // TODO: Signal a proper error. ++ return 0; ++ } + dr /= s1; + break; + case BPF_ALU64 | BPF_MOD | BPF_X: + case BPF_ALU64 | BPF_MOD | BPF_K: + if (s1 == 0) +- return 0; ++ { ++ // TODO: Signal a proper error. ++ return 0; ++ } + dr %= s1; + break; + +@@ -289,18 +294,23 @@ bpf_interpret(size_t ninsns, const struct bpf_insn insns[], + case BPF_ALU | BPF_MOV | BPF_K: dr = (uint32_t)s1; break; + case BPF_ALU | BPF_ARSH | BPF_X: + case BPF_ALU | BPF_ARSH | BPF_K: dr = (int32_t)dr >> s1; break; +- case BPF_ALU | BPF_NEG: dr = -(uint32_t)sr; +- /* Fallthrough */ ++ case BPF_ALU | BPF_NEG: dr = -(uint32_t)sr; break; + case BPF_ALU | BPF_DIV | BPF_X: + case BPF_ALU | BPF_DIV | BPF_K: + if ((uint32_t)s1 == 0) +- return 0; ++ { ++ // TODO: Signal a proper error. ++ return 0; ++ } + dr = (uint32_t)dr / (uint32_t)s1; + break; + case BPF_ALU | BPF_MOD | BPF_X: + case BPF_ALU | BPF_MOD | BPF_K: + if ((uint32_t)s1 == 0) +- return 0; ++ { ++ // TODO: Signal a proper error. ++ return 0; ++ } + dr = (uint32_t)dr % (uint32_t)s1; + break; + +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0030-PR23875-bpf.exp-fail-testcase-on-stack-smashing.patch b/SOURCES/rhbz1643997.0030-PR23875-bpf.exp-fail-testcase-on-stack-smashing.patch new file mode 100644 index 0000000..c8a4f72 --- /dev/null +++ b/SOURCES/rhbz1643997.0030-PR23875-bpf.exp-fail-testcase-on-stack-smashing.patch @@ -0,0 +1,26 @@ +From d3a33cd7b8c1b0bfe236bdea2bde83c9c6a83c05 Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Tue, 13 Nov 2018 12:27:05 -0500 +Subject: [PATCH 30/32] PR23875 bpf.exp: fail testcase on 'stack smashing' + +--- + testsuite/systemtap.bpf/bpf.exp | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/testsuite/systemtap.bpf/bpf.exp b/testsuite/systemtap.bpf/bpf.exp +index e8e94bf19..3f32b6c79 100644 +--- a/testsuite/systemtap.bpf/bpf.exp ++++ b/testsuite/systemtap.bpf/bpf.exp +@@ -58,6 +58,9 @@ proc stapbpf_run { TEST_NAME OUTPUT_STR args } { + + expect { + -timeout 20 ++ -re "stack smashing detected" { ++ set rc $bad_output ++ } + -re $pass_str { + set rc $pass + } +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0031-PR23875-another-testcase-that-loops-indefinitely.patch b/SOURCES/rhbz1643997.0031-PR23875-another-testcase-that-loops-indefinitely.patch new file mode 100644 index 0000000..56aa420 --- /dev/null +++ b/SOURCES/rhbz1643997.0031-PR23875-another-testcase-that-loops-indefinitely.patch @@ -0,0 +1,36 @@ +From ed0fac0c88da94f40f826a41cc87d0d9d7620f34 Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Tue, 13 Nov 2018 13:07:52 -0500 +Subject: [PATCH 31/32] PR23875: another testcase that loops indefinitely + +--- + testsuite/systemtap.bpf/bpf_tests/pr23875_loop.stp | 11 +++++++++++ + .../bpf_tests/{pr23875.stp => pr23875_smash.stp} | 0 + 2 files changed, 11 insertions(+) + create mode 100644 testsuite/systemtap.bpf/bpf_tests/pr23875_loop.stp + rename testsuite/systemtap.bpf/bpf_tests/{pr23875.stp => pr23875_smash.stp} (100%) + +diff --git a/testsuite/systemtap.bpf/bpf_tests/pr23875_loop.stp b/testsuite/systemtap.bpf/bpf_tests/pr23875_loop.stp +new file mode 100644 +index 000000000..5912ebf8c +--- /dev/null ++++ b/testsuite/systemtap.bpf/bpf_tests/pr23875_loop.stp +@@ -0,0 +1,11 @@ ++global t ++ ++probe begin { ++ println("BEGIN") ++ t["key"] = 6 ++} ++ ++probe end { ++ foreach (k in t) printf("%s -> %d\n", k, t[k]) ++ println("END PASS") ++} +diff --git a/testsuite/systemtap.bpf/bpf_tests/pr23875.stp b/testsuite/systemtap.bpf/bpf_tests/pr23875_smash.stp +similarity index 100% +rename from testsuite/systemtap.bpf/bpf_tests/pr23875.stp +rename to testsuite/systemtap.bpf/bpf_tests/pr23875_smash.stp +-- +2.14.5 + diff --git a/SOURCES/rhbz1643997.0032-PR23875-bpf_unparser-visit_foreach_loop-temporarily-.patch b/SOURCES/rhbz1643997.0032-PR23875-bpf_unparser-visit_foreach_loop-temporarily-.patch new file mode 100644 index 0000000..6001e56 --- /dev/null +++ b/SOURCES/rhbz1643997.0032-PR23875-bpf_unparser-visit_foreach_loop-temporarily-.patch @@ -0,0 +1,28 @@ +From 6fd1813950d708c5b94ed155213a7b9c84b7fbfc Mon Sep 17 00:00:00 2001 +From: Serhei Makarov +Date: Tue, 13 Nov 2018 13:13:14 -0500 +Subject: [PATCH 32/32] PR23875 bpf_unparser::visit_foreach_loop(): temporarily + disable string key iteration + +--- + bpf-translate.cxx | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/bpf-translate.cxx b/bpf-translate.cxx +index 20cd47032..0f35e0d3f 100644 +--- a/bpf-translate.cxx ++++ b/bpf-translate.cxx +@@ -1532,6 +1532,10 @@ bpf_unparser::visit_foreach_loop(foreach_loop* s) + throw SEMANTIC_ERROR(_("unknown type"), s->base->tok); + vardecl *arraydecl = a->referent; + ++ // TODO PR23875: foreach should handle string keys ++ if (arraydecl->index_types[0] != pe_long) ++ throw SEMANTIC_ERROR(_("unhandled string index type"), s->tok); ++ + auto g = glob.globals.find(arraydecl); + if (g == glob.globals.end()) + throw SEMANTIC_ERROR(_("unknown array"), arraydecl->tok); +-- +2.14.5 + diff --git a/SOURCES/rhbz1656795.patch b/SOURCES/rhbz1656795.patch new file mode 100644 index 0000000..65f6f74 --- /dev/null +++ b/SOURCES/rhbz1656795.patch @@ -0,0 +1,105 @@ +commit aacee6563b7fd835b9a21e4ae5a0b4f7f743c7ef +Author: Martin Cermak +Date: Thu Dec 6 13:52:20 2018 +0100 + + Make sysc_bdflush.stp compatible with 4.17+ kernels. + + The bdflush syscall itself appears to be obsolete since 2.6, + but this way we at least won't end up with pass 1 "resolution + failed in alias expansion builder" when randomly probing for it. + There is an old customer rh bz 544960 related to bdflush. + +diff --git a/tapset/linux/sysc_bdflush.stp b/tapset/linux/sysc_bdflush.stp +index 0798964..48a04b9 100644 +--- a/tapset/linux/sysc_bdflush.stp ++++ b/tapset/linux/sysc_bdflush.stp +@@ -11,6 +11,12 @@ + argstr = sprintf("%d, %s", func, data_str) + %) + ++@define _SYSCALL_BDFLUSH_REGARGS ++%( ++ func = int_arg(1) ++ data = long_arg(2) ++%) ++ + probe syscall.bdflush = dw_syscall.bdflush !, nd_syscall.bdflush ? {} + probe syscall.bdflush.return = dw_syscall.bdflush.return !, nd_syscall.bdflush.return ? {} + +@@ -35,20 +41,72 @@ probe dw_syscall.bdflush.return = kernel.function("sys_bdflush").return ? + + # nd_bdflush _____________________________________________________ + +-probe nd_syscall.bdflush = kprobe.function("sys_bdflush") ? ++ ++probe nd_syscall.bdflush = nd1_syscall.bdflush!, nd2_syscall.bdflush!, tp_syscall.bdflush ++{ ++} ++ ++probe nd1_syscall.bdflush = kprobe.function("sys_bdflush") ? + { + @_SYSCALL_BDFLUSH_NAME + asmlinkage() +- func = int_arg(1) +- data = long_arg(2) ++ @_SYSCALL_BDFLUSH_REGARGS + if ((func >= 2) && (func % 2 == 0)) + data_str = sprintf("%p", data) + else + data_str = sprintf("%d", data) + @_SYSCALL_BDFLUSH_ARGSTR + } +-probe nd_syscall.bdflush.return = kprobe.function("sys_bdflush").return ? ++ ++/* kernel 4.17+ */ ++probe nd2_syscall.bdflush = kprobe.function(@arch_syscall_prefix "sys_bdflush") ? ++{ ++ @_SYSCALL_BDFLUSH_NAME ++ asmlinkage() ++ @_SYSCALL_BDFLUSH_REGARGS ++ if ((func >= 2) && (func % 2 == 0)) ++ data_str = sprintf("%p", data) ++ else ++ data_str = sprintf("%d", data) ++ @_SYSCALL_BDFLUSH_ARGSTR ++} ++ ++/* kernel 3.5+, but undesirable because it affects all syscalls */ ++probe tp_syscall.bdflush = kernel.trace("sys_enter") ++{ ++ __set_syscall_pt_regs($regs) ++ @__syscall_compat_gate(@const("__NR_bdflush"), @const("__NR_compat_bdflush")) ++ @_SYSCALL_BDFLUSH_NAME ++ @_SYSCALL_BDFLUSH_REGARGS ++ if ((func >= 2) && (func % 2 == 0)) ++ data_str = sprintf("%p", data) ++ else ++ data_str = sprintf("%d", data) ++ @_SYSCALL_BDFLUSH_ARGSTR ++} ++ ++probe nd_syscall.bdflush.return = nd1_syscall.bdflush.return!, nd2_syscall.bdflush.return!, tp_syscall.bdflush.return ++{ ++} ++ ++probe nd1_syscall.bdflush.return = kprobe.function("sys_bdflush").return ? ++{ ++ @_SYSCALL_BDFLUSH_NAME ++ @SYSC_RETVALSTR(returnval()) ++} ++ ++/* kernel 4.17+ */ ++probe nd2_syscall.bdflush.return = kprobe.function(@arch_syscall_prefix "sys_bdflush").return ? + { + @_SYSCALL_BDFLUSH_NAME + @SYSC_RETVALSTR(returnval()) + } ++ ++/* kernel 3.5+, but undesirable because it affects all syscalls */ ++probe tp_syscall.bdflush.return = kernel.trace("sys_exit") ++{ ++ __set_syscall_pt_regs($regs) ++ @__syscall_compat_gate(@const("__NR_bdflush"), @const("__NR_compat_bdflush")) ++ @_SYSCALL_BDFLUSH_NAME ++ @SYSC_RETVALSTR($ret) ++} diff --git a/SOURCES/rhbz1657186.patch b/SOURCES/rhbz1657186.patch new file mode 100644 index 0000000..41aa076 --- /dev/null +++ b/SOURCES/rhbz1657186.patch @@ -0,0 +1,64 @@ +commit 8bc64034509474bee3fb7996b2a9e74c8bc27281 +Author: Frank Ch. Eigler +Date: Fri Oct 19 17:36:04 2018 -0400 + + nfsd tapset: adapt nfsd.proc4.read probe to different kernel versions + + Here too an if(@defined($u)) is appropriate, just like in the .write case. + +diff --git a/tapset/linux/nfsd.stp b/tapset/linux/nfsd.stp +index 2fe9b0f..674e3e3 100644 +--- a/tapset/linux/nfsd.stp ++++ b/tapset/linux/nfsd.stp +@@ -567,10 +567,17 @@ probe nfsd.proc4.read = kernel.function("nfsd4_read").call !, + version = 4 + fh = & @nfsd4_compound_state($cstate)->current_fh + +- count = $read->rd_length +- offset = $read->rd_offset +- vec = $rqstp->rq_vec +- vlen = $read->rd_vlen ++ if (@defined($u)) { ++ count = $u->read->rd_length ++ offset = $u->read->rd_offset ++ vec = $rqstp->rq_vec ++ vlen = $u->read->rd_vlen ++ } else { ++ count = $read->rd_length ++ offset = $read->rd_offset ++ vec = $rqstp->rq_vec ++ vlen = $read->rd_vlen ++ } + + uid = __rqstp_uid($rqstp) + gid = __rqstp_gid($rqstp) + +commit 4e81610ae8c233d876f378cca8374e34cc2ee0e8 +Author: Frank Ch. Eigler +Date: Fri Oct 19 19:31:31 2018 -0400 + + nfsd tapset: adapt nfsd.proc4.commit probe to different kernel versions + + Here too an if(@defined($u)) is appropriate, just like in + every other case. Audited remainder. + +diff --git a/tapset/linux/nfsd.stp b/tapset/linux/nfsd.stp +index 674e3e3..aa04f24 100644 +--- a/tapset/linux/nfsd.stp ++++ b/tapset/linux/nfsd.stp +@@ -852,8 +852,13 @@ probe nfsd.proc4.commit = kernel.function("nfsd4_commit").call !, + proto = $rqstp->rq_prot + version = 4 + fh = & @nfsd4_compound_state($cstate)->current_fh +- count = $commit->co_count +- offset = $commit->co_offset ++ if (@defined($u)) { ++ count = $u->commit->co_count ++ offset = $u->commit->co_offset ++ } else { ++ count = $commit->co_count ++ offset = $commit->co_offset ++ } + + uid = __rqstp_uid($rqstp) + gid = __rqstp_gid($rqstp) diff --git a/SOURCES/rhbz1657857.patch b/SOURCES/rhbz1657857.patch new file mode 100644 index 0000000..f1a1ced --- /dev/null +++ b/SOURCES/rhbz1657857.patch @@ -0,0 +1,66 @@ +commit 75640f70daa0e8bab73e398113ea755e23eab887 +Author: William Cohen +Date: Mon Nov 12 17:24:44 2018 -0500 + + Adjust the periodic.stp example to work with newer Linux kernels + + The data field in the timer_list struct was removed in newer kernels. + The various functions executed when a timer expires now use + container_of macros to find the struct that the timer_list was + embedded in. The periodic.stp script has been modified to use + container_of when the data field is not available. + +diff --git a/testsuite/systemtap.examples/profiling/periodic.stp b/testsuite/systemtap.examples/profiling/periodic.stp +index 3b41981c0..f18f18399 100755 +--- a/testsuite/systemtap.examples/profiling/periodic.stp ++++ b/testsuite/systemtap.examples/profiling/periodic.stp +@@ -7,7 +7,7 @@ + # + # stap --all-modules periodic.stp + +-global last_expire, period, funct, data ++global last_expire, period, funct, data, proc_info, delayed_work_info + + probe kernel.trace("timer_expire_entry") + { +@@ -17,7 +17,9 @@ probe kernel.trace("timer_expire_entry") + elapsed = new_expire - old_expire + period[$timer] <<< elapsed + funct[$timer] = $timer->function +- data[$timer] = $timer->data ++ data[$timer] = @defined($timer->data) ? $timer->data : 0 ++ proc_info[$timer] = @defined($timer->data) ? 0 : @container_of($timer, "struct process_timer", timer)->task ++ delayed_work_info[$timer] = @defined($timer->data) ? 0 : & @container_of($timer, "struct delayed_work", timer) + } + last_expire[$timer] = new_expire + } +@@ -29,14 +31,16 @@ function output() + foreach([timer] in period-) { + fname = symname(funct[timer]) + if (fname == "process_timeout") { ++ ptr = (data[timer] ? data[timer] : proc_info[timer]) + fname = sprintf("%s(%d)", +- kernel_string_n(@cast(data[timer], "struct task_struct", "kernel")->comm, 16), +- @cast(data[timer], "struct task_struct", "kernel")->pid) ++ kernel_string_n(@cast(ptr, "struct task_struct", "kernel")->comm, 16), ++ @cast(ptr, "struct task_struct", "kernel")->pid) + type="process" + } else if (fname == "delayed_work_timer_fn") { +- faddr = @defined(@cast(data[timer], "struct delayed_work", "kernel")->work->func) +- ? @cast(data[timer], "struct delayed_work", "kernel")->work->func +- : @cast(data[timer], "struct work_struct", "kernel")->func ++ ptr = (data[timer] ? data[timer] : delayed_work_info[timer]) ++ faddr = @defined(@cast(ptr, "struct delayed_work", "kernel")->work->func) ++ ? @cast(ptr, "struct delayed_work", "kernel")->work->func ++ : @cast(ptr, "struct work_struct", "kernel")->func + fname = sprintf("%s", symname(faddr)) + type="work_q" + } else { +@@ -68,5 +72,7 @@ probe timer.s($1) + delete period + delete funct + delete data ++ delete proc_info ++ delete delayed_work_info + } + %) diff --git a/SOURCES/rhbz1657909.patch b/SOURCES/rhbz1657909.patch new file mode 100644 index 0000000..dae906f --- /dev/null +++ b/SOURCES/rhbz1657909.patch @@ -0,0 +1,34 @@ +commit f6d68366654176541487a8916c3b61c5b1207736 +Author: William Cohen +Date: Mon Nov 19 14:23:15 2018 -0500 + + Adjust the vfs_open to provide cred variable with 4.18 kernels + + The kernel's git commit ae2bb293a3e8adbc54d08cede5afc22929030c03 + removed the cred argument from the vfs_open. Thus, there is no $cred + target variable available. This missing target variable lives on as a + field in the $file target variable. The patch makes the tapset use + that field if the $cred target variable is not available. Fixing this + allows the slowvfs.stp example to work with newer linux 4.18 kernels. + +diff --git a/tapset/linux/vfs.stp b/tapset/linux/vfs.stp +index 1fe71ae..7505dfa 100644 +--- a/tapset/linux/vfs.stp ++++ b/tapset/linux/vfs.stp +@@ -1296,7 +1296,7 @@ probe vfs.open = kernel.function("vfs_open") ? + { + name = "vfs.open" + path = $path +- cred = $cred ++ cred = @defined($cred) ? $cred : $file->f_cred + pathname = fullpath_struct_path($path) + argstr = sprintf("%s, %p", pathname, $cred) + } +@@ -1311,6 +1311,6 @@ probe vfs.open.return = kernel.function("vfs_open").return ? + error_str = error ? errno_str(error) : "" + + path = @entry($path) +- cred = @entry($cred) ++ cred = @entry(@defined($cred) ? $cred : $file->f_cred) + pathname = fullpath_struct_path(@entry($path)) + } diff --git a/SPECS/systemtap.spec b/SPECS/systemtap.spec new file mode 100644 index 0000000..faefc0e --- /dev/null +++ b/SPECS/systemtap.spec @@ -0,0 +1,1667 @@ +%{!?with_sqlite: %global with_sqlite 0%{?fedora} >= 17 || 0%{?rhel} >= 7} +# prefer prebuilt docs +%{!?with_docs: %global with_docs 0} +%{!?with_htmldocs: %global with_htmldocs 0} +%{!?with_monitor: %global with_monitor 1} +# crash is not available +%ifarch ppc ppc64 %{sparc} aarch64 ppc64le %{mips} +%{!?with_crash: %global with_crash 0} +%else +%{!?with_crash: %global with_crash 1} +%endif +%{!?with_rpm: %global with_rpm 1} +%{!?with_bundled_elfutils: %global with_bundled_elfutils 0} +%{!?elfutils_version: %global elfutils_version 0.142} +%{!?pie_supported: %global pie_supported 1} +%{!?with_boost: %global with_boost 0} +%ifarch %{ix86} x86_64 ppc ppc64 +%{!?with_dyninst: %global with_dyninst 0%{?fedora} >= 18 || 0%{?rhel} >= 7} +%else +%{!?with_dyninst: %global with_dyninst 0} +%endif +%{!?with_bpf: %global with_bpf 0%{?fedora} >= 22 || 0%{?rhel} >= 8} +%{!?with_systemd: %global with_systemd 0%{?fedora} >= 19 || 0%{?rhel} >= 7} +%{!?with_emacsvim: %global with_emacsvim 0%{?fedora} >= 19 || 0%{?rhel} >= 7} +%{!?with_java: %global with_java 0%{?fedora} >= 19 || 0%{?rhel} >= 7} +%{!?with_virthost: %global with_virthost 0%{?fedora} >= 19 || 0%{?rhel} >= 7} +%{!?with_virtguest: %global with_virtguest 1} +%{!?with_dracut: %global with_dracut 0%{?fedora} >= 19 || 0%{?rhel} >= 6} +%ifarch x86_64 +%{!?with_mokutil: %global with_mokutil 0%{?fedora} >= 18 || 0%{?rhel} >= 7} +%{!?with_openssl: %global with_openssl 0%{?fedora} >= 18 || 0%{?rhel} >= 7} +%else +%{!?with_mokutil: %global with_mokutil 0} +%{!?with_openssl: %global with_openssl 0} +%endif +%{!?with_pyparsing: %global with_pyparsing 0%{?fedora} >= 18 || 0%{?rhel} >= 7} +%{!?with_python3: %global with_python3 0%{?fedora} >= 23 || 0%{?rhel} > 7} +%{!?with_python2_probes: %global with_python2_probes (0%{?fedora} <= 28 && 0%{?rhel} <= 7)} +%{!?with_python3_probes: %global with_python3_probes (0%{?fedora} >= 23 || 0%{?rhel} > 7)} +%{!?with_httpd: %global with_httpd 0} + +%ifarch ppc64le aarch64 +%global with_virthost 0 +%endif + +%if 0%{?fedora} >= 18 || 0%{?rhel} >= 6 + %define initdir %{_initddir} +%else # RHEL5 doesn't know _initddir + %define initdir %{_initrddir} +%endif + +%if %{with_virtguest} + %if 0%{?fedora} >= 18 || 0%{?rhel} >= 7 + %define udevrulesdir /usr/lib/udev/rules.d + %else + %if 0%{?rhel} >= 6 + %define udevrulesdir /lib/udev/rules.d + %else # RHEL5 + %define udevrulesdir /etc/udev/rules.d + %endif + %endif +%endif + +%if 0%{?fedora} >= 19 || 0%{?rhel} >= 7 + %define dracutstap %{_prefix}/lib/dracut/modules.d/99stap +%else + %define dracutstap %{_prefix}/share/dracut/modules.d/99stap +%endif + +%if 0%{?rhel} == 6 || 0%{?rhel} == 7 + %define dracutbindir /sbin +%else + %define dracutbindir %{_bindir} +%endif + +%if 0%{?rhel} == 6 + %{!?_rpmmacrodir: %define _rpmmacrodir /etc/rpm/} +%else + %{!?_rpmmacrodir: %define _rpmmacrodir %{_rpmconfigdir}/macros.d} +%endif + +# To avoid testsuite/*/*.stp has shebang which doesn't start with '/' +%define __brp_mangle_shebangs_exclude_from .stp$ + +Name: systemtap +Version: 4.0 +Release: 7%{?release_override}%{?dist} +# for version, see also configure.ac + + +# Packaging abstract: +# +# systemtap empty req:-client req:-devel +# systemtap-server /usr/bin/stap-server*, req:-devel +# systemtap-devel /usr/bin/stap, runtime, tapset, req:kernel-devel +# systemtap-runtime /usr/bin/staprun, /usr/bin/stapsh, /usr/bin/stapdyn +# systemtap-client /usr/bin/stap, samples, docs, tapset(bonus), req:-runtime +# systemtap-initscript /etc/init.d/systemtap, dracut module, req:systemtap +# systemtap-sdt-devel /usr/include/sys/sdt.h /usr/bin/dtrace +# systemtap-testsuite /usr/share/systemtap/testsuite*, req:systemtap, req:sdt-devel +# systemtap-runtime-java libHelperSDT.so, HelperSDT.jar, stapbm, req:-runtime +# systemtap-runtime-virthost /usr/bin/stapvirt, req:libvirt req:libxml2 +# systemtap-runtime-virtguest udev rules, init scripts/systemd service, req:-runtime +# systemtap-runtime-python2 HelperSDT python2 module, req:-runtime +# systemtap-runtime-python3 HelperSDT python3 module, req:-runtime +# +# Typical scenarios: +# +# stap-client: systemtap-client +# stap-server: systemtap-server +# local user: systemtap +# +# Unusual scenarios: +# +# intermediary stap-client for --remote: systemtap-client (-runtime unused) +# intermediary stap-server for --use-server: systemtap-server (-devel unused) + +Summary: Programmable system-wide instrumentation system +Group: Development/System +License: GPLv2+ +URL: http://sourceware.org/systemtap/ +Source: ftp://sourceware.org/pub/systemtap/releases/systemtap-%{version}.tar.gz + +# backported patch series for RHBZ1643997, will go away after 4.1 release: +Patch2: rhbz1643997.0001-testsuite-systemtap.bpf-diagnose-a-bug-in-print_form.patch +Patch3: rhbz1643997.0002-stapbpf-assembler-WIP-1-basic-parser-and-control-flo.patch +Patch4: rhbz1643997.0003-stapbpf-assembler-WIP-2-testcases-no-driver-so-far.patch +Patch5: rhbz1643997.0004-stapbpf-assembler-WIP-3-additional-assembly-test-cas.patch +Patch6: rhbz1643997.0005-stapbpf-assembler-WIP-4-alloc-and-helper-call-operat.patch +Patch7: rhbz1643997.0006-stapbpf-assembler-WIP-5-basic-kernel_string-implemen.patch +Patch8: rhbz1643997.0007-stapbpf-assembler-WIP-6-other-call-functions-s-print.patch +Patch9: rhbz1643997.0008-stapbpf-assembler-WIP-7-fixed-kernel_string-tapset-a.patch +Patch10: rhbz1643997.0009-stapbpf-assembler-WIP-8-bpf-asm.exp-driver-and-more-.patch +Patch11: rhbz1643997.0010-bpf-translate.cxx-plug-an-exception-gap-in-is_numeri.patch +Patch12: rhbz1643997.0011-session.cxx-enable-caching-for-bpf-backend.patch +Patch13: rhbz1643997.0012-Adjust-the-BPF-translate-error-report-formatting-to-.patch +Patch14: rhbz1643997.0013-bpf-translate.cxx-fix-segfault-with-malformed-regist.patch +Patch15: rhbz1643997.0014-bpf-asm.exp-bugfix-bad_output-does-occur.patch +Patch16: rhbz1643997.0015-tapset-bpf-conversions.stp-bugfix-helper-name-in-ker.patch +Patch17: rhbz1643997.0016-tapset-bpf-context.stp-add-execname-triage-other-fun.patch +Patch18: rhbz1643997.0017-PR23849-temporarily-disable-stapbpf-script-caching.patch +Patch19: rhbz1643997.0018-tapset-bpf-task.stp-rudiment-of-task-tapset.patch +Patch20: rhbz1643997.0019-PR23829-fallback-defines-__BPF_FUNC_MAPPER-and-BPF_J.patch +Patch21: rhbz1643997.0020-PR23860-partial-fix-fix-BPF_NEG-opcode-generation.patch +Patch22: rhbz1643997.0021-bpf-behind-the-scenes-useful-DEBUG_CODEGEN-diagnosti.patch +Patch23: rhbz1643997.0022-standardize-ktime_get_ns-across-lkm-bpf-runtimes.patch +Patch24: rhbz1643997.0023-pr23860-verifier-workaround-be-sure-to-delete-all-mo.patch +Patch25: rhbz1643997.0024-PR23860-bugfix-incorrect-comparison-direction-in-str.patch +Patch26: rhbz1643997.0025-PR23860-additional-stack-protection-for-strings.patch +Patch27: rhbz1643997.0026-PR23860-additional-ugly-stack-clobber-protection-for.patch +Patch28: rhbz1643997.0027-PR23860-reduce-stack-pressure-from-format-strings.patch +Patch29: rhbz1643997.0028-testcase-for-PR23875.patch +Patch30: rhbz1643997.0029-PR23860-bpf_interpret-NEG-should-not-fall-through-to.patch +Patch31: rhbz1643997.0030-PR23875-bpf.exp-fail-testcase-on-stack-smashing.patch +Patch32: rhbz1643997.0031-PR23875-another-testcase-that-loops-indefinitely.patch +Patch33: rhbz1643997.0032-PR23875-bpf_unparser-visit_foreach_loop-temporarily-.patch +Patch34: rhbz1656795.patch +Patch35: rhbz1657186.patch +Patch36: rhbz1657857.patch +Patch37: rhbz1657909.patch + +# Build* +BuildRequires: gcc-c++ +BuildRequires: cpio +BuildRequires: gettext-devel +BuildRequires: pkgconfig(nss) +BuildRequires: pkgconfig(avahi-client) +%if %{with_dyninst} +BuildRequires: dyninst-devel >= 8.0 +BuildRequires: pkgconfig(libselinux) +%endif +%if %{with_sqlite} +BuildRequires: sqlite-devel > 3.7 +%endif +%if %{with_monitor} +BuildRequires: pkgconfig(json-c) +BuildRequires: pkgconfig(ncurses) +%endif +%if %{with_systemd} +BuildRequires: systemd +%endif +# Needed for libstd++ < 4.0, without +%if %{with_boost} +BuildRequires: boost-devel +%endif +%if %{with_crash} +BuildRequires: crash-devel zlib-devel +%endif +%if %{with_rpm} +BuildRequires: rpm-devel +%endif +%if %{with_bundled_elfutils} +Source1: elfutils-%{elfutils_version}.tar.gz +Patch1: elfutils-portability.patch +BuildRequires: m4 +%global setup_elfutils -a1 +%else +BuildRequires: elfutils-devel >= %{elfutils_version} +%endif +%if %{with_docs} +BuildRequires: /usr/bin/latex /usr/bin/dvips /usr/bin/ps2pdf +%if 0%{?fedora} >= 18 || 0%{?rhel} >= 7 +BuildRequires: tex(fullpage.sty) tex(fancybox.sty) tex(bchr7t.tfm) tex(graphicx.sty) +%endif +# For the html.sty mentioned in the .tex files, even though latex2html is +# not run during the build, only during manual scripts/update-docs runs: +BuildRequires: latex2html +%if %{with_htmldocs} +# On F10, xmlto's pdf support was broken off into a sub-package, +# called 'xmlto-tex'. To avoid a specific F10 BuildReq, we'll do a +# file-based buildreq on '/usr/share/xmlto/format/fo/pdf'. +BuildRequires: xmlto /usr/share/xmlto/format/fo/pdf +%endif +%endif +%if %{with_emacsvim} +BuildRequires: emacs +%endif +%if %{with_java} +BuildRequires: jpackage-utils java-devel +%endif +%if %{with_virthost} +# BuildRequires: libvirt-devel >= 1.0.2 +BuildRequires: pkgconfig(libvirt) +BuildRequires: pkgconfig(libxml-2.0) +%endif +BuildRequires: readline-devel +%if 0%{?rhel} <= 5 +BuildRequires: pkgconfig(ncurses) +%endif +%if %{with_python2_probes} +BuildRequires: python2-devel +%if 0%{?fedora} >= 1 +BuildRequires: python2-setuptools +%else +BuildRequires: python-setuptools +%endif +%endif +%if %{with_python3_probes} +BuildRequires: python3-devel +BuildRequires: python3-setuptools +%endif + +%if %{with_httpd} +BuildRequires: libmicrohttpd-devel +BuildRequires: libuuid-devel +%endif + +# Install requirements +Requires: systemtap-client = %{version}-%{release} +Requires: systemtap-devel = %{version}-%{release} + +%description +SystemTap is an instrumentation system for systems running Linux. +Developers can write instrumentation scripts to collect data on +the operation of the system. The base systemtap package contains/requires +the components needed to locally develop and execute systemtap scripts. + +# ------------------------------------------------------------------------ + +%package server +Summary: Instrumentation System Server +Group: Development/System +License: GPLv2+ +URL: http://sourceware.org/systemtap/ +Requires: systemtap-devel = %{version}-%{release} +Conflicts: systemtap-devel < %{version}-%{release} +Conflicts: systemtap-runtime < %{version}-%{release} +Conflicts: systemtap-client < %{version}-%{release} +Requires: nss coreutils +Requires: zip unzip +Requires(pre): shadow-utils +Requires(post): chkconfig +Requires(preun): chkconfig +BuildRequires: nss-devel avahi-devel +%if %{with_openssl} +Requires: openssl +%endif +%if %{with_systemd} +Requires: systemd +%else +Requires(preun): initscripts +Requires(postun): initscripts +%endif + +%description server +This is the remote script compilation server component of systemtap. +It announces itself to nearby clients with avahi (if available), and +compiles systemtap scripts to kernel objects on their demand. + + +%package devel +Summary: Programmable system-wide instrumentation system - development headers, tools +Group: Development/System +License: GPLv2+ +URL: http://sourceware.org/systemtap/ + +%if 0%{?rhel} >= 8 || 0%{?fedora} >= 20 +Recommends: kernel-debug-devel +Recommends: kernel-devel +%else +Requires: kernel-devel-uname-r +%endif + +Requires: gcc make +Conflicts: systemtap-client < %{version}-%{release} +Conflicts: systemtap-server < %{version}-%{release} +Conflicts: systemtap-runtime < %{version}-%{release} +# Suggest: kernel-debuginfo + +%description devel +This package contains the components needed to compile a systemtap +script from source form into executable (.ko) forms. It may be +installed on a self-contained developer workstation (along with the +systemtap-client and systemtap-runtime packages), or on a dedicated +remote server (alongside the systemtap-server package). It includes +a copy of the standard tapset library and the runtime library C files. + + +%package runtime +Summary: Programmable system-wide instrumentation system - runtime +Group: Development/System +License: GPLv2+ +URL: http://sourceware.org/systemtap/ +Requires(pre): shadow-utils +Conflicts: systemtap-devel < %{version}-%{release} +Conflicts: systemtap-server < %{version}-%{release} +Conflicts: systemtap-client < %{version}-%{release} + +%description runtime +SystemTap runtime contains the components needed to execute +a systemtap script that was already compiled into a module +using a local or remote systemtap-devel installation. + + +%package client +Summary: Programmable system-wide instrumentation system - client +Group: Development/System +License: GPLv2+ +URL: http://sourceware.org/systemtap/ +Requires: zip unzip +Requires: systemtap-runtime = %{version}-%{release} +Requires: coreutils grep sed unzip zip +Requires: openssh-clients +Conflicts: systemtap-devel < %{version}-%{release} +Conflicts: systemtap-server < %{version}-%{release} +Conflicts: systemtap-runtime < %{version}-%{release} +%if %{with_mokutil} +Requires: mokutil +%endif + +%description client +This package contains/requires the components needed to develop +systemtap scripts, and compile them using a local systemtap-devel +or a remote systemtap-server installation, then run them using a +local or remote systemtap-runtime. It includes script samples and +documentation, and a copy of the tapset library for reference. + + +%package initscript +Summary: Systemtap Initscripts +Group: Development/System +License: GPLv2+ +URL: http://sourceware.org/systemtap/ +Requires: systemtap = %{version}-%{release} +Requires(post): chkconfig +Requires(preun): chkconfig +%if %{with_systemd} +Requires: systemd +%else +Requires(preun): initscripts +Requires(postun): initscripts +%endif + +%description initscript +This package includes a SysVinit script to launch selected systemtap +scripts at system startup, along with a dracut module for early +boot-time probing if supported. + + +%package sdt-devel +Summary: Static probe support tools +Group: Development/System +License: GPLv2+ and Public Domain +URL: http://sourceware.org/systemtap/ +%if %{with_pyparsing} +%if %{with_python3} +Requires: python3-pyparsing +%else +%if 0%{?rhel} >= 7 +Requires: pyparsing +%else +Requires: python2-pyparsing +%endif +%endif +%endif + +%description sdt-devel +This package includes the header file used for static +instrumentation compiled into userspace programs and libraries, along +with the optional dtrace-compatibility preprocessor to process related +.d files into tracing-macro-laden .h headers. + + +%package testsuite +Summary: Instrumentation System Testsuite +Group: Development/System +License: GPLv2+ +URL: http://sourceware.org/systemtap/ +Requires: systemtap = %{version}-%{release} +Requires: systemtap-sdt-devel = %{version}-%{release} +Requires: systemtap-server = %{version}-%{release} +Requires: dejagnu which elfutils grep nc +Requires: gcc gcc-c++ make glibc-devel +# testsuite/systemtap.base/ptrace.exp needs strace +Requires: strace +# testsuite/systemtap.base/ipaddr.exp needs nc. Unfortunately, the rpm +# that provides nc has changed over time (from 'nc' to +# 'nmap-ncat'). So, we'll do a file-based require. +Requires: /usr/bin/nc +%ifnarch ia64 ppc64le aarch64 +%if 0%{?fedora} >= 21 || 0%{?rhel} >= 8 +# no prelink +%else +Requires: prelink +%endif +%endif +# testsuite/systemtap.server/client.exp needs avahi +Requires: avahi +%if %{with_crash} +# testsuite/systemtap.base/crash.exp needs crash +Requires: crash +%endif +%if %{with_java} +Requires: systemtap-runtime-java = %{version}-%{release} +%endif +%if %{with_python2_probes} +Requires: systemtap-runtime-python2 = %{version}-%{release} +%endif +%if %{with_python3_probes} +Requires: systemtap-runtime-python3 = %{version}-%{release} +%endif +%ifarch x86_64 +Requires: /usr/lib/libc.so +# ... and /usr/lib/libgcc_s.so.* +# ... and /usr/lib/libstdc++.so.* +%endif +%if 0%{?fedora} >= 18 +Requires: stress +%endif +# The following "meta" files for the systemtap examples run "perf": +# testsuite/systemtap.examples/hw_watch_addr.meta +# testsuite/systemtap.examples/memory/hw_watch_sym.meta +Requires: perf + +%description testsuite +This package includes the dejagnu-based systemtap stress self-testing +suite. This may be used by system administrators to thoroughly check +systemtap on the current system. + + +%if %{with_java} +%package runtime-java +Summary: Systemtap Java Runtime Support +Group: Development/System +License: GPLv2+ +URL: http://sourceware.org/systemtap/ +Requires: systemtap-runtime = %{version}-%{release} +Requires: byteman > 2.0 +Requires: iproute + +%description runtime-java +This package includes support files needed to run systemtap scripts +that probe Java processes running on the OpenJDK runtimes using Byteman. +%endif + +%if %{with_python2_probes} +%package runtime-python2 +Summary: Systemtap Python 2 Runtime Support +Group: Development/System +License: GPLv2+ +URL: http://sourceware.org/systemtap/ +Requires: systemtap-runtime = %{version}-%{release} + +%description runtime-python2 +This package includes support files needed to run systemtap scripts +that probe python 2 processes. +%endif + +%if %{with_python3_probes} +%package runtime-python3 +Summary: Systemtap Python 3 Runtime Support +Group: Development/System +License: GPLv2+ +URL: http://sourceware.org/systemtap/ +Requires: systemtap-runtime = %{version}-%{release} + +%if ! (%{with_python2_probes}) +# Provide an clean upgrade path when the python2 package is removed +Obsoletes: %{name}-runtime-python2 < %{version}-%{release} +%endif + +%description runtime-python3 +This package includes support files needed to run systemtap scripts +that probe python 3 processes. +%endif + +%if %{with_python3} +%package exporter +Summary: Systemtap-prometheus interoperation mechanism +Group: Development/System +License: GPLv2+ +URL: http://sourceware.org/systemtap/ +Requires: systemtap-runtime = %{version}-%{release} + +%description exporter +This package includes files for a systemd service that manages +systemtap sessions and relays prometheus metrics from the sessions +to remote requesters on demand. +%endif + +%if %{with_virthost} +%package runtime-virthost +Summary: Systemtap Cross-VM Instrumentation - host +Group: Development/System +License: GPLv2+ +URL: http://sourceware.org/systemtap/ +Requires: libvirt >= 1.0.2 +Requires: libxml2 + +%description runtime-virthost +This package includes the components required to run systemtap scripts +inside a libvirt-managed domain from the host without using a network +connection. +%endif + +%if %{with_virtguest} +%package runtime-virtguest +Summary: Systemtap Cross-VM Instrumentation - guest +Group: Development/System +License: GPLv2+ +URL: http://sourceware.org/systemtap/ +Requires: systemtap-runtime = %{version}-%{release} +%if %{with_systemd} +Requires(post): findutils coreutils +Requires(preun): grep coreutils +Requires(postun): grep coreutils +%else +Requires(post): chkconfig initscripts +Requires(preun): chkconfig initscripts +Requires(postun): initscripts +%endif + +%description runtime-virtguest +This package installs the services necessary on a virtual machine for a +systemtap-runtime-virthost machine to execute systemtap scripts. +%endif + +# ------------------------------------------------------------------------ + +%prep + +%setup -q %{?setup_elfutils} + +%if %{with_bundled_elfutils} +cd elfutils-%{elfutils_version} +%patch1 -p1 +sleep 1 +find . \( -name Makefile.in -o -name aclocal.m4 \) -print | xargs touch +sleep 1 +find . \( -name configure -o -name config.h.in \) -print | xargs touch +cd .. +%endif + +# backported patch series for RHBZ1643997, will go away after 4.1 release: +%patch2 -p1 +%patch3 -p1 +%patch4 -p1 +%patch5 -p1 +%patch6 -p1 +%patch7 -p1 +%patch8 -p1 +%patch9 -p1 +%patch10 -p1 +%patch11 -p1 +%patch12 -p1 +%patch13 -p1 +%patch14 -p1 +%patch15 -p1 +%patch16 -p1 +%patch17 -p1 +%patch18 -p1 +%patch19 -p1 +%patch20 -p1 +%patch21 -p1 +%patch22 -p1 +%patch23 -p1 +%patch24 -p1 +%patch25 -p1 +%patch26 -p1 +%patch27 -p1 +%patch28 -p1 +%patch29 -p1 +%patch30 -p1 +%patch31 -p1 +%patch32 -p1 +%patch33 -p1 +%patch34 -p1 +%patch35 -p1 +%patch36 -p1 +%patch37 -p1 + +%build + +%if %{with_bundled_elfutils} +# Build our own copy of elfutils. +%global elfutils_config --with-elfutils=elfutils-%{elfutils_version} + +# We have to prevent the standard dependency generation from identifying +# our private elfutils libraries in our provides and requires. +%global _use_internal_dependency_generator 0 +%global filter_eulibs() /bin/sh -c "%{1} | sed '/libelf/d;/libdw/d;/libebl/d'" +%global __find_provides %{filter_eulibs /usr/lib/rpm/find-provides} +%global __find_requires %{filter_eulibs /usr/lib/rpm/find-requires} + +# This will be needed for running stap when not installed, for the test suite. +%global elfutils_mflags LD_LIBRARY_PATH=`pwd`/lib-elfutils +%endif + +# Enable/disable the dyninst pure-userspace backend +%if %{with_dyninst} +%global dyninst_config --with-dyninst +%else +%global dyninst_config --without-dyninst +%endif + +# Enable/disable the sqlite coverage testing support +%if %{with_sqlite} +%global sqlite_config --enable-sqlite +%else +%global sqlite_config --disable-sqlite +%endif + +# Enable/disable the crash extension +%if %{with_crash} +%global crash_config --enable-crash +%else +%global crash_config --disable-crash +%endif + +# Enable/disable the code to find and suggest needed rpms +%if %{with_rpm} +%global rpm_config --with-rpm +%else +%global rpm_config --without-rpm +%endif + +%if %{with_docs} +%if %{with_htmldocs} +%global docs_config --enable-docs --enable-htmldocs +%else +%global docs_config --enable-docs --disable-htmldocs +%endif +%else +%global docs_config --enable-docs=prebuilt +%endif + +# Enable pie as configure defaults to disabling it +%if %{pie_supported} +%global pie_config --enable-pie +%else +%global pie_config --disable-pie +%endif + + +%if %{with_java} +%global java_config --with-java=%{_jvmdir}/java +%else +%global java_config --without-java +%endif + +%if %{with_python3} +%global python3_config --with-python3 +%else +%global python3_config --without-python3 +%endif +%if %{with_python2_probes} +%global python2_probes_config --with-python2-probes +%else +%global python2_probes_config --without-python2-probes +%endif +%if %{with_python3_probes} +%global python3_probes_config --with-python3-probes +%else +%global python3_probes_config --without-python3-probes +%endif + +%if %{with_virthost} +%global virt_config --enable-virt +%else +%global virt_config --disable-virt +%endif + +%if %{with_dracut} +%global dracut_config --with-dracutstap=%{dracutstap} --with-dracutbindir=%{dracutbindir} +%else +%global dracut_config %{nil} +%endif + +%if %{with_httpd} +%global httpd_config --enable-httpd +%else +%global httpd_config --disable-httpd +%endif + +%if %{with_bpf} +%global bpf_config --with-bpf +%else +%global bpf_config --without-bpf +%endif + +# We don't ship compileworthy python code, just oddball samples +%global py_auto_byte_compile 0 + +%configure %{?elfutils_config} %{dyninst_config} %{sqlite_config} %{crash_config} %{docs_config} %{pie_config} %{rpm_config} %{java_config} %{virt_config} %{dracut_config} %{python3_config} %{python2_probes_config} %{python3_probes_config} %{httpd_config} %{bpf_config} --disable-silent-rules --with-extra-version="rpm %{version}-%{release}" +make %{?_smp_mflags} + +%if %{with_emacsvim} +%{_emacs_bytecompile} emacs/systemtap-mode.el +%endif + +%install +rm -rf ${RPM_BUILD_ROOT} +make DESTDIR=$RPM_BUILD_ROOT install +%find_lang %{name} +for dir in $(ls -1d $RPM_BUILD_ROOT%{_mandir}/{??,??_??}) ; do + dir=$(echo $dir | sed -e "s|^$RPM_BUILD_ROOT||") + lang=$(basename $dir) + echo "%%lang($lang) $dir/man*/*" >> %{name}.lang +done + +ln -s %{_datadir}/systemtap/examples + +# Fix paths in the example scripts. +find $RPM_BUILD_ROOT%{_datadir}/systemtap/examples -type f -name '*.stp' -print0 | xargs -0 sed -i -r -e '1s@^#!.+stap@#!%{_bindir}/stap@' + +# To make rpmlint happy, remove any .gitignore files in the testsuite. +find testsuite -type f -name '.gitignore' -print0 | xargs -0 rm -f + +# Because "make install" may install staprun with whatever mode, the +# post-processing programs rpmbuild runs won't be able to read it. +# So, we change permissions so that they can read it. We'll set the +# permissions back to 04110 in the %files section below. +chmod 755 $RPM_BUILD_ROOT%{_bindir}/staprun + +#install the useful stap-prep script +install -c -m 755 stap-prep $RPM_BUILD_ROOT%{_bindir}/stap-prep + +# Copy over the testsuite +cp -rp testsuite $RPM_BUILD_ROOT%{_datadir}/systemtap + +# We want the manuals in the special doc dir, not the generic doc install dir. +# We build it in place and then move it away so it doesn't get installed +# twice. rpm can specify itself where the (versioned) docs go with the +# %doc directive. +mkdir docs.installed +mv $RPM_BUILD_ROOT%{_datadir}/doc/systemtap/*.pdf docs.installed/ +%if %{with_docs} +%if %{with_htmldocs} +mv $RPM_BUILD_ROOT%{_datadir}/doc/systemtap/tapsets docs.installed/ +mv $RPM_BUILD_ROOT%{_datadir}/doc/systemtap/SystemTap_Beginners_Guide docs.installed/ +%endif +%endif + +install -D -m 644 macros.systemtap $RPM_BUILD_ROOT%{_rpmmacrodir}/macros.systemtap + +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/stap-server +mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/lib/stap-server +mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/lib/stap-server/.systemtap +mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/log/stap-server +touch $RPM_BUILD_ROOT%{_localstatedir}/log/stap-server/log +mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/cache/systemtap +mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/run/systemtap +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d +install -m 644 initscript/logrotate.stap-server $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/stap-server + +# If using systemd systemtap.service file, retain the old init script in %{_libexecdir} as a helper. +%if %{with_systemd} +mkdir -p $RPM_BUILD_ROOT%{_unitdir} +touch $RPM_BUILD_ROOT%{_unitdir}/systemtap.service +install -m 644 initscript/systemtap.service $RPM_BUILD_ROOT%{_unitdir}/systemtap.service +mkdir -p $RPM_BUILD_ROOT%{_sbindir} +install -m 755 initscript/systemtap $RPM_BUILD_ROOT%{_sbindir}/systemtap-service +%else +mkdir -p $RPM_BUILD_ROOT%{initdir} +install -m 755 initscript/systemtap $RPM_BUILD_ROOT%{initdir} +mkdir -p $RPM_BUILD_ROOT%{_sbindir} +ln -sf %{initdir}/systemtap $RPM_BUILD_ROOT%{_sbindir}/systemtap-service +# TODO CHECK CORRECTNESS: symlink %{_sbindir}/systemtap-service to %{initdir}/systemtap +%endif + +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/systemtap +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/systemtap/conf.d +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/systemtap/script.d +install -m 644 initscript/config.systemtap $RPM_BUILD_ROOT%{_sysconfdir}/systemtap/config + +%if %{with_systemd} +mkdir -p $RPM_BUILD_ROOT%{_unitdir} +touch $RPM_BUILD_ROOT%{_unitdir}/stap-server.service +install -m 644 stap-server.service $RPM_BUILD_ROOT%{_unitdir}/stap-server.service +mkdir -p $RPM_BUILD_ROOT%{_tmpfilesdir} +install -m 644 stap-server.conf $RPM_BUILD_ROOT%{_tmpfilesdir}/stap-server.conf +%else +install -m 755 initscript/stap-server $RPM_BUILD_ROOT%{initdir} +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/stap-server/conf.d +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig +install -m 644 initscript/config.stap-server $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/stap-server +%endif + +%if %{with_emacsvim} +mkdir -p $RPM_BUILD_ROOT%{_emacs_sitelispdir} +install -p -m 644 emacs/systemtap-mode.el* $RPM_BUILD_ROOT%{_emacs_sitelispdir} +mkdir -p $RPM_BUILD_ROOT%{_emacs_sitestartdir} +install -p -m 644 emacs/systemtap-init.el $RPM_BUILD_ROOT%{_emacs_sitestartdir}/systemtap-init.el +for subdir in ftdetect ftplugin indent syntax +do + mkdir -p $RPM_BUILD_ROOT%{_datadir}/vim/vimfiles/$subdir + install -p -m 644 vim/$subdir/*.vim $RPM_BUILD_ROOT%{_datadir}/vim/vimfiles/$subdir +done +%endif + +%if %{with_virtguest} + mkdir -p $RPM_BUILD_ROOT%{udevrulesdir} + %if %{with_systemd} + install -p -m 644 staprun/guest/99-stapsh.rules $RPM_BUILD_ROOT%{udevrulesdir} + mkdir -p $RPM_BUILD_ROOT%{_unitdir} + install -p -m 644 staprun/guest/stapsh@.service $RPM_BUILD_ROOT%{_unitdir} + %else + install -p -m 644 staprun/guest/99-stapsh-init.rules $RPM_BUILD_ROOT%{udevrulesdir} + install -p -m 755 staprun/guest/stapshd $RPM_BUILD_ROOT%{initdir} + mkdir -p $RPM_BUILD_ROOT%{_libexecdir}/systemtap + install -p -m 755 staprun/guest/stapsh-daemon $RPM_BUILD_ROOT%{_libexecdir}/systemtap + mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/modules + # Technically, this is only needed for RHEL5, in which the MODULE_ALIAS is missing, but + # it does no harm in RHEL6 as well + install -p -m 755 staprun/guest/virtio_console.modules $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/modules + %endif +%endif + +%if %{with_dracut} + mkdir -p $RPM_BUILD_ROOT%{dracutstap} + install -p -m 755 initscript/99stap/module-setup.sh $RPM_BUILD_ROOT%{dracutstap} + install -p -m 755 initscript/99stap/install $RPM_BUILD_ROOT%{dracutstap} + install -p -m 755 initscript/99stap/check $RPM_BUILD_ROOT%{dracutstap} + install -p -m 755 initscript/99stap/start-staprun.sh $RPM_BUILD_ROOT%{dracutstap} + touch $RPM_BUILD_ROOT%{dracutstap}/params.conf +%endif + +%pre runtime +getent group stapusr >/dev/null || groupadd -g 156 -r stapusr 2>/dev/null || groupadd -r stapusr +getent group stapsys >/dev/null || groupadd -g 157 -r stapsys 2>/dev/null || groupadd -r stapsys +getent group stapdev >/dev/null || groupadd -g 158 -r stapdev 2>/dev/null || groupadd -r stapdev +exit 0 + +%pre server +getent group stap-server >/dev/null || groupadd -g 155 -r stap-server 2>/dev/null || groupadd -r stap-server +getent passwd stap-server >/dev/null || \ + useradd -c "Systemtap Compile Server" -u 155 -g stap-server -d %{_localstatedir}/lib/stap-server -r -s /sbin/nologin stap-server 2>/dev/null || \ + useradd -c "Systemtap Compile Server" -g stap-server -d %{_localstatedir}/lib/stap-server -r -s /sbin/nologin stap-server + +%pre testsuite +getent passwd stapusr >/dev/null || \ + useradd -c "Systemtap 'stapusr' User" -g stapusr -r -s /sbin/nologin stapusr +getent passwd stapsys >/dev/null || \ + useradd -c "Systemtap 'stapsys' User" -g stapsys -G stapusr -r -s /sbin/nologin stapsys +getent passwd stapdev >/dev/null || \ + useradd -c "Systemtap 'stapdev' User" -g stapdev -G stapusr -r -s /sbin/nologin stapdev +exit 0 + +%post server + +# We have some duplication between the %files listings for the +# ~stap-server directories and the explicit mkdir/chown/chmod bits +# here. Part of the reason may be that a preexisting stap-server +# account may well be placed somewhere other than +# %{_localstatedir}/lib/stap-server, but we'd like their permissions +# set similarly. + +test -e ~stap-server && chmod 750 ~stap-server + +if [ ! -f ~stap-server/.systemtap/rc ]; then + mkdir -p ~stap-server/.systemtap + chown stap-server:stap-server ~stap-server/.systemtap + # PR16276: guess at a reasonable number for a default --rlimit-nproc + numcpu=`/usr/bin/getconf _NPROCESSORS_ONLN` + if [ -z "$numcpu" -o "$numcpu" -lt 1 ]; then numcpu=1; fi + nproc=`expr $numcpu \* 30` + echo "--rlimit-as=614400000 --rlimit-cpu=60 --rlimit-nproc=$nproc --rlimit-stack=1024000 --rlimit-fsize=51200000" > ~stap-server/.systemtap/rc + chown stap-server:stap-server ~stap-server/.systemtap/rc +fi + +test -e %{_localstatedir}/log/stap-server/log || { + touch %{_localstatedir}/log/stap-server/log + chmod 644 %{_localstatedir}/log/stap-server/log + chown stap-server:stap-server %{_localstatedir}/log/stap-server/log +} +# Prepare the service +%if %{with_systemd} + # Note, Fedora policy doesn't allow network services enabled by default + # /bin/systemctl enable stap-server.service >/dev/null 2>&1 || : + /bin/systemd-tmpfiles --create %{_tmpfilesdir}/stap-server.conf >/dev/null 2>&1 || : +%else + /sbin/chkconfig --add stap-server +%endif +exit 0 + +%triggerin client -- systemtap-server +if test -e ~stap-server/.systemtap/ssl/server/stap.cert; then + # echo Authorizing ssl-peer/trusted-signer certificate for local systemtap-server + %{_libexecdir}/systemtap/stap-authorize-cert ~stap-server/.systemtap/ssl/server/stap.cert %{_sysconfdir}/systemtap/ssl/client >/dev/null + %{_libexecdir}/systemtap/stap-authorize-cert ~stap-server/.systemtap/ssl/server/stap.cert %{_sysconfdir}/systemtap/staprun >/dev/null +fi +exit 0 +# XXX: corresponding %triggerun? + +%preun server +# Check that this is the actual deinstallation of the package, as opposed to +# just removing the old package on upgrade. +if [ $1 = 0 ] ; then + %if %{with_systemd} + /bin/systemctl --no-reload disable stap-server.service >/dev/null 2>&1 || : + /bin/systemctl stop stap-server.service >/dev/null 2>&1 || : + %else + /sbin/service stap-server stop >/dev/null 2>&1 + /sbin/chkconfig --del stap-server + %endif +fi +exit 0 + +%postun server +# Check whether this is an upgrade of the package. +# If so, restart the service if it's running +if [ "$1" -ge "1" ] ; then + %if %{with_systemd} + /bin/systemctl condrestart stap-server.service >/dev/null 2>&1 || : + %else + /sbin/service stap-server condrestart >/dev/null 2>&1 || : + %endif +fi +exit 0 + +%post initscript +%if %{with_systemd} + /bin/systemctl enable systemtap.service >/dev/null 2>&1 || : +%else + /sbin/chkconfig --add systemtap +%endif +exit 0 + +%preun initscript +# Check that this is the actual deinstallation of the package, as opposed to +# just removing the old package on upgrade. +if [ $1 = 0 ] ; then + %if %{with_systemd} + /bin/systemctl --no-reload disable systemtap.service >/dev/null 2>&1 || : + /bin/systemctl stop systemtap.service >/dev/null 2>&1 || : + %else + /sbin/service systemtap stop >/dev/null 2>&1 + /sbin/chkconfig --del systemtap + %endif +fi +exit 0 + +%postun initscript +# Check whether this is an upgrade of the package. +# If so, restart the service if it's running +if [ "$1" -ge "1" ] ; then + %if %{with_systemd} + /bin/systemctl condrestart systemtap.service >/dev/null 2>&1 || : + %else + /sbin/service systemtap condrestart >/dev/null 2>&1 || : + %endif +fi +exit 0 + +%post runtime-virtguest +%if %{with_systemd} + # Start services if there are ports present + if [ -d /dev/virtio-ports ]; then + (find /dev/virtio-ports -iname 'org.systemtap.stapsh.[0-9]*' -type l \ + | xargs -n 1 basename \ + | xargs -n 1 -I {} /bin/systemctl start stapsh@{}.service) >/dev/null 2>&1 || : + fi +%else + /sbin/chkconfig --add stapshd + /sbin/chkconfig stapshd on + /sbin/service stapshd start >/dev/null 2>&1 || : +%endif +exit 0 + +%preun runtime-virtguest +# Stop service if this is an uninstall rather than an upgrade +if [ $1 = 0 ]; then + %if %{with_systemd} + # We need to stop all stapsh services. Because they are instantiated from + # a template service file, we can't simply call disable. We need to find + # all the running ones and stop them all individually + for service in `/bin/systemctl --full | grep stapsh@ | cut -d ' ' -f 1`; do + /bin/systemctl stop $service >/dev/null 2>&1 || : + done + %else + /sbin/service stapshd stop >/dev/null 2>&1 + /sbin/chkconfig --del stapshd + %endif +fi +exit 0 + +%postun runtime-virtguest +# Restart service if this is an upgrade rather than an uninstall +if [ "$1" -ge "1" ]; then + %if %{with_systemd} + # We need to restart all stapsh services. Because they are instantiated from + # a template service file, we can't simply call restart. We need to find + # all the running ones and restart them all individually + for service in `/bin/systemctl --full | grep stapsh@ | cut -d ' ' -f 1`; do + /bin/systemctl condrestart $service >/dev/null 2>&1 || : + done + %else + /sbin/service stapshd condrestart >/dev/null 2>&1 + %endif +fi +exit 0 + +%if %{with_python3} +%if %{with_systemd} +%preun exporter +if [ $1 = 0 ] ; then + /bin/systemctl stop stap-exporter.service >/dev/null 2>&1 || : + /bin/systemctl disable stap-exporter.service >/dev/null 2>&1 || : +fi +exit 0 + +%postun exporter +# Restart service if this is an upgrade rather than an uninstall +if [ "$1" -ge "1" ]; then + /bin/systemctl condrestart stap-exporter >/dev/null 2>&1 || : +fi +exit 0 +%endif +%endif + +%post +# Remove any previously-built uprobes.ko materials +(make -C %{_datadir}/systemtap/runtime/uprobes clean) >/dev/null 2>&1 || true +(/sbin/rmmod uprobes) >/dev/null 2>&1 || true + +%preun +# Ditto +(make -C %{_datadir}/systemtap/runtime/uprobes clean) >/dev/null 2>&1 || true +(/sbin/rmmod uprobes) >/dev/null 2>&1 || true + +# ------------------------------------------------------------------------ + +%if %{with_java} + +%triggerin runtime-java -- java-1.8.0-openjdk, java-1.7.0-openjdk, java-1.6.0-openjdk +for f in %{_libexecdir}/systemtap/libHelperSDT_*.so; do + %ifarch %{ix86} + arch=i386 + %else + arch=`basename $f | cut -f2 -d_ | cut -f1 -d.` + %endif + for archdir in %{_jvmdir}/*openjdk*/jre/lib/${arch}; do + if [ -d ${archdir} ]; then + ln -sf %{_libexecdir}/systemtap/libHelperSDT_${arch}.so ${archdir}/libHelperSDT_${arch}.so + ln -sf %{_libexecdir}/systemtap/HelperSDT.jar ${archdir}/../ext/HelperSDT.jar + fi + done +done + +%triggerun runtime-java -- java-1.8.0-openjdk, java-1.7.0-openjdk, java-1.6.0-openjdk +for f in %{_libexecdir}/systemtap/libHelperSDT_*.so; do + %ifarch %{ix86} + arch=i386 + %else + arch=`basename $f | cut -f2 -d_ | cut -f1 -d.` + %endif + for archdir in %{_jvmdir}/*openjdk*/jre/lib/${arch}; do + rm -f ${archdir}/libHelperSDT_${arch}.so + rm -f ${archdir}/../ext/HelperSDT.jar + done +done + +%triggerpostun runtime-java -- java-1.8.0-openjdk, java-1.7.0-openjdk, java-1.6.0-openjdk +# Restore links for any JDKs remaining after a package removal: +for f in %{_libexecdir}/systemtap/libHelperSDT_*.so; do + %ifarch %{ix86} + arch=i386 + %else + arch=`basename $f | cut -f2 -d_ | cut -f1 -d.` + %endif + for archdir in %{_jvmdir}/*openjdk*/jre/lib/${arch}; do + if [ -d ${archdir} ]; then + ln -sf %{_libexecdir}/systemtap/libHelperSDT_${arch}.so ${archdir}/libHelperSDT_${arch}.so + ln -sf %{_libexecdir}/systemtap/HelperSDT.jar ${archdir}/../ext/HelperSDT.jar + fi + done +done + +# XXX: analogous support for other types of JRE/JDK?? + +%endif + +# ------------------------------------------------------------------------ + +%files +# The master "systemtap" rpm doesn't include any files. + +%files server -f systemtap.lang +%defattr(-,root,root) +%{_bindir}/stap-server +%dir %{_libexecdir}/systemtap +%{_libexecdir}/systemtap/stap-serverd +%{_libexecdir}/systemtap/stap-start-server +%{_libexecdir}/systemtap/stap-stop-server +%{_libexecdir}/systemtap/stap-gen-cert +%{_libexecdir}/systemtap/stap-sign-module +%{_libexecdir}/systemtap/stap-authorize-cert +%{_libexecdir}/systemtap/stap-env +%{_mandir}/man7/error* +%{_mandir}/man7/stappaths.7* +%{_mandir}/man7/warning* +%{_mandir}/man8/stap-server.8* +%if %{with_systemd} +%{_unitdir}/stap-server.service +%{_tmpfilesdir}/stap-server.conf +%else +%{initdir}/stap-server +%dir %{_sysconfdir}/stap-server/conf.d +%config(noreplace) %{_sysconfdir}/sysconfig/stap-server +%endif +%config(noreplace) %{_sysconfdir}/logrotate.d/stap-server +%dir %{_sysconfdir}/stap-server +%dir %attr(0750,stap-server,stap-server) %{_localstatedir}/lib/stap-server +%dir %attr(0700,stap-server,stap-server) %{_localstatedir}/lib/stap-server/.systemtap +%dir %attr(0755,stap-server,stap-server) %{_localstatedir}/log/stap-server +%ghost %config(noreplace) %attr(0644,stap-server,stap-server) %{_localstatedir}/log/stap-server/log +%ghost %attr(0755,stap-server,stap-server) %{_localstatedir}/run/stap-server +%doc README README.unprivileged AUTHORS NEWS +%{!?_licensedir:%global license %%doc} +%license COPYING + + +%files devel -f systemtap.lang +%{_bindir}/stap +%{_bindir}/stap-prep +%{_bindir}/stap-report +%dir %{_datadir}/systemtap +%{_datadir}/systemtap/runtime +%{_datadir}/systemtap/tapset +%{_mandir}/man1/stap.1* +%{_mandir}/man1/stap-prep.1* +%{_mandir}/man1/stap-report.1* +%{_mandir}/man7/error* +%{_mandir}/man7/stappaths.7* +%{_mandir}/man7/warning* +%doc README README.unprivileged AUTHORS NEWS +%{!?_licensedir:%global license %%doc} +%license COPYING +%if %{with_java} +%dir %{_libexecdir}/systemtap +%{_libexecdir}/systemtap/libHelperSDT_*.so +%endif +%if %{with_bundled_elfutils} +%dir %{_libdir}/systemtap +%{_libdir}/systemtap/lib*.so* +%endif +%if %{with_emacsvim} +%{_emacs_sitelispdir}/*.el* +%{_emacs_sitestartdir}/systemtap-init.el +%{_datadir}/vim/vimfiles/*/*.vim +%endif +# Notice that the stap-resolve-module-function.py file is used by +# *both* the python2 and python3 subrpms. Both subrpms use that same +# python script to help list python probes. +%if %{with_python3_probes} || %{with_python2_probes} +%{_libexecdir}/systemtap/python/stap-resolve-module-function.py +%exclude %{_libexecdir}/systemtap/python/stap-resolve-module-function.py? +%endif + + +%files runtime -f systemtap.lang +%defattr(-,root,root) +%attr(4110,root,stapusr) %{_bindir}/staprun +%{_bindir}/stapsh +%{_bindir}/stap-merge +%{_bindir}/stap-report +%if %{with_dyninst} +%{_bindir}/stapdyn +%endif +%if %{with_bpf} +%{_bindir}/stapbpf +%endif +%dir %{_libexecdir}/systemtap +%{_libexecdir}/systemtap/stapio +%{_libexecdir}/systemtap/stap-authorize-cert +%if %{with_crash} +%dir %{_libdir}/systemtap +%{_libdir}/systemtap/staplog.so* +%endif +%{_mandir}/man1/stap-report.1* +%{_mandir}/man7/error* +%{_mandir}/man7/stappaths.7* +%{_mandir}/man7/warning* +%{_mandir}/man8/stapsh.8* +%{_mandir}/man8/staprun.8* +%if %{with_dyninst} +%{_mandir}/man8/stapdyn.8* +%endif +%if %{with_bpf} +%{_mandir}/man8/stapbpf.8* +%endif +%doc README README.security AUTHORS NEWS +%{!?_licensedir:%global license %%doc} +%license COPYING + + +%files client -f systemtap.lang +%defattr(-,root,root) +%doc README README.unprivileged AUTHORS NEWS +%{_datadir}/systemtap/examples +%{!?_licensedir:%global license %%doc} +%license COPYING +%doc docs.installed/*.pdf +%if %{with_docs} +%if %{with_htmldocs} +%doc docs.installed/tapsets/*.html +%doc docs.installed/SystemTap_Beginners_Guide +%endif +%endif +%{_bindir}/stap +%{_bindir}/stap-prep +%{_bindir}/stap-report +%{_mandir}/man1/stap.1* +%{_mandir}/man1/stap-prep.1* +%{_mandir}/man1/stap-merge.1* +%{_mandir}/man1/stap-report.1* +%{_mandir}/man1/stapref.1* +%{_mandir}/man3/* +%{_mandir}/man7/error* +%{_mandir}/man7/stappaths.7* +%{_mandir}/man7/warning* +%dir %{_datadir}/systemtap +%{_datadir}/systemtap/tapset + + + +%files initscript +%defattr(-,root,root) +%if %{with_systemd} +%{_unitdir}/systemtap.service +%{_sbindir}/systemtap-service +%else +%{initdir}/systemtap +%{_sbindir}/systemtap-service +%endif +%dir %{_sysconfdir}/systemtap +%dir %{_sysconfdir}/systemtap/conf.d +%dir %{_sysconfdir}/systemtap/script.d +%config(noreplace) %{_sysconfdir}/systemtap/config +%dir %{_localstatedir}/cache/systemtap +%ghost %{_localstatedir}/run/systemtap +%{_mandir}/man8/systemtap-service.8* +%if %{with_dracut} + %dir %{dracutstap} + %{dracutstap}/* +%endif + + +%files sdt-devel +%defattr(-,root,root) +%{_bindir}/dtrace +%{_includedir}/sys/sdt.h +%{_includedir}/sys/sdt-config.h +%{_mandir}/man1/dtrace.1* +%{_rpmmacrodir}/macros.systemtap +%doc README AUTHORS NEWS +%{!?_licensedir:%global license %%doc} +%license COPYING + + +%files testsuite +%defattr(-,root,root) +%dir %{_datadir}/systemtap +%{_datadir}/systemtap/testsuite + + +%if %{with_java} +%files runtime-java +%dir %{_libexecdir}/systemtap +%{_libexecdir}/systemtap/libHelperSDT_*.so +%{_libexecdir}/systemtap/HelperSDT.jar +%{_libexecdir}/systemtap/stapbm +%endif + +%if %{with_python2_probes} +%files runtime-python2 +%{python_sitearch}/HelperSDT +%{python_sitearch}/HelperSDT-*.egg-info +%endif +%if %{with_python3_probes} +%files runtime-python3 +%{python3_sitearch}/HelperSDT +%{python3_sitearch}/HelperSDT-*.egg-info +%endif + +%if %{with_virthost} +%files runtime-virthost +%{_mandir}/man1/stapvirt.1* +%{_bindir}/stapvirt +%endif + +%if %{with_virtguest} +%files runtime-virtguest +%if %{with_systemd} + %{udevrulesdir}/99-stapsh.rules + %{_unitdir}/stapsh@.service +%else + %{udevrulesdir}/99-stapsh-init.rules + %dir %{_libexecdir}/systemtap + %{_libexecdir}/systemtap/stapsh-daemon + %{initdir}/stapshd + %{_sysconfdir}/sysconfig/modules/virtio_console.modules +%endif +%endif + +%if %{with_python3} +%files exporter +%{_sysconfdir}/stap-exporter +%{_sysconfdir}/sysconfig/stap-exporter +%{_unitdir}/stap-exporter.service +%{_mandir}/man8/stap-exporter.8* +%{_sbindir}/stap-exporter +%endif + +# ------------------------------------------------------------------------ + +# Future new-release entries should be of the form +# * DDD MMM DD YYYY YOURNAME - V-R +# - Upstream release, see wiki page below for detailed notes. +# http://sourceware.org/systemtap/wiki/SystemTapReleases + +# PRERELEASE +%changelog +* Mon Dec 17 2018 Frank Ch. Eigler - 4.0-7 +- rhbz1657909: vfs tapset fixes for $cred + +* Mon Dec 10 2018 William Cohen - 4.0-6 +- rhbz1657857: Please, backport periodic.stp from current upstream aacee6563. + +* Fri Dec 07 2018 Frank Ch. Eigler - 4.0-5 +- rhbz1657186: fix/port nfsd.proc4.[read,commit] tapset aliases + +* Thu Dec 06 2018 Frank Ch. Eigler - 4.0-4 +- rhbz1656795: support bdflush syscall + +* Tue Dec 04 2018 Frank Ch. Eigler - 4.0-3 +- rhbz1655631: change kernel-devel* deps to Recommends: + +* Wed Nov 14 2018 Serhei Makarov - 4.0-2 +- rhbz1643997: backported string tapset functionality and bugfixes + +* Sat Oct 13 2018 Frank Ch. Eigler - 4.0-1 +- Upstream release. + +* Thu Aug 30 2018 Frank Ch. Eigler - 4.0-0.20180830git +- Enable brp-mangle for python scripts (rhbz1619413). + +* Fri Aug 10 2018 Frank Ch. Eigler - 4.0-0.20180810git +- Automated weekly rawhide release +- Applied spec changes from upstream git + +* Mon May 14 2018 Frank Ch. Eigler - 3.3-0.20180508git9c6ac6cda49e.2 +- respin w/ fixed glibc + +* Tue May 08 2018 Frank Ch. Eigler - 3.3-0.20180508git9c6ac6cda49e +- Automated weekly rawhide release +- Applied spec changes from upstream git + +* Thu Apr 19 2018 Mark Wielaard - 3.2-11 +- Add rhbz1549063.patch (/bin/env -> /usr/bin/env) +- Add rhbz1566745.patch (4.15 kernel tapset updates) +- Enable accidentially disabled Suggests: kernel-devel again. + +* Wed Apr 18 2018 Mark Wielaard - 3.2-10 +- Add unwind-fallback.patch +- rhbz1566422.patch + +* Tue Apr 17 2018 Mark Wielaard - 3.2-9 +- Add unwind-fallback.patch. + +* Tue Mar 27 2018 Björn Esser - 3.2-8 +- Rebuilt for libjson-c.so.4 (json-c v0.13.1) on fc28 + +* Thu Feb 22 2018 Sergey Avseyev - 3.2-7 +- rhbz1546563 (backport fix for removed timers in kernel 4.15) + +* Tue Feb 13 2018 Stan Cox - 3.2-6 +- rebuilt + +* Fri Feb 09 2018 Igor Gnatenko - 3.2-5 +- Escape macros in %%changelog + +* Wed Feb 07 2018 Igor Gnatenko - 3.2-4 +- Fix very old Requires + +* Sun Dec 10 2017 Björn Esser - 3.2-3 +- Rebuilt for libjson-c.so.3 + +* Fri Oct 20 2017 Frank Ch. Eigler - 3.2-2 +- rhbz1504009 (dtrace -G -o /dev/null) + +* Wed Oct 18 2017 Frank Ch. Eigler - 3.2-1 +- Upstream release. + +* Fri Aug 11 2017 Igor Gnatenko - 3.2-0.20170516gitc67d8f274b21 +- Rebuilt after RPM update (№ 3) + +* Thu Aug 10 2017 Igor Gnatenko - 3.2-0.20170515gitc67d8f274b21 +- Rebuilt for RPM soname bump + +* Thu Aug 03 2017 Fedora Release Engineering - 3.2-0.20170514gitc67d8f274b21 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Thu Jul 27 2017 Fedora Release Engineering - 3.2-0.20170513gitc67d8f274b21 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Fri May 12 2017 Frank Ch. Eigler - 3.2-0.20170512gitc67d8f274b21 +- Automated weekly rawhide release +- Applied spec changes from upstream git + +* Mon Apr 10 2017 Frank Ch. Eigler - 3.2-0.20170410gitcbf2583808d6 +- Automated weekly rawhide release +- Applied spec changes from upstream git + +* Thu Apr 06 2017 Frank Ch. Eigler - 3.2-0.20170406git83d186dc7f5c +- Automated weekly rawhide release +- Applied spec changes from upstream git + +* Tue Mar 21 2017 Frank Ch. Eigler - 3.2-0.20170321git272146660f54 +- Automated weekly rawhide release +- Applied spec changes from upstream git + +* Fri Mar 10 2017 Stan Cox - 3.1-2 +- Rebuild for dyninst 9.3 + +* Fri Feb 17 2017 Frank Ch. Eigler - 3.1-1 +- Upstream release. + +* Sun Mar 27 2016 Frank Ch. Eigler - 3.0-1 +- Upstream release. + +* Thu Oct 08 2015 Frank Ch. Eigler - 2.9-1 +- Upstream release. + +* Wed Jun 17 2015 Abegail Jakop - 2.8-1 +- Upstream release. + +* Wed Feb 18 2015 Frank Ch. Eigler - 2.7-1 +- Upstream release. + +* Fri Sep 05 2014 Josh Stone - 2.6-1 +- Upstream release. + +* Mon Jul 07 2014 Josh Stone +- Flip with_dyninst to an %%ifarch whitelist. + +* Wed Apr 30 2014 Jonathan Lebon - 2.5-1 +- Upstream release. + +* Thu Feb 13 2014 Lukas Berk +- Add directory checks for runtime-java sym links + +* Mon Jan 06 2014 Jonathan Lebon +- Added dracut module to initscript package + +* Wed Nov 06 2013 Frank Ch. Eigler - 2.4-1 +- Upstream release. + +* Wed Oct 09 2013 Jonathan Lebon +- Added runtime-virthost and runtime-virtguest packages. + +* Thu Jul 25 2013 Frank Ch. Eigler - 2.3-1 +- Upstream release. + +* Thu May 16 2013 Frank Ch. Eigler - 2.2.1-1 +- Upstream release. + +* Tue May 14 2013 Frank Ch. Eigler - 2.2-1 +- Upstream release. + +* Wed Feb 13 2013 Serguei Makarov - 2.1-1 +- Upstream release. + +* Tue Oct 09 2012 Josh Stone - 2.0-1 +- Upstream release. + +* Fri Jul 13 2012 Peter Robinson +- Fix ifarch statement +- use file based requires for glibc-devel on x86_64 so that we work in koji + +* Sun Jun 17 2012 Frank Ch. Eigler - 1.8-1 +- Upstream release. + +* Wed Feb 01 2012 Frank Ch. Eigler - 1.7-1 +- Upstream release. + +* Fri Jan 13 2012 David Smith - 1.6-2 +- Fixed /bin/mktemp require. + +* Mon Jul 25 2011 Stan Cox - 1.6-1 +- Upstream release. + +* Mon May 23 2011 Stan Cox - 1.5-1 +- Upstream release. + +* Mon Jan 17 2011 Frank Ch. Eigler - 1.4-1 +- Upstream release. + +* Wed Jul 21 2010 Josh Stone - 1.3-1 +- Upstream release. + +* Mon Mar 22 2010 Frank Ch. Eigler - 1.2-1 +- Upstream release. + +* Mon Dec 21 2009 David Smith - 1.1-1 +- Upstream release. + +* Tue Sep 22 2009 Josh Stone - 1.0-1 +- Upstream release. + +* Tue Aug 4 2009 Josh Stone - 0.9.9-1 +- Upstream release. + +* Thu Jun 11 2009 Josh Stone - 0.9.8-1 +- Upstream release. + +* Thu Apr 23 2009 Josh Stone - 0.9.7-1 +- Upstream release. + +* Fri Mar 27 2009 Josh Stone - 0.9.5-1 +- Upstream release. + +* Wed Mar 18 2009 Will Cohen - 0.9-2 +- Add location of man pages. + +* Tue Feb 17 2009 Frank Ch. Eigler - 0.9-1 +- Upstream release. + +* Thu Nov 13 2008 Frank Ch. Eigler - 0.8-1 +- Upstream release. + +* Tue Jul 15 2008 Frank Ch. Eigler - 0.7-1 +- Upstream release. + +* Fri Feb 1 2008 Frank Ch. Eigler - 0.6.1-3 +- Add zlib-devel to buildreq; missing from crash-devel +- Process testsuite .stp files for #!stap->#!/usr/bin/stap + +* Fri Jan 18 2008 Frank Ch. Eigler - 0.6.1-1 +- Add crash-devel buildreq to build staplog.so crash(8) module. +- Many robustness & functionality improvements: + +* Wed Dec 5 2007 Will Cohen - 0.6-2 +- Correct Source to point to location contain code. + +* Thu Aug 9 2007 David Smith - 0.6-1 +- Bumped version, added libcap-devel BuildRequires. + +* Wed Jul 11 2007 Will Cohen - 0.5.14-2 +- Fix Requires and BuildRequires for sqlite. + +* Mon Jul 2 2007 Frank Ch. Eigler - 0.5.14-1 +- Many robustness improvements: 1117, 1134, 1305, 1307, 1570, 1806, + 2033, 2116, 2224, 2339, 2341, 2406, 2426, 2438, 2583, 3037, + 3261, 3282, 3331, 3428 3519, 3545, 3625, 3648, 3880, 3888, 3911, + 3952, 3965, 4066, 4071, 4075, 4078, 4081, 4096, 4119, 4122, 4127, + 4146, 4171, 4179, 4183, 4221, 4224, 4254, 4281, 4319, 4323, 4326, + 4329, 4332, 4337, 4415, 4432, 4444, 4445, 4458, 4467, 4470, 4471, + 4518, 4567, 4570, 4579, 4589, 4609, 4664 + +* Mon Mar 26 2007 Frank Ch. Eigler - 0.5.13-1 +- An emergency / preliminary refresh, mainly for compatibility + with 2.6.21-pre kernels. + +* Mon Jan 1 2007 Frank Ch. Eigler - 0.5.12-1 +- Many changes, see NEWS file. + +* Tue Sep 26 2006 David Smith - 0.5.10-1 +- Added 'systemtap-runtime' subpackage. + +* Wed Jul 19 2006 Roland McGrath - 0.5.9-1 +- PRs 2669, 2913 + +* Fri Jun 16 2006 Roland McGrath - 0.5.8-1 +- PRs 2627, 2520, 2228, 2645 + +* Fri May 5 2006 Frank Ch. Eigler - 0.5.7-1 +- PRs 2511 2453 2307 1813 1944 2497 2538 2476 2568 1341 2058 2220 2437 + 1326 2014 2599 2427 2438 2465 1930 2149 2610 2293 2634 2506 2433 + +* Tue Apr 4 2006 Roland McGrath - 0.5.5-1 +- Many changes, affected PRs include: 2068, 2293, 1989, 2334, + 1304, 2390, 2425, 953. + +* Wed Feb 1 2006 Frank Ch. Eigler - 0.5.4-1 +- PRs 1916, 2205, 2142, 2060, 1379 + +* Mon Jan 16 2006 Roland McGrath - 0.5.3-1 +- Many changes, affected PRs include: 2056, 1144, 1379, 2057, + 2060, 1972, 2140, 2148 + +* Mon Dec 19 2005 Roland McGrath - 0.5.2-1 +- Fixed build with gcc 4.1, various tapset changes. + +* Wed Dec 7 2005 Roland McGrath - 0.5.1-1 +- elfutils update, build changes + +* Fri Dec 02 2005 Frank Ch. Eigler - 0.5-1 +- Many fixes and improvements: 1425, 1536, 1505, 1380, 1329, 1828, 1271, + 1339, 1340, 1345, 1837, 1917, 1903, 1336, 1868, 1594, 1564, 1276, 1295 + +* Mon Oct 31 2005 Roland McGrath - 0.4.2-1 +- Many fixes and improvements: PRs 1344, 1260, 1330, 1295, 1311, 1368, + 1182, 1131, 1332, 1366, 1456, 1271, 1338, 1482, 1477, 1194. + +* Wed Sep 14 2005 Roland McGrath - 0.4.1-1 +- Many fixes and improvements since 0.2.2; relevant PRs include: + 1122, 1134, 1155, 1172, 1174, 1175, 1180, 1186, 1187, 1191, 1193, 1195, + 1197, 1205, 1206, 1209, 1213, 1244, 1257, 1258, 1260, 1265, 1268, 1270, + 1289, 1292, 1306, 1335, 1257 + +* Wed Sep 7 2005 Frank Ch. Eigler +- Bump version. + +* Tue Aug 16 2005 Frank Ch. Eigler +- Bump version. + +* Wed Aug 3 2005 Martin Hunt - 0.2.2-1 +- Add directory /var/cache/systemtap +- Add stp_check to /usr/libexec/systemtap + +* Wed Aug 3 2005 Roland McGrath - 0.2.1-1 +- New version 0.2.1, various fixes. + +* Fri Jul 29 2005 Roland McGrath - 0.2-1 +- New version 0.2, requires elfutils 0.111 + +* Mon Jul 25 2005 Roland McGrath +- Clean up spec file, build bundled elfutils. + +* Thu Jul 21 2005 Martin Hunt +- Set Version to use version from autoconf. +- Fix up some of the path names. +- Add Requires and BuildRequires. + +* Tue Jul 19 2005 Will Cohen +- Initial creation of RPM.