|
 |
583230 |
From f2339483ab00eff7a1a45c33b968891a63668c98 Mon Sep 17 00:00:00 2001
|
|
 |
583230 |
From: Serhei Makarov <smakarov@redhat.com>
|
|
 |
583230 |
Date: Tue, 16 Oct 2018 18:16:48 -0400
|
|
 |
583230 |
Subject: [PATCH 05/32] stapbpf assembler WIP #4 :: alloc and (helper) call
|
|
 |
583230 |
operations
|
|
 |
583230 |
|
|
 |
583230 |
---
|
|
 |
583230 |
bpf-base.cxx | 46 ++++++----
|
|
 |
583230 |
bpf-internal.h | 8 +-
|
|
 |
583230 |
bpf-translate.cxx | 259 ++++++++++++++++++++++++++++++++++++++++-------------
|
|
 |
583230 |
tapset/logging.stp | 2 +
|
|
 |
583230 |
4 files changed, 237 insertions(+), 78 deletions(-)
|
|
 |
583230 |
|
|
 |
583230 |
diff --git a/bpf-base.cxx b/bpf-base.cxx
|
|
 |
583230 |
index c3e36efa1..277927b72 100644
|
|
 |
583230 |
--- a/bpf-base.cxx
|
|
 |
583230 |
+++ b/bpf-base.cxx
|
|
 |
583230 |
@@ -135,31 +135,43 @@ is_commutative(opcode code)
|
|
 |
583230 |
}
|
|
 |
583230 |
}
|
|
 |
583230 |
|
|
 |
583230 |
+/* Various functions for eBPF helper lookup: */
|
|
 |
583230 |
+
|
|
 |
583230 |
+std::map<unsigned, const char *> bpf_func_name_map;
|
|
 |
583230 |
+std::map<std::string, bpf_func_id> bpf_func_id_map;
|
|
 |
583230 |
+
|
|
 |
583230 |
+void
|
|
 |
583230 |
+init_bpf_helper_tables () // TODO call before script translation
|
|
 |
583230 |
+{
|
|
 |
583230 |
+#define __BPF_SET_FUNC_NAME(x) bpf_func_name_map[BPF_FUNC_ ## x] = #x
|
|
 |
583230 |
+#define __BPF_SET_FUNC_ID(x) bpf_func_id_map[#x] = BPF_FUNC_ ## x
|
|
 |
583230 |
+ __BPF_FUNC_MAPPER(__BPF_SET_FUNC_NAME)
|
|
 |
583230 |
+ __STAPBPF_FUNC_MAPPER(__BPF_SET_FUNC_NAME)
|
|
 |
583230 |
+ __BPF_FUNC_MAPPER(__BPF_SET_FUNC_ID)
|
|
 |
583230 |
+ __STAPBPF_FUNC_MAPPER(__BPF_SET_FUNC_ID)
|
|
 |
583230 |
+ (void)0;
|
|
 |
583230 |
+}
|
|
 |
583230 |
+
|
|
 |
583230 |
const char *
|
|
 |
583230 |
bpf_function_name (unsigned id)
|
|
 |
583230 |
{
|
|
 |
583230 |
- switch (id)
|
|
 |
583230 |
- {
|
|
 |
583230 |
- case BPF_FUNC_map_lookup_elem: return "map_lookup_elem";
|
|
 |
583230 |
- case BPF_FUNC_map_update_elem: return "map_update_elem";
|
|
 |
583230 |
- case BPF_FUNC_map_delete_elem: return "map_delete_elem";
|
|
 |
583230 |
- case BPF_FUNC_probe_read: return "probe_read";
|
|
 |
583230 |
- case BPF_FUNC_ktime_get_ns: return "ktime_get_ns";
|
|
 |
583230 |
- case BPF_FUNC_trace_printk: return "trace_printk";
|
|
 |
583230 |
- case BPF_FUNC_get_prandom_u32: return "get_prandom_u32";
|
|
 |
583230 |
- case BPF_FUNC_get_smp_processor_id: return "get_smp_processor_id";
|
|
 |
583230 |
- case BPF_FUNC_get_current_pid_tgid: return "get_current_pid_tgid";
|
|
 |
583230 |
- case BPF_FUNC_get_current_uid_gid: return "get_current_uid_gid";
|
|
 |
583230 |
- case BPF_FUNC_get_current_comm: return "get_current_comm";
|
|
 |
583230 |
- case BPF_FUNC_perf_event_read: return "perf_event_read";
|
|
 |
583230 |
- case BPF_FUNC_perf_event_output: return "perf_event_output";
|
|
 |
583230 |
- default: return NULL;
|
|
 |
583230 |
- }
|
|
 |
583230 |
+ if (bpf_func_name_map.count(id) != 0)
|
|
 |
583230 |
+ return bpf_func_name_map[id];
|
|
 |
583230 |
+ return NULL;
|
|
 |
583230 |
+}
|
|
 |
583230 |
+
|
|
 |
583230 |
+bpf_func_id
|
|
 |
583230 |
+bpf_function_id (const std::string& name)
|
|
 |
583230 |
+{
|
|
 |
583230 |
+ if (bpf_func_id_map.count(name) != 0)
|
|
 |
583230 |
+ return bpf_func_id_map[name];
|
|
 |
583230 |
+ return __BPF_FUNC_MAX_ID;
|
|
 |
583230 |
}
|
|
 |
583230 |
|
|
 |
