1

argv[1]x86 アセンブリ言語で格納されているものの最初の要素を取得しようとしています。の数を数えられるようにeax したいので、スタックを最初に 2 回ポップしました。次に、にポップしました。に入れることを考えています。ここから私は迷子になります。私は組み立ての経験がほとんどないか、まったくありません。理解したいと思っているだけです。argcargcargvebx[ebx]bl

main:
;
    mov ecx, 0 ;count output characters
    pop eax ;reject this 32 bits
    pop eax ;get argc
    ;
    pop ebx ; get argv
    ;mov bl, [ebx]
    ;
    add al, 30H ;convert integer to ascii
    mov edi, cline ;put char in output buffer
    mov byte [edi],al
    ;inc edi
    ;mov [edi], bl
    inc ecx ;increment output char count
    inc edi ;increment pointer to o/p buffer
    mov al, 0aH ;LF to end line
    mov byte[edi],al ;put it at end of output line
    inc ecx ;increment output char count
    push ecx ;save char count on stack
    mov edx,len ;length of string to write
    mov ecx,msg ;addr of string
    mov ebx,1 ;file descriptor 1 = stdout
    mov eax,4 ;"write" system call
    int 0x80 ;call the kernel
;
    pop edx ;restore char count into edx for system call
    mov ecx,cline ;address of string
    mov ebx,1 ;file descriptor 1 = stdout
4

2 に答える 2

6

こちらをご覧ください: NASM - Linux コマンドラインパラメーターの取得

仕組みは次のとおりです。

argc = [esp]
argv = [esp + 4 * ARG_NUMBER]

ARG_NUMBER は、argv への 1 ベースのインデックスです。

./test hello there
[esp] = 3
[esp + 4 * 1] = ./test (program path and name)
[esp + 4 * 2] = hello
[esp + 4 * 3] = there

わかりやすくするために、C ライブラリの printf を使用します。

extern printf, exit

section .data
fmtint  db  "%d", 10, 0
fmtstr  db  "%s", 10, 0

section .text
global main
main:

    push    dword[esp]
    push    fmtint      
    call    printf                      ; print argc
    add     esp, 4 * 2

    mov     ebx, 1  
PrintArgV:
    push    dword [esp + 4 * ebx]
    push    fmtstr
    call    printf                      ; print each param in argv
    add     esp, 4 * 2

    inc     ebx
    cmp     ebx, dword [esp]
    jng     PrintArgV

    call    exit

ここでは、単純にするためにエラー チェックは行いません。引数の数が予想を超えているかどうかを確認できます。

ここに画像の説明を入力

@Ed Cashin、OPがINTEL構文を学習している場合、なぜAT&Tと混同するのですか?

于 2013-11-03T21:33:55.663 に答える
0

次の 3 つの推奨事項があります。

  1. まだチェックしていない場合は、http://www.nasm.us/doc/nasmdoc9.htmlをチェックしてください。
  2. 差し迫った問題を解決しようとするコードを最小限に抑えます。
  3. 行き詰まったときは、可能であれば C コンパイラによって生成されたアセンブリを調べてください。

argv を取得するには、プログラムから argv[1] の最初の文字の ASCII コードを返すだけで、システム コールを回避できます。システム コールは argv の取得とは別の問題であるため、これを回避することで目前の問題に注意を向けることができます。

次に、最小限の C プログラムをコンパイルし、生成されたアセンブリを調べます。AT&T 構文アセンブリを読むことは、ニュージャージーにある AT&T に行くとき、目的地が米国の右側にあることを覚えていれば、それほど悪くはありません ;)

tmp$ cat main.c
int main(int argc, char *argv[])
{
        if (argc > 1)
                return argv[1][0];
        return 0;
}
tmp$ gcc -Wall -save-temps main.c

プログラムは、argv[1] の最初の文字の ASCII コードを返すだけです。「t」は 116 です。

tmp$ ./a.out test
tmp$ echo $?
116
tmp$ 

生成されたアセンブリを調べると、pop を使用せず、ベース ポインター ebp に対するスタック パラメーターの位置に基づいてレジスタをロードするだけであることがわかります。ベースポインターで mov を使用するこのスタイルが気に入っています。

私はあなたがやろうとしている方法でポップを使用していないので、他の誰かがポップを使用してそれを行う方法についてコメントするかもしれません.

何が起こっていると思うかについてのコメントで、アセンブリに少し注釈を付けました。修正は大歓迎です。

tmp$ cat main.s
        .file   "main.c"
        .text
.globl main
        .type   main,@function
main:
        pushl   %ebp         ; push the callers base pointer onto the stack
        movl    %esp, %ebp   ; save esp into the base pointer
        subl    $8, %esp     ; make some room on the stack for main ...
        andl    $-16, %esp   ; but pad to an aligned stack pointer
        movl    $0, %eax
        subl    %eax, %esp   ; dunno why gcc subtracts 0 from stack pointer
        cmpl    $1, 8(%ebp)  ; compare 1 and argc, which is 8 past the base pointer
        jle     .L2          ; jump to .L2 if argc <= 1
        movl    12(%ebp), %eax   ; fetch argv into eax
        addl    $4, %eax         ; skip the first 32 bits at that address
        movl    (%eax), %eax     ; fetch address from the resulting address
        movsbl  (%eax),%eax      ; load byte from that address into eax
        movl    %eax, -4(%ebp)   ; put that byte onto the stack (see note 1.)
        jmp     .L1
.L2:
        movl    $0, -4(%ebp)
.L1:
        movl    -4(%ebp), %eax   ; load return value from stack (see note 1.)
        leave
        ret
.Lfe1:
        .size   main,.Lfe1-main
        .ident  "GCC: (GNU) 3.2.2"
tmp$ 

私は 32 ビット マシンで便利な nasm を持っていません。x86_64 の呼び出し規則は、あなたが扱っているものと同じではないため、このアセンブリを nasm 構文に変換しませんでした。

  1. コンパイラは、頭をかきむしり、「それは頭がいいのか、頭が悪いのか?」と疑問に思うようなことを行います。この場合、戻り値を保持するためにスタックの代わりに eax 自体を使用することになると思いますが、時々グーグルは教育的です。たとえば、gccが「xor reg、reg」でレジスタをゼロにするのが好きな理由を、グーグルで学びました。
于 2013-11-03T01:57:50.867 に答える