Skip to content

Commit 0cdd7cd

Browse files
committed
elf: link library sections
So far, we don't resolve function references in sections that we link to the main program, which is problematic when using something like -ffunction-sections. Update loader.c to consistently test calling static and non-static functions in the same and in a different section. Fix the linker to also process linked code sections.
1 parent b013477 commit 0cdd7cd

10 files changed

+61
-41
lines changed

elf_reader_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ func TestLoadCollectionSpec(t *testing.T) {
100100
t.Fatal("Can't run program:", err)
101101
}
102102

103-
if ret != 1 {
104-
t.Error("Expected return value to be 1, got", ret)
103+
if ret != 5 {
104+
t.Error("Expected return value to be 5, got", ret)
105105
}
106106
})
107107
}

linker.go

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,35 +10,51 @@ import (
1010
// link resolves bpf-to-bpf calls.
1111
//
1212
// Each library may contain multiple functions / labels, and is only linked
13-
// if the program being edited references one of these functions.
13+
// if prog references one of these functions.
1414
//
15-
// Libraries must not require linking themselves.
15+
// Libraries also linked.
1616
func link(prog *ProgramSpec, libs []*ProgramSpec) error {
17-
for _, lib := range libs {
18-
insns, err := linkSection(prog.Instructions, lib.Instructions)
19-
if err != nil {
20-
return xerrors.Errorf("linking %s: %w", lib.Name, err)
21-
}
17+
var (
18+
linked = make(map[*ProgramSpec]bool)
19+
pending = []asm.Instructions{prog.Instructions}
20+
insns asm.Instructions
21+
)
22+
for len(pending) > 0 {
23+
insns, pending = pending[0], pending[1:]
24+
for _, lib := range libs {
25+
if linked[lib] {
26+
continue
27+
}
2228

23-
if len(insns) == len(prog.Instructions) {
24-
continue
25-
}
29+
needed, err := needSection(insns, lib.Instructions)
30+
if err != nil {
31+
return xerrors.Errorf("linking %s: %w", lib.Name, err)
32+
}
2633

27-
prog.Instructions = insns
28-
if prog.BTF != nil && lib.BTF != nil {
29-
if err := btf.ProgramAppend(prog.BTF, lib.BTF); err != nil {
30-
return xerrors.Errorf("linking BTF of %s: %w", lib.Name, err)
34+
if !needed {
35+
continue
36+
}
37+
38+
linked[lib] = true
39+
prog.Instructions = append(prog.Instructions, lib.Instructions...)
40+
pending = append(pending, lib.Instructions)
41+
42+
if prog.BTF != nil && lib.BTF != nil {
43+
if err := btf.ProgramAppend(prog.BTF, lib.BTF); err != nil {
44+
return xerrors.Errorf("linking BTF of %s: %w", lib.Name, err)
45+
}
3146
}
3247
}
3348
}
49+
3450
return nil
3551
}
3652

37-
func linkSection(insns, section asm.Instructions) (asm.Instructions, error) {
53+
func needSection(insns, section asm.Instructions) (bool, error) {
3854
// A map of symbols to the libraries which contain them.
3955
symbols, err := section.SymbolOffsets()
4056
if err != nil {
41-
return nil, err
57+
return false, err
4258
}
4359

4460
for _, ins := range insns {
@@ -61,11 +77,10 @@ func linkSection(insns, section asm.Instructions) (asm.Instructions, error) {
6177
}
6278

6379
// At this point we know that at least one function in the
64-
// library is called from insns. Merge the two sections.
65-
// The rewrite of ins.Constant happens in asm.Instruction.Marshal.
66-
return append(insns, section...), nil
80+
// library is called from insns, so we have to link it.
81+
return true, nil
6782
}
6883

69-
// None of the functions in the section are called. Do nothing.
70-
return insns, nil
84+
// None of the functions in the section are called.
85+
return false, nil
7186
}

linker_test.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,22 @@ func TestLink(t *testing.T) {
2020
License: "MIT",
2121
}
2222

23-
lib := &ProgramSpec{
24-
Instructions: asm.Instructions{
25-
asm.LoadImm(asm.R0, 1337, asm.DWord).Sym("my_func"),
26-
asm.Return(),
23+
libs := []*ProgramSpec{
24+
{
25+
Instructions: asm.Instructions{
26+
asm.LoadImm(asm.R0, 1337, asm.DWord).Sym("my_other_func"),
27+
asm.Return(),
28+
},
29+
},
30+
{
31+
Instructions: asm.Instructions{
32+
asm.Call.Label("my_other_func").Sym("my_func"),
33+
asm.Return(),
34+
},
2735
},
2836
}
2937

30-
err := link(spec, []*ProgramSpec{lib})
38+
err := link(spec, libs)
3139
if err != nil {
3240
t.Fatal(err)
3341
}

testdata/invalid_map.elf

16 Bytes
Binary file not shown.

testdata/loader-clang-6.0.elf

488 Bytes
Binary file not shown.

testdata/loader-clang-7.elf

672 Bytes
Binary file not shown.

testdata/loader-clang-8.elf

872 Bytes
Binary file not shown.

testdata/loader-clang-9.elf

904 Bytes
Binary file not shown.

testdata/loader.c

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,20 +44,20 @@ struct {
4444
} btf_map __section(".maps");
4545
#endif
4646

47-
static int __attribute__((noinline)) helper_func2(uint32_t arg) {
47+
static int __attribute__((noinline)) static_fn(uint32_t arg) {
4848
return arg;
4949
}
5050

51-
int __attribute__((noinline)) helper_func(uint32_t arg) {
52-
// Enforce bpf-to-bpf call in .text section
53-
return helper_func2(arg);
51+
int __attribute__((noinline)) global_fn2(uint32_t arg) {
52+
return arg++;
5453
}
5554

56-
// Static functions in other sections get different relocations
57-
// than global functions.
58-
static int __attribute__((noinline)) helper_func_static() {
59-
uint32_t key = 0;
60-
return !!map_lookup_elem(&hash_map, (void*)&key);
55+
int __attribute__((noinline)) __section("other") global_fn3(uint32_t arg) {
56+
return arg+1;
57+
}
58+
59+
int __attribute__((noinline)) global_fn(uint32_t arg) {
60+
return static_fn(arg) + global_fn2(arg) + global_fn3(arg);
6161
}
6262

6363
#if __clang_major__ >= 9
@@ -77,10 +77,7 @@ __section("xdp") int xdp_prog() {
7777
map_lookup_elem(&hash_map, (void*)&key1);
7878
map_lookup_elem(&hash_map2, (void*)&key2);
7979
map_lookup_elem(&hash_map2, (void*)&key3);
80-
if (helper_func_static()) {
81-
return 2;
82-
}
83-
return helper_func(arg);
80+
return static_fn(arg) + global_fn(arg);
8481
}
8582

8683
// This function has no relocations, and is thus parsed differently.

testdata/rewrite.elf

112 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)