583230 |
unsigned
|
|
 |
583230 |
bpf_function_nargs (unsigned id)
|
|
 |
583230 |
{
|
|
 |
583230 |
+ // ??? generalize to all bpf functions
|
|
 |
583230 |
switch (id)
|
|
 |
583230 |
{
|
|
 |
583230 |
case BPF_FUNC_map_lookup_elem: return 2;
|
|
 |
583230 |
diff --git a/bpf-internal.h b/bpf-internal.h
|
|
 |
583230 |
index 719446db8..61514db9f 100644
|
|
 |
583230 |
--- a/bpf-internal.h
|
|
 |
583230 |
+++ b/bpf-internal.h
|
|
 |
583230 |
@@ -96,14 +96,20 @@ bool is_move(opcode c);
|
|
 |
583230 |
bool is_ldst(opcode c);
|
|
 |
583230 |
bool is_binary(opcode c);
|
|
 |
583230 |
bool is_commutative(opcode c);
|
|
 |
583230 |
+
|
|
 |
583230 |
+void init_bpf_helper_tables();
|
|
 |
583230 |
const char *bpf_function_name (unsigned id);
|
|
 |
583230 |
+bpf_func_id bpf_function_id (const std::string &name);
|
|
 |
583230 |
unsigned bpf_function_nargs (unsigned id);
|
|
 |
583230 |
|
|
 |
583230 |
const opcode BPF_LD_MAP = BPF_LD | BPF_IMM | BPF_DW | (BPF_PSEUDO_MAP_FD << 8);
|
|
 |
583230 |
|
|
 |
583230 |
-// Not actual BPF helpers, but treating them like one simplifies some of the
|
|
 |
583230 |
+// Not actual BPF helpers, but treating them as such simplifies some of the
|
|
 |
583230 |
// interpreter logic. We give them IDs that shouldn't conflict with IDs of
|
|
 |
583230 |
// real BPF helpers.
|
|
 |
583230 |
+#define __STAPBPF_FUNC_MAPPER(FN) \
|
|
 |
583230 |
+ FN(map_get_next_key), \
|
|
 |
583230 |
+ FN(sprintf),
|
|
 |
583230 |
const bpf_func_id BPF_FUNC_map_get_next_key = (bpf_func_id) -1;
|
|
 |
583230 |
const bpf_func_id BPF_FUNC_sprintf = (bpf_func_id) -2;
|
|
 |
583230 |
|
|
 |
583230 |
diff --git a/bpf-translate.cxx b/bpf-translate.cxx
|
|
 |
583230 |
index 023ac6ce7..af3f54b50 100644
|
|
 |
583230 |
--- a/bpf-translate.cxx
|
|
 |
583230 |
+++ b/bpf-translate.cxx
|
|
 |
583230 |
@@ -179,7 +179,7 @@ struct bpf_unparser : public throwing_visitor
|
|
 |
583230 |
// TODO General triage of bpf-possible functionality:
|
|
 |
583230 |
virtual void visit_block (::block *s);
|
|
 |
583230 |
// TODO visit_try_block -> UNHANDLED
|
|
 |
583230 |
- virtual void visit_embeddedcode (embeddedcode *s); // TODO need to find testcase/example for this
|
|
 |
583230 |
+ virtual void visit_embeddedcode (embeddedcode *s);
|
|
 |
583230 |
virtual void visit_null_statement (null_statement *s);
|
|
 |
583230 |
virtual void visit_expr_statement (expr_statement *s);
|
|
 |
583230 |
virtual void visit_if_statement (if_statement* s);
|
|
 |
583230 |
@@ -192,7 +192,7 @@ struct bpf_unparser : public throwing_visitor
|
|
 |
583230 |
virtual void visit_continue_statement (continue_statement* s);
|
|
 |
583230 |
virtual void visit_literal_string (literal_string *e);
|
|
 |
583230 |
virtual void visit_literal_number (literal_number* e);
|
|
 |
583230 |
- // TODO visit_embedded_expr -> UNHANDLED, could be handled like embedded_code with a return value?
|
|
 |
583230 |
+ // TODO visit_embedded_expr -> UNHANDLED, could treat as embedded_code
|
|
 |
583230 |
virtual void visit_binary_expression (binary_expression* e);
|
|
 |
583230 |
virtual void visit_unary_expression (unary_expression* e);
|
|
 |
583230 |
virtual void visit_pre_crement (pre_crement* e);
|
|
 |
583230 |
@@ -200,7 +200,7 @@ struct bpf_unparser : public throwing_visitor
|
|
 |
583230 |
virtual void visit_logical_or_expr (logical_or_expr* e);
|
|
 |
583230 |
virtual void visit_logical_and_expr (logical_and_expr* e);
|
|
 |
583230 |
virtual void visit_array_in (array_in* e);
|
|
 |
583230 |
- // ??? visit_regex_query is UNHANDLED, requires adding new kernel functionality.
|
|
 |
583230 |
+ // ??? visit_regex_query -> UNHANDLED, requires new kernel functionality
|
|
 |
583230 |
virtual void visit_compound_expression (compound_expression *e);
|
|
 |
583230 |
virtual void visit_comparison (comparison* e);
|
|
 |
583230 |
// TODO visit_concatenation -> (2) pseudo-LOOP: copy the strings while concatenating
|
|
 |
583230 |
@@ -239,14 +239,21 @@ struct bpf_unparser : public throwing_visitor
|
|
 |
583230 |
value *emit_context_var(bpf_context_vardecl *v);
|
|
 |
583230 |
|
|
 |
583230 |
// Used for the embedded-code assembler:
|
|
 |
583230 |
+ int64_t parse_imm (const asm_stmt &stmt, const std::string &str);
|
|
 |
583230 |
size_t parse_asm_stmt (embeddedcode *s, size_t start,
|
|
 |
583230 |
/*OUT*/asm_stmt &stmt);
|
|
 |
583230 |
- value *emit_asm_arg(const asm_stmt &stmt, const std::string ®,
|
|
 |
583230 |
- bool allow_imm = true);
|
|
 |
583230 |
+ value *emit_asm_arg(const asm_stmt &stmt, const std::string &arg,
|
|
 |
583230 |
+ bool allow_imm = true, bool allow_emit = true);
|
|
 |
583230 |
value *emit_asm_reg(const asm_stmt &stmt, const std::string ®);
|
|
 |
583230 |
+ value *get_asm_reg(const asm_stmt &stmt, const std::string ®);
|
|
 |
583230 |
void emit_asm_opcode(const asm_stmt &stmt,
|
|
 |
583230 |
std::map<std::string, block *> label_map);
|
|
 |
583230 |
|
|
 |
583230 |
+ // Used for the embedded-code assembler's diagnostics:
|
|
 |
583230 |
+ source_loc adjusted_loc;
|
|
 |
583230 |
+ size_t adjust_pos;
|
|
 |
583230 |
+ std::vector<token *> adjusted_toks; // track for deallocation
|
|
 |
583230 |
+
|
|
 |
583230 |
// Used for string data:
|
|
 |
583230 |
value *emit_literal_string(const std::string &str, const token *tok);
|
|
 |
583230 |
value *emit_string_copy(value *dest, int ofs, value *src, bool zero_pad = false);
|
|
 |
583230 |
@@ -580,17 +587,22 @@ bpf_unparser::visit_block (::block *s)
|
|
 |
583230 |
|
|
 |
583230 |
Ahh for the days of 1960s FORTRAN.
|
|
 |
583230 |
|
|
 |
583230 |
- TODO: It might make more sense to implement an assembler based on
|
|
 |
583230 |
+ ??? It might make more sense to implement an assembler based on
|
|
 |
583230 |
the syntax used in official eBPF subsystem docs. */
|
|
 |
583230 |
|
|
 |
583230 |
-/* Possible assembly statement types include:
|
|
 |
583230 |
+/* Supported assembly statement types include:
|
|
 |
583230 |
|
|
 |
583230 |
<stmt> ::= label, <dest=label>;
|
|
 |
583230 |
+ <stmt> ::= alloc, <dest=reg>, <imm=imm>;
|
|
 |
583230 |
+ <stmt> ::= call, <dest=reg>, <param[0]=function name>, <param[1]=arg>, ...;
|
|
 |
583230 |
+ <stmt> ::= printf, <param[0]=string constant>, <param[1]=arg>, ...;
|
|
 |
583230 |
+ <stmt> ::= error, <param[0]=string constant>, <param[1]=arg>, ...;
|
|
 |
583230 |
<stmt> ::= <code=integer opcode>, <dest=reg>, <src1=reg>,
|
|
 |
583230 |
<off/jmp_target=off>, <imm=imm>;
|
|
 |
583230 |
|
|
 |
583230 |
- Possible argument types include:
|
|
 |
583230 |
+ Supported argument types include:
|
|
 |
583230 |
|
|
 |
583230 |
+ <arg> ::= <reg> | <imm>
|
|
 |
583230 |
<reg> ::= <register index> | r<register index> |
|
|
 |
583230 |
$<identifier> | $<integer constant> | $$ | <string constant>
|
|
 |
583230 |
<imm> ::= <integer constant> | BPF_MAXSTRINGLEN
|
|
 |
583230 |
@@ -598,6 +610,9 @@ bpf_unparser::visit_block (::block *s)
|
|
 |
583230 |
|
|
 |
583230 |
*/
|
|
 |
583230 |
|
|
 |
583230 |
+// TODO
|
|
 |
583230 |
+#define BPF_ASM_DEBUG
|
|
 |
583230 |
+
|
|
 |
583230 |
struct asm_stmt {
|
|
 |
583230 |
std::string kind;
|
|
 |
583230 |
|
|
 |
583230 |
@@ -609,9 +624,10 @@ struct asm_stmt {
|
|
 |
583230 |
bool has_fallthrough = false;
|
|
 |
583230 |
std::string jmp_target, fallthrough;
|
|
 |
583230 |
|
|
 |
583230 |
+ // metadata for call, error instructions
|
|
 |
583230 |
+ std::vector<std::string> params;
|
|
 |
583230 |
+
|
|
 |
583230 |
token *tok;
|
|
 |
583230 |
- bool deallocate_tok = false;
|
|
 |
583230 |
- ~asm_stmt() { if (deallocate_tok) delete tok; }
|
|
 |
583230 |
};
|
|
 |
583230 |
|
|
 |
583230 |
std::ostream&
|
|
 |
583230 |
@@ -647,10 +663,30 @@ is_numeric (const std::string &str)
|
|
 |
583230 |
stol(str, &pos, 0);
|
|
 |
583230 |
} catch (std::invalid_argument &e) {
|
|
 |
583230 |
return false;
|
|
 |
583230 |
+ } catch (std::out_of_range &e) {
|
|
 |
583230 |
+ /* XXX: probably numeric but not valid; give up */
|
|
 |
583230 |
+ return false;
|
|
 |
583230 |
}
|
|
 |
583230 |
return (pos == str.size());
|
|
 |
583230 |
}
|
|
 |
583230 |
|
|
 |
583230 |
+int64_t
|
|
 |
583230 |
+bpf_unparser::parse_imm (const asm_stmt &stmt, const std::string &str)
|
|
 |
583230 |
+{
|
|
 |
583230 |
+ int64_t val;
|
|
 |
583230 |
+ if (str == "BPF_MAXSTRINGLEN")
|
|
 |
583230 |
+ val = BPF_MAXSTRINGLEN;
|
|
 |
583230 |
+ else if (str == "-")
|
|
 |
583230 |
+ val = 0;
|
|
 |
583230 |
+ else try {
|
|
 |
583230 |
+ val = stol(str);
|
|
 |
583230 |
+ } catch (std::exception &e) { // XXX: invalid_argument, out_of_range
|
|
 |
583230 |
+ throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode operand '%s'",
|
|
 |
583230 |
+ str.c_str()), stmt.tok);
|
|
 |
583230 |
+ }
|
|
 |
