-
Notifications
You must be signed in to change notification settings - Fork 775
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
Open
shun159
wants to merge
16
commits into
cilium:main
Choose a base branch
from
shun159:feature/struct-ops-3
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+300
−10
Open
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
8698671
elf_reader: add struct_ops support
shun159 a48fdc8
elf: fix relocateInstruction since no need to treat structOpsSection
shun159 025f0f2
elf: move logic which finds fp member into a function
shun159 ef7d704
elf: KeySize of a struct_ops map should always be 4
shun159 40e9734
testing: reenable TestLibBPFCompat
shun159 0be509e
elf: keep the sec.Size condition
shun159 fb27620
elf: fix to refuse ".struct_ops" section explicitly
shun159 9921ee7
elf: reading user data from sec.Data only needs to be done onc.
shun159 8d2dc60
elf: refactored to use TypeByName/UnderlyingType
shun159 80c5463
elf, struct_ops: move helper function and section name
shun159 3ab9db7
elf,struct_ops: fix error message in ".struct_ops" guard
shun159 b2d4a33
add handle unknown struct_ops target
shun159 558dc8b
elf: reject standalone struct_ops prog
shun159 a3ac2c8
remove structOpsSpec, and merged loadStructOpsMaps into associateStru…
shun159 0743fe4
testing: fix typo in struct_ops_autocreate
shun159 d96bed6
elf: fix to add ignoreExtra flag so that allow standalone prog
shun159 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -116,8 +116,17 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) { | |
case sec.Type == elf.SHT_REL: | ||
// Store relocations under the section index of the target | ||
relSections[elf.SectionIndex(sec.Info)] = sec | ||
case sec.Type == elf.SHT_PROGBITS && (sec.Flags&elf.SHF_EXECINSTR) != 0 && sec.Size > 0: | ||
sections[idx] = newElfSection(sec, programSection) | ||
case sec.Type == elf.SHT_PROGBITS && sec.Size > 0: | ||
if (sec.Flags&elf.SHF_EXECINSTR) != 0 && sec.Size > 0 { | ||
sections[idx] = newElfSection(sec, programSection) | ||
} else if sec.Name == structOpsLinkSec { | ||
// classification based on sec names so that struct_ops-specific | ||
// sections (.struct_ops.link) is correctly recognized | ||
// as non-executable PROGBITS, allowing value placement and link metadata to be loaded. | ||
sections[idx] = newElfSection(sec, structOpsSection) | ||
} else if sec.Name == structOpsSec { | ||
return nil, fmt.Errorf("section %q: got '.struct_ops' section: %w", sec.Name, ErrNotSupported) | ||
} | ||
} | ||
} | ||
|
||
|
@@ -186,6 +195,15 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) { | |
return nil, fmt.Errorf("load programs: %w", err) | ||
} | ||
|
||
// assiociate members in structs with ProgramSpecs using relo | ||
if err := ec.associateStructOpsRelocs( | ||
progs, | ||
relSections, | ||
symbols, | ||
); err != nil { | ||
return nil, fmt.Errorf("load struct_ops: %w", err) | ||
} | ||
|
||
return &CollectionSpec{ | ||
ec.maps, | ||
progs, | ||
|
@@ -239,6 +257,7 @@ const ( | |
btfMapSection | ||
programSection | ||
dataSection | ||
structOpsSection | ||
) | ||
|
||
type elfSection struct { | ||
|
@@ -349,6 +368,10 @@ func (ec *elfCode) loadProgramSections() (map[string]*ProgramSpec, error) { | |
continue | ||
} | ||
|
||
if !(sec.Type == elf.SHT_PROGBITS && (sec.Flags&elf.SHF_EXECINSTR) != 0) { | ||
continue | ||
} | ||
|
||
if len(sec.symbols) == 0 { | ||
return nil, fmt.Errorf("section %v: missing symbols", sec.Name) | ||
} | ||
|
@@ -1379,6 +1402,135 @@ func (ec *elfCode) loadKsymsSection() error { | |
return nil | ||
} | ||
|
||
// associateStructOpsRelocs handles `.struct_ops.link` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TODO: need to be refactored. |
||
// and associates the target function with the correct struct member in the map. | ||
func (ec *elfCode) associateStructOpsRelocs( | ||
progs map[string]*ProgramSpec, | ||
relSecs map[elf.SectionIndex]*elf.Section, | ||
symbols []elf.Symbol, | ||
) error { | ||
willAttachToMap := make(map[string]bool) | ||
|
||
for secIdx, sec := range ec.sections { | ||
if sec.kind != structOpsSection { | ||
continue | ||
} | ||
|
||
userData, err := sec.Data() | ||
if err != nil { | ||
return fmt.Errorf("failed to read section data: %w", err) | ||
} | ||
|
||
// Resolve the BTF datasec describing variables in this section. | ||
var ds *btf.Datasec | ||
if err := ec.btf.TypeByName(sec.Name, &ds); err != nil { | ||
return fmt.Errorf("datasec %s: %w", sec.Name, err) | ||
} | ||
|
||
// Set flags for .struct_ops.link (BPF_F_LINK). | ||
flags := uint32(0) | ||
if sec.Name == structOpsLinkSec { | ||
flags = sys.BPF_F_LINK | ||
} | ||
|
||
type structOpsMapOfsSize struct { | ||
userOff uint64 | ||
userSize uint64 | ||
} | ||
|
||
ofsSizes := make(map[string]structOpsMapOfsSize) | ||
|
||
for _, vsi := range ds.Vars { | ||
varType := btf.UnderlyingType(vsi.Type).(*btf.Var) | ||
mapName := varType.Name | ||
|
||
userType, ok := btf.UnderlyingType(varType.Type).(*btf.Struct) | ||
if !ok { | ||
return fmt.Errorf("var %s: expect struct, got %T", varType.Name, varType.Type) | ||
} | ||
|
||
userSize := uint64(userType.Size) | ||
userOff := uint64(vsi.Offset) | ||
if userOff+userSize > uint64(len(userData)) { | ||
return fmt.Errorf("%s exceeds section", mapName) | ||
} | ||
|
||
// Register the MapSpec for this struct_ops instance. | ||
ec.maps[mapName] = &MapSpec{ | ||
Name: mapName, | ||
Type: StructOpsMap, | ||
Key: &btf.Int{Size: 4}, | ||
KeySize: 4, | ||
Value: userType, | ||
Flags: flags, | ||
MaxEntries: 1, | ||
Contents: []MapKV{ | ||
{ | ||
Key: uint32(0), | ||
Value: append([]byte(nil), userData[userOff:userOff+userSize]...), | ||
}, | ||
}, | ||
} | ||
ofsSizes[mapName] = | ||
structOpsMapOfsSize{userOff, userSize} | ||
} | ||
|
||
// Process relo sections that target this struct_ops section. | ||
for relSecIdx, relSec := range relSecs { | ||
if elf.SectionIndex(relSec.Info) != elf.SectionIndex(secIdx) { | ||
continue | ||
} | ||
|
||
if !(relSec.Type == elf.SHT_REL) { | ||
continue | ||
} | ||
|
||
// Load relocation entries (offset -> symbol). | ||
rels, err := ec.loadSectionRelocations(relSec, symbols) | ||
if err != nil { | ||
return fmt.Errorf("failed to load relocations for section %s (relIdx=%d -> target=%d): %w", | ||
relSec.Name, relSecIdx, secIdx, err) | ||
} | ||
|
||
for relOff, sym := range rels { | ||
var ms *MapSpec | ||
var msName string | ||
var baseOff uint64 | ||
|
||
for mapName, ofsSz := range ofsSizes { | ||
if relOff >= ofsSz.userOff && relOff < ofsSz.userOff+ofsSz.userSize { | ||
baseOff = ofsSz.userOff | ||
ms = ec.maps[mapName] | ||
msName = mapName | ||
break | ||
} | ||
} | ||
|
||
if ms == nil || ms.Type != StructOpsMap { | ||
return fmt.Errorf("struct_ops map %s not found or wrong type", msName) | ||
} | ||
|
||
userSt, ok := btf.As[*btf.Struct](ms.Value) | ||
if !ok { | ||
return fmt.Errorf("map %s value is not a btf.Struct", ms.Name) | ||
} | ||
|
||
moff := btf.Bits((relOff - baseOff) * 8) | ||
if memberName, ok := structOpsFuncPtrMemberAtOffset(userSt, moff); ok { | ||
p, ok := progs[sym.Name] | ||
if !(ok && p.Type == StructOps) { | ||
return fmt.Errorf("program %s not found or not StructOps", sym.Name) | ||
} | ||
p.AttachTo = userSt.Name + ":" + memberName | ||
willAttachToMap[sym.Name] = true | ||
} | ||
} | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
type libbpfElfSectionDef struct { | ||
pattern string | ||
programType sys.ProgType | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.