YASM程序集在jitted函数中调用stdout.write

时间:2015-12-22 11:42:36

标签: c++ assembly stdout machine-code yasm

我正在尝试编写一个即时编译器,而且我有一段代码并不想工作。我的平台是x86-64 ubuntu。

我有以下代码用yasm编写:

bits 64

mov rdx, 1
mov rcx, 'A'
mov rbx, 1
mov rax, 4
int 0x80
ret

因此,如果我理解正确,应将A写入stdout。现在我用

编译这段代码
yasm -f bin test.yasm

这导致了以下机器代码:

0x48 0xc7 0xc2 0x01 0x00 0x00 0x00 0x48 0xc7 0xc1 0x41 0x00
0x00 0x00 0x48 0xc7 0xc3 0x01 0x00 0x00 0x00 0x48 0xc7 0xc0
0x04 0x00 0x00 0x00 0xcd 0x80 0xc3

然后我用C ++读取结果代码并调用它:

void *memory = allocate_executable_memory(sizeof(code));
emit_code_into_memory(sizeof(code), code, memory);
JittedFunc func = reinterpret_cast<JittedFunc>(memory);
func();

我认为C ++部分很好,因为我已经通过简单的算术运算尝试了它并且运行良好。

所以无论如何都没有分段错误,代码似乎被执行但没有任何反应,stdout中什么都没有。

有什么建议吗?

//编辑:完整的C ++代码:

#include <stdio.h>
#include <string.h>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <sys/mman.h>

void* allocate_executable_memory(size_t size) {
    void *ptr = mmap(
        0,
        size,
        PROT_READ | PROT_WRITE | PROT_EXEC,
        MAP_PRIVATE | MAP_ANONYMOUS,
        -1,
        0
    );
    if (ptr == (void*)(-1)) {
        perror("mmap");
        return nullptr;
    }
    return ptr;
};

void emit_code_into_memory(size_t code_length, uint8_t *code, void *memory) {
    memcpy(reinterpret_cast<uint8_t*>(memory), code, code_length);
};

typedef void (*JittedFunc)();

int main(int argc, char* argv[]) {
    /* Use like this:
    bin/jit 0xb8 0x11 0x00 0x00 0x00 0xc3
    */
    if (argc <= 1) {
        return 1;
    }

    uint8_t code[argc-1];
    for (int i = 1; i < argc; i++) {
        code[i-1] = std::stoul(argv[i], nullptr, 16);
    }

    void *memory = allocate_executable_memory(sizeof(code));
    emit_code_into_memory(sizeof(code), code, memory);
    JittedFunc func = reinterpret_cast<JittedFunc>(memory);
    func();
    return 0;
};

1 个答案:

答案 0 :(得分:4)

write syscall期望指向要写入的东西的指针,而不是立即数。此外,64位使用具有不同调用约定的 { "firebase": "test", "public": "src", "ignore": [ "firebase.json", "**/.*", "**/node_modules/**" ] } 指令。这对于指针而言非常重要,否则这些指针将被截断为32位。此外,函数编号也不同,因此您的代码实际上会调用syscall系统调用,可以使用stat看到:

strace

您应该尝试以下代码:

stat(NULL, NULL) = -1 EFAULT (Bad address)

这使用堆栈存储要打印的字母。清理可以使用任何调用者保存的临时寄存器,也可以重写为push 'A' mov rdi, 1 ; stdout mov rsi, rsp ; buf mov rdx, 1 ; count mov rax, 1 ; sys_write syscall pop rdi ; cleanup ret 。系统调用的返回值位于add rsp, 8

32位版本可能如下所示:

eax

请注意,必须根据调用约定保留push ebx ; callee-saved push 'A' mov ebx, 1 ; stdout mov ecx, esp ; buf mov edx, 1 ; count mov eax, 4 ; sys_write int 0x80 pop edi ; cleanup buf pop ebx ; restore ebx ret