|
|
583230 |
From 1d21523d10628fd14c4a16d5f85d98add9b5c0a6 Mon Sep 17 00:00:00 2001
|
|
|
583230 |
From: Serhei Makarov <smakarov@redhat.com>
|
|
|
583230 |
Date: Tue, 23 Oct 2018 13:35:08 -0400
|
|
|
583230 |
Subject: [PATCH 07/32] stapbpf assembler WIP #6 :: other call functions
|
|
|
583230 |
({s}printf and tapset)
|
|
|
583230 |
|
|
|
583230 |
Only very limited support for tapset functions (restricted to exit()
|
|
|
583230 |
for now) due to the difficulty of resolving symbols after the semantic
|
|
|
583230 |
pass is already completed. Could address this in the future.
|
|
|
583230 |
|
|
|
583230 |
* bpf_internal.h (program::use_tmp_space): check for overflow.
|
|
|
583230 |
(globals::session): new field for systemtap_session (used by function lookup).
|
|
|
583230 |
|
|
|
583230 |
* bpf_translate.cxx (asm_stmt::has_jmp_target): new field.
|
|
|
583230 |
(operator <<): printing rules for alloc, call.
|
|
|
583230 |
(bpf_unparser::parse_asm_stmt): remove printf/error, BUGFIX alloc, call, string literal.
|
|
|
583230 |
Also calculate has_jmp_target in the resulting stmt.
|
|
|
583230 |
(bpf_unparser::visit_embeddedcode): handle printf, sprintf and exit().
|
|
|
583230 |
Also fix the way fallthrough fields are populated to avoid spurious extra jump.
|
|
|
583230 |
(bpf_unparser::emit_functioncall): new function. Factors out non-staptree code.
|
|
|
583230 |
(bpf_unparser::visit_functioncall): use new emit_functioncall().
|
|
|
583230 |
(print_format_add_tag): new function on std::string. Factors out string operations.
|
|
|
583230 |
(bpf_unparser::emit_print_format): new function. Factors out non-staptree code.
|
|
|
583230 |
(bpf_unparser::visit_print_format): use new emit_print_format().
|
|
|
583230 |
(translate_bpf_pass): store session in globals.
|
|
|
583230 |
---
|
|
|
583230 |
bpf-internal.h | 5 +
|
|
|
583230 |
bpf-translate.cxx | 346 +++++++++++++++++++++++++++++++++++++++---------------
|
|
|
583230 |
2 files changed, 255 insertions(+), 96 deletions(-)
|
|
|
583230 |
|
|
|
583230 |
diff --git a/bpf-internal.h b/bpf-internal.h
|
|
|
583230 |
index 61514db9f..3041bbdf5 100644
|
|
|
583230 |
--- a/bpf-internal.h
|
|
|
583230 |
+++ b/bpf-internal.h
|
|
|
583230 |
@@ -30,6 +30,7 @@ namespace bpf {
|
|
|
583230 |
#define MAX_BPF_STACK 512
|
|
|
583230 |
#define BPF_REG_SIZE 8
|
|
|
583230 |
#define BPF_MAXSTRINGLEN 64
|
|
|
583230 |
+// #define BPF_MAXSTRINGLEN 128 // TODO: Longer strings require storage allocator & better printf().
|
|
|
583230 |
#define BPF_MAXFORMATLEN 256
|
|
|
583230 |
#define BPF_MAXMAPENTRIES 2048
|
|
|
583230 |
// TODO: add BPF_MAXSPRINTFLEN
|
|
|
583230 |
@@ -245,6 +246,7 @@ struct program
|
|
|
583230 |
{
|
|
|
583230 |
if (max_tmp_space < bytes)
|
|
|
583230 |
max_tmp_space = bytes;
|
|
|
583230 |
+ assert(max_tmp_space <= MAX_BPF_STACK);
|
|
|
583230 |
}
|
|
|
583230 |
|
|
|
583230 |
void mk_ld(insn_inserter &ins, int sz, value *dest, value *base, int off);
|
|
|
583230 |
@@ -318,6 +320,9 @@ struct globals
|
|
|
583230 |
EXIT = 0,
|
|
|
583230 |
NUM_INTERNALS, // non-ABI
|
|
|
583230 |
};
|
|
|
583230 |
+
|
|
|
583230 |
+ // Used to resolve function symbols in embedded code.
|
|
|
583230 |
+ systemtap_session *session;
|
|
|
583230 |
};
|
|
|
583230 |
} // namespace bpf
|
|
|
583230 |
|
|
|
583230 |
diff --git a/bpf-translate.cxx b/bpf-translate.cxx
|
|
|
583230 |
index af3f54b50..df22401ad 100644
|
|
|
583230 |
--- a/bpf-translate.cxx
|
|
|
583230 |
+++ b/bpf-translate.cxx
|
|
|
583230 |
@@ -135,8 +135,10 @@ has_side_effects (expression *e)
|
|
|
583230 |
return t.side_effects;
|
|
|
583230 |
}
|
|
|
583230 |
|
|
|
583230 |
-/* forward declaration */
|
|
|
583230 |
+/* forward declarations */
|
|
|
583230 |
struct asm_stmt;
|
|
|
583230 |
+static void print_format_add_tag(std::string&);
|
|
|
583230 |
+static void print_format_add_tag(print_format*);
|
|
|
583230 |
|
|
|
583230 |
struct bpf_unparser : public throwing_visitor
|
|
|
583230 |
{
|
|
|
583230 |
@@ -238,6 +240,11 @@ struct bpf_unparser : public throwing_visitor
|
|
|
583230 |
value *emit_bool(expression *e);
|
|
|
583230 |
value *emit_context_var(bpf_context_vardecl *v);
|
|
|
583230 |
|
|
|
583230 |
+ value *emit_functioncall(functiondecl *f, const std::vector<value *> &args);
|
|
|
583230 |
+ value *emit_print_format(const std::string &format,
|
|
|
583230 |
+ const std::vector<value *> &actual,
|
|
|
583230 |
+ bool print_to_stream = true);
|
|
|
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 |
@@ -594,24 +601,22 @@ bpf_unparser::visit_block (::block *s)
|
|
|
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> ::= call, <dest=optreg>, <param[0]=function name>, <param[1]=arg>, ...;
|
|
|
583230 |
<stmt> ::= <code=integer opcode>, <dest=reg>, <src1=reg>,
|
|
|
583230 |
<off/jmp_target=off>, <imm=imm>;
|
|
|
583230 |
|
|
|
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 |
- <off> ::= <imm> | <jump label>
|
|
|
583230 |
+ <arg> ::= <reg> | <imm>
|
|
|
583230 |
+ <optreg> ::= <reg> | -
|
|
|
583230 |
+ <reg> ::= <register index> | r<register index> |
|
|
|
583230 |
+ $<identifier> | $<integer constant> | $$ | <string constant>
|
|
|
583230 |
+ <imm> ::= <integer constant> | BPF_MAXSTRINGLEN | -
|
|
|
583230 |
+ <off> ::= <imm> | <jump label>
|
|
|
583230 |
|
|
|
583230 |
*/
|
|
|
583230 |
|
|
|
583230 |
-// TODO
|
|
|
583230 |
-#define BPF_ASM_DEBUG
|
|
|
583230 |
+// #define BPF_ASM_DEBUG
|
|
|
583230 |
|
|
|
583230 |
struct asm_stmt {
|
|
|
583230 |
std::string kind;
|
|
|
583230 |
@@ -621,6 +626,8 @@ struct asm_stmt {
|
|
|
583230 |
int64_t off, imm;
|
|
|
583230 |
|
|
|
583230 |
// metadata for jmp instructions
|
|
|
583230 |
+ // ??? The logic around these flags could be pruned a bit.
|
|
|
583230 |
+ bool has_jmp_target = false;
|
|
|
583230 |
bool has_fallthrough = false;
|
|
|
583230 |
std::string jmp_target, fallthrough;
|
|
|
583230 |
|
|
|
583230 |
@@ -650,6 +657,19 @@ operator << (std::ostream& o, const asm_stmt& stmt)
|
|
|
583230 |
<< stmt.imm << ";"
|
|
|
583230 |
<< (stmt.has_fallthrough ? " +FALLTHROUGH " + stmt.fallthrough : "");
|
|
|
583230 |
}
|
|
|
583230 |
+ else if (stmt.kind == "alloc")
|
|
|
583230 |
+ {
|
|
|
583230 |
+ o << "alloc, " << stmt.dest << ", " << stmt.imm << ";";
|
|
|
583230 |
+ }
|
|
|
583230 |
+ else if (stmt.kind == "call")
|
|
|
583230 |
+ {
|
|
|
583230 |
+ o << "call, " << stmt.dest << ", ";
|
|
|
583230 |
+ for (unsigned k = 0; k < stmt.params.size(); k++)
|
|
|
583230 |
+ {
|
|
|
583230 |
+ o << stmt.params[k];
|
|
|
583230 |
+ o << (k >= stmt.params.size() - 1 ? ";" : ", ");
|
|
|
583230 |
+ }
|
|
|
583230 |
+ }
|
|
|
583230 |
else
|
|
|
583230 |
o << "<unknown asm_stmt kind '" << stmt.kind << "'>";
|
|
|
583230 |
return o;
|
|
|
583230 |
@@ -711,7 +731,7 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start,
|
|
|
583230 |
{
|
|
|
583230 |
char c = code[pos];
|
|
|
583230 |
char c2 = pos + 1 < n ? code [pos + 1] : 0;
|
|
|
583230 |
- if (isspace(c))
|
|
|
583230 |
+ if (isspace(c) && !in_string)
|
|
|
583230 |
continue; // skip
|
|
|
583230 |
else if (in_comment)
|
|
|
583230 |
{
|
|
|
583230 |
@@ -799,6 +819,10 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start,
|
|
|
583230 |
adjusted_loc.column++;
|
|
|
583230 |
}
|
|
|
583230 |
|
|
|
583230 |
+ // Now populate the statement data.
|
|
|
583230 |
+
|
|
|
583230 |
+ stmt = asm_stmt(); // clear pre-existing data
|
|
|
583230 |
+
|
|
|
583230 |
// set token with adjusted source location
|
|
|
583230 |
stmt.tok = s->tok->adjust_location(adjusted_loc);
|
|
|
583230 |
adjusted_toks.push_back(stmt.tok);
|
|
|
583230 |
@@ -830,14 +854,7 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start,
|
|
|
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 |
+ assert(stmt.params.empty());
|
|
|
583230 |
for (unsigned k = 2; k < args.size(); k++)
|
|
|
583230 |
stmt.params.push_back(args[k]);
|
|
|
583230 |
}
|
|
|
583230 |
@@ -855,16 +872,16 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start,
|
|
|
583230 |
stmt.dest = args[1];
|
|
|
583230 |
stmt.src1 = args[2];
|
|
|
583230 |
|
|
|
583230 |
- bool has_jmp_target =
|
|
|
583230 |
+ stmt.has_jmp_target =
|
|
|
583230 |
BPF_CLASS(stmt.code) == BPF_JMP
|
|
|
583230 |
&& BPF_OP(stmt.code) != BPF_EXIT
|
|
|
583230 |
&& BPF_OP(stmt.code) != BPF_CALL;
|
|
|
583230 |
stmt.has_fallthrough = // only for jcond
|
|
|
583230 |
- has_jmp_target
|
|
|
583230 |
+ stmt.has_jmp_target
|
|
|
583230 |
&& BPF_OP(stmt.code) != BPF_JA;
|
|
|
583230 |
// XXX: stmt.fallthrough is computed by visit_embeddedcode
|
|
|
583230 |
|
|
|
583230 |
- if (has_jmp_target)
|
|
|
583230 |
+ if (stmt.has_jmp_target)
|
|
|
583230 |
{
|
|
|
583230 |
stmt.off = 0;
|
|
|
583230 |
stmt.jmp_target = args[3];
|
|
|
583230 |
@@ -1168,6 +1185,7 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s)
|
|
|
583230 |
|
|
|
583230 |
if (after_jump != NULL && stmt.kind == "label")
|
|
|
583230 |
{
|
|
|
583230 |
+ after_jump->has_fallthrough = true;
|
|
|
583230 |
after_jump->fallthrough = stmt.dest;
|
|
|
583230 |
}
|
|
|
583230 |
else if (after_jump != NULL)
|
|
|
583230 |
@@ -1183,6 +1201,7 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s)
|
|
|
583230 |
label_map[fallthrough_label] = b;
|
|
|
583230 |
set_block(b);
|
|
|
583230 |
|
|
|
583230 |
+ after_jump->has_fallthrough = true;
|
|
|
583230 |
after_jump->fallthrough = fallthrough_label;
|
|
|
583230 |
}
|
|
|
583230 |
|
|
|
583230 |
@@ -1205,6 +1224,12 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s)
|
|
|
583230 |
after_label = false;
|
|
|
583230 |
after_jump = &*it; // be sure to refer to original, not copied stmt
|
|
|
583230 |
}
|
|
|
583230 |
+ else if (stmt.kind == "opcode" && BPF_CLASS(stmt.code) == BPF_JMP
|
|
|
583230 |
+ && BPF_OP(stmt.code) != BPF_CALL /* CALL stays in the same block */)
|
|
|
583230 |
+ {
|
|
|
583230 |
+ after_label = false;
|
|
|
583230 |
+ after_jump = &*it; // be sure to refer to original, not copied stmt
|
|
|
583230 |
+ }
|
|
|
583230 |
else
|
|
|
583230 |
{
|
|
|
583230 |
after_label = false;
|
|
|
583230 |
@@ -1216,7 +1241,7 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s)
|
|
|
583230 |
"fallthrough on final asm_stmt"), stmt.tok);
|
|
|
583230 |
|
|
|
583230 |
// emit statements
|
|
|
583230 |
- bool jumped_already = true;
|
|
|
583230 |
+ bool jumped_already = false;
|
|
|
583230 |
set_block(entry_block);
|
|
|
583230 |
for (std::vector<asm_stmt>::iterator it = statements.begin();
|
|
|
583230 |
it != statements.end(); it++)
|
|
|
583230 |
@@ -1234,24 +1259,27 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s)
|
|
|
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 |
+ int ofs = -this_prog.max_tmp_space - stmt.imm;
|
|
|
583230 |
this_prog.use_tmp_space(-ofs);
|
|
|
583230 |
+ // ??? Consider using a storage allocator and this_prog.new_obj().
|
|
|
583230 |
+
|
|
|
583230 |
+ value *dest = get_asm_reg(stmt, stmt.dest);
|
|
|
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 |
+ assert (!stmt.params.empty());
|
|
|
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 |
+ // ??? For diagnostics: 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 |
+ // ??? Could make params optional to avoid the MOVs,
|
|
|
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 |
@@ -1259,30 +1287,117 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s)
|
|
|
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 |
+ if (stmt.dest != "-")
|
|
|
583230 |
+ {
|
|
|
583230 |
+ value *dest = get_asm_reg(stmt, stmt.dest);
|
|
|
583230 |
+ this_prog.mk_mov(this_ins, dest,
|
|
|
583230 |
+ this_prog.lookup_reg(BPF_REG_0) /* returnval */);
|
|
|
583230 |
+ }
|
|
|
583230 |
+ // ??? For diagnostics: check other cases with stmt.dest.
|
|
|
583230 |
+ }
|
|
|
583230 |
+ else if (func_name == "printf" || func_name == "sprintf")
|
|
|
583230 |
+ {
|
|
|
583230 |
+ if (stmt.params.size() < 2)
|
|
|
583230 |
+ throw SEMANTIC_ERROR (_F("bpf embeddedcode '%s' expects format string, "
|
|
|
583230 |
+ "none provided", func_name.c_str()),
|
|
|
583230 |
+ stmt.tok);
|
|
|
583230 |
+ std::string format = stmt.params[1];
|
|
|
583230 |
+ if (format.size() < 2 || format[0] != '"'
|
|
|
583230 |
+ || format[format.size()-1] != '"')
|
|
|
583230 |
+ throw SEMANTIC_ERROR (_F("bpf embeddedcode '%s' expects format string, "
|
|
|
583230 |
+ "but first parameter is not a string literal",
|
|
|
583230 |
+ func_name.c_str()), stmt.tok);
|
|
|
583230 |
+ format = format.substr(1,format.size()-2); /* strip quotes */
|
|
|
583230 |
+ format = translate_escapes(format);
|
|
|
583230 |
+
|
|
|
583230 |
+ bool print_to_stream = (func_name == "printf");
|
|
|
583230 |
+ if (print_to_stream)
|
|
|
583230 |
+ print_format_add_tag(format);
|
|
|
583230 |
+
|
|
|
583230 |
+ size_t format_bytes = format.size() + 1;
|
|
|
583230 |
+ if (format_bytes > BPF_MAXFORMATLEN)
|
|
|
583230 |
+ throw SEMANTIC_ERROR(_("Format string for print too long"), stmt.tok);
|
|
|
583230 |
+
|
|
|
583230 |
+ std::vector<value *> args;
|
|
|
583230 |
+ for (unsigned k = 2; k < stmt.params.size(); k++)
|
|
|
583230 |
+ args.push_back(emit_asm_arg(stmt, stmt.params[k]));
|
|
|
583230 |
+ if (args.size() > 3)
|
|
|
583230 |
+ throw SEMANTIC_ERROR(_NF("additional argument to print",
|
|
|
583230 |
+ "too many arguments to print (%zu)",
|
|
|
583230 |
+ args.size(), args.size()), stmt.tok);
|
|
|
583230 |
+
|
|
|
583230 |
+ value *retval = emit_print_format(format, args, print_to_stream);
|
|
|
583230 |
+ if (retval != NULL && stmt.dest != "-")
|
|
|
583230 |
+ {
|
|
|
583230 |
+ value *dest = get_asm_reg(stmt, stmt.dest);
|
|
|
583230 |
+ this_prog.mk_mov(this_ins, dest, retval);
|
|
|
583230 |
+
|
|
|
583230 |
+ }
|
|
|
583230 |
+ // ??? For diagnostics: check other cases with retval and stmt.dest.
|
|
|
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 |
+ // TODO: Experimental code for supporting basic functioncalls.
|
|
|
583230 |
+ // Needs improvement and simplification to work with full generality.
|
|
|
583230 |
+ // But thus far, it is sufficient for calling exit().
|
|
|
583230 |
+#if 1
|
|
|
583230 |
+ if (func_name != "exit")
|
|
|
583230 |
+ throw SEMANTIC_ERROR(_("BUG: bpf embeddedcode non-helper 'call' operation only supports printf(),sprintf(),exit() for now"), stmt.tok);
|
|
|
583230 |
+#elif 1
|
|
|
583230 |
+ throw SEMANTIC_ERROR(_("BUG: bpf embeddedcode non-helper 'call' operation only supports printf(),sprintf() for now"), stmt.tok);
|
|
|
583230 |
+#endif
|
|
|
583230 |
+#if 1
|
|
|
583230 |
+ // ???: Passing systemtap_session through all the way to here
|
|
|
583230 |
+ // seems intrusive, but less intrusive than moving
|
|
|
583230 |
+ // embedded-code assembly to the translate_globals() pass.
|
|
|
583230 |
+ symresolution_info sym (*glob.session);
|
|
|
583230 |
+ functioncall *call = new functioncall;
|
|
|
583230 |
+ call->tok = stmt.tok;
|
|
|
583230 |
+ unsigned nargs = stmt.params.size() - 1;
|
|
|
583230 |
+ std::vector<functiondecl*> fds
|
|
|
583230 |
+ = sym.find_functions (call, func_name, nargs, stmt.tok);
|
|
|
583230 |
+ delete call;
|
|
|
583230 |
+
|
|
|
583230 |
+ if (fds.empty())
|
|
|
583230 |
+ // ??? Could call levenshtein_suggest() as in
|
|
|
583230 |
+ // symresolution_info::visit_functioncall().
|
|
|
583230 |
+ throw SEMANTIC_ERROR(_("bpf embeddedcode unresolved function call"), stmt.tok);
|
|
|
583230 |
+ if (fds.size() > 1)
|
|
|
583230 |
+ throw SEMANTIC_ERROR(_("bpf embeddedcode unhandled function overloading"), stmt.tok);
|
|
|
583230 |
+ functiondecl *f = fds[0];
|
|
|
583230 |
+ // TODO: Imitation of semantic_pass_symbols, does not
|
|
|
583230 |
+ // cover full generality of the lookup process.
|
|
|
583230 |
+ update_visitor_loop (*glob.session, glob.session->code_filters, f->body);
|
|
|
583230 |
+ sym.current_function = f; sym.current_probe = 0;
|
|
|
583230 |
+ f->body->visit (&sym);
|
|
|
583230 |
+
|
|
|
583230 |
+ // ??? For now, always inline the function call.
|
|
|
583230 |
+ for (auto i = func_calls.begin(); i != func_calls.end(); ++i)
|
|
|
583230 |
+ if (f == *i)
|
|
|
583230 |
+ throw SEMANTIC_ERROR (_("unhandled function recursion"), stmt.tok);
|
|
|
583230 |
+
|
|
|
583230 |
+ // Collect the function arguments.
|
|
|
583230 |
+ std::vector<value *> args;
|
|
|
583230 |
+ for (unsigned k = 1; k < stmt.params.size(); k++)
|
|
|
583230 |
+ args.push_back(emit_asm_arg(stmt, stmt.params[k]));
|
|
|
583230 |
+
|
|
|
583230 |
+ if (args.size () != f->formal_args.size())
|
|
|
583230 |
+ throw SEMANTIC_ERROR(_F("bpf embeddedcode call to function '%s' "
|
|
|
583230 |
+ "expected %zu arguments, got %zu",
|
|
|
583230 |
+ func_name.c_str(),
|
|
|
583230 |
+ f->formal_args.size(), args.size()),
|
|
|
583230 |
+ stmt.tok);
|
|
|
583230 |
+
|
|
|
583230 |
+ value *retval = emit_functioncall(f, args);
|
|
|
583230 |
+ if (stmt.dest != "-")
|
|
|
583230 |
+ {
|
|
|
583230 |
+ value *dest = get_asm_reg(stmt, stmt.dest);
|
|
|
583230 |
+ this_prog.mk_mov(this_ins, dest, retval);
|
|
|
583230 |
+ }
|
|
|
583230 |
+ // ??? For diagnostics: check other cases with retval and stmt.dest.
|
|
|
583230 |
+#endif
|
|
|
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 |
@@ -1291,9 +1406,13 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s)
|
|
|
583230 |
throw SEMANTIC_ERROR (_F("BUG: bpf embeddedcode contains unexpected "
|
|
|
583230 |
"asm_stmt kind '%s'", stmt.kind.c_str()),
|
|
|
583230 |
stmt.tok);
|
|
|
583230 |
- jumped_already = stmt.has_fallthrough;
|
|
|
583230 |
if (stmt.has_fallthrough)
|
|
|
583230 |
- set_block(label_map[stmt.fallthrough]);
|
|
|
583230 |
+ {
|
|
|
583230 |
+ jumped_already = true;
|
|
|
583230 |
+ set_block(label_map[stmt.fallthrough]);
|
|
|
583230 |
+ }
|
|
|
583230 |
+ else
|
|
|
583230 |
+ jumped_already = false;
|
|
|
583230 |
}
|
|
|
583230 |
|
|
|
583230 |
// housekeeping -- deallocate adjusted_toks along with statements
|
|
|
583230 |
@@ -1779,7 +1898,8 @@ bpf_unparser::visit_logical_and_expr (logical_and_expr* e)
|
|
|
583230 |
result = emit_bool (e);
|
|
|
583230 |
}
|
|
|
583230 |
|
|
|
583230 |
-// TODO: This matches translate.cxx, but it looks like the functionality is disabled in the parser.
|
|
|
583230 |
+// ??? This matches the code in translate.cxx, but it looks like the
|
|
|
583230 |
+// functionality has been disabled in the SystemTap parser.
|
|
|
583230 |
void
|
|
|
583230 |
bpf_unparser::visit_compound_expression (compound_expression* e)
|
|
|
583230 |
{
|
|
|
583230 |
@@ -2573,30 +2693,17 @@ bpf_unparser::emit_str_arg(value *arg, int ofs, value *str)
|
|
|
583230 |
emit_mov(arg, out);
|
|
|
583230 |
}
|
|
|
583230 |
|
|
|
583230 |
-void
|
|
|
583230 |
-bpf_unparser::visit_functioncall (functioncall *e)
|
|
|
583230 |
+value *
|
|
|
583230 |
+bpf_unparser::emit_functioncall (functiondecl *f, const std::vector<value *>& args)
|
|
|
583230 |
{
|
|
|
583230 |
- // ??? For now, always inline the function call.
|
|
|
583230 |
- // ??? Function overloading isn't handled.
|
|
|
583230 |
- if (e->referents.size () != 1)
|
|
|
583230 |
- throw SEMANTIC_ERROR (_("unhandled function overloading"), e->tok);
|
|
|
583230 |
- functiondecl *f = e->referents[0];
|
|
|
583230 |
-
|
|
|
583230 |
- for (auto i = func_calls.begin(); i != func_calls.end(); ++i)
|
|
|
583230 |
- if (f == *i)
|
|
|
583230 |
- throw SEMANTIC_ERROR (_("unhandled function recursion"), e->tok);
|
|
|
583230 |
-
|
|
|
583230 |
- assert (e->args.size () == f->formal_args.size ());
|
|
|
583230 |
-
|
|
|
583230 |
// Create a new map for the function's local variables.
|
|
|
583230 |
locals_map *locals = new_locals(f->locals);
|
|
|
583230 |
|
|
|
583230 |
- // Evaluate the function arguments and install in the map.
|
|
|
583230 |
- for (unsigned n = e->args.size (), i = 0; i < n; ++i)
|
|
|
583230 |
+ // Install locals in the map.
|
|
|
583230 |
+ unsigned n = args.size();
|
|
|
583230 |
+ for (unsigned i = 0; i < n; ++i)
|
|
|
583230 |
{
|
|
|
583230 |
- value *r = this_prog.new_reg ();
|
|
|
583230 |
- emit_mov (r, emit_expr (e->args[i]));
|
|
|
583230 |
- const locals_map::value_type v (f->formal_args[i], r);
|
|
|
583230 |
+ const locals_map::value_type v (f->formal_args[i], args[i]);
|
|
|
583230 |
auto ok = locals->insert (v);
|
|
|
583230 |
assert (ok.second);
|
|
|
583230 |
}
|
|
|
583230 |
@@ -2622,7 +2729,47 @@ bpf_unparser::visit_functioncall (functioncall *e)
|
|
|
583230 |
this_locals = old_locals;
|
|
|
583230 |
delete locals;
|
|
|
583230 |
|
|
|
583230 |
- result = retval;
|
|
|
583230 |
+ return retval;
|
|
|
583230 |
+}
|
|
|
583230 |
+
|
|
|
583230 |
+void
|
|
|
583230 |
+bpf_unparser::visit_functioncall (functioncall *e)
|
|
|
583230 |
+{
|
|
|
583230 |
+ // ??? Function overloading isn't handled.
|
|
|
583230 |
+ if (e->referents.size () != 1)
|
|
|
583230 |
+ throw SEMANTIC_ERROR (_("unhandled function overloading"), e->tok);
|
|
|
583230 |
+ functiondecl *f = e->referents[0];
|
|
|
583230 |
+
|
|
|
583230 |
+ // ??? For now, always inline the function call.
|
|
|
583230 |
+ for (auto i = func_calls.begin(); i != func_calls.end(); ++i)
|
|
|
583230 |
+ if (f == *i)
|
|
|
583230 |
+ throw SEMANTIC_ERROR (_("unhandled function recursion"), e->tok);
|
|
|
583230 |
+
|
|
|
583230 |
+ // XXX: Should have been checked in earlier pass.
|
|
|
583230 |
+ assert (e->args.size () == f->formal_args.size ());
|
|
|
583230 |
+
|
|
|
583230 |
+ // Evaluate and collect the function arguments.
|
|
|
583230 |
+ std::vector<value *> args;
|
|
|
583230 |
+ for (unsigned n = e->args.size (), i = 0; i < n; ++i)
|
|
|
583230 |
+ {
|
|
|
583230 |
+ value *r = this_prog.new_reg ();
|
|
|
583230 |
+ emit_mov (r, emit_expr (e->args[i]));
|
|
|
583230 |
+ args.push_back(r);
|
|
|
583230 |
+ }
|
|
|
583230 |
+
|
|
|
583230 |
+ result = emit_functioncall(f, args);
|
|
|
583230 |
+}
|
|
|
583230 |
+
|
|
|
583230 |
+static void
|
|
|
583230 |
+print_format_add_tag(std::string& format)
|
|
|
583230 |
+{
|
|
|
583230 |
+ // surround the string with <MODNAME>...</MODNAME> to facilitate
|
|
|
583230 |
+ // stapbpf recovering it from debugfs.
|
|
|
583230 |
+ std::string start_tag = module_name;
|
|
|
583230 |
+ start_tag = "<" + start_tag.erase(4,1) + ">";
|
|
|
583230 |
+ std::string end_tag = start_tag + "\n";
|
|
|
583230 |
+ end_tag.insert(1, "/");
|
|
|
583230 |
+ format = start_tag + format + end_tag;
|
|
|
583230 |
}
|
|
|
583230 |
|
|
|
583230 |
static void
|
|
|
583230 |
@@ -2677,6 +2824,32 @@ print_format_add_tag(print_format *e)
|
|
|
583230 |
}
|
|
|
583230 |
}
|
|
|
583230 |
|
|
|
583230 |
+value *
|
|
|
583230 |
+bpf_unparser::emit_print_format (const std::string& format,
|
|
|
583230 |
+ const std::vector<value *>& actual,
|
|
|
583230 |
+ bool print_to_stream)
|
|
|
583230 |
+{
|
|
|
583230 |
+ size_t nargs = actual.size();
|
|
|
583230 |
+
|
|
|
583230 |
+ // The bpf verifier requires that the format string be stored on the
|
|
|
583230 |
+ // bpf program stack. This is handled by bpf-opt.cxx lowering STR values.
|
|
|
583230 |
+ size_t format_bytes = format.size() + 1;
|
|
|
583230 |
+ this_prog.mk_mov(this_ins, this_prog.lookup_reg(BPF_REG_1),
|
|
|
583230 |
+ this_prog.new_str(format));
|
|
|
583230 |
+ emit_mov(this_prog.lookup_reg(BPF_REG_2), this_prog.new_imm(format_bytes));
|
|
|
583230 |
+ for (size_t i = 0; i < nargs; ++i)
|
|
|
583230 |
+ emit_mov(this_prog.lookup_reg(BPF_REG_3 + i), actual[i]);
|
|
|
583230 |
+
|
|
|
583230 |
+ if (print_to_stream)
|
|
|
583230 |
+ this_prog.mk_call(this_ins, BPF_FUNC_trace_printk, nargs + 2);
|
|
|
583230 |
+ else
|
|
|
583230 |
+ {
|
|
|
583230 |
+ this_prog.mk_call(this_ins, BPF_FUNC_sprintf, nargs + 2);
|
|
|
583230 |
+ return this_prog.lookup_reg(BPF_REG_0);
|
|
|
583230 |
+ }
|
|
|
583230 |
+ return NULL;
|
|
|
583230 |
+}
|
|
|
583230 |
+
|
|
|
583230 |
void
|
|
|
583230 |
bpf_unparser::visit_print_format (print_format *e)
|
|
|
583230 |
{
|
|
|
583230 |
@@ -2696,9 +2869,9 @@ bpf_unparser::visit_print_format (print_format *e)
|
|
|
583230 |
"too many arguments to print (%zu)",
|
|
|
583230 |
e->args.size(), e->args.size()), e->tok);
|
|
|
583230 |
|
|
|
583230 |
- value *actual[3] = { NULL, NULL, NULL };
|
|
|
583230 |
+ std::vector<value *> actual;
|
|
|
583230 |
for (i = 0; i < nargs; ++i)
|
|
|
583230 |
- actual[i] = emit_expr(e->args[i]);
|
|
|
583230 |
+ actual.push_back(emit_expr(e->args[i]));
|
|
|
583230 |
|
|
|
583230 |
std::string format;
|
|
|
583230 |
if (e->print_with_format)
|
|
|
583230 |
@@ -2750,36 +2923,17 @@ bpf_unparser::visit_print_format (print_format *e)
|
|
|
583230 |
if (e->print_with_newline)
|
|
|
583230 |
format += '\n';
|
|
|
583230 |
|
|
|
583230 |
- // surround the string with <MODNAME>...</MODNAME> to facilitate
|
|
|
583230 |
- // stapbpf recovering it from debugfs.
|
|
|
583230 |
if (e->print_to_stream)
|
|
|
583230 |
- {
|
|
|
583230 |
- std::string start_tag = module_name;
|
|
|
583230 |
- start_tag = "<" + start_tag.erase(4,1) + ">";
|
|
|
583230 |
- std::string end_tag = start_tag + "\n";
|
|
|
583230 |
- end_tag.insert(1, "/");
|
|
|
583230 |
- format = start_tag + format + end_tag;
|
|
|
583230 |
- }
|
|
|
583230 |
+ print_format_add_tag(format);
|
|
|
583230 |
}
|
|
|
583230 |
|
|
|
583230 |
- // The bpf verifier requires that the format string be stored on the
|
|
|
583230 |
- // bpf program stack. This is handled by bpf-opt.cxx lowering STR values.
|
|
|
583230 |
size_t format_bytes = format.size() + 1;
|
|
|
583230 |
if (format_bytes > BPF_MAXFORMATLEN)
|
|
|
583230 |
throw SEMANTIC_ERROR(_("Format string for print too long"), e->tok);
|
|
|
583230 |
- this_prog.mk_mov(this_ins, this_prog.lookup_reg(BPF_REG_1),
|
|
|
583230 |
- this_prog.new_str(format));
|
|
|
583230 |
- emit_mov(this_prog.lookup_reg(BPF_REG_2), this_prog.new_imm(format_bytes));
|
|
|
583230 |
- for (i = 0; i < nargs; ++i)
|
|
|
583230 |
- emit_mov(this_prog.lookup_reg(BPF_REG_3 + i), actual[i]);
|
|
|
583230 |
|
|
|
583230 |
- if (e->print_to_stream)
|
|
|
583230 |
- this_prog.mk_call(this_ins, BPF_FUNC_trace_printk, nargs + 2);
|
|
|
583230 |
- else
|
|
|
583230 |
- {
|
|
|
583230 |
- this_prog.mk_call(this_ins, BPF_FUNC_sprintf, nargs + 2);
|
|
|
583230 |
- result = this_prog.lookup_reg(BPF_REG_0);
|
|
|
583230 |
- }
|
|
|
583230 |
+ value *retval = emit_print_format(format, actual, e->print_to_stream);
|
|
|
583230 |
+ if (retval != NULL)
|
|
|
583230 |
+ result = retval;
|
|
|
583230 |
}
|
|
|
583230 |
|
|
|
583230 |
// } // anon namespace
|
|
|
583230 |
@@ -3409,7 +3563,7 @@ translate_bpf_pass (systemtap_session& s)
|
|
|
583230 |
return 1;
|
|
|
583230 |
|
|
|
583230 |
BPF_Output eo(fd);
|
|
|
583230 |
- globals glob;
|
|
|
583230 |
+ globals glob; glob.session = &s;
|
|
|
583230 |
int ret = 0;
|
|
|
583230 |
const token* t = 0;
|
|
|
583230 |
try
|
|
|
583230 |
--
|
|
|
583230 |
2.14.5
|
|
|
583230 |
|