在Assembly中打印字符串,在C

时间:2015-09-29 00:07:19

标签: c assembly nasm x86-64

所有。我试图用NASM编程,我也想学习如何在C中调用这些函数。我相当肯定我到目前为止的代码是正确的,因为我需要设置一个堆栈框架,然后在从例程返回之前撤消该堆栈帧。我也知道我需要返回零以确保没有错误。我也在使用debian linux,以防我需要调整我的操作系统。

代码:

global hello

section .data
message:    db "Hello, world!",0 ; A C string needs the null terminator.

section .text
hello:
        push    rbp         ; C calling convention demands that a
        mov     rbp,rsp     ; stack frame be set up like so.

        ;THIS IS WHERE THE MAGIC (DOESN'T) HAPPEN

        pop     rbp         ; Restore the stack
        mov     rax,0       ; normal, no error, return value
        ret                 ; return

我觉得好像我应该指出我问这个因为我找到的所有程序都对printf进行了外部调用。我不想这样做,我真的想学习如何在装配中打印东西。所以我想我的问题是:NASM中C函数的调用约定是什么?如何在NASM 64位组件中打印字符串?

另外,为了确保我有这个部分,这是在C中调用汇编函数的正确方法吗?

#include <stdio.h>

int main() {
    hello();
    return 0;
}
编辑:好的,我能够解决这个问题。这是汇编代码。我使用.asm

汇总了.c文件和nasm -f elf64 -l hello.lst hello.asm && gcc -o hello hello.c hello.o文件
section .text
global  hello

hello:
        push    rbp         ; C calling convention demands that a
        mov     rbp,rsp     ; stack frame be set up like so.
        mov     rdx,len     ; Message length
        mov     rcx,message ; Message to be written
        mov     rax,4       ; System call number (sys_write)
        int     0x80        ; Call kernel

        pop     rbp         ; Restore the stack
        mov     rax,0       ; normal, no error, return value
        ret             

section .data
message:    db "Hello, world!",0xa ; 0xa represents the newline char.
len:        equ $ - message

相关的C代码(hello.c)看起来像这样:

int main(int argc, char **argv) {
    hello();
    return 0;
}

由于在汇编文件中完成了I / O,因此一些解释包括缺少#include。我需要看到的另一件事是,所有的工作都没有在汇编中完成,因为我没有_start标识符,或者其他所谓的标识符。绝对需要了解有关系统调用的更多信息。非常感谢所有指出我正确方向的人。

1 个答案:

答案 0 :(得分:1)

正如评论中所述,外部世界和代码之间的任何互动都是通过系统调用完成的。 C stdio函数将文本格式化为输出缓冲区,然后使用write(2)编写它。或read(2)进入输入缓冲区,并扫描或读取该行。

在asm中写作并不代表你应该避免使用libc函数,例如:的printf / scanf函数。通常只有在asm中编写程序的一小部分才能获得速度。例如在asm中编写一个具有热循环的函数,并从C或其他任何语言调用它。使用系统调用返回值进行所有必要的错误检查来执行I / O在asm中不会很有趣。如果您对引擎盖下发生的事情感到好奇,请阅读编译器输出和/或单步执行asm。你有时会从编译器中学到很好的技巧,有时你会发现它生成的代码效率低于手工编写的代码。

这是一个问题

mov     rax,4       ; System call number (sys_write)
int     0x80        ; Call kernel

尽管64位进程可以使用i386 int 0x80系统调用ABI,但它 32位ABI,只有32位指针,依此类推。一旦你转到write(2)堆栈上的一个char数组就会出现问题(因为amd64 Linux进程以一个设置了高位的堆栈指针开始。堆内存和{{从可执行文件映射的1}}和.data内存映射到地址空间的低32b。)

本机amd64 ABI使用.rodata,系统呼叫号码与i386 ABI不同。我发现this table of syscalls列出了数字和哪个参数在哪个寄存器中。 syscall最终包含sys/syscall.h以获取实际的/usr/include/x86_64-linux-gnu/asm/unistd_64.h宏,依此类推。有一些标准规则可以映射参数以便注册。 (Given in the ABI doc, IIRC)。