From c48e2fca9eecb03f8acd2b36cd1cd6908daf8f27 Mon Sep 17 00:00:00 2001 From: Florian Lehner Date: Fri, 22 Aug 2025 17:07:07 +0200 Subject: [PATCH 1/5] interpreter/golables: use .gopclntab for symbol lookup and handle indirect case Use the mmap backed .gopclntab to lookup symbols for golabels. While further investigating the topic, a case with an indirect offset was also identified: ``` $ objdump -S -d go/src/github.com/open-telemetry/opentelemetry-ebpf-profiler/ebpf-profiler | grep -A 3 'runtime.stackcheck' 0000000000480700 : 480700: 64 48 8b 04 25 f8 ff mov %fs:0xfffffffffffffff8,%rax 480707: ff ff 480709: 48 39 60 08 cmp %rsp,0x8(%rax) 48070d: 77 05 ja 480714 $ sudo objdump -S -d elastic-agent-9.1.1-51565f/elastic-agent | grep -A 3 8945a20 0000000008945a20 : 8945a20: 48 8b 0d a1 c5 d0 17 mov 0x17d0c5a1(%rip),%rcx # 20651fc8 8945a27: 64 48 8b 01 mov %fs:(%rcx),%rax 8945a2b: 48 39 60 08 cmp %rsp,0x8(%rax) 8945a2f: 77 05 ja 8945a36 ``` Supersedes https://github.com/open-telemetry/opentelemetry-ebpf-profiler/pull/719 Fixes https://github.com/open-telemetry/opentelemetry-ebpf-profiler/issues/718 Signed-off-by: Florian Lehner --- interpreter/golabels/tls_amd64.go | 82 ++++++++++++++++++---- interpreter/golabels/tls_amd64_test.go | 73 +++++++++++++++++++ nativeunwind/elfunwindinfo/elfgopclntab.go | 16 +++++ 3 files changed, 156 insertions(+), 15 deletions(-) create mode 100644 interpreter/golabels/tls_amd64_test.go diff --git a/interpreter/golabels/tls_amd64.go b/interpreter/golabels/tls_amd64.go index f5de769a8..60f376708 100644 --- a/interpreter/golabels/tls_amd64.go +++ b/interpreter/golabels/tls_amd64.go @@ -6,43 +6,95 @@ package golabels // import "go.opentelemetry.io/ebpf-profiler/interpreter/golabels" import ( - log "github.com/sirupsen/logrus" + "errors" + "go.opentelemetry.io/ebpf-profiler/libpf/pfelf" + "go.opentelemetry.io/ebpf-profiler/nativeunwind/elfunwindinfo" "golang.org/x/arch/x86/x86asm" ) +var ( + errMissingSymbol = errors.New("failed to find runtime.stackcheck") + errUnexpectedAsm = errors.New("failed to disassemble runtime.stackcheck") +) + +// virtualMemoryReader allows to mock pfelf.File for testing. +type virtualMemoryReader interface { + VirtualMemory(addr int64, size int, align int) ([]byte, error) +} + // Most normal amd64 Go binaries use -8 as offset into TLS space for // storing the current g but "static" binaries it ends up as -80. There // may be dynamic relocating going on so just read it from a known // symbol if possible. func extractTLSGOffset(f *pfelf.File) (int32, error) { - syms, err := f.ReadSymbols() + pclntab, err := elfunwindinfo.NewGopclntab(f) if err != nil { return 0, err } + defer pclntab.Close() + // Dump of assembler code for function runtime.stackcheck: - // 0x0000000000470080 <+0>: mov %fs:0xfffffffffffffff8,%rax - sym, err := syms.LookupSymbol("runtime.stackcheck.abi0") - if err != nil { - // Binary must be stripped, hope default is correct and warn. - log.Warnf("Failed to find stackcheck symbol, Go labels might not work: %v", err) - return -8, nil + // Case 1 - direct offset + // mov %fs:0xfffffffffffffff8,%rax + // Case 2 - indirect offset + // mov 0x17d0c5a1(%rip),%rcx + // mov %fs:(%rcx),%rax + pc, ok := pclntab.PCForSymbol("runtime.stackcheck") + if !ok { + return 0, errMissingSymbol } - b, err := f.VirtualMemory(int64(sym.Address), 10, 10) + // Read enough bytes for two instructions + b, err := f.VirtualMemory(int64(pc), 16, 16) if err != nil { return 0, err } + return extractOffsetFromBytes(f, pc, b) +} - i, err := x86asm.Decode(b, 64) +func extractOffsetFromBytes(f virtualMemoryReader, pc uintptr, b []byte) (int32, error) { + i1, err := x86asm.Decode(b, 64) if err != nil { return 0, err } - if i.Op == x86asm.MOV { - mem, ok := i.Args[1].(x86asm.Mem) - if ok { + + // Case 1: mov %fs:0xfffffffffffffff8,%rax + if i1.Op == x86asm.MOV { + mem, ok := i1.Args[1].(x86asm.Mem) + reg, okReg := i1.Args[0].(x86asm.Reg) + if ok && okReg && mem.Segment == x86asm.FS && reg == x86asm.RAX { return int32(mem.Disp), nil } } - log.Warnf("Failed to decode stackcheck symbol, Go label collection might not work") - return -8, nil + + i2, err := x86asm.Decode(b[i1.Len:], 64) + if err != nil { + return 0, err + } + + // Case 2: mov 0x17d0c5a1(%rip),%rcx; mov %fs:(%rcx),%rax + if i1.Op == x86asm.MOV && i2.Op == x86asm.MOV { + mem1, ok1 := i1.Args[1].(x86asm.Mem) + reg1, okReg1 := i1.Args[0].(x86asm.Reg) + mem2, ok2 := i2.Args[1].(x86asm.Mem) + reg2, okReg2 := i2.Args[0].(x86asm.Reg) + reg2base := mem2.Base + // Check for the indirect pattern + if ok1 && okReg1 && ok2 && okReg2 && reg1 == x86asm.RCX && mem2.Segment == x86asm.FS && + reg2 == x86asm.RAX && reg2base == x86asm.RCX { + // Resolve the address loaded by the first instruction (RIP-relative) + addr := int64(pc) + int64(i1.Len) + mem1.Disp + offsetBytes, err := f.VirtualMemory(addr, 4, 4) + if err != nil { + return 0, errUnexpectedAsm + } + offset := int32(offsetBytes[0]) | + int32(offsetBytes[1])<<8 | + int32(offsetBytes[2])<<16 | + int32(offsetBytes[3])<<24 + return offset, nil + } + } + + return 0, errUnexpectedAsm } diff --git a/interpreter/golabels/tls_amd64_test.go b/interpreter/golabels/tls_amd64_test.go new file mode 100644 index 000000000..1dbe1d17d --- /dev/null +++ b/interpreter/golabels/tls_amd64_test.go @@ -0,0 +1,73 @@ +//go:build amd64 + +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package golabels + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/require" +) + +// mockPfelfFile implements minimal pfelf.File for testing +// Only VirtualMemory is used in extractOffsetFromBytes + +type mockPfelfFile struct { + mem map[int64][]byte +} + +//nolint:gocritic +func (m *mockPfelfFile) VirtualMemory(addr int64, _ int, _ int) ([]byte, error) { + if b, ok := m.mem[addr]; ok { + return b, nil + } + return nil, errors.New("not found") +} + +func TestExtractOffsetFromBytes(t *testing.T) { + tests := map[string]struct { + mockFileData map[int64][]byte + pc uintptr + ins []byte + expectedOffset int32 + err error + }{ + "UnexpectedASM": { + mockFileData: make(map[int64][]byte), + pc: 0x1000, + ins: []byte{0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90}, + err: errUnexpectedAsm, + }, + "Direct": { + mockFileData: make(map[int64][]byte), + pc: 0x1000, + ins: []byte{0x64, 0x48, 0x8b, 0x04, 0x25, 0xf8, + 0xff, 0xff, 0xff}, + expectedOffset: -8, + }, + "Indirect": { + mockFileData: map[int64][]byte{ + 0x1000 + 7 + 0x10: {0xf8, 0xff, 0xff, 0xff}, + }, + pc: 0x1000, + ins: []byte{0x48, 0x8b, 0x0d, 0x10, 0x00, + 0x00, 0x00, 0x64, 0x48, 0x8b, 0x01}, + expectedOffset: -8, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + f := &mockPfelfFile{ + mem: tc.mockFileData, + } + offset, err := extractOffsetFromBytes(f, tc.pc, tc.ins) + require.Equal(t, tc.err, err) + require.Equal(t, tc.expectedOffset, offset) + }) + } +} diff --git a/nativeunwind/elfunwindinfo/elfgopclntab.go b/nativeunwind/elfunwindinfo/elfgopclntab.go index 03cf1727d..b386d6c83 100644 --- a/nativeunwind/elfunwindinfo/elfgopclntab.go +++ b/nativeunwind/elfunwindinfo/elfgopclntab.go @@ -351,6 +351,22 @@ type Gopclntab struct { functab, funcdata, funcnametab, filetab, pctab, cutab []byte } +// PCForSymbol returns the start PC address for the given symbol name. +func (g *Gopclntab) PCForSymbol(symbol string) (uintptr, bool) { + for i := 0; i < g.numFuncs; i++ { + _, funcOff := g.getFuncMapEntry(i) + pc, fun := g.getFunc(funcOff) + if fun == nil { + continue + } + name := getString(g.funcnametab, int(fun.nameOff)) + if name == symbol { + return pc, true + } + } + return 0, false +} + // NewGopclntab parses and returns the parsed data for further operations. func NewGopclntab(ef *pfelf.File) (*Gopclntab, error) { data, err := extractGoPclntab(ef) From badbb70162c90205d1e9308b67a5aca18e758177 Mon Sep 17 00:00:00 2001 From: Florian Lehner Date: Tue, 26 Aug 2025 11:46:10 +0200 Subject: [PATCH 2/5] fixup: drop asm handling in favor of #730 Signed-off-by: Florian Lehner --- interpreter/golabels/tls_amd64.go | 63 ++++------------------ interpreter/golabels/tls_amd64_test.go | 73 -------------------------- 2 files changed, 9 insertions(+), 127 deletions(-) delete mode 100644 interpreter/golabels/tls_amd64_test.go diff --git a/interpreter/golabels/tls_amd64.go b/interpreter/golabels/tls_amd64.go index 60f376708..4ed58b704 100644 --- a/interpreter/golabels/tls_amd64.go +++ b/interpreter/golabels/tls_amd64.go @@ -8,6 +8,7 @@ package golabels // import "go.opentelemetry.io/ebpf-profiler/interpreter/golabe import ( "errors" + log "github.com/sirupsen/logrus" "go.opentelemetry.io/ebpf-profiler/libpf/pfelf" "go.opentelemetry.io/ebpf-profiler/nativeunwind/elfunwindinfo" "golang.org/x/arch/x86/x86asm" @@ -15,14 +16,8 @@ import ( var ( errMissingSymbol = errors.New("failed to find runtime.stackcheck") - errUnexpectedAsm = errors.New("failed to disassemble runtime.stackcheck") ) -// virtualMemoryReader allows to mock pfelf.File for testing. -type virtualMemoryReader interface { - VirtualMemory(addr int64, size int, align int) ([]byte, error) -} - // Most normal amd64 Go binaries use -8 as offset into TLS space for // storing the current g but "static" binaries it ends up as -80. There // may be dynamic relocating going on so just read it from a known @@ -35,66 +30,26 @@ func extractTLSGOffset(f *pfelf.File) (int32, error) { defer pclntab.Close() // Dump of assembler code for function runtime.stackcheck: - // Case 1 - direct offset - // mov %fs:0xfffffffffffffff8,%rax - // Case 2 - indirect offset - // mov 0x17d0c5a1(%rip),%rcx - // mov %fs:(%rcx),%rax + // 0x0000000000470080 <+0>: mov %fs:0xfffffffffffffff8,%rax pc, ok := pclntab.PCForSymbol("runtime.stackcheck") if !ok { return 0, errMissingSymbol } - // Read enough bytes for two instructions - b, err := f.VirtualMemory(int64(pc), 16, 16) + b, err := f.VirtualMemory(int64(pc), 10, 10) if err != nil { return 0, err } - return extractOffsetFromBytes(f, pc, b) -} -func extractOffsetFromBytes(f virtualMemoryReader, pc uintptr, b []byte) (int32, error) { - i1, err := x86asm.Decode(b, 64) + i, err := x86asm.Decode(b, 64) if err != nil { return 0, err } - - // Case 1: mov %fs:0xfffffffffffffff8,%rax - if i1.Op == x86asm.MOV { - mem, ok := i1.Args[1].(x86asm.Mem) - reg, okReg := i1.Args[0].(x86asm.Reg) - if ok && okReg && mem.Segment == x86asm.FS && reg == x86asm.RAX { + if i.Op == x86asm.MOV { + mem, ok := i.Args[1].(x86asm.Mem) + if ok { return int32(mem.Disp), nil } } - - i2, err := x86asm.Decode(b[i1.Len:], 64) - if err != nil { - return 0, err - } - - // Case 2: mov 0x17d0c5a1(%rip),%rcx; mov %fs:(%rcx),%rax - if i1.Op == x86asm.MOV && i2.Op == x86asm.MOV { - mem1, ok1 := i1.Args[1].(x86asm.Mem) - reg1, okReg1 := i1.Args[0].(x86asm.Reg) - mem2, ok2 := i2.Args[1].(x86asm.Mem) - reg2, okReg2 := i2.Args[0].(x86asm.Reg) - reg2base := mem2.Base - // Check for the indirect pattern - if ok1 && okReg1 && ok2 && okReg2 && reg1 == x86asm.RCX && mem2.Segment == x86asm.FS && - reg2 == x86asm.RAX && reg2base == x86asm.RCX { - // Resolve the address loaded by the first instruction (RIP-relative) - addr := int64(pc) + int64(i1.Len) + mem1.Disp - offsetBytes, err := f.VirtualMemory(addr, 4, 4) - if err != nil { - return 0, errUnexpectedAsm - } - offset := int32(offsetBytes[0]) | - int32(offsetBytes[1])<<8 | - int32(offsetBytes[2])<<16 | - int32(offsetBytes[3])<<24 - return offset, nil - } - } - - return 0, errUnexpectedAsm + log.Warnf("Failed to decode stackcheck symbol, Go label collection might not work") + return -8, nil } diff --git a/interpreter/golabels/tls_amd64_test.go b/interpreter/golabels/tls_amd64_test.go deleted file mode 100644 index 1dbe1d17d..000000000 --- a/interpreter/golabels/tls_amd64_test.go +++ /dev/null @@ -1,73 +0,0 @@ -//go:build amd64 - -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package golabels - -import ( - "errors" - "testing" - - "github.com/stretchr/testify/require" -) - -// mockPfelfFile implements minimal pfelf.File for testing -// Only VirtualMemory is used in extractOffsetFromBytes - -type mockPfelfFile struct { - mem map[int64][]byte -} - -//nolint:gocritic -func (m *mockPfelfFile) VirtualMemory(addr int64, _ int, _ int) ([]byte, error) { - if b, ok := m.mem[addr]; ok { - return b, nil - } - return nil, errors.New("not found") -} - -func TestExtractOffsetFromBytes(t *testing.T) { - tests := map[string]struct { - mockFileData map[int64][]byte - pc uintptr - ins []byte - expectedOffset int32 - err error - }{ - "UnexpectedASM": { - mockFileData: make(map[int64][]byte), - pc: 0x1000, - ins: []byte{0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90}, - err: errUnexpectedAsm, - }, - "Direct": { - mockFileData: make(map[int64][]byte), - pc: 0x1000, - ins: []byte{0x64, 0x48, 0x8b, 0x04, 0x25, 0xf8, - 0xff, 0xff, 0xff}, - expectedOffset: -8, - }, - "Indirect": { - mockFileData: map[int64][]byte{ - 0x1000 + 7 + 0x10: {0xf8, 0xff, 0xff, 0xff}, - }, - pc: 0x1000, - ins: []byte{0x48, 0x8b, 0x0d, 0x10, 0x00, - 0x00, 0x00, 0x64, 0x48, 0x8b, 0x01}, - expectedOffset: -8, - }, - } - - for name, tc := range tests { - t.Run(name, func(t *testing.T) { - f := &mockPfelfFile{ - mem: tc.mockFileData, - } - offset, err := extractOffsetFromBytes(f, tc.pc, tc.ins) - require.Equal(t, tc.err, err) - require.Equal(t, tc.expectedOffset, offset) - }) - } -} From 64902b5aee8551dfe42811ac96c02595ef4380fc Mon Sep 17 00:00:00 2001 From: Florian Lehner Date: Wed, 27 Aug 2025 08:28:12 +0200 Subject: [PATCH 3/5] fixup: align API Signed-off-by: Florian Lehner --- interpreter/apmint/apmint.go | 3 +-- interpreter/golabels/tls_amd64.go | 14 ++++---------- libpf/pfelf/file.go | 9 +++------ libpf/symbol.go | 7 +++++++ nativeunwind/elfunwindinfo/elfgopclntab.go | 15 ++++++++++----- 5 files changed, 25 insertions(+), 23 deletions(-) diff --git a/interpreter/apmint/apmint.go b/interpreter/apmint/apmint.go index 5e9967ffa..8420889b5 100644 --- a/interpreter/apmint/apmint.go +++ b/interpreter/apmint/apmint.go @@ -20,7 +20,6 @@ import ( "go.opentelemetry.io/ebpf-profiler/host" "go.opentelemetry.io/ebpf-profiler/interpreter" "go.opentelemetry.io/ebpf-profiler/libpf" - "go.opentelemetry.io/ebpf-profiler/libpf/pfelf" "go.opentelemetry.io/ebpf-profiler/remotememory" "go.opentelemetry.io/ebpf-profiler/support" ) @@ -66,7 +65,7 @@ func Loader(_ interpreter.EbpfHandler, info *interpreter.LoaderInfo) (interprete // Resolve process storage symbol. procStorageSym, err := ef.LookupSymbol(procStorageExport) if err != nil { - if errors.Is(err, pfelf.ErrSymbolNotFound) { + if errors.Is(err, libpf.ErrSymbolNotFound) { // APM<->profiling integration not supported by agent. return nil, nil } diff --git a/interpreter/golabels/tls_amd64.go b/interpreter/golabels/tls_amd64.go index 4ed58b704..2d4e88d36 100644 --- a/interpreter/golabels/tls_amd64.go +++ b/interpreter/golabels/tls_amd64.go @@ -6,18 +6,12 @@ package golabels // import "go.opentelemetry.io/ebpf-profiler/interpreter/golabels" import ( - "errors" - log "github.com/sirupsen/logrus" "go.opentelemetry.io/ebpf-profiler/libpf/pfelf" "go.opentelemetry.io/ebpf-profiler/nativeunwind/elfunwindinfo" "golang.org/x/arch/x86/x86asm" ) -var ( - errMissingSymbol = errors.New("failed to find runtime.stackcheck") -) - // Most normal amd64 Go binaries use -8 as offset into TLS space for // storing the current g but "static" binaries it ends up as -80. There // may be dynamic relocating going on so just read it from a known @@ -31,11 +25,11 @@ func extractTLSGOffset(f *pfelf.File) (int32, error) { // Dump of assembler code for function runtime.stackcheck: // 0x0000000000470080 <+0>: mov %fs:0xfffffffffffffff8,%rax - pc, ok := pclntab.PCForSymbol("runtime.stackcheck") - if !ok { - return 0, errMissingSymbol + sym, err := pclntab.LookupSymbol("runtime.stackcheck") + if err != nil { + return 0, err } - b, err := f.VirtualMemory(int64(pc), 10, 10) + b, err := f.VirtualMemory(int64(sym.Address), 10, 10) if err != nil { return 0, err } diff --git a/libpf/pfelf/file.go b/libpf/pfelf/file.go index b75c60429..34e56b299 100644 --- a/libpf/pfelf/file.go +++ b/libpf/pfelf/file.go @@ -51,9 +51,6 @@ const ( // List of public errors. var ( - // ErrSymbolNotFound is returned when the requested symbol was not found. - ErrSymbolNotFound = errors.New("symbol not found") - // ErrNotELF is returned when the file is not an ELF file. ErrNotELF = errors.New("not an ELF file") ) @@ -955,7 +952,7 @@ func (f *File) LookupSymbol(symbol libpf.SymbolName) (*libpf.Symbol, error) { mask := uint(1)<<(h%ptrSizeBits) | uint(1)<<((h>>hdr.bloomShift)%ptrSizeBits) if bloom&mask != mask { - return nil, ErrSymbolNotFound + return nil, libpf.ErrSymbolNotFound } // Read the initial symbol index to start looking from @@ -966,7 +963,7 @@ func (f *File) LookupSymbol(symbol libpf.SymbolName) (*libpf.Symbol, error) { return nil, err } if i == 0 { - return nil, ErrSymbolNotFound + return nil, libpf.ErrSymbolNotFound } // Search the hash bucket @@ -1021,7 +1018,7 @@ func (f *File) LookupSymbol(symbol libpf.SymbolName) (*libpf.Symbol, error) { return nil, errors.New("symbol hash not present") } - return nil, ErrSymbolNotFound + return nil, libpf.ErrSymbolNotFound } // LookupSymbol searches for a given symbol in the ELF diff --git a/libpf/symbol.go b/libpf/symbol.go index dd3c490ae..fd338589e 100644 --- a/libpf/symbol.go +++ b/libpf/symbol.go @@ -4,11 +4,18 @@ package libpf // import "go.opentelemetry.io/ebpf-profiler/libpf" import ( + "errors" "fmt" "sort" "strings" ) +// List of public errors. +var ( + // ErrSymbolNotFound is returned when the requested symbol was not found. + ErrSymbolNotFound = errors.New("symbol not found") +) + // SymbolValue represents the value associated with a symbol, e.g. either an // offset or an absolute address type SymbolValue uint64 diff --git a/nativeunwind/elfunwindinfo/elfgopclntab.go b/nativeunwind/elfunwindinfo/elfgopclntab.go index b386d6c83..3a379c86f 100644 --- a/nativeunwind/elfunwindinfo/elfgopclntab.go +++ b/nativeunwind/elfunwindinfo/elfgopclntab.go @@ -17,6 +17,7 @@ import ( "strings" "unsafe" + "go.opentelemetry.io/ebpf-profiler/libpf" "go.opentelemetry.io/ebpf-profiler/libpf/pfelf" sdtypes "go.opentelemetry.io/ebpf-profiler/nativeunwind/stackdeltatypes" "go.opentelemetry.io/ebpf-profiler/support" @@ -351,8 +352,9 @@ type Gopclntab struct { functab, funcdata, funcnametab, filetab, pctab, cutab []byte } -// PCForSymbol returns the start PC address for the given symbol name. -func (g *Gopclntab) PCForSymbol(symbol string) (uintptr, bool) { +// LookupSymbol searches for a given symbol in .gopclntab. +func (g *Gopclntab) LookupSymbol(symbol libpf.SymbolName) (*libpf.Symbol, error) { + symString := string(symbol) for i := 0; i < g.numFuncs; i++ { _, funcOff := g.getFuncMapEntry(i) pc, fun := g.getFunc(funcOff) @@ -360,11 +362,14 @@ func (g *Gopclntab) PCForSymbol(symbol string) (uintptr, bool) { continue } name := getString(g.funcnametab, int(fun.nameOff)) - if name == symbol { - return pc, true + if name == symString { + return &libpf.Symbol{ + Name: symbol, + Address: libpf.SymbolValue(pc), + }, nil } } - return 0, false + return nil, libpf.ErrSymbolNotFound } // NewGopclntab parses and returns the parsed data for further operations. From 90dd1d41d7555ab2a530f739381bb4834e9403d8 Mon Sep 17 00:00:00 2001 From: Florian Lehner Date: Thu, 28 Aug 2025 08:21:02 +0200 Subject: [PATCH 4/5] fixup: populate size field Signed-off-by: Florian Lehner --- nativeunwind/elfunwindinfo/elfgopclntab.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/nativeunwind/elfunwindinfo/elfgopclntab.go b/nativeunwind/elfunwindinfo/elfgopclntab.go index 3a379c86f..3331a832e 100644 --- a/nativeunwind/elfunwindinfo/elfgopclntab.go +++ b/nativeunwind/elfunwindinfo/elfgopclntab.go @@ -363,9 +363,19 @@ func (g *Gopclntab) LookupSymbol(symbol libpf.SymbolName) (*libpf.Symbol, error) } name := getString(g.funcnametab, int(fun.nameOff)) if name == symString { + var size uint64 + if i < g.numFuncs-1 { + nextPc, _ := g.getFuncMapEntry(i + 1) + size = uint64(nextPc - pc) + } else { + // Last symbol: size unknown, set to 0. + size = 0 + } + return &libpf.Symbol{ Name: symbol, Address: libpf.SymbolValue(pc), + Size: size, }, nil } } From c02045239aeac7546a5b34fde8f19f96584a285c Mon Sep 17 00:00:00 2001 From: Florian Lehner Date: Fri, 29 Aug 2025 09:00:39 +0200 Subject: [PATCH 5/5] fixup: remove if Signed-off-by: Florian Lehner --- nativeunwind/elfunwindinfo/elfgopclntab.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/nativeunwind/elfunwindinfo/elfgopclntab.go b/nativeunwind/elfunwindinfo/elfgopclntab.go index 3331a832e..4d82a104e 100644 --- a/nativeunwind/elfunwindinfo/elfgopclntab.go +++ b/nativeunwind/elfunwindinfo/elfgopclntab.go @@ -363,14 +363,8 @@ func (g *Gopclntab) LookupSymbol(symbol libpf.SymbolName) (*libpf.Symbol, error) } name := getString(g.funcnametab, int(fun.nameOff)) if name == symString { - var size uint64 - if i < g.numFuncs-1 { - nextPc, _ := g.getFuncMapEntry(i + 1) - size = uint64(nextPc - pc) - } else { - // Last symbol: size unknown, set to 0. - size = 0 - } + nextPc, _ := g.getFuncMapEntry(i + 1) + size := uint64(nextPc - pc) return &libpf.Symbol{ Name: symbol,