Skip to content

Member sizes read from btf are always zero #1788

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

Closed
jschwinger233 opened this issue May 28, 2025 · 4 comments
Closed

Member sizes read from btf are always zero #1788

jschwinger233 opened this issue May 28, 2025 · 4 comments
Labels
bug Something isn't working

Comments

@jschwinger233
Copy link
Member

Describe the bug

I tried to read __sk_buff->gso_size's offset and size from BTF, found zero size as a result.

How to reproduce

package main

import (
	"fmt"

	"github.com/cilium/ebpf/btf"
)

func main() {
	btfSpec, err := btf.LoadKernelSpec()
	if err != nil {
		panic(fmt.Sprintf("Failed to load BTF spec: %v", err))
	}

	iter := btfSpec.Iterate()
	for iter.Next() {
		if strct, ok := iter.Type.(*btf.Struct); ok && strct.Name == "__sk_buff" {
			for _, member := range strct.Members {
				offsetInBytes := member.Offset.Bytes()
				SizeInBytes := member.BitfieldSize.Bytes()
				println("Member:", member.Name, "Offset:", offsetInBytes, "Size:", SizeInBytes)
			}
		}
	}

}

The output is:

Member: len Offset: 0 Size: 0
Member: pkt_type Offset: 4 Size: 0
Member: mark Offset: 8 Size: 0
Member: queue_mapping Offset: 12 Size: 0
Member: protocol Offset: 16 Size: 0
Member: vlan_present Offset: 20 Size: 0
Member: vlan_tci Offset: 24 Size: 0
Member: vlan_proto Offset: 28 Size: 0
Member: priority Offset: 32 Size: 0
Member: ingress_ifindex Offset: 36 Size: 0
Member: ifindex Offset: 40 Size: 0
Member: tc_index Offset: 44 Size: 0
Member: cb Offset: 48 Size: 0
Member: hash Offset: 68 Size: 0
Member: tc_classid Offset: 72 Size: 0
Member: data Offset: 76 Size: 0
Member: data_end Offset: 80 Size: 0
Member: napi_id Offset: 84 Size: 0
Member: family Offset: 88 Size: 0
Member: remote_ip4 Offset: 92 Size: 0
Member: local_ip4 Offset: 96 Size: 0
Member: remote_ip6 Offset: 100 Size: 0
Member: local_ip6 Offset: 116 Size: 0
Member: remote_port Offset: 132 Size: 0
Member: local_port Offset: 136 Size: 0
Member: data_meta Offset: 140 Size: 0
Member:  Offset: 144 Size: 0
Member: tstamp Offset: 152 Size: 0
Member: wire_len Offset: 160 Size: 0
Member: gso_segs Offset: 164 Size: 0
Member:  Offset: 168 Size: 0
Member: gso_size Offset: 176 Size: 0
Member: tstamp_type Offset: 180 Size: 0
Member: hwtstamp Offset: 184 Size: 0

Version information

github.com/cilium/ebpf v0.18.0

@jschwinger233 jschwinger233 added the bug Something isn't working label May 28, 2025
@florianl
Copy link
Contributor

Hi 👋
Can you please also share the architecture and Kernel version on which you are running this experiment?

@jschwinger233
Copy link
Member Author

jschwinger233 commented May 28, 2025

$ uname -a
Linux graymon-Latitude-5530 6.11.0-26-generic #26~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Apr 17 19:20:47 UTC 2 x86_64 x86_64 x86_64 GNU/Linux

pahole and bpftool are both working properly with btf:

$ pahole -C __sk_buff | grep gso_size
               __u32                      gso_size;             /*   176     4 */
$ sudo bpftool btf d id 1 | grep __sk_buff -A50 | grep gso_size
               'gso_size' type_id=14 bits_offset=1408

$ sudo bpftool btf d id 1 f c | grep 'struct __sk_buff' -A50 | grep gso_size
	__u32 gso_size;

@Ghostbaby
Copy link
Contributor

Ghostbaby commented May 30, 2025

Hi @jschwinger233 .

  1. BitfieldSize is only meaningful for bitfield members (when BitfieldSize > 0)
  2. For regular struct members, BitfieldSize is typically 0
  3. To get the actual size of a member, you need to use btf.Sizeof(member.Type)

From the debugger screenshot, we can see that the __u32 type has an actual size of 4 bytes, but BitfieldSize shows 0, causing all members to display a size of 0.

The code needs to distinguish between bitfield members and regular members, then use the appropriate method to calculate the size:

// ... existing code ...
iter := btfSpec.Iterate()
for iter.Next() {
    if strct, ok := iter.Type.(*btf.Struct); ok && strct.Name == "__sk_buff" {
        for _, member := range strct.Members {
            offsetInBytes := member.Offset.Bytes()
            
            // Check if this is a bitfield member
            var sizeInBytes uint32
            if member.BitfieldSize > 0 {
                // For bitfield members, use BitfieldSize
                sizeInBytes = member.BitfieldSize.Bytes()
            } else {
                // For regular members, calculate size from type
                size, err := btf.Sizeof(member.Type)
                if err != nil {
                    fmt.Printf("Failed to get size for member %s: %v\n", member.Name, err)
                    continue
                }
                sizeInBytes = uint32(size)
            }
            
            println("Member:", member.Name, "Offset:", offsetInBytes, "Size:", sizeInBytes)
        }
    }
}
// ... existing code ...

@jschwinger233
Copy link
Member Author

Closed as resolved. Thank you @Ghostbaby 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants