2012-10-02 45 views
4

我很新的汇编,我想知道如何将输出写入stderr。我知道您可以访问C标准库函数(如printf)来打印到控制台。但我无法弄清楚如何打印到stderr。我试图使用fprintf,但我只是在猜测参数,我不知道如何指定stderr作为文件指针。谢谢。在x86汇编中写入stderr

编辑:按照sehe的建议下,我尝试这样做:

.586 
.model small,c 
.stack 100h 

.data 
msg db 'test', 0Ah 

.code 
includelib MSVCRT 
extrn fprintf:near 
extrn exit:near 

public main 
main proc 
    push offset msg 
    push 2  ;specify stderr 
    call fprintf ;print to stderr 
    push 0 
    call exit ;exit status code 0 

main endp 

end main 

,但它只是使我的程序崩溃。任何其他建议?

回答

7

您是否使用MSVCRT DLL中的fprintf?

第一个参数是指向流的指针。以下是如何在程序集中使用fprintf。 另外,从Assembly调用C函数时,需要在每次调用参数后调整堆栈。

另外,BIGGIE ...你的字符串不是NULL终止!你必须NULL终止你的字符串,那就是函数如何找到字符串的长度。 不知道汇编你使用的是什么,但是这是你如何能做到这在MASM:

include masm32rt.inc 

_iobuf STRUCT 
    _ptr  DWORD ? 
    _cnt  DWORD ? 
    _base  DWORD ? 
    _flag  DWORD ? 
    _file  DWORD ? 
    _charbuf DWORD ? 
    _bufsiz  DWORD ? 
    _tmpfname DWORD ? 
_iobuf ENDS 

FILE TYPEDEF _iobuf 

.data 
msg   db 'test', 0Ah, 0  

.data? 
stdin  dd ? 
stdout  dd ? 
stderr  dd ? 

.code 
start: 

    call crt___p__iob 
    mov  stdin,eax   
    add  eax,SIZEOF(FILE) 
    mov  stdout,eax   
    add  eax,SIZEOF(FILE) 
    mov  stderr,eax   

    push offset msg 
    push eax 
    call crt_fprintf 
    add  esp, 4 * 2 

    push 0 
    call crt_exit 

end start 
1

我来到这里是因为我遇到了麻烦,写在Debian的Linux标准错误。我使用yasm,因为我正在阅读Ray Seyfarth的书。我正在编写英特尔语法并为elf64格式和dwarf2调试协议进行汇编。

我第一次尝试使用fprintf会遇到分段错误。我看着stdio的内部。我设法写一个相当于一行程序:

 int main() 
    { 
     fprintf(stderr, "%d\n", 3); 
    } 

但是当我修改它时,我开始再次发生分段错误。我的目标是获得大会相当于:

int main(int argc, char* argv[]) 
    { 
     if (argc != 2) { 
     fprintf(stderr, "Usage: %s n\n", argv[0]); 
     } else { 
     /* program code */ 
     } 
    } 

我的最终解决方案是使用一个的sprintf()调用,然后用一个文件描述符写入()系统调用。调用strlen()也是需要的。这种策略应该可以在任何可以用汇编代码调用C库函数的环境中工作。这里是一些示例代码。如果没有给出参数或参数太多,则会打印一条使用消息,程序将退出。如果提供了一个参数,它将转换为一个整数并打印出来。

 segment .text 
     global main 
     extern printf, atoi, sprintf, strlen, write 

    main: 
    .argc equ 0 
    .argv equ 4 

     segment .data 
    .f1 db "Usage: %s n", 0x0a, 0 
    .f2 db "%d", 0x0a, 0 

     segment .bss 
    .buf resb 255 

     segment .text 

     push rbp 
     mov rbp, rsp 
     sub rsp, 16 

     ; Save the arguments. 
     mov [rsp+.argc], rdi 
     mov [rsp+.argv], rsi 

     ; if (argc != 2) 
     cmp rdi, 2 
     jne .usage 

     ; Convert argv[1] to an integer, then print it. 
     ; We could have printed argv[1] as a string, 
     ; but in an actual program we might want to do 
     ; n = atoi(argv[1]) 
     ; because we are passing a number on the command line. 

     ; printf("%d\n", atoi(argv[1])) 

     ; Get argv[1]. 
     mov rax, [rsp+.argv] 
     mov rdi, [rax+8]  ; rdi gets *(argv + 1), or argv[1]. 
     call atoi   ; Return value in eax. 

     lea rdi, [.f2] 
     mov esi, eax 
     xor eax, eax 
     call printf 

     jmp .done 

    .usage: 
     ; Get argv[0]. 
     mov rax, [rsp+.argv] 
     mov rax, [rax] 

     ; sprintf(buf, "Usage: %s n\n", argv[0]) 
     lea rdi, [.buf] 
     lea rsi, [.f1] 
     mov rdx, rax 
     xor eax, eax 
     call sprintf 

     ; len = strlen(buf); 
     lea rdi, [.buf] 
     call strlen  ; string length is placed in eax. 

     ; write(2, buf, strlen(buf)) 
     mov edi, 2  ; fd for stderr 
     lea rsi, [.buf] 
     mov edx, eax 
     call write 

    .done: 
     leave 
     ret 

假设上面的代码被包含在progname.asm,这被组装和连接,如下所示:

yasm -f elf64 -g dwarf2 -l progname.lst progname.asm 
    gcc progname.o -o progname