[DTrace-devel] [PATCH 6/6] Add disassembler annotations for assoc arrays

Eugene Loh eugene.loh at oracle.com
Tue Mar 8 21:04:43 UTC 2022


On 3/7/22 2:51 PM, Kris Van Hees via DTrace-devel wrote:

> diff --git a/libdtrace/dt_dis.c b/libdtrace/dt_dis.c
> +	} else if (strcmp(fn, "dt_get_assoc") == 0) {
> +		uint_t		varid;
> +		dof_relodesc_t	*rp = dp->dtdo_breltab;
> +		int		cnt = dp->dtdo_brelen;
> +		const char	*rname = NULL;
> +
> +		/*
> +		 * We know that the previous four instructions exist and
> +		 * move the variable id to a register in the first instruction
> +		 * of that seqeuence (because we wrote the code generator to
> +		 * emit the instructions in this exact order.)
> +		 */
> +		in -= 4;
> +		varid = in->imm + DIF_VAR_OTHER_UBASE;
> +
> +		/*
> +		 * We need to determine whether this is a associative TLS array
> +		 * or a global array.  We know that for a TLS assoc, a call to
> +		 * dt_tlskey must be present 5 instructions earlier (actually
> +		 * 9 instructions before the call to dt_get_assoc).
> +		 */
> +		if (addr < 9 || in[-5].code != (BPF_JMP | BPF_CALL) ||
> +				in[-5].src_reg != BPF_PSEUDO_CALL)
> +			goto gvar_assoc;
> +
> +		addr -= 9;
> +		for (; cnt; cnt--, rp++) {
> +			if (rp->dofr_offset > addr * sizeof(uint64_t))
> +				break;
> +			if (rp->dofr_offset == addr * sizeof(uint64_t))
> +				rname = dt_difo_getstr(dp, rp->dofr_name);
> +		}
> +
> +		if (strcmp(rname, "dt_tlskey") != 0)
> +			goto gvar_assoc;
> +
> +		snprintf(buf, len, "self->%s[]",
> +			 dt_dis_varname_id(dp, varid, DIFV_SCOPE_THREAD, addr));
> +		return buf;
> +
> +gvar_assoc:
> +		snprintf(buf, len, "%s[]",
> +			 dt_dis_varname_id(dp, varid, DIFV_SCOPE_GLOBAL, addr));
> +		return buf;
>   	}

If we really are going to rely on the fact that only we are generating 
dt_get_assoc() calls, it would seem that this reverse engineering could 
be a bunch easier.  The 8th instruction before is either going to be a 
store reg or a store imm.  That's the only signature we need.  So how about:

     } else if (strcmp(fn, "dt_get_assoc") == 0) {
         uint_t        varid;

         /*
          * We know that the previous four instructions exist and
          * move the variable id to a register in the first instruction
          * of that seqeuence (because we wrote the code generator to
          * emit the instructions in this exact order.)
          */
         in -= 4;
         varid = in->imm + DIF_VAR_OTHER_UBASE;

         /*
          * Similarly, four instructions prior to that, there was either
          * a STORE or STORE_IMM operation, indicating TLS or global.
          */
         in -= 4;
         if (in->code == (BPF_STX | BPF_MEM | BPF_DW))
             snprintf(buf, len, "self->%s[]",
                  dt_dis_varname_id(dp, varid, DIFV_SCOPE_THREAD, addr));
         else if (in->code == (BPF_ST | BPF_MEM | BPF_DW))
             snprintf(buf, len, "%s[]",
                  dt_dis_varname_id(dp, varid, DIFV_SCOPE_GLOBAL, addr));
         else
             snprintf(buf, len, "???[]");

         return buf;
     }

And one more thing...

