Skip to content

Commit 758acb9

Browse files
Phoenix500526anakryiko
authored andcommitted
libbpf: Fix USDT SIB argument handling causing unrecognized register error
On x86-64, USDT arguments can be specified using Scale-Index-Base (SIB) addressing, e.g. "1@-96(%rbp,%rax,8)". The current USDT implementation in libbpf cannot parse this format, causing `bpf_program__attach_usdt()` to fail with -ENOENT (unrecognized register). This patch fixes this by implementing the necessary changes: - add correct handling for SIB-addressed arguments in `bpf_usdt_arg`. - add adaptive support to `__bpf_usdt_arg_type` and `__bpf_usdt_arg_spec` to represent SIB addressing parameters. Signed-off-by: Jiawei Zhao <[email protected]> Signed-off-by: Andrii Nakryiko <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent d3abefe commit 758acb9

File tree

2 files changed

+99
-7
lines changed

2 files changed

+99
-7
lines changed

tools/lib/bpf/usdt.bpf.h

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,32 @@ enum __bpf_usdt_arg_type {
3434
BPF_USDT_ARG_CONST,
3535
BPF_USDT_ARG_REG,
3636
BPF_USDT_ARG_REG_DEREF,
37+
BPF_USDT_ARG_SIB,
3738
};
3839