583230 |
+ return val;
|
|
 |
583230 |
+}
|
|
 |
583230 |
+
|
|
 |
583230 |
/* Parse an assembly statement starting from position start in code,
|
|
 |
583230 |
then write the output in stmt. Returns a position immediately after
|
|
 |
583230 |
the parsed statement. */
|
|
 |
583230 |
@@ -663,31 +699,14 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start,
|
|
 |
583230 |
retry:
|
|
 |
583230 |
std::vector<std::string> args;
|
|
 |
583230 |
unsigned n = code.size();
|
|
 |
583230 |
+ size_t pos;
|
|
 |
583230 |
bool in_comment = false;
|
|
 |
583230 |
bool in_string = false;
|
|
 |
583230 |
|
|
 |
583230 |
- // compute token with adjusted source location for diagnostics
|
|
 |
583230 |
- source_loc adjusted_loc; // TODO: ought to create a proper copy constructor for source_loc
|
|
 |
583230 |
- adjusted_loc.file = s->tok->location.file;
|
|
 |
583230 |
- adjusted_loc.line = s->tok->location.line;
|
|
 |
583230 |
- adjusted_loc.column = s->tok->location.column;
|
|
 |
583230 |
- for (size_t pos = 0; pos < start && pos < n; pos++)
|
|
 |
