4

由于这里无法解释的复杂原因,我需要在 x86 子集的平台上运行 x86 GCC 编译的 Linux 程序。这个平台没有 %gs 寄存器,这意味着它必须被模拟,因为 GCC 依赖于 %gs 寄存器的存在。

目前我有一个包装器,它在程序尝试访问 %gs 寄存器时捕获异常并模拟它。但这是狗慢。有没有办法可以使用等效指令提前修补 ELF 中的操作码,从而避免陷阱和模拟?

4

2 回答 2

4

您是否尝试过使用该-mno-tls-direct-seg-refs选项编译代码?从我的 GCC 手册页(i686-apple-darwin10-gcc-4.2.1):

   -mtls-direct-seg-refs
   -mno-tls-direct-seg-refs
       Controls whether TLS variables may be accessed with offsets from
       the TLS segment register (%gs for 32-bit, %fs for 64-bit), or
       whether the thread base pointer must be added.  Whether or not this
       is legal depends on the operating system, and whether it maps the
       segment to cover the entire TLS area.

       For systems that use GNU libc, the default is on.
于 2011-08-02T04:38:21.403 回答
3

(这是假设 Adam Rosenfields 解决方案不适用。它或类似的方法可能是解决它的更好方法。)

你还没有说明你是如何模拟 %gs 寄存器的,但是除非你对程序有一些特殊的知识,否则可能很难修补每个用法,因为否则你只有 2 个字节(在最坏的情况下,常见情况)您可以使用补丁进行修改。当然,如果您使用 %es = %gs 之类的东西,它应该相对简单。

假设这可以以某种方式在您的情况下工作,策略是扫描 ELF 文件的可执行部分并修补任何使用或修改 GS 寄存器的指令。那至少是以下说明:

  • 任何带有 GS 段覆盖前缀65的指令(期望分支指令,在这种情况下前缀表示其他内容)
  • push gs( 0F A8)
  • pop gs( 0F A9)
  • mov r/m16, gs( 8C /r)
  • mov gs, r/m16( 8E /r)
  • mov gs, r/m64( REX.W 8E /r)(如果您支持 64 位模式)

以及任何其他允许段寄存器的指令(我认为没有那么多,但我不是 100% 确定)。

这一切都来自英特尔® 64 和 IA-32 架构软件开发人员手册组合卷 2A 和 2B:指令集参考,AZ。请注意,指令有时带有其他前缀,有时没有,因此您可能应该使用来进行指令解码,而不是盲目地搜索字节序列。

上面的一些指令应该相对直接地变成call my_patch或类似的,但是你可能很难找到适合两个字节并且通常工作的东西。int XX如果您可以设置中断向量, ( CD XX) 可能是一个不错的选择,但我不确定它是否会比您当前使用的方法更快。您当然需要记录修补了哪条指令,并让中断处理程序(或其他)根据返回地址(您的处理程序接收到的)做出不同的反应。

如果您可以在 -128..127 字节内找到空间并使用JMP rel8( EB cb) 跳转到蹦床(通常是另一个JMP,但这次为目标地址留出更多空间),您可能可以设置蹦床,然后它会处理指令仿真并跳回到修补后的 %gs 用法之后的指令。

最后,我建议保持陷阱和模拟代码运行,以捕捉您可能没有想到的任何情况(例如自我修改或注入代码)。这样,您还可以记录任何未处理的案例并将它们添加到您的解决方案中。

于 2011-08-02T12:31:10.323 回答