Skip to content

Commit a0d7da2

Browse files
anakryikoAlexei Starovoitov
authored and
Alexei Starovoitov
committed
libbpf: Fix call relocation offset calculation bug
When relocating subprogram call, libbpf doesn't take into account relo->text_off, which comes from symbol's value. This generally works fine for subprograms implemented as static functions, but breaks for global functions. Taking a simplified test_pkt_access.c as an example: __attribute__ ((noinline)) static int test_pkt_access_subprog1(volatile struct __sk_buff *skb) { return skb->len * 2; } __attribute__ ((noinline)) static int test_pkt_access_subprog2(int val, volatile struct __sk_buff *skb) { return skb->len + val; } SEC("classifier/test_pkt_access") int test_pkt_access(struct __sk_buff *skb) { if (test_pkt_access_subprog1(skb) != skb->len * 2) return TC_ACT_SHOT; if (test_pkt_access_subprog2(2, skb) != skb->len + 2) return TC_ACT_SHOT; return TC_ACT_UNSPEC; } When compiled, we get two relocations, pointing to '.text' symbol. .text has st_value set to 0 (it points to the beginning of .text section): 0000000000000008 000000050000000a R_BPF_64_32 0000000000000000 .text 0000000000000040 000000050000000a R_BPF_64_32 0000000000000000 .text test_pkt_access_subprog1 and test_pkt_access_subprog2 offsets (targets of two calls) are encoded within call instruction's imm32 part as -1 and 2, respectively: 0000000000000000 test_pkt_access_subprog1: 0: 61 10 00 00 00 00 00 00 r0 = *(u32 *)(r1 + 0) 1: 64 00 00 00 01 00 00 00 w0 <<= 1 2: 95 00 00 00 00 00 00 00 exit 0000000000000018 test_pkt_access_subprog2: 3: 61 10 00 00 00 00 00 00 r0 = *(u32 *)(r1 + 0) 4: 04 00 00 00 02 00 00 00 w0 += 2 5: 95 00 00 00 00 00 00 00 exit 0000000000000000 test_pkt_access: 0: bf 16 00 00 00 00 00 00 r6 = r1 ===> 1: 85 10 00 00 ff ff ff ff call -1 2: bc 01 00 00 00 00 00 00 w1 = w0 3: b4 00 00 00 02 00 00 00 w0 = 2 4: 61 62 00 00 00 00 00 00 r2 = *(u32 *)(r6 + 0) 5: 64 02 00 00 01 00 00 00 w2 <<= 1 6: 5e 21 08 00 00 00 00 00 if w1 != w2 goto +8 <LBB0_3> 7: bf 61 00 00 00 00 00 00 r1 = r6 ===> 8: 85 10 00 00 02 00 00 00 call 2 9: bc 01 00 00 00 00 00 00 w1 = w0 10: 61 62 00 00 00 00 00 00 r2 = *(u32 *)(r6 + 0) 11: 04 02 00 00 02 00 00 00 w2 += 2 12: b4 00 00 00 ff ff ff ff w0 = -1 13: 1e 21 01 00 00 00 00 00 if w1 == w2 goto +1 <LBB0_3> 14: b4 00 00 00 02 00 00 00 w0 = 2 0000000000000078 LBB0_3: 15: 95 00 00 00 00 00 00 00 exit Now, if we compile example with global functions, the setup changes. Relocations are now against specifically test_pkt_access_subprog1 and test_pkt_access_subprog2 symbols, with test_pkt_access_subprog2 pointing 24 bytes into its respective section (.text), i.e., 3 instructions in: 0000000000000008 000000070000000a R_BPF_64_32 0000000000000000 test_pkt_access_subprog1 0000000000000048 000000080000000a R_BPF_64_32 0000000000000018 test_pkt_access_subprog2 Calls instructions now encode offsets relative to function symbols and are both set ot -1: 0000000000000000 test_pkt_access_subprog1: 0: 61 10 00 00 00 00 00 00 r0 = *(u32 *)(r1 + 0) 1: 64 00 00 00 01 00 00 00 w0 <<= 1 2: 95 00 00 00 00 00 00 00 exit 0000000000000018 test_pkt_access_subprog2: 3: 61 20 00 00 00 00 00 00 r0 = *(u32 *)(r2 + 0) 4: 0c 10 00 00 00 00 00 00 w0 += w1 5: 95 00 00 00 00 00 00 00 exit 0000000000000000 test_pkt_access: 0: bf 16 00 00 00 00 00 00 r6 = r1 ===> 1: 85 10 00 00 ff ff ff ff call -1 2: bc 01 00 00 00 00 00 00 w1 = w0 3: b4 00 00 00 02 00 00 00 w0 = 2 4: 61 62 00 00 00 00 00 00 r2 = *(u32 *)(r6 + 0) 5: 64 02 00 00 01 00 00 00 w2 <<= 1 6: 5e 21 09 00 00 00 00 00 if w1 != w2 goto +9 <LBB2_3> 7: b4 01 00 00 02 00 00 00 w1 = 2 8: bf 62 00 00 00 00 00 00 r2 = r6 ===> 9: 85 10 00 00 ff ff ff ff call -1 10: bc 01 00 00 00 00 00 00 w1 = w0 11: 61 62 00 00 00 00 00 00 r2 = *(u32 *)(r6 + 0) 12: 04 02 00 00 02 00 00 00 w2 += 2 13: b4 00 00 00 ff ff ff ff w0 = -1 14: 1e 21 01 00 00 00 00 00 if w1 == w2 goto +1 <LBB2_3> 15: b4 00 00 00 02 00 00 00 w0 = 2 0000000000000080 LBB2_3: 16: 95 00 00 00 00 00 00 00 exit Thus the right formula to calculate target call offset after relocation should take into account relocation's target symbol value (offset within section), call instruction's imm32 offset, and (subtracting, to get relative instruction offset) instruction index of call instruction itself. All that is shifted by number of instructions in main program, given all sub-programs are copied over after main program. Convert few selftests relying on bpf-to-bpf calls to use global functions instead of static ones. Fixes: 48cca7e ("libbpf: add support for bpf_call") Reported-by: Alexei Starovoitov <[email protected]> Signed-off-by: Andrii Nakryiko <[email protected]> Acked-by: Yonghong Song <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent 3de88c9 commit a0d7da2

