Skip to content

debug/elf: add SHT_GNU_VERDEF section parsing #63952

@benbaker76

Description

@benbaker76

I'm porting @brendangregg's eBPF profiling application profile.py to Golang. I'm attempting to use the debug/elf package for retrieving debug symbol info but have stumbled upon a limitation.

As a demonstration I've picked a library called libtiffxx.so as it's relatively small and clearly demontrates the issues I have with the package.

Let's take a look at the .gnu.version* sections contained in the library:

$ readelf -SW /usr/lib/x86_64-linux-gnu/libtiffxx.so | grep .gnu.version
  [ 6] .gnu.version      VERSYM          0000000000000882 000882 000036 02   A  4   0  2
  [ 7] .gnu.version_d    VERDEF          00000000000008b8 0008b8 000038 00   A  5   2  8
  [ 8] .gnu.version_r    VERNEED         00000000000008f0 0008f0 000090 00   A  5   3  8

There's important debug symbol information contained in the .gnu.version_d (SHT_GNU_VERDEF) section which is currently not parsed by the package. Let's look at the last four functions in the .dynsym section.

$ readelf -sW /usr/lib/x86_64-linux-gnu/libtiffxx.so | tail -4
    23: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZNSt8ios_base4InitD1Ev@GLIBCXX_3.4 (3)
    24: 0000000000000000     0 OBJECT  GLOBAL DEFAULT  ABS LIBTIFFXX_4.0
    25: 0000000000001890   169 FUNC    GLOBAL DEFAULT   15 _Z14TIFFStreamOpenPKcPSo@@LIBTIFFXX_4.0
    26: 0000000000001940    19 FUNC    GLOBAL DEFAULT   15 _Z14TIFFStreamOpenPKcPSi@@LIBTIFFXX_4.0

The debug/elf package can retreive the function name, but what about the @ or @@, the library name after it or even the number in the brackets? In my proposal and my current implementation I provide a way to access this important debug symbol information.

If we take a look at the dynamic symbol output of these four functions in the current implementation of debug/elf we have the following output:

		Symbol{
			Name:     "_ZNSt8ios_base4InitD1Ev",
			Info:     0x12,
			Other:    0x0,
			Section:  0x0,
			Value:    0x0,
			Size:     0x0,
			Version:  "GLIBCXX_3.4",
			Library:  "libstdc++.so.6",
		},
		Symbol{
			Name:     "LIBTIFFXX_4.0",
			Info:     0x11,
			Other:    0x0,
			Section:  0xFFF1,
			Value:    0x0,
			Size:     0x0,
		},
		Symbol{
			Name:     "_Z14TIFFStreamOpenPKcPSo",
			Info:     0x12,
			Other:    0x0,
			Section:  0xF,
			Value:    0x1890,
			Size:     0xA9,
		},
		Symbol{
			Name:     "_Z14TIFFStreamOpenPKcPSi",
			Info:     0x12,
			Other:    0x0,
			Section:  0xF,
			Value:    0x1940,
			Size:     0x13,
		},

In my new implemenation we get this output:

		Symbol{
			Name:     "_ZNSt8ios_base4InitD1Ev",
			Info:     0x12,
			Other:    0x0,
			Section:  0x0,
			Value:    0x0,
			Size:     0x0,
			Version:  "GLIBCXX_3.4",
			Library:  "libstdc++.so.6",
			VerInfo:  0x1,
			VerOther: 0x3,
		},
		Symbol{
			Name:     "LIBTIFFXX_4.0",
			Info:     0x11,
			Other:    0x0,
			Section:  0xFFF1,
			Value:    0x0,
			Size:     0x0,
			Version:  "LIBTIFFXX_4.0",
			Library:  "",
			VerInfo:  0x2,
			VerOther: 0x1,
		},
		Symbol{
			Name:     "_Z14TIFFStreamOpenPKcPSo",
			Info:     0x12,
			Other:    0x0,
			Section:  0xF,
			Value:    0x1890,
			Size:     0xA9,
			Version:  "LIBTIFFXX_4.0",
			Library:  "",
			VerInfo:  0x2,
			VerOther: 0x1,
		},
		Symbol{
			Name:     "_Z14TIFFStreamOpenPKcPSi",
			Info:     0x12,
			Other:    0x0,
			Section:  0xF,
			Value:    0x1940,
			Size:     0x13,
			Version:  "LIBTIFFXX_4.0",
			Library:  "",
			VerInfo:  0x2,
			VerOther: 0x1,
		},

We now have the Version entry as is often retrieved from SHT_GNU_VERNEED but in the last three entries can only be obtained from the SHT_GNU_VERDEF section. We also have two new Symbol members called VerInfo and VerOther.

So what are these new values and where do they come from? Using binutil's readelf.c as a reference we can find out where this data is located and how to extract it.

VerInfo in this case refers to the number in the brackets. In the case of _ZNSt8ios_base4InitD1Ev@GLIBCXX_3.4 (3) the number 3 comes from VerOther which is the version number. If it's 1 we don't display it in the brackets. The single @ comes from VerInfo which can be one of three values:

// Versioned symbol info
const (
	SymUndefined byte = 0
	SymHidden    byte = 1
	SymPublic    byte = 2
)

If the value is SymHidden it will be @ and if it's SymPublic it will be @@. This is taken from readelf.c

So I have implemented the code to do the necessary parsing of the SHT_GNU_VERDEF section and am writing this proposal so I can submit my changes to Gerrit for review by the Golang team.

Currently I have made changes to the following files in src/debug/elf:

  • Change file_test.go
  • Change file.go
  • Change symbols_test.go
  • Added testdata/libtiffxx.so (should be renamed?)

As I mentioned I use libtiffxx.so as my testdata ELF binary because it's relatively small (14.4 kB) and it demonstrates the new data from which my new branch can parse.

My changes currently pass all tests except for the API check

##### API check
+pkg debug/elf, const SymHidden = 1
+pkg debug/elf, const SymHidden uint8
+pkg debug/elf, const SymPublic = 2
+pkg debug/elf, const SymPublic uint8
+pkg debug/elf, const SymUndefined = 0
+pkg debug/elf, const SymUndefined uint8
+pkg debug/elf, type ImportedSymbol struct, VerInfo uint8
+pkg debug/elf, type ImportedSymbol struct, VerOther uint8
+pkg debug/elf, type Symbol struct, VerInfo uint8
+pkg debug/elf, type Symbol struct, VerOther uint8
--- FAIL: TestCheck (26.62s)
    main_test.go:184: API differences found

Thanks for taking the time to review my proposal.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    Accepted

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions