Skip to content

Conversation

@florianl
Copy link
Contributor

@florianl florianl commented Oct 24, 2025

Adapt the extraction for relative offsets from RIP.

This is the case that will be supported with this PR:

0000000005f06ca0 <runtime.stackcheck.abi0@@Base>:
 5f06ca0:	48 8b 0d 01 13 1b 10 	mov    0x101b1301(%rip),%rcx        # 160b7fa8 <runtime.tlsg@@Base+0x160b7fa8>
 5f06ca7:	64 48 8b 01          	mov    %fs:(%rcx),%rax
 5f06cab:	48 39 60 08          	cmp    %rsp,0x8(%rax)
 5f06caf:	77 05                	ja     5f06cb6 <runtime.stackcheck.abi0@@Base+0x16>
 5f06cb1:	e8 ca ff ff ff       	call   5f06c80 <runtime.abort.abi0@@Base>
 5f06cb6:	48 3b 20             	cmp    (%rax),%rsp
 5f06cb9:	77 05                	ja     5f06cc0 <runtime.stackcheck.abi0@@Base+0x20>
 5f06cbb:	e8 c0 ff ff ff       	call   5f06c80 <runtime.abort.abi0@@Base>
 5f06cc0:	c3                   	ret

Adapt the extraction for relative offsets from RIP.

Signed-off-by: Florian Lehner <[email protected]>
@florianl florianl marked this pull request as ready for review October 24, 2025 13:16
@florianl florianl requested review from a team as code owners October 24, 2025 13:16
Copy link
Contributor

@fabled fabled left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a unit test for the new sequence?

Signed-off-by: Florian Lehner <[email protected]>
Signed-off-by: Florian Lehner <[email protected]>
@florianl
Copy link
Contributor Author

Could you add a unit test for the new sequence?

So far we don't know the reason that causes this relative offset from RIP. I can name a bunch of Go executables (each multiple MB in size) that have this relative offset. But adding them to the repo as test feels wrong.
And the use of f.VirtualMemory() makes it hard to construct a mocked test. An option could be to split extractTLSGOffset() into several parts, like (a) identify gopclntab, (b) walk code and (c) introduce a function to read the memory, so that f.VirtualMemory() can be mocked for tests. Should we got this route @fabled ?

@florianl
Copy link
Contributor Author

@fabled I did add a unit test with abc27cc as described in #890 (comment).

)

// readMemory is a helper that can be mocked for testing.
type readMemory func(addr int64, sz, maxSize int) ([]byte, error)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is not needed. io.ReadAt interface could be used instead. This would allow passing the pfelf.File in the main code path directly (it's ReadAt works on virtual memory). And then just ship a mock object on testing side implementing the inteface. That would avoid generating closures.

if err != nil {
break
}
pc += op.Len
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The it asm.amd.Interpreter object tracks already pc. Could you instead add PC accessor to it to read it, and use it? I think adding it there would be useful in future.

Comment on lines +92 to +103
// RIP-relative load: mov disp(%rip), %reg
if mem, ok := op.Args[1].(x86asm.Mem); ok && mem.Base == x86asm.RIP {
curAddr := int64(sym.Address) + int64(pc)
if dst, ok := op.Args[0].(x86asm.Reg); ok {
target := curAddr + int64(mem.Disp)

// Read 8-byte TLS value from target address
b, err := extract(target, 8, 8)
if err == nil && len(b) >= 8 {
ripRelLoads[dst] = int64(npsr.Uint64(b, 0))
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel that this tracking should be done in the amd.Interpreter, there should a be a new expression type for RIP relative value. That would generalize the code greatly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this feels too complicated at this time, I can do it as a follow up PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry for the delay 🙏 I created the follow up with #984.

Comment on lines +98 to +102
// Read 8-byte TLS value from target address
b, err := extract(target, 8, 8)
if err == nil && len(b) >= 8 {
ripRelLoads[dst] = int64(npsr.Uint64(b, 0))
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the rip relative address should be stored to the map, not the dereferenced value. This might do unneeded extra reads. Only the actual rip relative value that is needed, should trigger the dereference. And again, the rip relative PC ideally comes from Interpreter.

Comment on lines +37 to +39
if err != nil {
t.Fatal(err)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: use testify

Suggested change
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)

Comment on lines +40 to +42
if offset != 0 {
t.Fatalf("Expected offset '0' got '%d'", offset)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: use testify

Suggested change
if offset != 0 {
t.Fatalf("Expected offset '0' got '%d'", offset)
}
require.Equal(t, 0, offset)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants