diff --git a/.gitignore b/.gitignore
index d41f27e..e49c437 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1 @@
-SOURCES/systemtap-3.3.tar.gz
+SOURCES/systemtap-4.0.tar.gz
diff --git a/.systemtap.metadata b/.systemtap.metadata
index ba55853..eb6d659 100644
--- a/.systemtap.metadata
+++ b/.systemtap.metadata
@@ -1 +1 @@
-cc065bb1047f75ae0255709185e1e7ba889e7e5e SOURCES/systemtap-3.3.tar.gz
+40a21d71b0d42bc216f75befd3fca82701821211 SOURCES/systemtap-4.0.tar.gz
diff --git a/SOURCES/bpf-unwarn.patch b/SOURCES/bpf-unwarn.patch
new file mode 100644
index 0000000..acf7e7e
--- /dev/null
+++ b/SOURCES/bpf-unwarn.patch
@@ -0,0 +1,49 @@
+commit cbf3b6e6ac7d26d6667b306b9d8677678624bec8
+Author: Frank Ch. Eigler <fche@redhat.com>
+Date:   Thu Nov 15 22:02:18 2018 -0500
+
+    bpf-translate.cxx: quiet a compiler warning
+    
+    It's a false positive with rawhide gcc, but easy to quiet anyway.
+
+diff --git a/bpf-translate.cxx b/bpf-translate.cxx
+index b382a2e..78a5a8d 100644
+--- a/bpf-translate.cxx
++++ b/bpf-translate.cxx
+@@ -1150,6 +1150,7 @@ bpf_unparser::emit_asm_opcode (const asm_stmt &stmt,
+                                 stmt.fallthrough.c_str()), stmt.tok);
+       block *target = label_map[stmt.jmp_target];
+       block *fallthrough = label_map[stmt.fallthrough];
++      c = EQ; // defeat compiler warning about uninitialized c
+       this_prog.mk_jcond(this_ins, c, v_dest, v_src1, target, fallthrough);
+     }
+   else // regular opcode
+
+commit e6227e5df0851bea0e5a94b17fe0a82c280ed600
+Author: Serhei Makarov <smakarov@redhat.com>
+Date:   Fri Nov 16 12:12:07 2018 -0500
+
+    bpf-translate.cxx: don't clobber any earlier value of c
+
+diff --git a/bpf-translate.cxx b/bpf-translate.cxx
+index 78a5a8d..92f0ea4 100644
+--- a/bpf-translate.cxx
++++ b/bpf-translate.cxx
+@@ -1027,7 +1027,8 @@ bpf_unparser::emit_asm_opcode (const asm_stmt &stmt,
+     throw SEMANTIC_ERROR (_("invalid bpf code"), stmt.tok);
+ 
+   bool r_dest = false, r_src0 = false, r_src1 = false, i_src1 = false;
+-  bool op_jmp = false, op_jcond = false; condition c;
++  bool op_jmp = false, op_jcond = false;
++  condition c = EQ; // <- quiet a compiler warning about uninitialized c
+   switch (BPF_CLASS (stmt.code))
+     {
+     case BPF_LDX:
+@@ -1150,7 +1151,6 @@ bpf_unparser::emit_asm_opcode (const asm_stmt &stmt,
+                                 stmt.fallthrough.c_str()), stmt.tok);
+       block *target = label_map[stmt.jmp_target];
+       block *fallthrough = label_map[stmt.fallthrough];
+-      c = EQ; // defeat compiler warning about uninitialized c
+       this_prog.mk_jcond(this_ins, c, v_dest, v_src1, target, fallthrough);
+     }
+   else // regular opcode
diff --git a/SOURCES/rhbz1544689.patch b/SOURCES/rhbz1544689.patch
deleted file mode 100644
index 066150b..0000000
--- a/SOURCES/rhbz1544689.patch
+++ /dev/null
@@ -1,101 +0,0 @@
-commit 98b21b2bcf013f59b67b8c4c5944282fa93a6be5
-Author: Frank Ch. Eigler <fche@redhat.com>
-Date:   Tue Jun 26 12:16:04 2018 -0400
-
-    configury: add --without-bpf configure option
-    
-    Fully autoconf'ing bpf capabilities is error-prone, so provide a blunt
-    instrument.
-
-diff --git a/configure b/configure
-index 9a8a41b..b7a8790 100755
---- a/configure
-+++ b/configure
-@@ -917,6 +917,7 @@ with_dyninst
- enable_virt
- with_python2_probes
- with_python3_probes
-+with_bpf
- with_selinux
- with_java
- with_extra_version
-@@ -1639,6 +1640,7 @@ Optional Packages:
-   --without-python3-probes
-                           Disable building python version 3 probe support,
-                           even if it is available
-+  --without-bpf           Do not try to build BPF components
-   --without-selinux       Do not use libselinux even if present
-   --with-java=DIRECTORY   Specify JDK directory to compile libHelperSDT.so
-                           against (default is /usr/lib/jvm/java)
-@@ -12297,9 +12299,16 @@ if test $stap_cv_sectionq = yes; then
- fi
- 
- 
--$as_echo "#define HAVE_BPF_DECLS 0" >>confdefs.h
- 
--ac_fn_c_check_decl "$LINENO" "BPF_PROG_TYPE_PERF_EVENT" "ac_cv_have_decl_BPF_PROG_TYPE_PERF_EVENT" "#include <linux/bpf.h>
-+# Check whether --with-bpf was given.
-+if test "${with_bpf+set}" = set; then :
-+  withval=$with_bpf;
-+fi
-+
-+
-+if test "x$with_bpf" != "xno"; then :
-+
-+    ac_fn_c_check_decl "$LINENO" "BPF_PROG_TYPE_PERF_EVENT" "ac_cv_have_decl_BPF_PROG_TYPE_PERF_EVENT" "#include <linux/bpf.h>
- "
- if test "x$ac_cv_have_decl_BPF_PROG_TYPE_PERF_EVENT" = xyes; then :
-   ac_have_decl=1
-@@ -12311,10 +12320,13 @@ cat >>confdefs.h <<_ACEOF
- #define HAVE_DECL_BPF_PROG_TYPE_PERF_EVENT $ac_have_decl
- _ACEOF
- if test $ac_have_decl = 1; then :
--  $as_echo "#define HAVE_BPF_DECLS 1" >>confdefs.h
-+
-+$as_echo "#define HAVE_BPF_DECLS 1" >>confdefs.h
- 
- fi
- 
-+
-+fi
-  if test "x$ac_cv_have_decl_BPF_PROG_TYPE_PERF_EVENT" == "xyes"; then
-   HAVE_BPF_DECLS_TRUE=
-   HAVE_BPF_DECLS_FALSE='#'
-@@ -12323,7 +12335,6 @@ else
-   HAVE_BPF_DECLS_FALSE=
- fi
- 
--
- ac_config_files="$ac_config_files includes/sys/sdt-config.h po/Makefile.in"
- 
- 
-diff --git a/configure.ac b/configure.ac
-index ea7ffdc..12fc337 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -789,14 +789,20 @@ if test $stap_cv_sectionq = yes; then
-   support_section_question=1
- fi
- 
--dnl Check if we have <linux/bpf.h> for bpf backend.
--AC_DEFINE([HAVE_BPF_DECLS], [0], [Define to 1 if you have the necessary declarations in bpf.h])
--AC_CHECK_DECLS([BPF_PROG_TYPE_PERF_EVENT],
--               [AC_DEFINE([HAVE_BPF_DECLS], [1])],
-+
-+AC_ARG_WITH([bpf],
-+  AS_HELP_STRING([--without-bpf],[Do not try to build BPF components]))
-+
-+dnl Allow --without-bpf to disable this autodetection, as some kernels
-+dnl have some headers but missing some decls like __NR_bpf.  Too hard
-+dnl to detect all the prereqs here.
-+AS_IF([test "x$with_bpf" != "xno"], [
-+    AC_CHECK_DECLS([BPF_PROG_TYPE_PERF_EVENT],
-+               [AC_DEFINE([HAVE_BPF_DECLS], [1], [Define to 1 if you have the necessary declarations in bpf.h])],
-                [],
-                [#include <linux/bpf.h>])
-+    ])
- AM_CONDITIONAL(HAVE_BPF_DECLS, [test "x$ac_cv_have_decl_BPF_PROG_TYPE_PERF_EVENT" == "xyes"])
--
- AC_CONFIG_FILES([includes/sys/sdt-config.h po/Makefile.in])
- 
- if test $build_elfutils = yes -a $enable_translator = yes; then
diff --git a/SOURCES/rhbz1547238.patch b/SOURCES/rhbz1547238.patch
deleted file mode 100644
index 89ba2d7..0000000
--- a/SOURCES/rhbz1547238.patch
+++ /dev/null
@@ -1,64 +0,0 @@
-commit e40b26c5bbaab6fc8068fe5c111bdfeff0963408
-Author: Paulo Andrade <pandrade@redhat.com>
-Date:   Tue Jun 12 18:48:26 2018 -0400
-
-    rhbz1547238: adapt vfs.add_to_page_cache probes
-    
-    The add_to_page_cache_lru variant should also be probed, along with
-    kernel.function("add_to_page_cache_locked"), but if present, not the
-    add_to_page_cache variant.  This backward compatibility needed a
-    little bit of tapset probe point operator jiujitsu to go beyond
-    Paulo's initial patch.
-
-diff --git a/tapset/linux/vfs.stp b/tapset/linux/vfs.stp
-index 6f5fe77..1fe71ae 100644
---- a/tapset/linux/vfs.stp
-+++ b/tapset/linux/vfs.stp
-@@ -908,9 +908,16 @@ probe __vfs.ext4_mpage_readpages.return =
- 	size = @entry($nr_pages)
- }
- 
-+
-+/* newer style */
-+probe vfs.__add_to_page_cache =
-+	kernel.function("add_to_page_cache_locked"),
-+	kernel.function("add_to_page_cache_lru")
-+     { }
-+
- probe vfs.add_to_page_cache =
--	kernel.function("add_to_page_cache_locked") !,
--	kernel.function("add_to_page_cache")
-+	vfs.__add_to_page_cache !,
-+	kernel.function("add_to_page_cache")  /* older style */
- {
- 	dev = $mapping->host->i_sb->s_dev
- 	devname = __find_bdevname(dev, $mapping->host->i_sb->s_bdev)
-@@ -925,9 +932,16 @@ probe vfs.add_to_page_cache =
- 	argstr = sprintf("%d, %d", ino, $offset)
- }
- 
-+
-+/* newer style */
-+probe vfs.__add_to_page_cache.return =
-+	kernel.function("add_to_page_cache_locked").return,
-+	kernel.function("add_to_page_cache_lru").return
-+     { }
-+
- probe vfs.add_to_page_cache.return =
--	kernel.function("add_to_page_cache_locked").return !,
--	kernel.function("add_to_page_cache").return
-+	vfs.__add_to_page_cache.return !,
-+	kernel.function("add_to_page_cache").return  /* older style */
- {
- 	name = "vfs.add_to_page_cache"
- 	retstr = sprintf("%d", $return)
-diff --git a/testsuite/semok/vfs_add_to_page_cache.stp b/testsuite/semok/vfs_add_to_page_cache.stp
-new file mode 100755
-index 0000000..5edea75
---- /dev/null
-+++ b/testsuite/semok/vfs_add_to_page_cache.stp
-@@ -0,0 +1,4 @@
-+#! stap -p2
-+
-+probe vfs.add_to_page_cache { println(name, argstr) }
-+probe vfs.add_to_page_cache.return { println(name, retstr) }
diff --git a/SOURCES/rhbz1560044.1.patch b/SOURCES/rhbz1560044.1.patch
deleted file mode 100644
index c8e647d..0000000
--- a/SOURCES/rhbz1560044.1.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-commit c7a952858e846738ee9983796a20585b6635429c
-Author: Aaron Merey <amerey@redhat.com>
-Date:   Wed Jun 20 18:26:36 2018 -0400
-
-    bpf translator: binary operations now act on copy of left operand.
-    
-    Needed for cases where evaluating right operand causes side effects that
-    mutate the left (ex. x + x++).
-
-diff --git a/bpf-translate.cxx b/bpf-translate.cxx
-index fd9021e70..adfef97a5 100644
---- a/bpf-translate.cxx
-+++ b/bpf-translate.cxx
-@@ -1008,7 +1008,11 @@ bpf_unparser::visit_binary_expression (binary_expression* e)
-   else
-     throw SEMANTIC_ERROR (_("unhandled binary operator"), e->tok);
- 
--  value *s0 = emit_expr (e->left);
-+  value *s0 = this_prog.new_reg();
-+  // copy e->left into a seperate reg incase evaluating e->right
-+  // causes e->left to mutate (ex. x + x++).
-+  this_prog.mk_mov(this_ins, s0, emit_expr (e->left));
-+
-   value *s1 = emit_expr (e->right);
-   value *d = this_prog.new_reg ();
-   this_prog.mk_binary (this_ins, code, d, s0, s1);
diff --git a/SOURCES/rhbz1560044.2.patch b/SOURCES/rhbz1560044.2.patch
deleted file mode 100644
index 1cacfb6..0000000
--- a/SOURCES/rhbz1560044.2.patch
+++ /dev/null
@@ -1,19 +0,0 @@
-commit 8f316871901ec2f54b5ef66416303ef5e87f1226
-Author: Serhei Makarov <smakarov@redhat.com>
-Date:   Fri Sep 14 14:15:05 2018 -0400
-
-    update man/stap.1.in with a note that stapbpf(8) is only present on x86_64
-
-diff --git a/man/stap.1.in b/man/stap.1.in
-index 4db25479f..a0f0d5814 100644
---- a/man/stap.1.in
-+++ b/man/stap.1.in
-@@ -2538,7 +2538,7 @@ Filter (eBPF) programs instead of a kernel module. eBPF programs are verified
- by the kernel for safety and are executed by an in-kernel virtual machine.
- This runtime is in an early stage of development and currently lacks support
- for a number of features available in the default runtime. Please see the
--\fIstapbpf\fR(8) man page for more information.
-+\fIstapbpf\fR(8) man page for more information (only on x86_64 systems).
- 
- .SH EXIT STATUS
- 
diff --git a/SOURCES/rhbz1560044.3.patch b/SOURCES/rhbz1560044.3.patch
deleted file mode 100644
index cbb28df..0000000
--- a/SOURCES/rhbz1560044.3.patch
+++ /dev/null
@@ -1,70 +0,0 @@
-commit 488adf462bbbba541d2ee4448a9c30458084cfb8
-Author: Aaron Merey <amerey@redhat.com>
-Date:   Wed Jun 20 18:42:53 2018 -0400
-
-    testsuite: fix formatting of bpf test increment2.stp
-
-diff --git a/testsuite/systemtap.bpf/bpf_tests/increment2.stp b/testsuite/systemtap.bpf/bpf_tests/increment2.stp
-index ccc2609fa..89c5f1d07 100644
---- a/testsuite/systemtap.bpf/bpf_tests/increment2.stp
-+++ b/testsuite/systemtap.bpf/bpf_tests/increment2.stp
-@@ -1,18 +1,48 @@
-+global flag
- probe begin {
-+	printf("BEGIN\n")
- 	a = 0; b = 0; c = 0;
-        	d = 0; e = 0; f = 0;
-        	g = 0; h = 0
- 
--#	a = a + a++
--#	b = b + ++b
--#	c = ++c + c
--#	d = ++d + ++c
--#	e = ++e + e++
--#	f = f++ + f
--#	g = g++ + ++g
--#	h = h++ + h++
--	printf("%d %d %d ", a,b,c)
--	printf("%d %d %d ", d,e,f)
--	printf("%d %d\n", g,h)
-+	a = a + a++
-+	b = b + ++b
-+	c = ++c + c
-+	d = ++d + ++c
-+	e = ++e + e++
-+	f = f++ + f
-+	g = g++ + ++g
-+	h = h++ + h++
-+
-+	if (a == 0 && b == 1 && c == 3 && d == 4 
-+            && e == 2 && f == 1 && g == 2 && h == 1)
-+          flag = 1
-+}
-+
-+probe kernel.function("sys_read") {
-+	a = 0; b = 0; c = 0;
-+       	d = 0; e = 0; f = 0;
-+       	g = 0; h = 0
-+
-+	a = a + a++
-+	b = b + ++b
-+	c = ++c + c
-+	d = ++d + ++c
-+	e = ++e + e++
-+	f = f++ + f
-+	g = g++ + ++g
-+	h = h++ + h++
-+
-+	if (a != 0 || b != 1 || c != 3 || d != 4
-+            || e != 2 || f != 1 || g != 2 || h != 1)
-+	  flag = 0
-+
-         exit()
- }
-+
-+probe end {
-+	if (flag)
-+          printf("END PASS\n")
-+	else
-+	  printf("END FAIL\n")
-+}
diff --git a/SOURCES/rhbz1591267.patch b/SOURCES/rhbz1591267.patch
deleted file mode 100644
index 325e6be..0000000
--- a/SOURCES/rhbz1591267.patch
+++ /dev/null
@@ -1,50 +0,0 @@
-commit 97a9ceb2a294ee3218987daf84a5ab3d9d361e27
-Author: Martin Cermak <mcermak@redhat.com>
-Date:   Thu Jun 14 14:35:26 2018 +0200
-
-    Improve the foreach_limit(2).exp test results.
-    
-    Without this update, one can observe following issue with rhel7
-    powerpc kernels:
-    
-    =======
-     # stap -p4 testsuite/systemtap.maps/foreach_limit.stp
-     ...
-     /usr/local/share/systemtap/runtime/map.c:275:26: error: ‘a’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
-        struct mlist_head *c, *a, *last, *tmp;
-                               ^
-     /usr/local/share/systemtap/runtime/map.c:275:26: error: ‘a’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
-     cc1: all warnings being treated as errors
-    =======
-    
-    This problem turns out to start happening after the powerpc kernel
-    build system started using -O3 instead of -O2 as one can see in
-    http://vault.centos.org/7.5.1804/os/Source/SPackages/kernel-3.10.0-862.el7.src.rpm
-    
-    =======
-     # powerpc is compiled with -O3, via specfile rpmbuild -- see rhbz1051067.
-     # we need to keep consistency here, however, for out of tree kmod builds --
-     # see rhbz1431029 for reference
-     ifeq ($(SRCARCH), powerpc)
-     KBUILD_CFLAGS   += -O3
-     else
-     KBUILD_CFLAGS   += -O2
-     endif
-    =======
-    
-    Reverting this change (using -O2 instead of -O3) works the problem around
-    as well as this systemtap-side update.  For more details, see rhbz1591267.
-
-diff --git a/runtime/map.c b/runtime/map.c
-index 5caf739..ef91456 100644
---- a/runtime/map.c
-+++ b/runtime/map.c
-@@ -272,7 +272,7 @@ static void _stp_map_sortn(MAP map, int n, int keynum, int dir,
- 		_stp_map_sort(map, keynum, dir, get_key);
- 	} else {
- 		struct mlist_head *head = &map->head;
--		struct mlist_head *c, *a, *last, *tmp;
-+		struct mlist_head *c, *a = 0, *last, *tmp;
- 		int num, swaps = 1;
- 
- 		if (mlist_empty(head))
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 <smakarov@redhat.com>
+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 <smakarov@redhat.com>
+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 &reg,
++                      bool allow_imm = true);
++  value *emit_asm_reg(const asm_stmt &stmt, const std::string &reg);
++  void emit_asm_opcode(const asm_stmt &stmt,
++                       std::map<std::string, block *> 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:
++
++   <stmt> ::= label, <dest=label>;
++   <stmt> ::= <code=integer opcode>, <dest=reg>, <src1=reg>,
++              <off/jmp_target=off>, <imm=imm>;
++
++   Possible argument types include:
++
++   <reg> ::= <register index> | r<register index> |
++             $<identifier> | $<integer constant> | $$ | <string constant>
++   <imm> ::= <integer constant> | BPF_MAXSTRINGLEN
++   <off> ::= <imm> | <jump label>
++
++*/
++
++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 << "<unknown asm_stmt kind '" << stmt.kind << "'>";
++  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<std::string> 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 <reg> or <imm> 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 &reg)
++{
++  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<std::string, block *> 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<asm_stmt> 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<std::string, block *> 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<asm_stmt>::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<asm_stmt>::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 <smakarov@redhat.com>
+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 <smakarov@redhat.com>
+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 <smakarov@redhat.com>
+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<unsigned, const char *> bpf_func_name_map;
++std::map<std::string, bpf_func_id> 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 &reg,
+-                      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 &reg);
++  value *get_asm_reg(const asm_stmt &stmt, const std::string &reg);
+   void emit_asm_opcode(const asm_stmt &stmt,
+                        std::map<std::string, block *> label_map);
+ 
++  // Used for the embedded-code assembler's diagnostics:
++  source_loc adjusted_loc;
++  size_t adjust_pos;
++  std::vector<token *> 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:
+ 
+    <stmt> ::= label, <dest=label>;
++   <stmt> ::= alloc, <dest=reg>, <imm=imm>;
++   <stmt> ::= call, <dest=reg>, <param[0]=function name>, <param[1]=arg>, ...;
++   <stmt> ::= printf, <param[0]=string constant>, <param[1]=arg>, ...;
++   <stmt> ::= error, <param[0]=string constant>, <param[1]=arg>, ...;
+    <stmt> ::= <code=integer opcode>, <dest=reg>, <src1=reg>,
+               <off/jmp_target=off>, <imm=imm>;
+ 
+-   Possible argument types include:
++   Supported argument types include:
+ 
++   <arg> ::= <reg> | <imm>
+    <reg> ::= <register index> | r<register index> |
+              $<identifier> | $<integer constant> | $$ | <string constant>
+    <imm> ::= <integer constant> | 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<std::string> 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<std::string> 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 &reg)
+ {
+   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 &reg)
++{
++  return emit_asm_arg(stmt, reg, /*allow_imm=*/false, /*allow_emit=*/false);
++}
++
+ void
+ bpf_unparser::emit_asm_opcode (const asm_stmt &stmt,
+                                std::map<std::string, block *> 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<asm_stmt> 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<token *>::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 <smakarov@redhat.com>
+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 <smakarov@redhat.com>
+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<value *> &args);
++  value *emit_print_format(const std::string &format,
++                           const std::vector<value *> &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)
+ 
+    <stmt> ::= label, <dest=label>;
+    <stmt> ::= alloc, <dest=reg>, <imm=imm>;
+-   <stmt> ::= call, <dest=reg>, <param[0]=function name>, <param[1]=arg>, ...;
+-   <stmt> ::= printf, <param[0]=string constant>, <param[1]=arg>, ...;
+-   <stmt> ::= error, <param[0]=string constant>, <param[1]=arg>, ...;
++   <stmt> ::= call, <dest=optreg>, <param[0]=function name>, <param[1]=arg>, ...;
+    <stmt> ::= <code=integer opcode>, <dest=reg>, <src1=reg>,
+               <off/jmp_target=off>, <imm=imm>;
+ 
+    Supported argument types include:
+ 
+-   <arg> ::= <reg> | <imm>
+-   <reg> ::= <register index> | r<register index> |
+-             $<identifier> | $<integer constant> | $$ | <string constant>
+-   <imm> ::= <integer constant> | BPF_MAXSTRINGLEN
+-   <off> ::= <imm> | <jump label>
++   <arg>    ::= <reg> | <imm>
++   <optreg> ::= <reg> | -
++   <reg>    ::= <register index> | r<register index> |
++                $<identifier> | $<integer constant> | $$ | <string constant>
++   <imm>    ::= <integer constant> | BPF_MAXSTRINGLEN | -
++   <off>    ::= <imm> | <jump label>
+ 
+ */
+ 
+-// 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 << "<unknown asm_stmt kind '" << stmt.kind << "'>";
+   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<asm_stmt>::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<value *> 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<functiondecl*> 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<value *> 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<value *>& 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<value *> 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 <MODNAME>...</MODNAME> 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<value *>& 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<value *> 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 <MODNAME>...</MODNAME> 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 <smakarov@redhat.com>
+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 <smakarov@redhat.com>
+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 <smakarov@redhat.com>
+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 <smakarov@redhat.com>
+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 <wcohen@redhat.com>
+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 <smakarov@redhat.com>
+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 <smakarov@redhat.com>
+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 <smakarov@redhat.com>
+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 <smakarov@redhat.com>
+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, $$, "<unknown>", -, -; /* mov $$, <unknown> */
++
++  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 <smakarov@redhat.com>
+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 <smakarov@redhat.com>
+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 <smakarov@redhat.com>
+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<unsigned, const char *> bpf_func_name_map;
+ std::map<std::string, bpf_func_id> 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 <linux/bpf.h>
+ }
+ 
++/* 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 <smakarov@redhat.com>
+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 << "<BUG:unknown operand>";
+     }
+ }
+ 
+@@ -359,7 +359,7 @@ opcode_name(opcode op)
+     case BPF_JMP | BPF_JSET | BPF_K:	opn = "jsetk"; break;
+ 
+     default:
+-      abort();
++      opn = "<BUG:unknown opcode>";
+     }
+ 
+   return opn;
+@@ -457,7 +457,7 @@ insn::print(std::ostream &o) const
+       return o << opn << "\t" << *src0 << "," << *src1;
+ 
+     default:
+-      abort ();
++      return o << "<BUG:unknown instruction format>";
+     }
+ }
+ 
+@@ -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 <smakarov@redhat.com>
+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<std::string> 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<asm_stmt> 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" <fche@redhat.com>
+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<std::string> &strings, char *fstr,
+   return reinterpret_cast<uint64_t>(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<int> &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 <smakarov@redhat.com>
+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 <smakarov@redhat.com>
+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 <smakarov@redhat.com>
+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 <smakarov@redhat.com>
+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<std::string, value *>& 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<std::string, value *>(str, v));
++  value *v = new value(value::mk_str(str, format_str));
++  auto ok = m.insert(std::pair<std::string, value *>(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<int64_t, value *> imm_map;
+   std::unordered_map<std::string, value *> str_map;
++  std::unordered_map<std::string, value *> 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 <smakarov@redhat.com>
+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 <smakarov@redhat.com>
+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 <smakarov@redhat.com>
+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 <smakarov@redhat.com>
+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 <smakarov@redhat.com>
+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 <smakarov@redhat.com>
+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 <mcermak@redhat.com>
+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 <fche@redhat.com>
+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 <fche@redhat.com>
+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 <wcohen@redhat.com>
+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<linux/sched.h>")->comm, 16),
+-                      @cast(data[timer], "struct task_struct", "kernel<linux/sched.h>")->pid)
++                      kernel_string_n(@cast(ptr, "struct task_struct", "kernel<linux/sched.h>")->comm, 16),
++                      @cast(ptr, "struct task_struct", "kernel<linux/sched.h>")->pid)
+       type="process"
+     } else if (fname == "delayed_work_timer_fn") {
+-      faddr = @defined(@cast(data[timer], "struct delayed_work", "kernel<linux/workqueue.h>")->work->func)
+-      ? @cast(data[timer], "struct delayed_work", "kernel<linux/workqueue.h>")->work->func
+-      : @cast(data[timer], "struct work_struct", "kernel<linux/workqueue.h>")->func
++      ptr = (data[timer] ? data[timer] : delayed_work_info[timer])
++      faddr = @defined(@cast(ptr, "struct delayed_work", "kernel<linux/workqueue.h>")->work->func)
++      ? @cast(ptr, "struct delayed_work", "kernel<linux/workqueue.h>")->work->func
++      : @cast(ptr, "struct work_struct", "kernel<linux/workqueue.h>")->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 <wcohen@redhat.com>
+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/SOURCES/stapbpf-kmsg.patch b/SOURCES/stapbpf-kmsg.patch
new file mode 100644
index 0000000..29fc0df
--- /dev/null
+++ b/SOURCES/stapbpf-kmsg.patch
@@ -0,0 +1,49 @@
+commit 82349a5eb0e1122d5532a03367d91d5ee838722d
+Author: Serhei Makarov <smakarov@redhat.com>
+Date:   Fri Mar 1 11:16:32 2019 -0500
+
+    Don't print to /dev/kmsg if it's unavailable
+    
+    * stapbpf/stapbpf.cxx (kmsg): initialize to NULL.
+    (prog_load): only print to kmsg if it's available.
+    (main): warn if kmsg is unavailable.
+
+diff --git a/stapbpf/stapbpf.cxx b/stapbpf/stapbpf.cxx
+index 188724b..ae97d4b 100644
+--- a/stapbpf/stapbpf.cxx
++++ b/stapbpf/stapbpf.cxx
+@@ -73,7 +73,7 @@ static int warnings = 1;
+ static int exit_phase = 0;
+ static int interrupt_message = 0;
+ static FILE *output_f = stdout;
+-static FILE *kmsg;
++static FILE *kmsg = NULL;
+ 
+ static const char *module_name;
+ static const char *module_basename;
+@@ -341,9 +341,12 @@ prog_load(Elf_Data *data, const char *name)
+   if (data->d_size % sizeof(bpf_insn))
+     fatal("program size not a multiple of %zu\n", sizeof(bpf_insn));
+ 
+-  fprintf (kmsg, "%s (%s): stapbpf: %s, name: %s, d_size: %lu\n",
+-           module_basename, script_name, VERSION, name, (unsigned long)data->d_size);
+-  fflush (kmsg); // Otherwise, flush will only happen after the prog runs.
++  if (kmsg != NULL)
++    {
++      fprintf (kmsg, "%s (%s): stapbpf: %s, name: %s, d_size: %lu\n",
++               module_basename, script_name, VERSION, name, (unsigned long)data->d_size);
++      fflush (kmsg); // Otherwise, flush will only happen after the prog runs.
++    }
+   int fd = bpf_prog_load(prog_type, static_cast<bpf_insn *>(data->d_buf),
+ 			 data->d_size, module_license, kernel_version);
+   if (fd < 0)
+@@ -1575,5 +1578,7 @@ main(int argc, char **argv)
+ 
+-  kmsg = fopen("/dev/kmsg", "a");
++  kmsg = fopen("/dev/kmsg", "w");
++  if (kmsg == NULL)
++    fprintf(stderr, "WARNING: could not open /dev/kmsg for diagnostics: %s\n", strerror(errno));
+ 
+   load_bpf_file(argv[optind]);
+   init_internal_globals();
+
diff --git a/SPECS/systemtap.spec b/SPECS/systemtap.spec
index 1b86b50..47f86e8 100644
--- a/SPECS/systemtap.spec
+++ b/SPECS/systemtap.spec
@@ -84,11 +84,11 @@
 %endif
 
 # To avoid testsuite/*/*.stp has shebang which doesn't start with '/'
-%undefine __brp_mangle_shebangs  
+%define __brp_mangle_shebangs_exclude_from .stp$
 
 Name: systemtap
-Version: 3.3
-Release: 3%{?dist}
+Version: 4.0
+Release: 9%{?release_override}%{?dist}
 # for version, see also configure.ac
 
 
@@ -125,8 +125,49 @@ 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
+Patch50: bpf-unwarn.patch
+Patch51: stapbpf-kmsg.patch
+
 # Build*
 BuildRequires: gcc-c++
+BuildRequires: cpio
 BuildRequires: gettext-devel
 BuildRequires: pkgconfig(nss)
 BuildRequires: pkgconfig(avahi-client)
@@ -214,14 +255,6 @@ BuildRequires: libuuid-devel
 Requires: systemtap-client = %{version}-%{release}
 Requires: systemtap-devel = %{version}-%{release}
 
-Patch10: rhbz1547238.patch
-Patch11: rhbz1591267.patch
-Patch12: rhbz1544689.patch
-Patch13: rhbz1560044.1.patch
-Patch14: rhbz1560044.2.patch
-Patch15: rhbz1560044.3.patch
-
-
 %description
 SystemTap is an instrumentation system for systems running Linux.
 Developers can write instrumentation scripts to collect data on
@@ -236,19 +269,23 @@ 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
-Requires(preun): initscripts
-Requires(postun): initscripts
 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
@@ -262,12 +299,18 @@ Summary: Programmable system-wide instrumentation system - development headers, 
 Group: Development/System
 License: GPLv2+
 URL: http://sourceware.org/systemtap/
-# The virtual provide 'kernel-devel-uname-r' tries to get the right
-# kernel variant  (kernel-PAE, kernel-debug, etc.) devel package
-# installed.
+
+%if 0%{?rhel} >= 8 || 0%{?fedora} >= 20
+Recommends: kernel-debug-devel
+Recommends: kernel-devel
+%else
 Requires: kernel-devel-uname-r
-%{?fedora:Suggests: kernel-devel}
+%endif
+
 Requires: gcc make
+Conflicts: systemtap-client < %{version}-%{release}
+Conflicts: systemtap-server < %{version}-%{release}
+Conflicts: systemtap-runtime < %{version}-%{release}
 # Suggest: kernel-debuginfo
 
 %description devel
@@ -285,6 +328,9 @@ 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
@@ -301,6 +347,9 @@ 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
@@ -321,8 +370,12 @@ 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
@@ -423,8 +476,7 @@ Requires: iproute
 
 %description runtime-java
 This package includes support files needed to run systemtap scripts
-that probe Java processes running on the OpenJDK 1.6 and OpenJDK 1.7
-runtimes using Byteman.
+that probe Java processes running on the OpenJDK runtimes using Byteman.
 %endif
 
 %if %{with_python2_probes}
@@ -458,6 +510,20 @@ 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
@@ -498,6 +564,7 @@ systemtap-runtime-virthost machine to execute systemtap scripts.
 # ------------------------------------------------------------------------
 
 %prep
+
 %setup -q %{?setup_elfutils}
 
 %if %{with_bundled_elfutils}
@@ -510,13 +577,45 @@ 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
+%patch50 -p1
+%patch51 -p1
 
 %build
 
@@ -691,12 +790,27 @@ 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
@@ -924,6 +1038,24 @@ if [ "$1" -ge "1" ]; then
 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
@@ -988,7 +1120,7 @@ done
 
 # ------------------------------------------------------------------------
 
-%files -f systemtap.lang
+%files
 # The master "systemtap" rpm doesn't include any files.
 
 %files server -f systemtap.lang
@@ -1132,14 +1264,20 @@ done
 
 %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.8*
+%{_mandir}/man8/systemtap-service.8*
 %if %{with_dracut}
    %dir %{dracutstap}
    %{dracutstap}/*
@@ -1203,6 +1341,15 @@ done
 %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
@@ -1212,6 +1359,15 @@ done
 
 # PRERELEASE
 %changelog
+* Wed Apr 03 2019 Frank Ch. Eigler <fche@elastic.org> - 4.0-9
+- Backport stapbpf /dev/ksmg-related patches.
+
+* Tue Feb 26 2019 Frank Ch. Eigler <fche@elastic.org> - 4.0-8
+- Turn on with_bpf support on x86-64 only.
+
+* Tue Feb 26 2019 Frank Ch. Eigler <fche@elastic.org> - 4.0-7
+- Rebase to systemtap 4.0, add rhel8 beta-era patches
+
 * Fri Sep 14 2018 Serhei Makarov <smakarov@redhat.com> - 3.3-3
 - Add BPF left-operand fix (rhbz1560044) (x86-64 only)
 - Add stap.1 note on missing stapbpf.8 (rhbz1560044)