4

私はアセンブリにかなり慣れていないので、stderrに出力を書き込む方法を考えていました。printfなどのC標準ライブラリ関数にアクセスして、コンソールに印刷できることは知っています。しかし、stderrに印刷する方法がわかりません。fprintfを使おうとしていましたが、引数を推測しているだけで、ファイルポインタとして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

しかし、それは私のプログラムをクラッシュさせました。他に何かアドバイスはありますか?

4

2 に答える 2

8

MSVCRT dllのfprintfを使用していますか?

最初のパラメーターは、ストリームへのポインターです。アセンブリでfprintfを使用する方法は次のとおりです。また、アセンブリから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 
于 2012-10-03T01:39:18.280 に答える
1

Debian Linuxでstderrに書き込むのに問題があったため、ここに到着しました。私はレイ・ザイファルトの本を読んでいるので、yasmを使用します。私はIntel構文で記述し、elf64形式とdwarf2デバッグプロトコル用にアセンブルしています。

fprintfを使用する最初の試みで、セグメンテーション違反が発生しました。stdioの内部を見ました。私はなんとか1行のプログラムに相当するものを書くことができました:

     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()呼び出しを使用してから、ファイル記述子を使用してwrite()システムコールを使用することでした。strlen()の呼び出しも必要です。この戦略は、アセンブリコードからCライブラリ関数を呼び出すことができるすべての環境で機能するはずです。ここにいくつかのサンプルコードがあります。引数が指定されていないか、引数が多すぎる場合は、使用法メッセージが出力され、プログラムが終了します。引数が1つ指定されている場合は、整数に変換されて出力されます。

        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
于 2017-01-12T01:57:19.737 に答える