diff --git a/ebpf/symtab/elf/go_table.go b/ebpf/symtab/elf/go_table.go index abae81f619..5c4051b7d4 100644 --- a/ebpf/symtab/elf/go_table.go +++ b/ebpf/symtab/elf/go_table.go @@ -65,6 +65,67 @@ var ( errGoSymbolsNotFound = errors.New("gosymtab: no go symbols found") ) +// findRuntimeTextSymbol looks up the "runtime.text" symbol from .symtab. +// Since Go 1.26, pcHeader.textStart is always zero: the linker no longer +// stores the text base in pclntab (the field required the only relocation +// in that section and was otherwise unused). +// See: https://github.com/golang/go/commit/0e1bd8b5f17e337df0ffb57af03419b96c695fe4 +func (f *MMapedElfFile) findRuntimeTextSymbol() uint64 { + symtabSection := f.sectionByType(elf.SHT_SYMTAB) + if symtabSection == nil { + return 0 + } + // Find the linked string table section. + if int(symtabSection.Link) >= len(f.Sections) { + return 0 + } + strtabSection := &f.Sections[symtabSection.Link] + + symData, err := f.SectionData(symtabSection) + if err != nil { + return 0 + } + strData, err := f.SectionData(strtabSection) + if err != nil { + return 0 + } + + var symSize int + var getValue func([]byte) uint64 + switch f.Class { + case elf.ELFCLASS64: + symSize = elf.Sym64Size + getValue = func(b []byte) uint64 { return f.ByteOrder.Uint64(b[8:16]) } + case elf.ELFCLASS32: + symSize = elf.Sym32Size + getValue = func(b []byte) uint64 { return uint64(f.ByteOrder.Uint32(b[4:8])) } + default: + return 0 + } + + // Skip first (null) entry. + if len(symData) < symSize { + return 0 + } + symData = symData[symSize:] + + target := "runtime.text\x00" + for len(symData) >= symSize { + entry := symData[:symSize] + symData = symData[symSize:] + + nameIdx := int(f.ByteOrder.Uint32(entry[:4])) + end := nameIdx + len(target) + if nameIdx < 0 || end > len(strData) { + continue + } + if string(strData[nameIdx:end]) == target { + return getValue(entry) + } + } + return 0 +} + func (f *MMapedElfFile) NewGoTable() (*GoTable, error) { obj := f var err error @@ -90,7 +151,12 @@ func (f *MMapedElfFile) NewGoTable() (*GoTable, error) { textStart := gosym2.ParseRuntimeTextFromPclntab18(pclntabHeader) if textStart == 0 { - // for older versions text.Addr is enough + // pcHeader.textStart is zero (Go 1.26+), fall back to .symtab. + textStart = f.findRuntimeTextSymbol() + } + if textStart == 0 { + // Fallback: use the .text section virtual address. + // For non-PIE pure-Go binaries, runtime.text == .text section address. // https://github.com/golang/go/commit/b38ab0ac5f78ac03a38052018ff629c03e36b864 textStart = text.Addr } diff --git a/ebpf/symtab/elf/go_table_test.go b/ebpf/symtab/elf/go_table_test.go index 979fe69292..0d646d911e 100644 --- a/ebpf/symtab/elf/go_table_test.go +++ b/ebpf/symtab/elf/go_table_test.go @@ -35,6 +35,8 @@ func TestSelfGoSymbolComparison(t *testing.T) { {"./testdata/elfs/go16-static", false}, // this one switches from 32 to 64 in the middle {"./testdata/elfs/go18-static", false}, // this one starts with 64 {"./testdata/elfs/go20-static", true}, + {"./testdata/elfs/go26", true}, // Go 1.26: pcHeader.textStart is always zero + {"./testdata/elfs/go26-static", true}, // Go 1.26: static build } for _, testcase := range ts { t.Run(testcase.f, func(t *testing.T) { diff --git a/ebpf/symtab/elf/testdata/Dockerfile b/ebpf/symtab/elf/testdata/Dockerfile index 395bdc5294..119fece9c4 100644 --- a/ebpf/symtab/elf/testdata/Dockerfile +++ b/ebpf/symtab/elf/testdata/Dockerfile @@ -26,6 +26,11 @@ ADD hello.go hello.go RUN go build hello.go RUN go build -ldflags="-extldflags=-static" -o hello-static hello.go +FROM --platform=linux/amd64 golang:1.26 as go126 +ADD hello.go hello.go +RUN go build hello.go +RUN go build -ldflags="-extldflags=-static" -o hello-static hello.go + FROM scratch COPY --from=builder elf elf.debug elf.stripped elf.debuglink elf.nopie elf.nobuildid libexample.so ./elfs/ COPY --from=builder /usr/lib/debug/ ./usr/lib/debug/ @@ -33,7 +38,9 @@ COPY --from=go12 /go/hello ./elfs/go12 COPY --from=go116 /go/hello ./elfs/go16 COPY --from=go118 /go/hello ./elfs/go18 COPY --from=go120 /go/hello ./elfs/go20 +COPY --from=go126 /go/hello ./elfs/go26 COPY --from=go12 /go/hello-static ./elfs/go12-static COPY --from=go116 /go/hello-static ./elfs/go16-static COPY --from=go118 /go/hello-static ./elfs/go18-static COPY --from=go120 /go/hello-static ./elfs/go20-static +COPY --from=go126 /go/hello-static ./elfs/go26-static \ No newline at end of file diff --git a/ebpf/symtab/elf/testdata/elfs/go26 b/ebpf/symtab/elf/testdata/elfs/go26 new file mode 100755 index 0000000000..eaa5701c93 Binary files /dev/null and b/ebpf/symtab/elf/testdata/elfs/go26 differ diff --git a/ebpf/symtab/elf/testdata/elfs/go26-static b/ebpf/symtab/elf/testdata/elfs/go26-static new file mode 100755 index 0000000000..e5cfbc46c1 Binary files /dev/null and b/ebpf/symtab/elf/testdata/elfs/go26-static differ