我想使用我为 Windows 编译的 QBE(一个简单的编译器后端)。
为了尝试一下,我想编译示例文件
hello.ssa
:
function w $add(w %a, w %b) { # Define a function add
@start
%c =w add %a, %b # Adds the 2 arguments
ret %c # Return the result
}
export function w $main() { # Main function
@start
%r =w call $add(w 1, w 1) # Call add(1, 1)
call $printf(l $fmt, ..., w %r) # Show the result
ret 0
}
data $fmt = { b "One and one make %d!\n", b 0 }
我用QBE编译的:
> qbe.exe -o out.s hello.ssa
这给了我以下
out.s
文件:
.text
add:
pushq %rbp
movq %rsp, %rbp
movl %edi, %eax
addl %esi, %eax
leave
ret
.type add, @function
.size add, .-add
/* end function add */
.text
.globl main
main:
pushq %rbp
movq %rsp, %rbp
movl $1, %esi
movl $1, %edi
callq add
movl %eax, %esi
leaq fmt(%rip), %rdi
movl $0, %eax
callq printf
movl $0, %eax
leave
ret
.type main, @function
.size main, .-main
/* end function main */
.data
.balign 8
fmt:
.ascii "One and one make %d!\n"
.byte 0
/* end data */
.section .note.GNU-stack,"",@progbits
然后我想用 MinGW 创建一个可执行文件:
> gcc -o hello.exe out.s
out.s: Assembler messages:
out.s:9: Warning: .type pseudo-op used outside of .def/.endef: ignored.
out.s:9: Error: junk at end of line, first unrecognized character is `a'
out.s:10: Warning: .size pseudo-op used outside of .def/.endef: ignored.
out.s:10: Error: junk at end of line, first unrecognized character is `a'
out.s:28: Warning: .type pseudo-op used outside of .def/.endef: ignored.
out.s:28: Error: junk at end of line, first unrecognized character is `m'
out.s:29: Warning: .size pseudo-op used outside of .def/.endef: ignored.
out.s:29: Error: junk at end of line, first unrecognized character is `m'
out.s:39: Error: junk at end of line, first unrecognized character is `-'
这是我运行示例时得到的输出。它在 wsl 上使用 GCC 编译得很好:
wsl gcc -o hello out.s
但是 MinGW gcc 编译器似乎不理解汇编。
我注意到两个编译器之间简单的 C Hello, World 的默认 .s 输出也不同。例如:
gcc -S hello.c
根据使用的平台提供不同的 .s 文件。
那么我可以使用什么编译器/编译器标志/汇编器从 Windows 上的 qbe 生成的 out.s 文件中获取机器代码?
其次,我认为QBE目前仅支持system-V ABI,所以我需要链接system-V存根函数而不是CRT库函数是否正确,或者mingw CRT是否已经使用system-V ABI?
我通过修补 QBE 使其编译,使其不在其汇编输出中发出 .size 和 .type。所以当我编译时:
function w $add(w %a, w %b) {
@start
%c =w add %a, %b
ret %c
}
export function w $main() {
@start
%r =w call $add(w 1, w 1)
call $sysv_printf(l $fmt, ..., w %r) # Calling my wrapper
ret 0
}
data $fmt = { b "One and one make %d!\n", b 0 }
通过 QBE 我得到:
.text
add:
pushq %rbp
movq %rsp, %rbp
movl %edi, %eax
addl %esi, %eax
leave
ret
/* end function add */
.text
.globl main
main:
pushq %rbp
movq %rsp, %rbp
movl $1, %esi
movl $1, %edi
callq add
movl %eax, %esi
leaq fmt(%rip), %rdi
movl $0, %eax
callq sysv_printf
movl $0, %eax
leave
ret
/* end function main */
.data
.balign 8
fmt:
.ascii "One and one make %d!\n"
.byte 0
/* end data */
我创建了一个 C 包装器:
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
int __attribute__((sysv_abi)) sysv_printf( const char *restrict fmt, ... ) {
printf("Called with %s", fmt);
assert(strcmp(fmt, "One and one make %d!\n") == 0);
va_list ap;
va_start(ap, fmt);
int smth = va_arg(ap, int);
printf("%d\n", smth);
va_end(ap);
printf("Finish");
}
所以如果我在批处理文件中运行这些步骤
call qbe -o out.s hello.ssa
call gcc -c out.s
call gcc -c stub.c
call gcc -S -fno-asynchronous-unwind-tables stub.c
call gcc -o main.exe stub.o out.o
我得到了一个可执行的 main.exe。
但是,当我运行它时,某些东西不适用于可变参数:
Called with One and one make %d!
格式字符串有效,但整数似乎未正确传递。 如果我检查返回值:
> echo %errorlevel%
-1073741819
这可能是由于我的程序导致分段错误。