`%fs:0xfffffffffffffff8` 的真实地址是什么?

What is the real address of `%fs:0xfffffffffffffff8`?

我想使用 ebpf.

追踪 go 程序的 goid

阅读一些帖子和博客后,我知道 %fs:0xfffffffffffffff8 指向 go 的 g 结构,并且 mov %fs:0xfffffffffffffff8,%rcx 指令总是出现在 go 函数的开头。

main.main为例:

func main() {
 177341   458330:   64 48 8b 0c 25 f8 ff    mov    %fs:0xfffffffffffffff8,%rcx
 177342   458337:   ff ff
 177343   458339:   48 3b 61 10             cmp    0x10(%rcx),%rsp
 177344   45833d:   76 1a                   jbe    458359 <main.main+0x29>
 177345   45833f:   48 83 ec 08             sub    [=10=]x8,%rsp
 177346   458343:   48 89 2c 24             mov    %rbp,(%rsp)
 177347   458347:   48 8d 2c 24             lea    (%rsp),%rbp
 177348     myFunc()
 177349   45834b:   e8 10 00 00 00          callq  458360 <main.myFunc>
 177350 }

我也知道goid的信息保存在go的g结构体中。 fs寄存器的值可以通过ebpf函数的ctx参数获取。

但是我不知道%fs:0xfffffffffffffff8的真实地址是什么,因为我是汇编语言的新手。谁能给我一些提示?

如果fs寄存器的值为0x88,那么%fs:0xfffffffffffffff8的值是多少?

这是一个负数,所以它是 FS 基数之前的一个 qword。您需要 FS 基地址,它是 而不是 您可以通过调试器看到的 FS 段寄存器中的选择器值。

您的进程可能进行了系统调用以请求 OS 进行设置,或者可能在支持它的系统上的某个时刻使用了 wrfsbase 指令。

请注意,至少在 Go 之外,Linux 通常使用 FS 进行线程本地存储。

(我不确定实际找到 FS 基的标准方法是什么;显然 OS 依赖于在用户 space 中执行此操作,其中 rdmsr 不是不可用;FS 和 GS 基数作为 MSR 公开,因此 OSes use that instead of actually modifying a GDT or LDT entry. rdfsbase 需要通过在支持 FSGSBASE ISA 扩展的 CPU 上的 CR4 中的内核设置位来启用,因此您不能指望它工作。)

@MargaretBloom 建议 user-space 可能触发无效页面错误;大多数 OSes 将错误的虚拟地址报告给用户-space。例如,在 Linux 中,SIGSEGV 具有地址。 (或者 SIGBUS,如果它是非规范的,IIRC。即不在虚拟地址 space 的低 47 位或高 47 位中,而是在 "hole" 中,地址不是低 48.)

因此,您想要为这些信号安装信号处理程序,并尝试从位于内核 space 中间的偏移量(基数为 0)加载,或类似的东西。如果由于某种原因没有出错,请将虚拟地址递增 1TiB 或循环中的某些内容。通常没有 MMIO 映射到 user-space 的虚拟地址 space 所以仅仅读取没有副作用。