File tree

4 files changed

+12
-8
lines changed

4 files changed

+12
-8
lines changed

tools/lib/bpf/libbpf.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1870,9 +1870,13 @@ bpf_program__collect_reloc(struct bpf_program *prog, GElf_Shdr *shdr,
18701870
pr_warn("incorrect bpf_call opcode\n");
18711871
return -LIBBPF_ERRNO__RELOC;
18721872
}
1873+
if (sym.st_value % 8) {
1874+
pr_warn("bad call relo offset: %lu\n", sym.st_value);
1875+
return -LIBBPF_ERRNO__RELOC;
1876+
}
18731877
prog->reloc_desc[i].type = RELO_CALL;
18741878
prog->reloc_desc[i].insn_idx = insn_idx;
1875-
prog->reloc_desc[i].text_off = sym.st_value;
1879+
prog->reloc_desc[i].text_off = sym.st_value / 8;
18761880
obj->has_pseudo_calls = true;
18771881
continue;
18781882
}
@@ -3573,7 +3577,7 @@ bpf_program__reloc_text(struct bpf_program *prog, struct bpf_object *obj,
35733577
prog->section_name);
35743578
}
35753579
insn = &prog->insns[relo->insn_idx];
3576-
insn->imm += prog->main_prog_cnt - relo->insn_idx;
3580+
insn->imm += relo->text_off + prog->main_prog_cnt - relo->insn_idx;
35773581
return 0;
35783582
}
35793583

tools/testing/selftests/bpf/progs/test_btf_haskv.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ struct dummy_tracepoint_args {
2626
};
2727

2828
__attribute__((noinline))
29-
static int test_long_fname_2(struct dummy_tracepoint_args *arg)
29+
int test_long_fname_2(struct dummy_tracepoint_args *arg)
3030
{
3131
struct ipv_counts *counts;
3232
int key = 0;
@@ -44,7 +44,7 @@ static int test_long_fname_2(struct dummy_tracepoint_args *arg)
4444
}
4545

4646
__attribute__((noinline))
47-
static int test_long_fname_1(struct dummy_tracepoint_args *arg)
47+
int test_long_fname_1(struct dummy_tracepoint_args *arg)
4848
{
4949
return test_long_fname_2(arg);
5050
}

tools/testing/selftests/bpf/progs/test_btf_newkv.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ struct dummy_tracepoint_args {
3434
};
3535

3636
__attribute__((noinline))
37-
static int test_long_fname_2(struct dummy_tracepoint_args *arg)
37+
int test_long_fname_2(struct dummy_tracepoint_args *arg)
3838
{
3939
struct ipv_counts *counts;
4040
int key = 0;
@@ -57,7 +57,7 @@ static int test_long_fname_2(struct dummy_tracepoint_args *arg)
5757
}
5858

5959
__attribute__((noinline))
60-
static int test_long_fname_1(struct dummy_tracepoint_args *arg)
60+
int test_long_fname_1(struct dummy_tracepoint_args *arg)
6161
{
6262
return test_long_fname_2(arg);
6363
}

tools/testing/selftests/bpf/progs/test_btf_nokv.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ struct dummy_tracepoint_args {
2323
};
2424

2525
__attribute__((noinline))
26-
static int test_long_fname_2(struct dummy_tracepoint_args *arg)
26+
int test_long_fname_2(struct dummy_tracepoint_args *arg)
2727
{
2828
struct ipv_counts *counts;
2929
int key = 0;
@@ -41,7 +41,7 @@ static int test_long_fname_2(struct dummy_tracepoint_args *arg)
4141
}
4242

4343
__attribute__((noinline))
44-
static int test_long_fname_1(struct dummy_tracepoint_args *arg)
44+
int test_long_fname_1(struct dummy_tracepoint_args *arg)
4545
{
4646
return test_long_fname_2(arg);
4747
}

0 commit comments

Comments
 (0)