583230 |
- {
|
|
 |
583230 |
- // TODO: should save adjusted_loc state between parse_asm_stmt invocations; add field?
|
|
 |
583230 |
- char c = code[pos];
|
|
 |
583230 |
- if (c == '\n')
|
|
 |
583230 |
- {
|
|
 |
583230 |
- adjusted_loc.line++;
|
|
 |
583230 |
- adjusted_loc.column = 1;
|
|
 |
583230 |
- }
|
|
 |
583230 |
- else
|
|
 |
583230 |
- adjusted_loc.column++;
|
|
 |
583230 |
- }
|
|
 |
583230 |
-
|
|
 |
583230 |
- // TODO: As before, parser is extremely non-rigorous and could do
|
|
 |
583230 |
+ // ??? As before, parser is extremely non-rigorous and could do
|
|
 |
583230 |
// with some tightening in terms of the inputs it accepts.
|
|
 |
583230 |
- size_t pos;
|
|
 |
583230 |
std::string arg = "";
|
|
 |
583230 |
+ size_t save_start = start; // -- position for diagnostics
|
|
 |
583230 |
for (pos = start; pos < n; pos++)
|
|
 |
583230 |
{
|
|
 |
583230 |
char c = code[pos];
|
|
 |
583230 |
@@ -714,6 +733,9 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start,
|
|
 |
583230 |
++pos, in_comment = true;
|
|
 |
583230 |
else if (c == '"') // found a literal string
|
|
 |
583230 |
{
|
|
 |
583230 |
+ if (arg.empty() && args.empty())
|
|
 |
583230 |
+ save_start = pos; // start of first argument
|
|
 |
583230 |
+
|
|
 |
583230 |
// XXX: This allows '"' inside an arg and will treat the
|
|
 |
583230 |
// string as a sequence of weird identifier characters. A
|
|
 |
583230 |
// more rigorous parser would error on mixing strings and
|
|
 |
583230 |
@@ -738,6 +760,9 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start,
|
|
 |
583230 |
}
|
|
 |
583230 |
else // found (we assume) a regular char
|
|
 |
583230 |
{
|
|
 |
583230 |
+ if (arg.empty() && args.empty())
|
|
 |
583230 |
+ save_start = pos; // start of first argument
|
|
 |
583230 |
+
|
|
 |
583230 |
// XXX: As before, this strips whitespace within args
|
|
 |
583230 |
// (so '$ab', '$ a b' and '$a b' are equivalent).
|
|
 |
583230 |
//
|
|
 |
583230 |
@@ -760,28 +785,73 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start,
|
|
 |
583230 |
goto retry;
|
|
 |
583230 |
}
|
|
 |
583230 |
|
|
 |
583230 |
+ // compute token with adjusted source location for diagnostics
|
|
 |
583230 |
+ // TODO: needs some attention to how multiline tokens are printed in error reporting -- with this code, caret aligns incorrectly
|
|
 |
583230 |
+ for (/* use saved adjust_pos */; adjust_pos < save_start && adjust_pos < n; adjust_pos++)
|
|
 |
583230 |
+ {
|
|
 |
583230 |
+ char c = code[adjust_pos];
|
|
 |
583230 |
+ if (c == '\n')
|
|
 |
583230 |
+ {
|
|
 |
583230 |
+ adjusted_loc.line++;
|
|
 |
583230 |
+ adjusted_loc.column = 1;
|
|
 |
583230 |
+ }
|
|
 |
583230 |
+ else
|
|
 |
583230 |
+ adjusted_loc.column++;
|
|
 |
583230 |
+ }
|
|
 |
583230 |
+
|
|
 |
583230 |
// set token with adjusted source location
|
|
 |
583230 |
- //stmt.tok = (token *)s->tok;
|
|
 |
583230 |
- // TODO this segfaults for some reason, some data not copied?
|
|
 |
583230 |
stmt.tok = s->tok->adjust_location(adjusted_loc);
|
|
 |
583230 |
- stmt.deallocate_tok = false; // TODO must avoid destroy-on-copy
|
|
 |
583230 |
+ adjusted_toks.push_back(stmt.tok);
|
|
 |
583230 |
|
|
 |
583230 |
- std::cerr << "DEBUG GOT stmt "; // TODO
|
|
 |
583230 |
- for (unsigned k = 0; k < args.size(); k++) std::cerr << args[k] << " / ";
|
|
 |
583230 |
- std::cerr << std::endl; // TODO
|
|
 |
583230 |
+#ifdef BPF_ASM_DEBUG
|
|
 |
583230 |
+ std::cerr << "bpf_asm parse_asm_stmt: tokenizer got ";
|
|
 |
583230 |
+ for (unsigned k = 0; k < args.size(); k++)
|
|
 |
583230 |
+ std::cerr << args[k] << ", ";
|
|
 |
583230 |
+ std::cerr << std::endl;
|
|
 |
583230 |
+#endif
|
|
 |
583230 |
if (args[0] == "label")
|
|
 |
583230 |
{
|
|
 |
583230 |
if (args.size() != 2)
|
|
 |
583230 |
- throw SEMANTIC_ERROR (_("invalid bpf embeddedcode syntax"), stmt.tok);
|
|
 |
583230 |
+ throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode syntax (label expects 1 arg, found %lu)", args.size()-1), stmt.tok);
|
|
 |
583230 |
+ stmt.kind = args[0];
|
|
 |
583230 |
+ stmt.dest = args[1];
|
|
 |
583230 |
+ }
|
|
 |
583230 |
+ else if (args[0] == "alloc")
|
|
 |
583230 |
+ {
|
|
 |
583230 |
+ if (args.size() != 3)
|
|
 |
583230 |
+ throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode syntax (alloc expects 2 args, found %lu)", args.size()-1), stmt.tok);
|
|
 |
583230 |
stmt.kind = args[0];
|
|
 |
583230 |
stmt.dest = args[1];
|
|
 |
583230 |
+ stmt.imm = parse_imm(stmt, args[2]);
|
|
 |
583230 |
+ }
|
|
 |
583230 |
+ else if (args[0] == "call")
|
|
 |
583230 |
+ {
|
|
 |
583230 |
+ if (args.size() < 3)
|
|
 |
583230 |
+ throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode syntax (call expects at least 2 args, found %lu)", args.size()-1), stmt.tok);
|
|
 |
583230 |
+ stmt.kind = args[0];
|
|
 |
583230 |
+ stmt.dest = args[1];
|
|
 |
583230 |
+ for (unsigned k = 2; k < args.size(); k++)
|
|
 |
583230 |
+ stmt.params.push_back(args[k]);
|
|
 |
583230 |
+ }
|
|
 |
583230 |
+ else if (args[0] == "printf" || args[0] == "error")
|
|
 |
583230 |
+ {
|
|
 |
583230 |
+ if (args.size() < 2)
|
|
 |
583230 |
+ 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);
|
|
 |
583230 |
+ stmt.kind = args[0];
|
|
 |
583230 |
+ for (unsigned k = 2; k < args.size(); k++)
|
|
 |
583230 |
+ stmt.params.push_back(args[k]);
|
|
 |
583230 |
}
|
|
 |
