-
Notifications
You must be signed in to change notification settings - Fork 773
elf_reader: add struct_ops support #1869
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
This commit adds struct_ops support to the ELF reader: it classifies non-executable PROGBITS sections, parses their BTF Datasec to build MapSpecs, associates relocs with func-pointer members to set ps.AttachTo, and adds TestStructOps. Related: cilium#1845 Signed-off-by: shun159 <[email protected]>
``` Section Headers: [Nr] Name Type Address Off Size ES Flg Lk Inf Al [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 [ 1] .strtab STRTAB 0000000000000000 00036b 000077 00 0 0 1 [ 2] .text PROGBITS 0000000000000000 000040 000000 00 AX 0 0 4 [ 3] struct_ops/test_1 PROGBITS 0000000000000000 000040 000010 00 AX 0 0 8 [ 4] license PROGBITS 0000000000000000 000050 000004 00 WA 0 0 1 [ 5] .struct_ops.link PROGBITS 0000000000000000 000058 000018 00 WA 0 0 8 [ 6] .rel.struct_ops.link REL 0000000000000000 000318 000010 10 I 12 5 8 [ 7] .BTF PROGBITS 0000000000000000 000070 0001e0 00 0 0 4 [ 8] .rel.BTF REL 0000000000000000 000328 000020 10 I 12 7 8 [ 9] .BTF.ext PROGBITS 0000000000000000 000250 000050 00 0 0 4 [10] .rel.BTF.ext REL 0000000000000000 000348 000020 10 I 12 9 8 [11] .llvm_addrsig LLVM_ADDRSIG 0000000000000000 000368 000003 00 E 0 0 1 [12] .symtab SYMTAB 0000000000000000 0002a0 000078 18 1 2 8 ``` Signed-off-by: shun159 <[email protected]>
Signed-off-by: shun159 <[email protected]>
Signed-off-by: shun159 <[email protected]>
6e099b2
to
ef7d704
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please refuse loading bare .struct_ops
and re-enable the LibBPFCompat
test of StructOps.
elf_reader.go
Outdated
} | ||
|
||
for _, vsi := range dataSec.Vars { | ||
varType, ok := btf.As[*btf.Var](vsi.Type) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why mix btf.As and btf.UnderlyingType here?
elf_reader.go
Outdated
|
||
// Retrieve raw data from the ELF section. | ||
// This data contains the initial values for the struct_ops map. | ||
userData, err := sec.Data() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do this for each iteration? Can be done once.
elf_reader.go
Outdated
case sec.Type == elf.SHT_PROGBITS: | ||
if (sec.Flags&elf.SHF_EXECINSTR) != 0 && sec.Size > 0 { | ||
sections[idx] = newElfSection(sec, programSection) | ||
} else if sec.Name == ".struct_ops" || sec.Name == ".struct_ops.link" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refuse ".struct_ops"
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to still allow .struct_ops
parsing to Spec, but then not allow loading unless you manually add BPF_F_LINK
to the map spec?
If you block it in ELF parsing, then you force users to modify the section, and take away the ability to patch this in userspace.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason for not using BPF_F_LINK is to get compat with older kernels, no? Adding BPF_F_LINK manually then kind of defeats the purpose?
I agree that it's probably nicer to refuse loading a StructOpsMap without BPF_F_LINK instead, then we can test more of the libbpf ELFs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason for not using BPF_F_LINK is to get compat with older kernels, no? Adding BPF_F_LINK manually then kind of defeats the purpose?
Yea, BPF_F_LINK
got added in 6.4, and struct ops originally got added in 5.6. In the end, its all about the attachment, there is not difference in the how you write the programs. Its only really tcp_congestion_ops that existed before BPF_F_LINK
so its examples and selftests use .struct_ops
. Schedext and HIDBPF both have macros for defining the struct which do use .struct_ops.link
, since those features came after 6.4.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ack. let's refuse ".struct_ops"
here.
continue | ||
} | ||
|
||
if !(sec.Type == elf.SHT_PROGBITS && (sec.Flags&elf.SHF_EXECINSTR) != 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this necessary? if kind == programSection
then this should always be true.
elf_reader.go
Outdated
} | ||
|
||
// Process the struct_ops section to create the map | ||
dataType, err := ec.btf.AnyTypeByName(sec.Name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should use TypeByName.
elf_reader.go
Outdated
Type: StructOpsMap, | ||
Key: &btf.Int{Size: 4}, | ||
KeySize: 4, | ||
Value: userType, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe also set ValueSize for completeness sake?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lmb At this point, the size of the value type is not yet determined, so my understanding is that it’s difficult to specify this field for now.
elf_reader.go
Outdated
Name string | ||
} | ||
|
||
type structOpsSpec struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is necessary. elfSection.relocations
already carries this information in the form of an elf.Symbol
.
elf_reader.go
Outdated
|
||
// loadStructOpsMapsFromSections creates StructOps MapSpecs from DataSec sections | ||
// ".struct_ops" and ".struct_ops.link" found in the object BTF. | ||
func (ec *elfCode) loadStructOpsMaps() error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be merged with associateStructOpsRelocs unless there is a reason why they need to be separate which I don't understand.
elf_reader.go
Outdated
relSecs map[elf.SectionIndex]*elf.Section, | ||
symbols []elf.Symbol, | ||
) error { | ||
for _, sec := range relSecs { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This just duplicates the loop in loadRelocations? You can iterate sec.relocations instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
outdated, but I will take a look this.
Signed-off-by: shun159 <[email protected]>
Co-authored-by: Lorenz Bauer <[email protected]> Signed-off-by: Eishun Kondoh <[email protected]>
Signed-off-by: shun159 <[email protected]>
Signed-off-by: shun159 <[email protected]>
Signed-off-by: shun159 <[email protected]>
Signed-off-by: shun159 <[email protected]>
Signed-off-by: shun159 <[email protected]>
Signed-off-by: shun159 <[email protected]>
It seems that in the test cases for programs that include "autocreate", the program fails when load because it cannot determine which map it belongs to. test case: https://github.com/cilium/ebpf/actions/runs/18612775255/job/53073227432?pr=1869#step:8:1224 |
Signed-off-by: shun159 <[email protected]>
…ctOpsRelocs Signed-off-by: shun159 <[email protected]>
return nil | ||
} | ||
|
||
// associateStructOpsRelocs handles `.struct_ops.link` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: need to be refactored.
Signed-off-by: shun159 <[email protected]>
This commit adds struct_ops support to the ELF reader: it classifies non-executable PROGBITS sections, parses their BTF Datasec to build MapSpecs, associates relocs with func-pointer members to set ps.AttachTo, and adds TestStructOps.
Related: #1845