@@ -41,20 +41,30 @@ type ksymMeta struct {
4141 Name string
4242}
4343
44+ type structOpsSpec struct {
45+ name string
46+ // section index of .struct_ops / .struct_ops.link
47+ secIdx elf.SectionIndex
48+ // byte offset of the variable in that section
49+ userOff uint64
50+ userSize uint64
51+ }
52+
4453// elfCode is a convenience to reduce the amount of arguments that have to
4554// be passed around explicitly. You should treat its contents as immutable.
4655type elfCode struct {
4756 * internal.SafeELFFile
48- sections map [elf.SectionIndex ]* elfSection
49- license string
50- version uint32
51- btf * btf.Spec
52- extInfo * btf.ExtInfos
53- maps map [string ]* MapSpec
54- vars map [string ]* VariableSpec
55- kfuncs map [string ]* btf.Func
56- ksyms map [string ]struct {}
57- kconfig * MapSpec
57+ sections map [elf.SectionIndex ]* elfSection
58+ license string
59+ version uint32
60+ btf * btf.Spec
61+ extInfo * btf.ExtInfos
62+ maps map [string ]* MapSpec
63+ vars map [string ]* VariableSpec
64+ kfuncs map [string ]* btf.Func
65+ ksyms map [string ]struct {}
66+ kconfig * MapSpec
67+ structOps map [string ]* structOpsSpec
5868}
5969
6070// LoadCollectionSpec parses an ELF file into a CollectionSpec.
@@ -116,8 +126,15 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) {
116126 case sec .Type == elf .SHT_REL :
117127 // Store relocations under the section index of the target
118128 relSections [elf .SectionIndex (sec .Info )] = sec
119- case sec .Type == elf .SHT_PROGBITS && (sec .Flags & elf .SHF_EXECINSTR ) != 0 && sec .Size > 0 :
120- sections [idx ] = newElfSection (sec , programSection )
129+ case sec .Type == elf .SHT_PROGBITS :
130+ if (sec .Flags & elf .SHF_EXECINSTR ) != 0 && sec .Size > 0 {
131+ sections [idx ] = newElfSection (sec , programSection )
132+ } else if sec .Name == ".struct_ops" || sec .Name == ".struct_ops.link" {
133+ //classification based on sec names so that struct_ops-specific
134+ // sections (.struct_ops, .struct_ops.link) are correctly recognized
135+ // as non-executable PROGBITS, allowing value placement and link metadata to be loaded.
136+ sections [idx ] = newElfSection (sec , structOpsSection )
137+ }
121138 }
122139 }
123140
@@ -147,6 +164,7 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) {
147164 vars : make (map [string ]* VariableSpec ),
148165 kfuncs : make (map [string ]* btf.Func ),
149166 ksyms : make (map [string ]struct {}),
167+ structOps : make (map [string ]* structOpsSpec ),
150168 }
151169
152170 symbols , err := f .Symbols ()
@@ -164,6 +182,10 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) {
164182 return nil , fmt .Errorf ("load maps: %w" , err )
165183 }
166184
185+ if err := ec .loadStructOpsMaps (); err != nil {
186+ return nil , fmt .Errorf ("struct_ops maps: %w" , err )
187+ }
188+
167189 if err := ec .loadBTFMaps (); err != nil {
168190 return nil , fmt .Errorf ("load BTF maps: %w" , err )
169191 }
@@ -186,6 +208,15 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) {
186208 return nil , fmt .Errorf ("load programs: %w" , err )
187209 }
188210
211+ // assiociate members in structs with ProgramSpecs using relo
212+ if err := ec .associateStructOpsRelocs (
213+ progs ,
214+ relSections ,
215+ symbols ,
216+ ); err != nil {
217+ return nil , fmt .Errorf ("load struct_ops: %w" , err )
218+ }
219+
189220 return & CollectionSpec {
190221 ec .maps ,
191222 progs ,
@@ -239,6 +270,7 @@ const (
239270 btfMapSection
240271 programSection
241272 dataSection
273+ structOpsSection
242274)
243275
244276type elfSection struct {
@@ -349,6 +381,10 @@ func (ec *elfCode) loadProgramSections() (map[string]*ProgramSpec, error) {
349381 continue
350382 }
351383
384+ if ! (sec .Type == elf .SHT_PROGBITS && (sec .Flags & elf .SHF_EXECINSTR ) != 0 ) {
385+ continue
386+ }
387+
352388 if len (sec .symbols ) == 0 {
353389 return nil , fmt .Errorf ("section %v: missing symbols" , sec .Name )
354390 }
@@ -564,7 +600,7 @@ func (ec *elfCode) relocateInstruction(ins *asm.Instruction, rel elf.Symbol) err
564600 ins .Constant = int64 (uint64 (offset ) << 32 )
565601 ins .Src = asm .PseudoMapValue
566602
567- case programSection :
603+ case programSection , structOpsSection :
568604 switch opCode := ins .OpCode ; {
569605 case opCode .JumpOp () == asm .Call :
570606 if ins .Src != asm .PseudoCall {
@@ -1379,6 +1415,163 @@ func (ec *elfCode) loadKsymsSection() error {
13791415 return nil
13801416}
13811417
1418+ // loadStructOpsMapsFromSections creates StructOps MapSpecs from DataSec sections
1419+ // ".struct_ops" and ".struct_ops.link" found in the object BTF.
1420+ func (ec * elfCode ) loadStructOpsMaps () error {
1421+ for secIdx , sec := range ec .sections {
1422+ if sec .kind != structOpsSection {
1423+ continue
1424+ }
1425+
1426+ // Process the struct_ops section to create the map
1427+ dataType , err := ec .btf .AnyTypeByName (sec .Name )
1428+ if err != nil {
1429+ return fmt .Errorf ("datasec %s: %w" , sec .Name , err )
1430+ }
1431+
1432+ dataSec , ok := btf.As [* btf.Datasec ](dataType )
1433+ if ! ok {
1434+ return fmt .Errorf ("%s BTF is not a Datasec" , sec .Name )
1435+ }
1436+
1437+ for _ , vsi := range dataSec .Vars {
1438+ varType , ok := btf.As [* btf.Var ](vsi .Type )
1439+ if ! ok {
1440+ return fmt .Errorf ("var type in %s: want *btf.Var, got %T" , sec .Name , btf .UnderlyingType (vsi .Type ))
1441+ }
1442+ mapName := varType .Name
1443+
1444+ userType , ok := btf .UnderlyingType (varType .Type ).(* btf.Struct )
1445+ if ! ok {
1446+ return fmt .Errorf ("var %s: expect struct, got %T" , varType .Name , varType .Type )
1447+ }
1448+
1449+ // Retrieve raw data from the ELF section.
1450+ // This data contains the initial values for the struct_ops map.
1451+ userData , err := sec .Data ()
1452+ if err != nil {
1453+ return fmt .Errorf ("failed to read section data: %w" , err )
1454+ }
1455+
1456+ flags := uint32 (0 )
1457+ if sec .Name == ".struct_ops.link" {
1458+ flags = sys .BPF_F_LINK
1459+ }
1460+
1461+ userSize := uint64 (userType .Size )
1462+ userOff := uint64 (vsi .Offset )
1463+ if userOff + userSize > uint64 (len (userData )) {
1464+ return fmt .Errorf ("%s exceeds section" , mapName )
1465+ }
1466+
1467+ ec .structOps [mapName ] =
1468+ & structOpsSpec {
1469+ name : mapName ,
1470+ secIdx : secIdx ,
1471+ userOff : userOff ,
1472+ userSize : userSize ,
1473+ }
1474+
1475+ ec .maps [mapName ] =
1476+ & MapSpec {
1477+ Name : mapName ,
1478+ Type : StructOpsMap ,
1479+ Key : & btf.Int {Size : 4 },
1480+ Value : userType ,
1481+ Flags : flags ,
1482+ MaxEntries : 1 ,
1483+ Contents : []MapKV {
1484+ {
1485+ Key : uint32 (0 ),
1486+ Value : append ([]byte (nil ), userData [userOff :userOff + userSize ]... ),
1487+ },
1488+ },
1489+ }
1490+ }
1491+ }
1492+
1493+ return nil
1494+ }
1495+
1496+ // associateStructOpsRelocs handles `.struct_ops(.link)`
1497+ // and associates the target function with the correct struct member in the map.
1498+ func (ec * elfCode ) associateStructOpsRelocs (
1499+ progs map [string ]* ProgramSpec ,
1500+ relSecs map [elf.SectionIndex ]* elf.Section ,
1501+ symbols []elf.Symbol ,
1502+ ) error {
1503+ for _ , sec := range relSecs {
1504+ if ! strings .HasPrefix (sec .Name , ".rel" ) {
1505+ continue
1506+ }
1507+
1508+ targetIdx := elf .SectionIndex (sec .Info )
1509+ targetSec , ok := ec .sections [targetIdx ]
1510+ if ! (ok && strings .HasPrefix (targetSec .Name , ".struct_ops" )) {
1511+ continue
1512+ }
1513+
1514+ // Load the relocations from the relocation section
1515+ rels , err := ec .loadSectionRelocations (sec , symbols )
1516+ if err != nil {
1517+ return fmt .Errorf ("failed to load relocations for section %s: %w" , sec .Name , err )
1518+ }
1519+
1520+ for relOff , sym := range rels {
1521+ var ms * MapSpec
1522+ var meta * structOpsSpec
1523+
1524+ for _ , mapSpec := range ec .maps {
1525+ if mapSpec .Type != StructOpsMap || len (mapSpec .Contents ) == 0 {
1526+ continue
1527+ }
1528+
1529+ stOps , ok := ec .structOps [mapSpec .Name ]
1530+ if ! ok {
1531+ continue
1532+ }
1533+
1534+ if uint64 (targetIdx ) == uint64 (stOps .secIdx ) &&
1535+ stOps .userOff <= relOff &&
1536+ (relOff - stOps .userOff ) < stOps .userSize {
1537+ meta = stOps
1538+ ms = mapSpec
1539+ }
1540+ }
1541+
1542+ if ms == nil {
1543+ return fmt .Errorf ("no struct_ops map found for secIdx %d and relOffset %d" , targetIdx , relOff )
1544+ }
1545+
1546+ moff := btf .Bits ((relOff - meta .userOff ) * 8 )
1547+
1548+ userSt , ok := btf.As [* btf.Struct ](ms .Value )
1549+ if ! ok {
1550+ return fmt .Errorf ("provided value is not a btf.Struct" )
1551+ }
1552+
1553+ for _ , m := range userSt .Members {
1554+ if m .Offset != moff {
1555+ continue
1556+ }
1557+
1558+ mType := btf .UnderlyingType (m .Type )
1559+ if mPtr , isPtr := btf.As [* btf.Pointer ](mType ); isPtr {
1560+ if _ , isFuncProto := btf.As [* btf.FuncProto ](mPtr .Target ); isFuncProto {
1561+ p , ok := progs [sym .Name ]
1562+ if ! (ok && p .Type == StructOps ) {
1563+ return fmt .Errorf ("program %q not found or not StructOps" , sym .Name )
1564+ }
1565+ p .AttachTo = userSt .Name + ":" + m .Name
1566+ }
1567+ }
1568+ }
1569+ }
1570+ }
1571+
1572+ return nil
1573+ }
1574+
13821575type libbpfElfSectionDef struct {
13831576 pattern string
13841577 programType sys.ProgType
0 commit comments