|
4 | 4 | package pfelf |
5 | 5 |
|
6 | 6 | import ( |
| 7 | + "fmt" |
7 | 8 | "os" |
| 9 | + "runtime" |
8 | 10 | "testing" |
9 | 11 |
|
10 | 12 | "github.com/stretchr/testify/assert" |
11 | 13 | "github.com/stretchr/testify/require" |
12 | 14 | "go.opentelemetry.io/ebpf-profiler/testsupport" |
13 | 15 |
|
14 | 16 | "go.opentelemetry.io/ebpf-profiler/libpf" |
| 17 | + xx "golang.org/x/arch/x86/x86asm" |
15 | 18 | ) |
16 | 19 |
|
17 | 20 | func getPFELF(path string, t *testing.T) *File { |
@@ -78,3 +81,88 @@ func TestPFELFIsGolang(t *testing.T) { |
78 | 81 | testPFELFIsGolang(t, "testdata/go-binary", true) |
79 | 82 | testPFELFIsGolang(t, "testdata/without-debug-syms", false) |
80 | 83 | } |
| 84 | + |
| 85 | +func symbolOffsetFromCodeX86(code []byte) (int64, error) { |
| 86 | + // e.g. mov eax,DWORD PTR fs:0xfffffffffffffffc |
| 87 | + offset := 0 |
| 88 | + for { |
| 89 | + insn, err := xx.Decode(code[offset:], 64) |
| 90 | + if err != nil { |
| 91 | + return 0, err |
| 92 | + } |
| 93 | + offset += insn.Len |
| 94 | + if insn.Op != xx.MOV { |
| 95 | + continue |
| 96 | + } |
| 97 | + switch a := insn.Args[1].(type) { |
| 98 | + case xx.Mem: |
| 99 | + if a.Segment != xx.FS { |
| 100 | + continue |
| 101 | + } |
| 102 | + // for some reason the Go disassembler |
| 103 | + // reports the displacement as a 32-bit value |
| 104 | + // embedded in a 64-bit one; e.g., it represents -16 as 0x00000000fffffff0 . |
| 105 | + // So this double cast is necessary. |
| 106 | + return int64(int32(a.Disp)), nil |
| 107 | + default: |
| 108 | + continue |
| 109 | + } |
| 110 | + } |
| 111 | +} |
| 112 | + |
| 113 | +func TestLookupTlsSymbolOffset(t *testing.T) { |
| 114 | + for _, test := range []struct { |
| 115 | + exe string |
| 116 | + hasTbss bool |
| 117 | + hasTdata bool |
| 118 | + }{ |
| 119 | + {"tls-tbss", true, false}, |
| 120 | + {"tls-aligned-tbss", true, false}, |
| 121 | + {"tls-tdata", false, true}, |
| 122 | + {"tls-aligned-tdata", false, true}, |
| 123 | + {"tls-tbss-tdata", true, true}, |
| 124 | + {"tls-aligned-tbss-tdata", true, true}, |
| 125 | + {"tls-tbss-aligned-tdata", true, true}, |
| 126 | + {"tls-aligned-tbss-aligned-tdata", true, true}, |
| 127 | + } { |
| 128 | + // Testing this on arm is nontrivial, because we need to actually follow some |
| 129 | + // pointers in-process to get the address of the tls block. So let's |
| 130 | + // ignore it and just test x86. |
| 131 | + if runtime.GOARCH == "amd64" { |
| 132 | + return |
| 133 | + } |
| 134 | + ef, err := Open(fmt.Sprintf("testdata/%s", test.exe)) |
| 135 | + assert.NoError(t, err) |
| 136 | + |
| 137 | + if test.hasTbss { |
| 138 | + sym, err := ef.LookupSymbol("get_tbss") |
| 139 | + assert.NoError(t, err) |
| 140 | + code := make([]byte, sym.Size) |
| 141 | + _, err = ef.ReadVirtualMemory(code, int64(sym.Address)) |
| 142 | + assert.NoError(t, err) |
| 143 | + |
| 144 | + offset, err := symbolOffsetFromCodeX86(code) |
| 145 | + assert.NoError(t, err) |
| 146 | + |
| 147 | + offset2, err := ef.LookupTlsSymbolOffset("tbss") |
| 148 | + assert.NoError(t, err) |
| 149 | + |
| 150 | + assert.Equal(t, offset, offset2) |
| 151 | + } |
| 152 | + if test.hasTdata { |
| 153 | + sym, err := ef.LookupSymbol("get_tdata") |
| 154 | + assert.NoError(t, err) |
| 155 | + code := make([]byte, sym.Size) |
| 156 | + _, err = ef.ReadVirtualMemory(code, int64(sym.Address)) |
| 157 | + assert.NoError(t, err) |
| 158 | + |
| 159 | + offset, err := symbolOffsetFromCodeX86(code) |
| 160 | + assert.NoError(t, err) |
| 161 | + |
| 162 | + offset2, err := ef.LookupTlsSymbolOffset("tdata") |
| 163 | + assert.NoError(t, err) |
| 164 | + |
| 165 | + assert.Equal(t, offset, offset2) |
| 166 | + } |
| 167 | + } |
| 168 | +} |
0 commit comments