从内存移动到C x86-64 jit中的xmm0

时间:2019-06-26 18:12:00

标签: c assembly x86-64 jit

我正在尝试编写一个小型的x86-64 JIT,而我在一些地方有些不知所措。

我正在尝试JIT一个简单的函数,该函数将float的值分配给xmm0寄存器,然后将其返回,但是我不确定如何对movsd调用的参数进行编码。

任何帮助将不胜感激。

/* main.c */
#include <stdio.h>
#include <sys/mman.h>

#define xmm(n) (n)

typedef double(*fn)();

fn jit(){
    char* memory = mmap(NULL,
                        4096,
                        PROT_READ|PROT_WRITE|PROT_EXEC,
                        MAP_PRIVATE|MAP_ANONYMOUS,
                        -1, 0);
    int i=0;
    float myfloat = 3.1f;
    memory[i++] = 0x48; /* REX.W */
    memory[i++] = 0xf2; /*******************/
    memory[i++] = 0x0f; /* MOVSD xmm0, m64 */
    memory[i++] = 0x10; /*******************/

    memory[i++] = 0x47 | xmm(0) << 3; /* Not 100% sure this is correct */

    memory[i++] = 0; /* what goes here to load myfloat into xmm0? */

    memory[i++] = 0xc3; /* RET */
    return (fn) memory;
}

int main(){
    fn f = jit();
    printf("result: %f\n", (*f)());
    return 0;
}

1 个答案:

答案 0 :(得分:2)

SSE指令通常不支持立即数,除了一些罕见的指令(具有一个字节的立即数来控制其操作)。因此,您需要:

  • myfloat存储到附近的某个存储区域
  • 生成对该区域的引用的内存操作数

这两个步骤都很容易。第一步,我只使用memory的开头,然后让代码从头开始。请注意,在这种情况下,您需要确保返回指向函数开头而不是memory开头的指针。其他解决方案也是可能的。只需确保将myfloat存储在代码的±2 GiB之内即可。

要生成操作数,请重新访问英特尔手册。所需的寻址模式是32位RIP相对操作数。这是使用mod = 0,r / m = 5生成的。位移是一个有符号的32位数字,在指令末尾添加到RIP的值中(这是+4的出处,必须考虑)在位移的最深处)。

因此我们有类似的东西:

memory[i++] = 0xf2; /*******************/
memory[i++] = 0x0f; /* MOVSD xmm0, m64 */
memory[i++] = 0x10; /*******************/
memory[i++] = 0005 | xmm(0) << 3; /* mod = 0, r/m = 5: [rip + disp32] */
*(int *)(memory + i) = memory + i + 4 - addr_of_myfloat;
i += 4;
memory[i++] = 0xc3; /* RET */

请注意,此处不需要REX前缀。