40+
/*
41+
* This struct layout is designed specifically to be backwards/forward
42+
* compatible between libbpf versions for ARG_CONST, ARG_REG, and
43+
* ARG_REG_DEREF modes. ARG_SIB requires libbpf v1.7+.
44+
*/
3945
struct __bpf_usdt_arg_spec {
4046
/* u64 scalar interpreted depending on arg_type, see below */
4147
__u64 val_off;
48+
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
4249
/* arg location case, see bpf_usdt_arg() for details */
43-
enum __bpf_usdt_arg_type arg_type;
50+
enum __bpf_usdt_arg_type arg_type: 8;
51+
/* index register offset within struct pt_regs */
52+
__u16 idx_reg_off: 12;
53+
/* scale factor for index register (1, 2, 4, or 8) */
54+
__u16 scale_bitshift: 4;
55+
/* reserved for future use, keeps reg_off offset stable */
56+
__u8 __reserved: 8;
57+
#else
58+
__u8 __reserved: 8;
59+
__u16 idx_reg_off: 12;
60+
__u16 scale_bitshift: 4;
61+
enum __bpf_usdt_arg_type arg_type: 8;
62+
#endif
4463
/* offset of referenced register within struct pt_regs */
4564
short reg_off;
4665
/* whether arg should be interpreted as signed value */
@@ -149,7 +168,7 @@ int bpf_usdt_arg(struct pt_regs *ctx, __u64 arg_num, long *res)
149168
{
150169
struct __bpf_usdt_spec *spec;
151170
struct __bpf_usdt_arg_spec *arg_spec;
152-
unsigned long val;
171+
unsigned long val, idx;
153172
int err, spec_id;
154173

155174
*res = 0;
@@ -202,6 +221,27 @@ int bpf_usdt_arg(struct pt_regs *ctx, __u64 arg_num, long *res)
202221
return err;
203222
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
204223
val >>= arg_spec->arg_bitshift;
224+
#endif
225+
break;
226+
case BPF_USDT_ARG_SIB:
227+
/* Arg is in memory addressed by SIB (Scale-Index-Base) mode
228+
* (e.g., "-1@-96(%rbp,%rax,8)" in USDT arg spec). We first
229+
* fetch the base register contents and the index register
230+
* contents from pt_regs. Then we calculate the final address
231+
* as base + (index * scale) + offset, and do a user-space
232+
* probe read to fetch the argument value.
233+
*/
234+
err = bpf_probe_read_kernel(&val, sizeof(val), (void *)ctx + arg_spec->reg_off);
235+
if (err)
236+
return err;
237+
err = bpf_probe_read_kernel(&idx, sizeof(idx), (void *)ctx + arg_spec->idx_reg_off);
238+
if (err)
239+
return err;
240+
err = bpf_probe_read_user(&val, sizeof(val), (void *)(val + (idx << arg_spec->scale_bitshift) + arg_spec->val_off));
241+
if (err)
242+
return err;
243+
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
244+
val >>= arg_spec->arg_bitshift;
205245
#endif
206246
break;
207247
default:

tools/lib/bpf/usdt.c

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -200,12 +200,23 @@ enum usdt_arg_type {
200200
USDT_ARG_CONST,
201201
USDT_ARG_REG,
202202
USDT_ARG_REG_DEREF,
203+
USDT_ARG_SIB,
203204
};
204205

205206
/* should match exactly struct __bpf_usdt_arg_spec from usdt.bpf.h */
206207
struct usdt_arg_spec {
207208
__u64 val_off;
208-
enum usdt_arg_type arg_type;
209+
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
210+
enum usdt_arg_type arg_type: 8;
211+
__u16 idx_reg_off: 12;
212+
__u16 scale_bitshift: 4;
213+
__u8 __reserved: 8; /* keep reg_off offset stable */
214+
#else
215+
__u8 __reserved: 8; /* keep reg_off offset stable */
216+
__u16 idx_reg_off: 12;
217+
__u16 scale_bitshift: 4;
218+
enum usdt_arg_type arg_type: 8;
219+
#endif
209220
short reg_off;
210221
bool arg_signed;
211222
char arg_bitshift;
@@ -1283,11 +1294,51 @@ static int calc_pt_regs_off(const char *reg_name)
12831294

12841295
static int parse_usdt_arg(const char *arg_str, int arg_num, struct usdt_arg_spec *arg, int *arg_sz)
12851296
{
1286-
char reg_name[16];
1287-
int len, reg_off;
1288-
long off;
1297+
char reg_name[16] = {0}, idx_reg_name[16] = {0};
1298+
int len, reg_off, idx_reg_off, scale = 1;
1299+
long off = 0;
1300+
1301+
if (sscanf(arg_str, " %d @ %ld ( %%%15[^,] , %%%15[^,] , %d ) %n",
1302+
arg_sz, &off, reg_name, idx_reg_name, &scale, &len) == 5 ||
1303+
sscanf(arg_str, " %d @ ( %%%15[^,] , %%%15[^,] , %d ) %n",
1304+
arg_sz, reg_name, idx_reg_name, &scale, &len) == 4 ||
1305+
sscanf(arg_str, " %d @ %ld ( %%%15[^,] , %%%15[^)] ) %n",
1306+
arg_sz, &off, reg_name, idx_reg_name, &len) == 4 ||
1307+
sscanf(arg_str, " %d @ ( %%%15[^,] , %%%15[^)] ) %n",
1308+
arg_sz, reg_name, idx_reg_name, &len) == 3
1309+
) {
1310+
/*
1311+
* Scale Index Base case:
1312+
* 1@-96(%rbp,%rax,8)
1313+
* 1@(%rbp,%rax,8)
1314+
* 1@-96(%rbp,%rax)
1315+
* 1@(%rbp,%rax)
1316+
*/
1317+
arg->arg_type = USDT_ARG_SIB;
1318+
arg->val_off = off;
12891319

1290-
if (sscanf(arg_str, " %d @ %ld ( %%%15[^)] ) %n", arg_sz, &off, reg_name, &len) == 3) {
1320+
reg_off = calc_pt_regs_off(reg_name);
1321+
if (reg_off < 0)
1322+
return reg_off;
1323+
arg->reg_off = reg_off;
1324+
1325+
idx_reg_off = calc_pt_regs_off(idx_reg_name);
1326+
if (idx_reg_off < 0)
1327+
return idx_reg_off;
1328+
arg->idx_reg_off = idx_reg_off;
1329+
1330+
/* validate scale factor and set fields directly */
1331+
switch (scale) {
1332+
case 1: arg->scale_bitshift = 0; break;
1333+
case 2: arg->scale_bitshift = 1; break;
1334+
case 4: arg->scale_bitshift = 2; break;
1335+
case 8: arg->scale_bitshift = 3; break;
1336+
default:
1337+
pr_warn("usdt: invalid SIB scale %d, expected 1, 2, 4, 8\n", scale);
1338+
return -EINVAL;
1339+
}
1340+
} else if (sscanf(arg_str, " %d @ %ld ( %%%15[^)] ) %n",
1341+
arg_sz, &off, reg_name, &len) == 3) {
12911342
/* Memory dereference case, e.g., -4@-20(%rbp) */
12921343
arg->arg_type = USDT_ARG_REG_DEREF;
12931344
arg->val_off = off;
@@ -1306,6 +1357,7 @@ static int parse_usdt_arg(const char *arg_str, int arg_num, struct usdt_arg_spec
13061357
} else if (sscanf(arg_str, " %d @ %%%15s %n", arg_sz, reg_name, &len) == 2) {
13071358
/* Register read case, e.g., -4@%eax */
13081359
arg->arg_type = USDT_ARG_REG;
1360+
/* register read has no memory offset */
13091361
arg->val_off = 0;
13101362

13111363
reg_off = calc_pt_regs_off(reg_name);

0 commit comments

Comments
 (0)