Skip to content

Commit

Permalink
Add support for inet_ntoa() subroutine
Browse files Browse the repository at this point in the history
For the time being, there is no definition of ipaddr_t, which
traditionally has been defined in the kernel portion of DTrace.  So
for now, the prototype for the subroutine is not string(ipaddr_t*)
but string(void*) and the test defines ipaddr_t itself.

Signed-off-by: Eugene Loh <eugene.loh@oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees@oracle.com>
  • Loading branch information
euloh authored and kvanhees committed Apr 22, 2022
1 parent 42f9bc2 commit f424f44
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 4 deletions.
1 change: 1 addition & 0 deletions bpf/Build
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ bpf_dlib_SOURCES = \
get_bvar.c \
get_dvar.c \
index.S \
inet_ntoa.S \
lltostr.S \
mutex_owned.S \
mutex_owner.S \
Expand Down
220 changes: 220 additions & 0 deletions bpf/inet_ntoa.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
*/

.text

/*
* // This helper writes a uint8 as a string.
* // The output buffer is dst[STRSZ] and we start writing at &dst[off].
*
* uint64_t dt_inet_ntoa_write_uint8(uint64_t inp, char *dst, uint64_t off) {
* uint64_t dig, nzr, len;
*
* nzr = 0; // nonzero if any nonzero digits have been written
* len = STRSZ;
* inp &= 0xff;
*
* // write 100s digit
* if (off >= len) return off;
* dig = inp / 100;
* nzr += dig;
* dst[off] = '0' + dig;
* if (nzr != 0) off++;
*
* // write 10s digit
* if (off >= len) return off;
* inp -= 100 * dig;
* dig = inp / 10;
* nzr += dig;
* dst[off] = '0' + dig;
* if (nzr != 0) off++;
*
* // write 1s digit
* if (off >= len) return off;
* inp -= 10 * dig;
* dst[off] = '0' + inp;
* off++;
*
* return off;
* }
*
*/

.align 4
.global dt_inet_ntoa_write_uint8
.type dt_inet_ntoa_write_uint8, @function
dt_inet_ntoa_write_uint8:

#define OFF %r0
#define INP %r1
#define DST %r2
#define DIG %r5
#define NZR %r6
#define LEN %r7
#define CHR %r8
#define PTR %r9
mov OFF, %r3

mov NZR, 0
lddw LEN, STRSZ
and INP, 0xff

/* write 100s digit */
jlt OFF, LEN, 1
exit
mov DIG, INP
div DIG, 100
add NZR, DIG
mov CHR, DIG
add CHR, '0'
mov PTR, DST
add PTR, OFF
stxb [PTR+0], CHR
jeq NZR, 0, 1
add OFF, 1

/* write 10s digit */
jlt OFF, LEN, 1
exit
mul DIG, 100
sub INP, DIG
mov DIG, INP
div DIG, 10
add NZR, DIG
mov CHR, DIG
add CHR, '0'
mov PTR, DST
add PTR, OFF
stxb [PTR+0], CHR
jeq NZR, 0, 1
add OFF, 1

/* write 1s digit */
jlt OFF, LEN, 1
exit
mul DIG, 10
sub INP, DIG
add INP, '0'
add DST, OFF
stxb [DST+0], INP
add OFF, 1
exit
.size dt_inet_ntoa_write_uint8, .-dt_inet_ntoa_write_uint8
#undef OFF
#undef INP
#undef DST
#undef DIG
#undef NZR
#undef LEN
#undef CHR
#undef PTR

/*
* void dt_inet_ntoa(uint8_t *src, char *dst) {
* uint64_t off, inp, len;
*
* off = 0
* len = STRSZ;
*
* inp = src[0];
* off = dt_inet_ntoa_write_uint8(inp, dst, off);
* if (off >= STRSZ) goto done:
* dst[off++] = ':';
*
* inp = src[1];
* off = dt_inet_ntoa_write_uint8(inp, dst, off);
* if (off >= STRSZ) goto done:
* dst[off++] = ':';
*
* inp = src[2];
* off = dt_inet_ntoa_write_uint8(inp, dst, off);
* if (off >= STRSZ) goto done:
* dst[off++] = ':';
*
* inp = src[3];
* off = dt_inet_ntoa_write_uint8(inp, dst, off);
*
* done:
* dst[off] = '\0';
* }
*
*/
.align 4
.global dt_inet_ntoa
.type dt_inet_ntoa, @function
dt_inet_ntoa:

/* off cycles between %r3 (input arg to subroutine) and %r0 (its return) */
#define INP %r1
#define SRC %r6
#define DST %r7
#define LEN %r8

mov SRC, %r1
mov DST, %r2

