-
Notifications
You must be signed in to change notification settings - Fork 778
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
Conversation
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?
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.
removed such code now.
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.
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.
fixed
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.
elf_reader.go
Outdated
| 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.
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.
@shun159 still TODO.
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 yes, right. it is unnecessary guard.
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.
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 was addressed
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.
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.
Size should be btf.Sizeof(userType) no?
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 this is the size of the section data of the user-side struct, which is different from the size of the kernel-side value 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.
we specified the length of the user-type as the value type 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.
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.
right. this was removed.
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.
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.
Merged
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.
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.
we can use sec.relocations. fixed now.
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 |
That failure just tells me that AttachTo is not populated. Can you explain why that is? |
@lmb when given the code below, (for now) we cannot derive which struct_ops map the standalone program #include "common.h"
char _license[] __section("license") = "GPL";
struct bpf_testmod_ops {
int (*test_1)(void);
int data;
};
__section("?struct_ops/test_1") int foo(void) {
return 0;
}
__section("?struct_ops/test_1") int bar(void) {
return 0;
}
__section(".struct_ops.link") struct bpf_testmod_ops testmod_ops = {
.test_1 = (void *)bar,
}; |
How will we derive that in the future then? IMO the correct semantics here is to set |
d96bed6 to
79cc8a4
Compare
|
rebased to |
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 take a look at elfSection.relocations, that should contain what you need. Thanks for adding a thorough test case again!
elf_reader.go
Outdated
| 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.
@shun159 still TODO.
|
@lmb could you please review at your leisure? |
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.
Some nits, I think this is good to go. We're going to cut a release and merge this afterwards so that we get some more time to iterate.
|
@lmb I've addressed your feedback! 😄 |
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]>
992536f to
f34a9d8
Compare
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