Skip to content

Commit b013477

Browse files
committed
elf: correctly relocate calls to static functions
Clang emits calls to static functions in a different section by encoding the offset of the function in the imm field. The first instruction in another section is encoded with an immediate value of -1. The relocation for the function call the references a section in the ELF. I suspect this has to do with saving work for the linker. For the library this is a problem: we track function calls by name and not by offset. Reverse the offset calculation to find the real function name, and force the immediate to -1. From that point onwards static calls follow the same path as non-static ones. Fixes cilium#72
1 parent d199038 commit b013477

File tree

7 files changed

+85
-40
lines changed

7 files changed

+85
-40
lines changed

asm/instruction.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error {
394394
num := 0
395395
for i, ins := range insns {
396396
switch {
397-
case ins.OpCode.JumpOp() == Call && ins.Constant == -1:
397+
case ins.OpCode.JumpOp() == Call && ins.Src == PseudoCall && ins.Constant == -1:
398398
// Rewrite bpf to bpf call
399399
offset, ok := absoluteOffsets[ins.Reference]
400400
if !ok {

elf_reader.go

Lines changed: 74 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import (
2020
type elfCode struct {
2121
*elf.File
2222
symbols []elf.Symbol
23-
symbolsPerSection map[elf.SectionIndex]map[uint64]string
23+
symbolsPerSection map[elf.SectionIndex]map[uint64]elf.Symbol
2424
license string
2525
version uint32
2626
}
@@ -173,20 +173,20 @@ func (ec *elfCode) loadPrograms(progSections map[elf.SectionIndex]*elf.Section,
173173
return nil, xerrors.Errorf("section %v: missing symbols", prog.Name)
174174
}
175175

176-
funcSym := syms[0]
177-
if funcSym == "" {
176+
funcSym, ok := syms[0]
177+
if !ok {
178178
return nil, xerrors.Errorf("section %v: no label at start", prog.Name)
179179
}
180180

181181
insns, length, err := ec.loadInstructions(prog, syms, relocations[idx])
182182
if err != nil {
183-
return nil, xerrors.Errorf("program %s: can't unmarshal instructions: %w", funcSym, err)
183+
return nil, xerrors.Errorf("program %s: can't unmarshal instructions: %w", funcSym.Name, err)
184184
}
185185

186186
progType, attachType := getProgType(prog.Name)
187187

188188
spec := &ProgramSpec{
189-
Name: funcSym,
189+
Name: funcSym.Name,
190190
Type: progType,
191191
AttachType: attachType,
192192
License: ec.license,
@@ -223,7 +223,7 @@ func (ec *elfCode) loadPrograms(progSections map[elf.SectionIndex]*elf.Section,
223223
return res, nil
224224
}
225225

226-
func (ec *elfCode) loadInstructions(section *elf.Section, symbols map[uint64]string, relocations map[uint64]elf.Symbol) (asm.Instructions, uint64, error) {
226+
func (ec *elfCode) loadInstructions(section *elf.Section, symbols, relocations map[uint64]elf.Symbol) (asm.Instructions, uint64, error) {
227227
var (
228228
r = section.Open()
229229
insns asm.Instructions
@@ -239,7 +239,7 @@ func (ec *elfCode) loadInstructions(section *elf.Section, symbols map[uint64]str
239239
return nil, 0, xerrors.Errorf("offset %d: %w", offset, err)
240240
}
241241

242-
ins.Symbol = symbols[offset]
242+
ins.Symbol = symbols[offset].Name
243243

244244
if rel, ok := relocations[offset]; ok {
245245
if err = ec.relocateInstruction(&ins, rel); err != nil {
@@ -256,9 +256,20 @@ func (ec *elfCode) relocateInstruction(ins *asm.Instruction, rel elf.Symbol) err
256256
var (
257257
typ = elf.ST_TYPE(rel.Info)
258258
bind = elf.ST_BIND(rel.Info)
259-
ref = rel.Name
259+
name = rel.Name
260260
)
261261

262+
if typ == elf.STT_SECTION {
263+
// Symbols with section type do not have a name set. Get it
264+
// from the section itself.
265+
idx := int(rel.Section)
266+
if idx > len(ec.Sections) {
267+
return xerrors.New("out-of-bounds section index")
268+
}
269+
270+
name = ec.Sections[idx].Name
271+
}
272+
262273
outer:
263274
switch {
264275
case ins.OpCode == asm.LoadImmOp(asm.DWord):
@@ -272,18 +283,10 @@ outer:
272283
// This is a direct load since the referenced symbol is a
273284
// section. Weirdly, the offset of the real symbol in the
274285
// section is encoded in the instruction stream.
275-
idx := int(rel.Section)
276-
if idx > len(ec.Sections) {
277-
return xerrors.New("out-of-bounds section index")
278-
}
279-
280286
if bind != elf.STB_LOCAL {
281-
return xerrors.Errorf("direct load: %s: unsupported relocation %s", ref, bind)
287+
return xerrors.Errorf("direct load: %s: unsupported relocation %s", name, bind)
282288
}
283289

284-
// Make the instruction reference the map it's loading from.
285-
ref = ec.Sections[idx].Name
286-
287290
// For some reason, clang encodes the offset of the symbol its
288291
// section in the first basic BPF instruction, while the kernel
289292
// expects it in the second one.
@@ -293,7 +296,7 @@ outer:
293296
case elf.STT_NOTYPE:
294297
if bind == elf.STB_GLOBAL && rel.Section == elf.SHN_UNDEF {
295298
// This is a relocation generated by inline assembly.
296-
// We can't do more than assing ins.Reference.
299+
// We can't do more than assigning ins.Reference.
297300
break outer
298301
}
299302

@@ -303,10 +306,13 @@ outer:
303306

304307
case elf.STT_OBJECT:
305308
if bind != elf.STB_GLOBAL {
306-
return xerrors.Errorf("load: %s: unsupported relocation %s", ref, bind)
309+
return xerrors.Errorf("load: %s: unsupported binding: %s", name, bind)
307310
}
308311

309312
ins.Src = asm.PseudoMapFD
313+
314+
default:
315+
return xerrors.Errorf("load: %s: unsupported relocation: %s", name, typ)
310316
}
311317

312318
// Mark the instruction as needing an update when creating the
@@ -316,19 +322,47 @@ outer:
316322
}
317323

318324
case ins.OpCode.JumpOp() == asm.Call:
319-
if bind != elf.STB_GLOBAL {
320-
return xerrors.Errorf("call: %s: unsupported relocation %s", ref, bind)
325+
if ins.Src != asm.PseudoCall {
326+
return xerrors.Errorf("call: %s: incorrect source register", name)
321327
}
322328

323-
if typ != elf.STT_NOTYPE && typ != elf.STT_FUNC {
324-
return xerrors.Errorf("call: %s: invalid symbol type %s", ref, typ)
329+
switch typ {
330+
case elf.STT_NOTYPE, elf.STT_FUNC:
331+
if bind != elf.STB_GLOBAL {
332+
return xerrors.Errorf("call: %s: unsupported binding: %s", name, bind)
333+
}
334+
335+
case elf.STT_SECTION:
336+
if bind != elf.STB_LOCAL {
337+
return xerrors.Errorf("call: %s: unsupported binding: %s", name, bind)
338+
}
339+
340+
// The function we want to call is in the indicated section,
341+
// at the offset encoded in the instruction itself. Reverse
342+
// the calculation to find the real function we're looking for.
343+
// A value of -1 references the first instruction in the section.
344+
offset := int64(int32(ins.Constant)+1) * asm.InstructionSize
345+
if offset < 0 {
346+
return xerrors.Errorf("call: %s: invalid offset %d", offset)
347+
}
348+
349+
sym, ok := ec.symbolsPerSection[rel.Section][uint64(offset)]
350+
if !ok {
351+
return xerrors.Errorf("call: %s: no symbol at offset %d", name, offset)
352+
}
353+
354+
ins.Constant = -1
355+
name = sym.Name
356+
357+
default:
358+
return xerrors.Errorf("call: %s: invalid symbol type %s", name, typ)
325359
}
326360

327361
default:
328362
return xerrors.Errorf("relocation for unsupported instruction: %s", ins.OpCode)
329363
}
330364

331-
ins.Reference = ref
365+
ins.Reference = name
332366
return nil
333367
}
334368

@@ -348,19 +382,19 @@ func (ec *elfCode) loadMaps(maps map[string]*MapSpec, mapSections map[elf.Sectio
348382
size = sec.Size / uint64(len(syms))
349383
)
350384
for i, offset := 0, uint64(0); i < len(syms); i, offset = i+1, offset+size {
351-
mapSym := syms[offset]
352-
if mapSym == "" {
385+
mapSym, ok := syms[offset]
386+
if !ok {
353387
return xerrors.Errorf("section %s: missing symbol for map at offset %d", sec.Name, offset)
354388
}
355389

356-
if maps[mapSym] != nil {
390+
if maps[mapSym.Name] != nil {
357391
return xerrors.Errorf("section %v: map %v already exists", sec.Name, mapSym)
358392
}
359393

360394
lr := io.LimitReader(r, int64(size))
361395

362396
spec := MapSpec{
363-
Name: SanitizeName(mapSym, -1),
397+
Name: SanitizeName(mapSym.Name, -1),
364398
}
365399
switch {
366400
case binary.Read(lr, ec.ByteOrder, &spec.Type) != nil:
@@ -379,7 +413,7 @@ func (ec *elfCode) loadMaps(maps map[string]*MapSpec, mapSections map[elf.Sectio
379413
return xerrors.Errorf("map %v: unknown and non-zero fields in definition", mapSym)
380414
}
381415

382-
maps[mapSym] = &spec
416+
maps[mapSym.Name] = &spec
383417
}
384418
}
385419

@@ -398,21 +432,22 @@ func (ec *elfCode) loadBTFMaps(maps map[string]*MapSpec, mapSections map[elf.Sec
398432
}
399433

400434
for _, sym := range syms {
401-
if maps[sym] != nil {
435+
name := sym.Name
436+
if maps[name] != nil {
402437
return xerrors.Errorf("section %v: map %v already exists", sec.Name, sym)
403438
}
404439

405-
btfMap, btfMapMembers, err := spec.Map(sym)
440+
btfMap, btfMapMembers, err := spec.Map(name)
406441
if err != nil {
407-
return xerrors.Errorf("map %v: can't get BTF: %w", sym, err)
442+
return xerrors.Errorf("map %v: can't get BTF: %w", name, err)
408443
}
409444

410445
spec, err := mapSpecFromBTF(btfMap, btfMapMembers)
411446
if err != nil {
412-
return xerrors.Errorf("map %v: %w", sym, err)
447+
return xerrors.Errorf("map %v: %w", name, err)
413448
}
414449

415-
maps[sym] = spec
450+
maps[name] = spec
416451
}
417452
}
418453

@@ -640,9 +675,9 @@ func (ec *elfCode) loadRelocations(sections map[elf.SectionIndex]*elf.Section) (
640675
return result, nil
641676
}
642677

643-
func symbolsPerSection(symbols []elf.Symbol) map[elf.SectionIndex]map[uint64]string {
644-
result := make(map[elf.SectionIndex]map[uint64]string)
645-
for i, sym := range symbols {
678+
func symbolsPerSection(symbols []elf.Symbol) map[elf.SectionIndex]map[uint64]elf.Symbol {
679+
result := make(map[elf.SectionIndex]map[uint64]elf.Symbol)
680+
for _, sym := range symbols {
646681
switch elf.ST_TYPE(sym.Info) {
647682
case elf.STT_NOTYPE:
648683
// Older versions of LLVM doesn't tag
@@ -666,9 +701,9 @@ func symbolsPerSection(symbols []elf.Symbol) map[elf.SectionIndex]map[uint64]str
666701

667702
idx := sym.Section
668703
if _, ok := result[idx]; !ok {
669-
result[idx] = make(map[uint64]string)
704+
result[idx] = make(map[uint64]elf.Symbol)
670705
}
671-
result[idx][sym.Value] = symbols[i].Name
706+
result[idx][sym.Value] = sym
672707
}
673708
return result
674709
}

testdata/loader-clang-6.0.elf

696 Bytes
Binary file not shown.

testdata/loader-clang-7.elf

672 Bytes
Binary file not shown.

testdata/loader-clang-8.elf

1.09 KB
Binary file not shown.

testdata/loader-clang-9.elf

1.12 KB
Binary file not shown.

testdata/loader.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ int __attribute__((noinline)) helper_func(uint32_t arg) {
5353
return helper_func2(arg);
5454
}
5555

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);
61+
}
62+
5663
#if __clang_major__ >= 9
5764
static volatile unsigned int key1 = 0; // .bss
5865
static volatile unsigned int key2 = 1; // .data
@@ -70,6 +77,9 @@ __section("xdp") int xdp_prog() {
7077
map_lookup_elem(&hash_map, (void*)&key1);
7178
map_lookup_elem(&hash_map2, (void*)&key2);
7279
map_lookup_elem(&hash_map2, (void*)&key3);
80+
if (helper_func_static()) {
81+
return 2;
82+
}
7383
return helper_func(arg);
7484
}
7585

0 commit comments

Comments
 (0)