aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoryonghong-song <ys114321@gmail.com>2018-06-13 06:12:22 -0700
committerGitHub <noreply@github.com>2018-06-13 06:12:22 -0700
commit2da34267fcae4485f4e05a17521214749f6f0edd (patch)
tree032f5886be5d2a66c0346d7f6d0f2e1261da10bf /src
parenteebd4856b02fde16ee1d4f4ba02a9045bcb5b5e5 (diff)
downloadbcc-2da34267fcae4485f4e05a17521214749f6f0edd.tar.gz
generate indirect parameter assignment if arch uses syscall wrapper (#1816)
Fix issue #1802. On x64, the following commit (in 4.17) changed the raw parameter passed to the syscall entry function from a list of parameters supplied in user space to a single `pt_regs *` parameter. Also in 4.17, x64 syscall entry function is changed from `sys_<name>` to `__x64_sys_<name>`. ``` commit fa697140f9a20119a9ec8fd7460cc4314fbdaff3 Author: Dominik Brodowski <linux@dominikbrodowski.net> Date: Thu Apr 5 11:53:02 2018 +0200 syscalls/x86: Use 'struct pt_regs' based syscall calling convention for 64-bit syscalls Let's make use of ARCH_HAS_SYSCALL_WRAPPER=y on pure 64-bit x86-64 systems: Each syscall defines a stub which takes struct pt_regs as its only argument. It decodes just those parameters it needs, e.g: asmlinkage long sys_xyzzy(const struct pt_regs *regs) { return SyS_xyzzy(regs->di, regs->si, regs->dx); } This approach avoids leaking random user-provided register content down the call chain. ... ``` In bcc, we support kprobe function signatures in the bpf program. The rewriter will automatically generate proper assignment to these parameters. With the above function signature change, the original method does not work any more. This patch enhanced rewriter to generate two version codes guarded with CONFIG_ARCH_HAS_SYSCALL_WRAPPER. But we need to identify whether a function will be attached to syscall entry function or not during prog load time at which time the program has not attached to any event. The prefix `kprobe__` is used for kprobe autoload, we can use `kprobe____x64_sys_` as the prefix to identify x64 syscall entry functions. To support other architecture or not-autoloading program, the prefix `syscall__` is introduced to signal it is a syscall entry function. trace.py and other tools which uses kprobe syscall entry functions are also modified with the new interface so that they can work properly with 4.17. Signed-off-by: Yonghong Song <yhs@fb.com>
Diffstat (limited to 'src')
-rw-r--r--src/cc/frontends/clang/b_frontend_action.cc102
-rw-r--r--src/cc/frontends/clang/b_frontend_action.h5
2 files changed, 83 insertions, 24 deletions
diff --git a/src/cc/frontends/clang/b_frontend_action.cc b/src/cc/frontends/clang/b_frontend_action.cc
index d7423f82..a1e1bda8 100644
--- a/src/cc/frontends/clang/b_frontend_action.cc
+++ b/src/cc/frontends/clang/b_frontend_action.cc
@@ -429,9 +429,83 @@ DiagnosticBuilder ProbeVisitor::error(SourceLocation loc, const char (&fmt)[N])
BTypeVisitor::BTypeVisitor(ASTContext &C, BFrontendAction &fe)
: C(C), diag_(C.getDiagnostics()), fe_(fe), rewriter_(fe.rewriter()), out_(llvm::errs()) {}
-bool BTypeVisitor::VisitFunctionDecl(FunctionDecl *D) {
+void BTypeVisitor::genParamDirectAssign(FunctionDecl *D, string& preamble,
+ const char **calling_conv_regs) {
+ for (size_t idx = 0; idx < fn_args_.size(); idx++) {
+ ParmVarDecl *arg = fn_args_[idx];
+
+ if (idx >= 1) {
+ // Move the args into a preamble section where the same params are
+ // declared and initialized from pt_regs.
+ // Todo: this init should be done only when the program requests it.
+ string text = rewriter_.getRewrittenText(expansionRange(arg->getSourceRange()));
+ arg->addAttr(UnavailableAttr::CreateImplicit(C, "ptregs"));
+ size_t d = idx - 1;
+ const char *reg = calling_conv_regs[d];
+ preamble += " " + text + " = " + fn_args_[0]->getName().str() + "->" +
+ string(reg) + ";";
+ }
+ }
+}
+
+void BTypeVisitor::genParamIndirectAssign(FunctionDecl *D, string& preamble,
+ const char **calling_conv_regs) {
+ string new_ctx;
+
+ for (size_t idx = 0; idx < fn_args_.size(); idx++) {
+ ParmVarDecl *arg = fn_args_[idx];
+
+ if (idx == 0) {
+ new_ctx = "__" + arg->getName().str();
+ preamble += " struct pt_regs * " + new_ctx + " = " +
+ arg->getName().str() + "->" +
+ string(calling_conv_regs[0]) + ";";
+ } else {
+ // Move the args into a preamble section where the same params are
+ // declared and initialized from pt_regs.
+ // Todo: this init should be done only when the program requests it.
+ string text = rewriter_.getRewrittenText(expansionRange(arg->getSourceRange()));
+ size_t d = idx - 1;
+ const char *reg = calling_conv_regs[d];
+ preamble += "\n " + text + ";";
+ preamble += " bpf_probe_read(&" + arg->getName().str() + ", sizeof(" +
+ arg->getName().str() + "), &" + new_ctx + "->" +
+ string(reg) + ");";
+ }
+ }
+}
+
+void BTypeVisitor::rewriteFuncParam(FunctionDecl *D) {
const char **calling_conv_regs = get_call_conv();
+ string preamble = "{\n";
+ if (D->param_size() > 1) {
+ // If function prefix is "syscall__" or "kprobe____x64_sys_",
+ // the function will attach to a kprobe syscall function.
+ // Guard parameter assiggnment with CONFIG_ARCH_HAS_SYSCALL_WRAPPER.
+ // For __x64_sys_* syscalls, this is always true, but we guard
+ // it in case of "syscall__" for other architectures.
+ if (strncmp(D->getName().str().c_str(), "syscall__", 9) == 0 ||
+ strncmp(D->getName().str().c_str(), "kprobe____x64_sys_", 18) == 0) {
+ preamble += "#ifdef CONFIG_ARCH_HAS_SYSCALL_WRAPPER\n";
+ genParamIndirectAssign(D, preamble, calling_conv_regs);
+ preamble += "\n#else\n";
+ genParamDirectAssign(D, preamble, calling_conv_regs);
+ preamble += "\n#endif\n";
+ } else {
+ genParamDirectAssign(D, preamble, calling_conv_regs);
+ }
+ rewriter_.ReplaceText(
+ expansionRange(SourceRange(D->getParamDecl(0)->getLocEnd(),
+ D->getParamDecl(D->getNumParams() - 1)->getLocEnd())),
+ fn_args_[0]->getName());
+ }
+ // for each trace argument, convert the variable from ptregs to something on stack
+ if (CompoundStmt *S = dyn_cast<CompoundStmt>(D->getBody()))
+ rewriter_.ReplaceText(S->getLBracLoc(), 1, preamble);
+}
+
+bool BTypeVisitor::VisitFunctionDecl(FunctionDecl *D) {
// put each non-static non-inline function decl in its own section, to be
// extracted by the MemoryManager
auto real_start_loc = rewriter_.getSourceMgr().getFileLoc(D->getLocStart());
@@ -447,37 +521,17 @@ bool BTypeVisitor::VisitFunctionDecl(FunctionDecl *D) {
"too many arguments, bcc only supports in-register parameters");
return false;
}
- // remember the arg names of the current function...first one is the ctx
+
fn_args_.clear();
- string preamble = "{";
for (auto arg_it = D->param_begin(); arg_it != D->param_end(); arg_it++) {
- auto arg = *arg_it;
+ auto *arg = *arg_it;
if (arg->getName() == "") {
error(arg->getLocEnd(), "arguments to BPF program definition must be named");
return false;
}
fn_args_.push_back(arg);
- if (fn_args_.size() > 1) {
- // Move the args into a preamble section where the same params are
- // declared and initialized from pt_regs.
- // Todo: this init should be done only when the program requests it.
- string text = rewriter_.getRewrittenText(expansionRange(arg->getSourceRange()));
- arg->addAttr(UnavailableAttr::CreateImplicit(C, "ptregs"));
- size_t d = fn_args_.size() - 2;
- const char *reg = calling_conv_regs[d];
- preamble += " " + text + " = " + fn_args_[0]->getName().str() + "->" +
- string(reg) + ";";
- }
- }
- if (D->param_size() > 1) {
- rewriter_.ReplaceText(
- expansionRange(SourceRange(D->getParamDecl(0)->getLocEnd(),
- D->getParamDecl(D->getNumParams() - 1)->getLocEnd())),
- fn_args_[0]->getName());
}
- // for each trace argument, convert the variable from ptregs to something on stack
- if (CompoundStmt *S = dyn_cast<CompoundStmt>(D->getBody()))
- rewriter_.ReplaceText(S->getLBracLoc(), 1, preamble);
+ rewriteFuncParam(D);
} else if (D->hasBody() &&
rewriter_.getSourceMgr().getFileID(real_start_loc)
== rewriter_.getSourceMgr().getMainFileID()) {
diff --git a/src/cc/frontends/clang/b_frontend_action.h b/src/cc/frontends/clang/b_frontend_action.h
index f5c3b442..72498db2 100644
--- a/src/cc/frontends/clang/b_frontend_action.h
+++ b/src/cc/frontends/clang/b_frontend_action.h
@@ -70,6 +70,11 @@ class BTypeVisitor : public clang::RecursiveASTVisitor<BTypeVisitor> {
private:
clang::SourceRange expansionRange(clang::SourceRange range);
bool checkFormatSpecifiers(const std::string& fmt, clang::SourceLocation loc);
+ void genParamDirectAssign(clang::FunctionDecl *D, std::string& preamble,
+ const char **calling_conv_regs);
+ void genParamIndirectAssign(clang::FunctionDecl *D, std::string& preamble,
+ const char **calling_conv_regs);
+ void rewriteFuncParam(clang::FunctionDecl *D);
template <unsigned N>
clang::DiagnosticBuilder error(clang::SourceLocation loc, const char (&fmt)[N]);
template <unsigned N>