#if 1
#define BPF_FUNC_probe_read 4
/*
* FIXME: copy src data over
*
* Currently, the code generator's handling of DT_CG_ALLOCA_NULLPTR
* is such that there are scenarios where the BPF verifier thinks an
* unsuitable value is being passed in as the "src" argument. Until
* this is fixed, simply use bpf_probe_read() to copy the src data
* onto the BPF stack. Reset the src argument to point to that
* stack location.
*/
mov %r3, %r1
mov %r2, 4
mov SRC, %fp
add SRC, -4
mov %r1, SRC
call BPF_FUNC_probe_read
#endif

mov %r3, 0
lddw LEN, STRSZ

ldxb INP, [SRC+0]
mov %r2, DST
call dt_inet_ntoa_write_uint8
jge %r0, LEN, .Ldone
mov %r3, %r0
add %r3, 1
add %r0, DST
stb [%r0+0], '.'

ldxb INP, [SRC+1]
mov %r2, DST
call dt_inet_ntoa_write_uint8
jge %r0, LEN, .Ldone
mov %r3, %r0
add %r3, 1
add %r0, DST
stb [%r0+0], '.'

ldxb INP, [SRC+2]
mov %r2, DST
call dt_inet_ntoa_write_uint8
jge %r0, LEN, .Ldone
mov %r3, %r0
add %r3, 1
add %r0, DST
stb [%r0+0], '.'

ldxb INP, [SRC+3]
mov %r2, DST
call dt_inet_ntoa_write_uint8

.Ldone:
add DST, %r0
stb [DST+0], 0
exit
.size dt_inet_ntoa, .-dt_inet_ntoa
#undef INP
#undef SRC
#undef DST
#undef LEN
8 changes: 7 additions & 1 deletion libdtrace/dt_cg.c
Original file line number Diff line number Diff line change
Expand Up @@ -4699,6 +4699,12 @@ dt_cg_subr_htonll(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
emit(dlp, BPF_END_REG(BPF_DW, dnp->dn_reg, BPF_TO_BE));
}

static void
dt_cg_subr_inet_ntoa(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
{
dt_cg_subr_arg_to_tstring(dnp, dlp, drp, "dt_inet_ntoa");
}

typedef void dt_cg_subr_f(dt_node_t *, dt_irlist_t *, dt_regset_t *);

static dt_cg_subr_f *_dt_cg_subr[DIF_SUBR_MAX + 1] = {
Expand Down Expand Up @@ -4744,7 +4750,7 @@ static dt_cg_subr_f *_dt_cg_subr[DIF_SUBR_MAX + 1] = {
[DIF_SUBR_NTOHL] = &dt_cg_subr_htonl,
[DIF_SUBR_NTOHLL] = &dt_cg_subr_htonll,
[DIF_SUBR_INET_NTOP] = NULL,
[DIF_SUBR_INET_NTOA] = NULL,
[DIF_SUBR_INET_NTOA] = &dt_cg_subr_inet_ntoa,
[DIF_SUBR_INET_NTOA6] = NULL,
[DIF_SUBR_D_PATH] = NULL,
[DIF_SUBR_LINK_NTOP] = NULL,
Expand Down
2 changes: 1 addition & 1 deletion libdtrace/dt_open.c
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ static const dt_ident_t _dtrace_globals[] = {
{ "index", DT_IDENT_FUNC, 0, DIF_SUBR_INDEX, DT_ATTR_STABCMN, DT_VERS_1_1,
&dt_idops_func, "int(const char *, const char *, [int])" },
{ "inet_ntoa", DT_IDENT_FUNC, 0, DIF_SUBR_INET_NTOA, DT_ATTR_STABCMN,
DT_VERS_1_5, &dt_idops_func, "string(ipaddr_t *)" },
DT_VERS_1_5, &dt_idops_func, "string(void *)" }, /* FIXME should be ipaddr_t* */
{ "inet_ntoa6", DT_IDENT_FUNC, 0, DIF_SUBR_INET_NTOA6, DT_ATTR_STABCMN,
DT_VERS_1_5, &dt_idops_func, "string(struct in6_addr *)" },
{ "inet_ntop", DT_IDENT_FUNC, 0, DIF_SUBR_INET_NTOP, DT_ATTR_STABCMN,
Expand Down
5 changes: 3 additions & 2 deletions test/unittest/funcs/tst.inet_ntoa.d
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
/*
* Oracle Linux DTrace.
* Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 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.
*/
/* @@xfail: dtv2 */

#pragma D option quiet

typedef vmlinux`__be32 ipaddr_t; /* FIXME: how should this really be handled? */

ipaddr_t *ip4a;
ipaddr_t *ip4b;
ipaddr_t *ip4c;
Expand Down

0 comments on commit f424f44

Please sign in to comment.