583230 |
else if (is_numeric(args[0]))
|
|
 |
583230 |
{
|
|
 |
583230 |
- if (args.size() != 5) // TODO change to 4 to test err+tok
|
|
 |
583230 |
- throw SEMANTIC_ERROR (_("invalid bpf embeddedcode syntax"), stmt.tok);
|
|
 |
583230 |
+ if (args.size() != 5)
|
|
 |
583230 |
+ throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode syntax (opcode expects 4 args, found %lu)", args.size()-1), stmt.tok);
|
|
 |
583230 |
stmt.kind = "opcode";
|
|
 |
583230 |
- stmt.code = stoul(args[0], 0, 0); // TODO signal error
|
|
 |
583230 |
+ try {
|
|
 |
583230 |
+ stmt.code = stoul(args[0], 0, 0);
|
|
 |
583230 |
+ } catch (std::exception &e) { // XXX: invalid_argument, out_of_range
|
|
 |
583230 |
+ throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode opcode '%s'",
|
|
 |
583230 |
+ args[0].c_str()), stmt.tok);
|
|
 |
583230 |
+ }
|
|
 |
583230 |
stmt.dest = args[1];
|
|
 |
583230 |
stmt.src1 = args[2];
|
|
 |
583230 |
|
|
 |
