【问题标题】:Named Address Spaces for MSVCMSVC 的命名地址空间
【发布时间】:2021-03-12 06:02:26
【问题描述】:

GCC 和 Clang 支持名为 Named Address Space 的功能,允许用户通过 FSGS 寄存器指示编译器生成对某些变量或成员的访问。

MSVC 是否支持这样的功能?

(我在 MSDN 上找不到任何东西,但是看到 Windows 长期以来如何允许用户使用 FSGSBASE 更改段寄存器的值,编译器必须提供某种方法来实际适当地排列数据,对吧?)

【问题讨论】:

  • fs 和 gs 不是已经用于 x86 上的 tls 了吗?
  • 我猜你需要some assembly
  • @phuclv 该链接似乎只包含有关如何设置寄存器的信息,而不是如何哄骗编译器生成通过 FS 或 GS​​ 偏移来访问某些变量的代码。

标签: c++ windows visual-c++ x86 cpu-registers


【解决方案1】:

在 MSVC 中,大多数低级的事情都是通过内在函数完成的。和这个一样

x64 (amd64) intrinsics list

要对相对于基地址的指针进行操作

这是一个例子

#include <iostream>
#include <intrin.h>
#include <immintrin.h>
#include <cstdint>

int main()
{
    int data[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    
    std::cout << "&data = " << (void*)&data << '\n';
    std::cout << "GS base before                    = " << (void*)_readgsbase_u64() << '\n';

    _writegsbase_u64((uintptr_t)&data);
    std::cout << "GS base after pointing to data[0] = " << (void*)_readgsbase_u64() << '\n';

    std::cout << "GS:[1] = " << __readgsdword(1) << ", GS:[3] = " << __readgsdword(3) << '\n';

    _writegsbase_u64((uintptr_t)&data[7]);
    std::cout << "GS base after pointing to data[7] = " << (void*)_readgsbase_u64() << '\n';

    std::cout << "GS:[0] = " << __readgsdword(0) << ", GS:[2] = " << __readgsdword(2) << '\n';
}

不幸的是,我不知道为什么 intrin.h 中的 GS 函数仅在 64 位模式下可用,而 FS 函数仅在 32 位模式下可用。由于 GS 用于 64 位 Windows 中的 TLS,因此我无法更改它。当我越过程序时,调用_writegsbase_u64似乎什么也没做,设置前后基地址仍然相同。所以目前上面的代码在我的机器上不起作用

【讨论】:

  • 请注意,rdgsbase 和相关指令不是 x86-64 的基线(IvyBridge 中具有 FSGSBASE 功能的新功能),并且内核必须通过CR4 中的位,否则为#UD。根据英特尔的手册felixcloutier.com/x86/rdfsbase:rdgsbase(仅限 64 位模式),它们在受保护/兼容模式下也无法识别,因此 MSVC 在 32 位模式下具有内在函数是很奇怪的。
  • 或者您的意思是__readfsdword / __addfsdword 等生成具有fs:[ ... ] 等寻址模式的指令仅在 32 位模式下可用(使用 FS base),不是说_readfsbase_u32读取 FS base)可用吗?
  • 当我跳过程序时调用 _writegsbase_u64 似乎什么都不做 - 你所说的“什么都没有”是什么意思?这个例子实际上不起作用吗? godbolt.org/z/Y5dxanoKq 表明它可以编译为 lea rax, data$[rsp] / wrgsbase rax
  • @PeterCordes 我的意思是调用_writegsbase_u64 时GS 基础不会改变。设置前后的 GS 基数相同。 _readfsbase 仅在 immintrin.h 中定义的 64 位模式下可用,但解引用内部函数仅在一种模式下定义,例如 32 位中的 __writefsword 和 64 位模式下的 __writegsword
  • 哦,你说的时候单步。这可能意味着内核或调试器在您进入内核时将 GSBASE 设置为“应该”用于线程本地存储。 (通过 MSR,或者通过内核中的wrgsbase。)但是如果你在用户空间中运行这些指令而不停止,你会在进行系统调用和重置 GSBASE 之前将结果添加到 cout I/O 缓冲区。因此,Windows 不支持在用户空间代码中设置自定义 GS 基数是合理的。 (而且它们是 Intel 内在函数,而不是 MSVC,当然在支持它们的 CPU 上的内核代码中很有用。)
猜你喜欢
  • 2016-07-26
  • 2015-10-05
  • 1970-01-01
  • 2011-09-19
  • 2020-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-15
相关资源
最近更新 更多