bcc: Use bpf_probe_read_user in tools and provide backward compatibility

s390 has overlapping address space for user and kernel. Hence separation of
bpf_probe_read_user and bpf_probe_read_kernel is essential. Commit 6ae08ae3dea2
("bpf: Add probe_read_{user, kernel} and probe_read_{user, kernel}_str
helpers") introduced these changes into the kernel. However, bcc tools does not
respect it.

As a workaround, perform the following:
1. Use bpf_probe_read_user() explicitly in the bcc tools.
2. When kernel version < 5.5, perform the checks if the
   bpf_probe_read_user kernel helper is present in the backported kernel
   as well. If not found, then fallback from bpf_probe_read_user to
   bpf_probe_read.

Signed-off-by: Sumanth Korikkar <sumanthk@linux.ibm.com>
diff --git a/src/cc/export/helpers.h b/src/cc/export/helpers.h
index b38b3f2..b0ffc4f 100644
--- a/src/cc/export/helpers.h
+++ b/src/cc/export/helpers.h
@@ -602,6 +602,7 @@
 static int (*bpf_skb_output)(void *ctx, void *map, __u64 flags, void *data,
                              __u64 size) =
   (void *)BPF_FUNC_skb_output;
+
 static int (*bpf_probe_read_user)(void *dst, __u32 size,
                                   const void *unsafe_ptr) =
   (void *)BPF_FUNC_probe_read_user;
@@ -887,8 +888,8 @@
 #if defined(__TARGET_ARCH_x86)
 #define bpf_target_x86
 #define bpf_target_defined
-#elif defined(__TARGET_ARCH_s930x)
-#define bpf_target_s930x
+#elif defined(__TARGET_ARCH_s390x)
+#define bpf_target_s390x
 #define bpf_target_defined
 #elif defined(__TARGET_ARCH_arm64)
 #define bpf_target_arm64
@@ -905,7 +906,7 @@
 #if defined(__x86_64__)
 #define bpf_target_x86
 #elif defined(__s390x__)
-#define bpf_target_s930x
+#define bpf_target_s390x
 #elif defined(__aarch64__)
 #define bpf_target_arm64
 #elif defined(__powerpc__)
@@ -923,7 +924,7 @@
 #define PT_REGS_RC(ctx)		((ctx)->gpr[3])
 #define PT_REGS_IP(ctx)		((ctx)->nip)
 #define PT_REGS_SP(ctx)		((ctx)->gpr[1])
-#elif defined(bpf_target_s930x)
+#elif defined(bpf_target_s390x)
 #define PT_REGS_PARM1(x) ((x)->gprs[2])
 #define PT_REGS_PARM2(x) ((x)->gprs[3])
 #define PT_REGS_PARM3(x) ((x)->gprs[4])
diff --git a/src/cc/frontends/clang/b_frontend_action.cc b/src/cc/frontends/clang/b_frontend_action.cc
index 89511ef..6087c80 100644
--- a/src/cc/frontends/clang/b_frontend_action.cc
+++ b/src/cc/frontends/clang/b_frontend_action.cc
@@ -37,6 +37,7 @@
 #include "bcc_libbpf_inc.h"
 
 #include "libbpf.h"
+#include "bcc_syms.h"
 
 namespace ebpf {
 
@@ -82,6 +83,30 @@
   return ret;
 }
 
+static std::string check_bpf_probe_read_user(llvm::StringRef probe) {
+  if (probe.str() == "bpf_probe_read_user" ||
+      probe.str() == "bpf_probe_read_user_str") {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+    return probe.str();
+#else
+    // Check for probe_user symbols in backported kernels before fallback
+    void *resolver = bcc_symcache_new(-1, nullptr);
+    uint64_t addr = 0;
+    bool found = bcc_symcache_resolve_name(resolver, nullptr,
+                  "bpf_probe_read_user", &addr) >= 0 ? true: false;
+    if (found)
+      return probe.str();
+
+    if (probe.str() == "bpf_probe_read_user") {
+      return "bpf_probe_read";
+    } else {
+      return "bpf_probe_read_str";
+    }
+#endif
+  }
+  return "";
+}
+
 using std::map;
 using std::move;
 using std::set;
@@ -947,6 +972,22 @@
   } else if (Call->getCalleeDecl()) {
     NamedDecl *Decl = dyn_cast<NamedDecl>(Call->getCalleeDecl());
     if (!Decl) return true;
+
+    string text;
+
+    std::string probe = check_bpf_probe_read_user(Decl->getName());
+    if (probe != "") {
+      vector<string> probe_args;
+
+      for (auto arg : Call->arguments())
+        probe_args.push_back(
+            rewriter_.getRewrittenText(expansionRange(arg->getSourceRange())));
+
+      text = probe + "(" + probe_args[0] + ", " + probe_args[1] + ", " +
+             probe_args[2] + ")";
+      rewriter_.ReplaceText(expansionRange(Call->getSourceRange()), text);
+    }
+
     if (AsmLabelAttr *A = Decl->getAttr<AsmLabelAttr>()) {
       // Functions with the tag asm("llvm.bpf.extra") are implemented in the
       // rewriter rather than as a macro since they may also include nested
@@ -959,10 +1000,10 @@
         }
 
         vector<string> args;
+
         for (auto arg : Call->arguments())
           args.push_back(rewriter_.getRewrittenText(expansionRange(arg->getSourceRange())));
 
-        string text;
         if (Decl->getName() == "incr_cksum_l3") {
           text = "bpf_l3_csum_replace_(" + fn_args_[0]->getName().str() + ", (u64)";
           text += args[0] + ", " + args[1] + ", " + args[2] + ", sizeof(" + args[2] + "))";
@@ -994,8 +1035,10 @@
           text = "({ u64 __addr = 0x0; ";
           text += "_bpf_readarg_" + current_fn_ + "_" + args[0] + "(" +
                   args[1] + ", &__addr, sizeof(__addr));";
-          text += "bpf_probe_read(" + args[2] + ", " + args[3] +
-                  ", (void *)__addr);";
+
+          text += check_bpf_probe_read_user(StringRef("bpf_probe_read_user"));
+
+          text += "(" + args[2] + ", " + args[3] + ", (void *)__addr);";
           text += "})";
           rewriter_.ReplaceText(expansionRange(Call->getSourceRange()), text);
         } else if (Decl->getName() == "bpf_usdt_readarg") {
diff --git a/tools/ttysnoop.py b/tools/ttysnoop.py
index 058dc7e..24c1180 100755
--- a/tools/ttysnoop.py
+++ b/tools/ttysnoop.py
@@ -80,7 +80,7 @@
     // bpf_probe_read() can only use a fixed size, so truncate to count
     // in user space:
     struct data_t data = {};
-    bpf_probe_read(&data.buf, BUFSIZE, (void *)buf);
+    bpf_probe_read_user(&data.buf, BUFSIZE, (void *)buf);
     if (count > BUFSIZE)
         data.count = BUFSIZE;
     else