583230 |
@@ -799,25 +869,16 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start,
|
|
 |
583230 |
stmt.off = 0;
|
|
 |
583230 |
stmt.jmp_target = args[3];
|
|
 |
583230 |
}
|
|
 |
583230 |
- else if (args[3] == "BPF_MAXSTRINGLEN")
|
|
 |
583230 |
- stmt.off = BPF_MAXSTRINGLEN;
|
|
 |
583230 |
- else if (args[3] == "-")
|
|
 |
583230 |
- stmt.off = 0;
|
|
 |
583230 |
else
|
|
 |
583230 |
- stmt.off = stol(args[3]); // TODO signal error
|
|
 |
583230 |
+ stmt.off = parse_imm(stmt, args[3]);
|
|
 |
583230 |
|
|
 |
583230 |
- if (args[4] == "BPF_MAXSTRINGLEN")
|
|
 |
583230 |
- stmt.imm = BPF_MAXSTRINGLEN;
|
|
 |
583230 |
- else if (args[4] == "-")
|
|
 |
583230 |
- stmt.imm = 0;
|
|
 |
583230 |
- else
|
|
 |
583230 |
- stmt.imm = stol(args[4]); // TODO signal error
|
|
 |
583230 |
+ stmt.imm = parse_imm(stmt, args[4]);
|
|
 |
583230 |
}
|
|
 |
583230 |
else
|
|
 |
583230 |
throw SEMANTIC_ERROR (_F("unknown bpf embeddedcode operator '%s'",
|
|
 |
583230 |
args[0].c_str()), stmt.tok);
|
|
 |
583230 |
|
|
 |
583230 |
- // we returned a statement, so there's more parsing to be done
|
|
 |
583230 |
+ // we returned one statement, there may be more parsing to be done
|
|
 |
583230 |
return pos;
|
|
 |
583230 |
}
|
|
 |
583230 |
|
|
 |
583230 |
@@ -828,7 +889,7 @@ std::string translate_escapes (const interned_string &str);
|
|
 |
583230 |
May emit code to store a string constant on the stack. */
|
|
 |
583230 |
value *
|
|
 |
583230 |
bpf_unparser::emit_asm_arg (const asm_stmt &stmt, const std::string &arg,
|
|
 |
583230 |
- bool allow_imm)
|
|
 |
583230 |
+ bool allow_imm, bool allow_emit)
|
|
 |
