2012-10-02 84 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