> diff --git a/test/unittest/disasm/tst.ann-gvar-assoc.r b/test/unittest/disasm/tst.ann-gvar-assoc.r
> new file mode 100644
> index 00000000..522ba6f6
> --- /dev/null
> +++ b/test/unittest/disasm/tst.ann-gvar-assoc.r
> @@ -0,0 +1 @@
> +85 0 1 0000 ffffffff    call dt_get_assoc             ! var[]
> diff --git a/test/unittest/disasm/tst.ann-gvar-assoc.sh b/test/unittest/disasm/tst.ann-gvar-assoc.sh
> new file mode 100755
> index 00000000..c0fc4bf2
> --- /dev/null
> +++ b/test/unittest/disasm/tst.ann-gvar-assoc.sh
> @@ -0,0 +1,19 @@
> +#!/bin/bash
> +#
> +# Oracle Linux DTrace.
> +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
> +# Licensed under the Universal Permissive License v 1.0 as shown at
> +# http://oss.oracle.com/licenses/upl.
> +#
> +
> +dtrace=$1
> +
> +$dtrace $dt_flags -Sen '
> +BEGIN
> +{
> +	var["foo"] = 42;
> +	exit(0);
> +}
> +' 2>&1 | awk '/ call dt_get_assoc/ { sub(/^[^:]+: /, ""); print; }'
> +
> +exit $?
> diff --git a/test/unittest/disasm/tst.ann-tvar-assoc.r b/test/unittest/disasm/tst.ann-tvar-assoc.r
> new file mode 100644
> index 00000000..2ae89edb
> --- /dev/null
> +++ b/test/unittest/disasm/tst.ann-tvar-assoc.r
> @@ -0,0 +1 @@
> +85 0 1 0000 ffffffff    call dt_get_assoc             ! self->var[]
> diff --git a/test/unittest/disasm/tst.ann-tvar-assoc.sh b/test/unittest/disasm/tst.ann-tvar-assoc.sh
> new file mode 100755
> index 00000000..e4690a28
> --- /dev/null
> +++ b/test/unittest/disasm/tst.ann-tvar-assoc.sh
> @@ -0,0 +1,19 @@
> +#!/bin/bash
> +#
> +# Oracle Linux DTrace.
> +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
> +# Licensed under the Universal Permissive License v 1.0 as shown at
> +# http://oss.oracle.com/licenses/upl.
> +#
> +
> +dtrace=$1
> +
> +$dtrace $dt_flags -Sen '
> +BEGIN
> +{
> +	self->var["foo"] = 42;
> +	exit(0);
> +}
> +' 2>&1 | awk '/ call dt_get_assoc/ { sub(/^[^:]+: /, ""); print; }'
> +
> +exit $?
There are two code paths -- dt_cg_store_var() and dt_cg_assoc_op() -- 
that emit dt_get_assoc() calls but the tests check only one of them:  
store.  Also, they only examine varid==0 and it could be easy to stumble 
into such a value by luck.  (That's what happened when I tried to break 
the tests!)  So how about:

diff --git a/test/unittest/disasm/tst.ann-gvar-assoc.r 
b/test/unittest/disasm/tst.ann-gvar-assoc.r
@@ -1 +1,2 @@
  85 0 1 0000 ffffffff    call dt_get_assoc             ! var[]
+85 0 1 0000 ffffffff    call dt_get_assoc             ! var[]
diff --git a/test/unittest/disasm/tst.ann-gvar-assoc.sh 
b/test/unittest/disasm/tst.ann-gvar-assoc.sh
@@ -11,7 +11,9 @@ dtrace=$1
  $dtrace $dt_flags -Sen '
  BEGIN
  {
+       dummy = 1234;
         var["foo"] = 42;
+       trace(var["foo"]);
         exit(0);
  }
  ' 2>&1 | awk '/ call dt_get_assoc/ { sub(/^[^:]+: /, ""); print; }'
diff --git a/test/unittest/disasm/tst.ann-tvar-assoc.r 
b/test/unittest/disasm/tst.ann-tvar-assoc.r
@@ -1 +1,2 @@
  85 0 1 0000 ffffffff    call dt_get_assoc             ! self->var[]
+85 0 1 0000 ffffffff    call dt_get_assoc             ! self->var[]
diff --git a/test/unittest/disasm/tst.ann-tvar-assoc.sh 
b/test/unittest/disasm/tst.ann-tvar-assoc.sh
@@ -11,7 +11,9 @@ dtrace=$1
  $dtrace $dt_flags -Sen '
  BEGIN
  {
+       self->dummy = 1234;
         self->var["foo"] = 42;
+       trace(self->var["foo"]);
         exit(0);
  }
  ' 2>&1 | awk '/ call dt_get_assoc/ { sub(/^[^:]+: /, ""); print; }'



More information about the DTrace-devel mailing list