583230 |
{
|
|
 |
583230 |
if (arg == "$$")
|
|
 |
583230 |
{
|
|
 |
583230 |
@@ -859,7 +920,6 @@ bpf_unparser::emit_asm_arg (const asm_stmt &stmt, const std::string &arg,
|
|
 |
583230 |
auto ok = this_locals->insert (v);
|
|
 |
583230 |
assert (ok.second);
|
|
 |
583230 |
return reg;
|
|
 |
583230 |
- // TODO write a testcase
|
|
 |
583230 |
}
|
|
 |
583230 |
else if (is_numeric(arg) && allow_imm)
|
|
 |
583230 |
{
|
|
 |
583230 |
@@ -879,13 +939,17 @@ bpf_unparser::emit_asm_arg (const asm_stmt &stmt, const std::string &arg,
|
|
 |
583230 |
}
|
|
 |
583230 |
else if (arg[0] == '"')
|
|
 |
583230 |
{
|
|
 |
583230 |
- // TODO verify correctness
|
|
 |
583230 |
+ if (!allow_emit)
|
|
 |
583230 |
+ throw SEMANTIC_ERROR (_F("invalid bpf argument %s "
|
|
 |
583230 |
+ "(string literal not allowed here)",
|
|
 |
583230 |
+ arg.c_str()), stmt.tok);
|
|
 |
583230 |
+
|
|
 |
583230 |
/* arg is a string constant */
|
|
 |
583230 |
if (arg[arg.size() - 1] != '"')
|
|
 |
583230 |
throw SEMANTIC_ERROR (_F("BUG: improper string %s",
|
|
 |
583230 |
arg.c_str()), stmt.tok);
|
|
 |
583230 |
std::string escaped_str = arg.substr(1,arg.size()-2); /* strip quotes */
|
|
 |
583230 |
- std::string str = translate_escapes(escaped_str); // TODO interned_str?
|
|
 |
583230 |
+ std::string str = translate_escapes(escaped_str);
|
|
 |
583230 |
return emit_literal_string(str, stmt.tok);
|
|
 |
583230 |
}
|
|
 |
583230 |
else if (arg == "BPF_MAXSTRINGLEN")
|
|
 |
583230 |
@@ -913,12 +977,22 @@ bpf_unparser::emit_asm_arg (const asm_stmt &stmt, const std::string &arg,
|
|
 |
583230 |
|
|
 |
583230 |
}
|
|
 |
583230 |
|
|
 |
583230 |
+/* As above, but don't accept immediate values.
|
|
 |
583230 |
+ Do accept string constants (since they're stored in a register). */
|
|
 |
583230 |
value *
|
|
 |
583230 |
bpf_unparser::emit_asm_reg (const asm_stmt &stmt, const std::string ®)
|
|
 |
583230 |
{
|
|
 |
583230 |
return emit_asm_arg(stmt, reg, /*allow_imm=*/false);
|
|
 |
583230 |
}
|
|
 |
583230 |
|
|
 |
583230 |
+/* As above, but don't allow string constants or anything that emits code.
|
|
 |
583230 |
+ Useful if the context requires an lvalue. */
|
|
 |
583230 |
+value *
|
|
 |
583230 |
+bpf_unparser::get_asm_reg (const asm_stmt &stmt, const std::string ®)
|
|
 |
583230 |
+{
|
|
 |
583230 |
+ return emit_asm_arg(stmt, reg, /*allow_imm=*/false, /*allow_emit=*/false);
|
|
 |
583230 |
+}
|
|
 |
583230 |
+
|
|
 |
583230 |
void
|
|
 |
583230 |
bpf_unparser::emit_asm_opcode (const asm_stmt &stmt,
|
|
 |
583230 |
std::map<std::string, block *> label_map)
|
|
 |
583230 |
@@ -1013,7 +1087,7 @@ bpf_unparser::emit_asm_opcode (const asm_stmt &stmt,
|
|
 |
583230 |
|
|
 |
583230 |
value *v_dest = NULL;
|
|
 |
583230 |
if (r_dest || r_src0)
|
|
 |
583230 |
- v_dest = emit_asm_reg(stmt, stmt.dest);
|
|
 |
583230 |
+ v_dest = get_asm_reg(stmt, stmt.dest);
|
|
 |
583230 |
else if (stmt.dest != "0" && stmt.dest != "-")
|
|
 |
583230 |
throw SEMANTIC_ERROR (_F("invalid register field '%s' in bpf code",
|
|
 |
583230 |
stmt.dest.c_str()), stmt.tok);
|
|
 |
583230 |
@@ -1069,6 +1143,10 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s)
|
|
 |
583230 |
std::vector<asm_stmt> statements;
|
|
 |
583230 |
asm_stmt stmt;
|
|
 |
583230 |
|
|
 |
583230 |
+ // track adjusted source location for each stmt
|
|
 |
583230 |
+ adjusted_loc = s->tok->location;
|
|
 |
583230 |
+ adjust_pos = 0;
|
|
 |
583230 |
+
|
|
 |
583230 |
size_t pos = 0;
|
|
 |
583230 |
while ((pos = parse_asm_stmt(s, pos, stmt)) != std::string::npos)
|
|
 |
583230 |
{
|
|
 |
583230 |
@@ -1133,7 +1211,7 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s)
|
|
 |
583230 |
after_jump = NULL;
|
|
 |
583230 |
}
|
|
 |
583230 |
}
|
|
 |
583230 |
- if (after_jump != NULL) // TODO: should just fall through to exit
|
|
 |
583230 |
+ if (after_jump != NULL) // ??? should just fall through to exit
|
|
 |
583230 |
throw SEMANTIC_ERROR (_("BUG: bpf embeddedcode doesn't support "
|
|
 |
583230 |
"fallthrough on final asm_stmt"), stmt.tok);
|
|
 |
583230 |
|
|
 |
583230 |
@@ -1144,14 +1222,67 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s)
|
|
 |
583230 |
it != statements.end(); it++)
|
|
 |
