【问题标题】:Converting VS to GCC inline assembly将 VS 转换为 GCC 内联汇编
【发布时间】:2016-10-07 20:07:01
【问题描述】:

我有这段代码

__asm
{
    mov eax, fs:[18h]
    mov eax, [eax + 30h]
    mov eax, [eax + 0Ch]
    mov pmInfo, eax
}

这应该是在第一行检索TEB,在第二行检索PEB,最后一个是包含进程模块信息的结构体。

typedef struct _ProcessModuleInfo
{
    unsigned int size;
    unsigned int initialized;
    HANDLE SsHandle;
    LIST_ENTRY LoadOrder;
    LIST_ENTRY InitOrder;
    LIST_ENTRY MemoryOrder;
} ProcessModuleInfo, *pProcessModuleInfo;

我的问题是,如何将那段程序集转换为代码块的 GCC 编译器?

【问题讨论】:

  • 你会使用GCC extended assembler template。但是,如果您是 GCC 的新手,这非常重要。除非你知道你在用 GCC inline 做什么,否则很难做到正确。您最好将该代码作为函数放在汇编文件中并将其链接到您的 C 代码。
  • 感谢您的回复。我检查了很多链接,但正如你所说,这确实非常重要。我现在真正在寻找的只是这个特定问题的快速解决方案。我从来没有使用过汇编文件链接,但我可以研究它是如何完成的。如果有人有任何其他快速解决方案或 GCC 的代码本身,我将不胜感激!
  • 谢谢!它似乎有效。
  • 您是否将代码移植到非 Windows?如果是这样,您确定相同的说明甚至是您想要的吗?例如您可能应该使用 C/C++ 语法进行线程本地存储,并让编译器生成对线程本地存储块的访问。
  • @PeterCordes :我可能错误地认为他仍然针对 Windows 并且在 Windows 上使用 Codeblocks(使用 TDM-GCC 编译器)。由于我没有看到 Linux 的提及,我认为他正在转向 Win32 平台上的不同编译器(MSVC 到 Win32/TDM GCC)。如果这对他有用,我的假设可能是正确的,在尝试取消引用 Linux 上的指针时,我预计会出现段错误)

标签: visual-studio gcc x86 porting inline-assembly


【解决方案1】:

您可以在 GCC 文档中了解有关 GCC assembler templates 的更多信息。如果您想了解更多信息,Peter Cordes 有一个 Stackoverflow 答案,其中包含 inline assembler 列表。 MSVCGCC 差别很大。 GCC 不知道模板做什么或指令执行。 GCC 会根据 constraints 的列表进行替换 - 输入、输出和破坏。

一个 GCC 扩展的汇编器模板,它与 MSVC 内联汇编器非常接近,可能如下所示:

__asm__ (
    "mov %%fs:0x18, %0\n\t"
    "mov 0x30(%0), %0\n\t"
    "mov 0x0C(%0), %0\n\t"
    : "=r"(pmInfo));

在您的情况下,您需要一个用于内部使用的寄存器。它不需要是 EAX,但可以是任何 GCC 可用的寄存器。我们使用"=r"输出约束,表示我们希望GCC 选择一个可用的寄存器。我们通过汇编器模板中的%0 引用第一个(在本例中也是唯一一个)约束。约束上的"=r"(pmInfo) 表示,当模板中的指令完成时,所选寄存器中的值将被放入pmInfo

GCC 扩展汇编器使用起来很棘手,而且很容易出错。您最好使用返回指向 ProcessModuleInfo 结构的指针的函数创建汇编程序文件。这样就无需了解 GCC 的扩展汇编器模板并处理其复杂性。

Stackoverflow 用户 DavidWohlferd 写了一个 GCC wiki article 给出了为什么应该避免使用 GCC 内联汇编器的原因如果可能。


如果%0 看起来有点混乱,则可以为每个输入和输出操作数指定一个符号名称,该名称可用作替代% 和模板中的数字。代码可能看起来像这样:

__asm__ (
    "mov %%fs:0x18, %[tempreg]\n\t"
    "mov 0x30(%[tempreg]), %[tempreg]\n\t"
    "mov 0x0C(%[tempreg]), %[tempreg]\n\t"
    : [tempreg]"=r"(pmInfo));

方括号中的[tempreg] 为操作数提供了一个名称,以替代使用序数位置。这也使得阅读模板更容易一些。

【讨论】:

  • 它会帮助优化器在 C 中做更多的事情吗?例如只是做需要段覆盖的负载?尽管如果您需要将fs:[18h] 视为volatile,那么您不妨将asm volatile 用于整个负载链。如果您不使用asm volatile,那么编译器应该能够将整个负载链提升到循环之外。所以让编译器看到链中接下来的两个加载可能没有任何好处。 IIRC,gcc 并没有真正为 x86 做指令调度,因为它假设 CPU 是乱序的。
  • 如果 OP 有要遍历的数据结构,我只需在 fs:0x18 处获取原始值并在 C 中进行间接寻址。OP 没有提供任何2 个中间数据结构(如 TEB)的数据结构,以及您生成的 C 代码看起来相当混乱。我制作了一个版本,它进行了一堆强制转换(以及在 void 或 uint32_t* 上的指针索引)并且以可读性的方式 - 这更容易并且易于遵循。由于 OP 要求直接转换,并且只想要一个解决方案工作(我在给出回应的问题下进行了这样的询问)我交付了
  • @PeterCordes :FS 是一个具有指向 Win32 线程信息块的基础的段。 fs:0x18 包含指向线程环境块的指针。 TEB 中偏移量 0x30 处的指针是指向进程环境块 (PEB) 的指针。 PEB 包含指向偏移 0x0C 处的进程模块信息结构的指针。
  • PeterCordes :附带说明,最近版本的 MSVC 具有内在的 __readfsdword(但它并不总是可用),因此针对旧 MSVC 的代码将使用内联汇编程序。 __readfsdword(0x18) 将读取 fs:0x18 处的 DWORD 值。我实际上去寻找,但我没有看到一个我可以保证会工作的 GCC 。我目前没有他的 TDM GCC 编译器版本来测试任何东西。在 MSVC Windows 环境中,大多数数据结构(我提到的所有数据结构)都在 C 头文件中。在这种情况下,__readfsdword 和取消引用非常方便
  • @PeterCordes:回滚我错过了您的评论,并注意到您提到了类似 struct _ProcessModuleInfo *** 的内容。我的变种之一是pProcessModuleInfo **pTIB; __asm__ ( "mov %%fs:0x18, %[TIB]\n\t" : [TIB]"=r"(pTIB)); pmInfo = pTIB[12][3]; 但这很不合时宜,主要是因为我们利用了所有偏移量都是 4 倍 div 的事实。不要认为编译器能比它做得更好原始的,对于未来的维护者来说,他们一开始可能会摸不着头脑。我觉得这不太可读。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-05-11
  • 1970-01-01
  • 2012-02-07
  • 2012-10-20
  • 2013-04-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多