为什么OSX在amd64间接跳转时出现总线错误?

时间:2012-08-13 10:35:09

标签: windows macos assembly x86-64

我试图为x86和amd64编写trampolines,以便将给定的函数调用立即导向存储在已知内存位置的地址(目的是确保第一个目标地址存在于给定的DLL中(视窗))。

以下代码尝试使用_fn作为内存位置(或其中一组)来启动实际目标地址:

(*_fn[IDX])(); // rough equivalent in C

.globl _asmfn
_asmfn:
  jmp *_fn+8*IDX(%rip)

IDX旨在使用一些CPP宏来构造,以提供一系列嵌入式DLL向量,每个向量唯一地映射到_fn函数指针数组中的一个槽。 这可以在一个简单的测试程序中工作,但是当我实际将它放入共享库(目前在OSX上进行测试)时,在尝试向_asmfn代码传递时出现总线错误:

Invalid memory access of location 0x10aa1f320 rip=0x10aa1f320

这段代码的最终目标是Windows,虽然我还没有在那里尝试过(我想我至少可以在OSX / intel的测试用例中证明这个程序集)。 amd64跳跃至少在名义上是正确的,还是我错过了什么?

关于trampolines on amd64的一个很好的参考。

修改

跳转 在Windows 7上正常工作(最终有机会测试)。但是,我仍然很想知道为什么它在OSX上失败了。总线错误是由KERN_PROTECTION_FAILURE引起的,这似乎表明操作系统保护阻止执行该代码。目标地址分配的内存(它是由libffi生成的蹦床),但我相信它被正确标记为可执行内存。如果它是可执行内存问题,那就可以解释为什么我的独立测试代码有效(回调蹦床被编译,未分配)。

2 个答案:

答案 0 :(得分:0)

使用PC相对寻址时,请记住偏移量必须在+ - 2GB范围内。这意味着你的跳台和蹦床不能相距太远。关于这样的蹦床,可以在Windows x64上进行传输而不需要破坏任何寄存器:

  1. 一个序列:
    PUSH <high32>
    MOV DWORD PTR [ RSP - 4 ], <low32>
    RET
    这适用于Win64和UN * X x86_64。虽然在UN * X上,如果该函数使用了redzone,那么你就是在破坏......

  2. 一个序列:
    JMP [ RIP ]
    .L: <tgtaddr64>
    再次,适用于Win64和UN * X x86_64。

  3. 一个序列:
    MOV DWORD PTR [ RSP + c ], <low32>
    MOV DWORD PTR [ RSP + 8 ], <high32>
    JMP [ RSP + 8 ]
    这是Win64特有的,因为它(ab)使用Win64 ABI保留的32字节“参数空间”的一部分(在堆栈上的返回地址上面); UN * X x86_64相当于这将是(ab)使用保留的128字节“红色区域”的一部分(只是下面的堆栈上的返回地址)那里:
    MOV DWORD PTR [ RSP - c ], <low32>
    MOV DWORD PTR [ RSP - 8 ], <high32>
    JMP [ RSP - 8 ]
    只有在调用蹦床时才能接受(覆盖)其中的内容,两者都可以使用。

  4. 如果可以在内存中直接构建这样一个与位置无关的寄存器中立蹦床 - 就像这样(对于方法1):

    #include <stdint.h>
    #include <stdio.h>
    
    char *mystr = "Hello, World!\n";
    
    int main(int argc, char **argv)
    {
        struct __attribute__((packed)) {
                    char PUSH;
                    uint32_t CONST_TO_PUSH;
                    uint32_t MOV_TO_4PLUS_RSP;
                    uint32_t CONST_TO_MOV;
                    char RET;
        } mycode = {
                    0x68, ((uint32_t)printf),
                    0x042444c7, (uint32_t)((uintptr_t)printf >> 32),
                    0xc3
        };
        void *buf = /* fill in an OS-specific way to get an executable buffer */;
        memcpy(buf, &mycode, sizeof(mycode));
    
        __asm__ __volatile__(
            "push $0f\n\t"         // this is to make the "jmp" return
            "jmp *%0\n\t"
            "0:\n\t" : : "r"(buf), "D"(mystr), "a"(0));
    
        return 0;
    }
    

    请注意,这并未考虑是否有任何非易失性寄存器被“调用”函数破坏;我也忽略了如何使trampoline缓冲区可执行(堆栈通常不在Win64 / x86_64上)。

答案 1 :(得分:0)

@HarryJohnston有权利,只在OS X上遇到权限问题。代码在其目标Windows环境中运行良好。