583230 |
{
|
|
 |
583230 |
stmt = *it;
|
|
 |
583230 |
- std::cerr << "DEBUG processing " << stmt << std::endl; // TODO
|
|
 |
583230 |
+#ifdef BPF_ASM_DEBUG
|
|
 |
583230 |
+ std::cerr << "bpf_asm visit_embeddedcode: " << stmt << std::endl;
|
|
 |
583230 |
+#endif
|
|
 |
583230 |
if (stmt.kind == "label")
|
|
 |
583230 |
{
|
|
 |
583230 |
- // TODO: be sure there's no gap in the edge
|
|
 |
583230 |
if (!jumped_already)
|
|
 |
583230 |
emit_jmp (label_map[stmt.dest]);
|
|
 |
583230 |
set_block(label_map[stmt.dest]);
|
|
 |
583230 |
}
|
|
 |
583230 |
+ else if (stmt.kind == "alloc")
|
|
 |
583230 |
+ {
|
|
 |
583230 |
+ /* Reserve stack space and store its address in dest. */
|
|
 |
583230 |
+ int ofs = this_prog.max_tmp_space + stmt.imm;
|
|
 |
583230 |
+ value *dest = get_asm_reg(stmt, stmt.dest);
|
|
 |
583230 |
+ this_prog.use_tmp_space(-ofs);
|
|
 |
583230 |
+ this_prog.mk_binary(this_ins, BPF_ADD, dest,
|
|
 |
583230 |
+ this_prog.lookup_reg(BPF_REG_10) /*frame*/,
|
|
 |
583230 |
+ this_prog.new_imm(ofs));
|
|
 |
583230 |
+ }
|
|
 |
583230 |
+ else if (stmt.kind == "call")
|
|
 |
583230 |
+ {
|
|
 |
583230 |
+ std::string func_name = stmt.params[0];
|
|
 |
583230 |
+ bpf_func_id hid = bpf_function_id(func_name);
|
|
 |
583230 |
+ if (hid != __BPF_FUNC_MAX_ID)
|
|
 |
583230 |
+ {
|
|
 |
583230 |
+ // TODO diagnostic: check if the number of arguments is correct
|
|
 |
583230 |
+ regno r = BPF_REG_1; unsigned nargs = 0;
|
|
 |
583230 |
+ for (unsigned k = 1; k < stmt.params.size(); k++)
|
|
 |
583230 |
+ {
|
|
 |
583230 |
+ // ??? Could make params optional to avoid this part,
|
|
 |
583230 |
+ // ??? since the calling convention is well-known.
|
|
 |
583230 |
+ value *from_reg = emit_asm_arg(stmt, stmt.params[k]);
|
|
 |
583230 |
+ value *to_reg = this_prog.lookup_reg(r);
|
|
 |
583230 |
+ this_prog.mk_mov(this_ins, to_reg, from_reg);
|
|
 |
583230 |
+ nargs++; r++;
|
|
 |
583230 |
+ }
|
|
 |
583230 |
+ this_prog.mk_call(this_ins, hid, nargs);
|
|
 |
583230 |
+ this_prog.mk_mov(this_ins, get_asm_reg(stmt, stmt.dest),
|
|
 |
583230 |
+ this_prog.lookup_reg(BPF_REG_0) /* returnval */);
|
|
 |
583230 |
+ // ??? Could make stmt.dest optional to avoid this extra mov,
|
|
 |
583230 |
+ // ??? since the BPF_REG_0 convention is well-known.
|
|
 |
583230 |
+ }
|
|
 |
583230 |
+ else
|
|
 |
583230 |
+ {
|
|
 |
583230 |
+ // TODO function_name = params[0];
|
|
 |
583230 |
+ // TODO args = parse_reg(params[1]), parse_reg(params[2]), ...
|
|
 |
583230 |
+ // TODO emit_functioncall() with good bits from visit_functioncall()
|
|
 |
583230 |
+ throw SEMANTIC_ERROR (_("BUG: bpf embeddedcode non-helper 'call' not yet supported"),
|
|
 |
583230 |
+ stmt.tok);
|
|
 |
583230 |
+ }
|
|
 |
583230 |
+ }
|
|
 |
583230 |
+ else if (stmt.kind == "printf" || stmt.kind == "error")
|
|
 |
583230 |
+ {
|
|
 |
583230 |
+ // TODO Note that error() should be modeled on the tapset function in tapset/logging.stp
|
|
 |
583230 |
+ // TODO format = params[0];
|
|
 |
583230 |
+ // TODO args = parse_reg(params[1]), parse_reg(params[2]), ...
|
|
 |
583230 |
+ // TODO emit_print_format() with good bits from visit_print_format()
|
|
 |
583230 |
+ // TODO if (stmt.kind == "error") emit functioncall to exit()
|
|
 |
583230 |
+ throw SEMANTIC_ERROR (_("BUG: bpf embeddedcode 'printf/error' not yet supported"),
|
|
 |
583230 |
+ stmt.tok);
|
|
 |
583230 |
+ }
|
|
 |
583230 |
else if (stmt.kind == "opcode")
|
|
 |
583230 |
{
|
|
 |
583230 |
emit_asm_opcode (stmt, label_map);
|
|
 |
583230 |
@@ -1164,6 +1295,12 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s)
|
|
 |
583230 |
if (stmt.has_fallthrough)
|
|
 |
583230 |
set_block(label_map[stmt.fallthrough]);
|
|
 |
583230 |
}
|
|
 |
583230 |
+
|
|
 |
583230 |
+ // housekeeping -- deallocate adjusted_toks along with statements
|
|
 |
583230 |
+ for (std::vector<token *>::iterator it = adjusted_toks.begin();
|
|
 |
583230 |
+ it != adjusted_toks.end(); it++)
|
|
 |
583230 |
+ delete *it;
|
|
 |
583230 |
+ adjusted_toks.clear();
|
|
 |
583230 |
}
|
|
 |
583230 |
|
|
 |
583230 |
void
|
|
 |
583230 |
@@ -3260,6 +3397,8 @@ translate_bpf_pass (systemtap_session& s)
|
|
 |
583230 |
{
|
|
 |
583230 |
using namespace bpf;
|
|
 |
583230 |
|
|
 |
583230 |
+ init_bpf_helper_tables();
|
|
 |
583230 |
+
|
|
 |
583230 |
if (elf_version(EV_CURRENT) == EV_NONE)
|
|
 |
583230 |
return 1;
|
|
 |
583230 |
|
|
 |
583230 |
diff --git a/tapset/logging.stp b/tapset/logging.stp
|
|
 |
583230 |
index 59edce3c8..839239b8f 100644
|
|
 |
583230 |
--- a/tapset/logging.stp
|
|
 |
583230 |
+++ b/tapset/logging.stp
|
|
 |
583230 |
@@ -128,6 +128,8 @@ function error (msg:string)
|
|
 |
583230 |
exit() // TODO: should support MAXERRORS, probe error {}
|
|
 |
583230 |
}
|
|
 |
583230 |
%)
|
|
 |
583230 |
+// NOTE: The 'error' statement in the eBPF assembler in bpf-translate.cxx
|
|
 |
583230 |
+// should be kept up-to-date with the behaviour of this function.
|
|
 |
583230 |
|
|
 |
583230 |
/**
|
|
 |
583230 |
* sfunction assert - evaluate assertion
|
|
 |
583230 |
--
|
|
 |
583230 |
2.14.5
|
|
 |
583230 |
|