1
# 0x00007f33caf5a85f: cmp rax, 0xffffffff80000000
# 0x00007f33caf5a865: jnl 0x7f33caf5a898
...
target_of_jnl:
# 0x00007f33caf5a898: cmp rax, 0x7fffffff
# 0x00007f33caf5a89e: jle 0x7f33caf5a8c8

上述代码片段是 libstdc++ 中函数 _M_extract_int() 执行流程的一部分。

我不明白这两个比较的意思。我认为 0xffffffff80000000 是可以用于用户模式的顶级物理内存地址。但是 0x7fffffff 呢?他们在检查什么?

4

1 回答 1

3

请注意,0x...865and 0x...898不是连续的,即使它们在问题中显示为一个没有空行的连续块的一部分。一个是另一个的分支目标。

它似乎正在检查一个可以适合 32 位 2 的补码整数的值,即在 INT32_MIN 和 INT32_MAX(包括)之间。像x == (int32_t)x

但是高半和低半的分支目标是不同的,否则它可以简单地movsxd rdx, eax// cmp rax, rdxje

jnl 与 的条件相同jge,因此它正在跳转(int64_t)rax >= INT_MIN, INT_MIN 当然符号扩展为 64 位。

   cmp rax, 0xffffffff80000000                   # INT32_MIN
   jge   x_ge_INT32_MIN
# else fall-through: x < INT32_MIN
  ... other code here

x_ge_INT32_MIN:
   cmp rax, 0x7fffffff                           # INT32_MAX
   jle   x_fits_in_int32_t

  ... else it doesn't, x > INT32_MAX

注意0xffffffff80000000表示负符号整数,(int64_t)INT32_MIN。这是 32-bit 的符号扩展0x80000000


我不知道是什么_M_extract_int(),而且您没有链接任何 libstdc++ 源以获取使用信息。


我认为0xffffffff80000000是可以用于用户模式的顶级物理内存地址

不,0xffffffff80000000位于虚拟地址空间的高半部分,因此还不算太高。有关x86-64 48 位规范虚拟地址空间的图表,请参阅地址规范形式和指针算法。

Linux(我认为所有主流的 x86-64 操作系统)保留整个上半部分供内核使用,用户空间能够在下半部分分配/映射页面。即用户空间可以使用整个低47位虚拟地址空间。(也许不是最底层,例如 Linux 默认停止进程映射低 64k,所以nullptr取消引用,即使有偏移,仍然会出错。MacOS 保留整个低 4GiB。)

使用 5 级页表(Intel 的 PML5 扩展),用户空间可以使用 57 位虚拟地址空间的低 56 位。

无论哪种方式,这与此代码正在寻找的内容相似,但 48 或 57 位值正确符号扩展为 64 位,而不是 32 位。

身体的

不。libstdc++ 仅用于用户空间,因此它所看到的唯一地址是虚拟地址。

于 2020-10-26T09:06:54.020 回答