10

私はアセンブリプログラミングにかなり慣れていません。GCC (Linux) で x86 プラットフォームを使用しています。

C から次のように呼び出したい関数があります。

myfunc ( unsigned char * s1, unsigned char * s2, int someint );

この関数は、s1 と s2 のメモリ位置を取得してそれらを比較し、インクリメントと比較などを行いながら、いくつかの処理を行います。これは memcmp のようなものですが、私はもっとやっています。

私の質問:アセンブリ関数にポインタを渡すと? では、「このメモリ アドレスに格納されている値を教えてください」と言うにはどうすればよいでしょうか。

これが私がこれまでに持っているものです:

スタックから最初の関数 arg ("s1") を取得するには、次のようにします (someaddress は 32 ビット整数で、私は 32 ビット プロセッサで作業しています)。

movl  8(%esp), %ecx
movl  %ecx, someaddress

(またはなど)に入れsomevar、で printf すると、そのアドレスと、渡した unsigned char ポインタ " " のアドレスが同じであることがわかります。しかし、私が実際に行ったことは、メモリアドレスを取得し、それを整数に変換してから、その整数をいくつかのアドレスに入れたことだと思います。%eax%ebx%ps1

たとえば、次のようにすると:

movl  pos1, %eax
movl  pos2, %ebx
cmp   (%eax),(%ebx)

「エラー: `cmp' のメモリ参照が多すぎます」というメッセージが表示されます。「あなたが台無しにした」以外は、それが何を意味するのか完全にはわかりません;-)

そう...

  • ポインターを渡し、ポインターとして保持する方法は?
  • アセンブリでそのポインタの値を使用する方法は? (例えば、*ptrC のように)

LEA オペランドを見たいですか?

私は Richard Blum の "Professional Assembly Programming" をガイドとして使用していますが、Blum はこのケースを扱っていないようです。

アップデート

勉強になる回答ありがとうございます!

残念ながら、私はまだ逆参照できません。

簡単な例を次に示します。アセンブリ関数はポインターを受け取り、それをエコー バックする必要があります。代わりに私は得る:

first_ptr points to 81 (should be 81) <-- from C program
the value is -1543299247 <-- printf called from within assembler
the value is -6028513 <-- printf called from within assembler
my function returned -6028513 <-- return value printed from C program

C プログラム:

#include <stdio.h>
#include <string.h>

int main (void) {
        unsigned char first;
        unsigned char * first_ptr;

        first = 'Q';
        first_ptr = &first;

        printf ("first_ptr points to %i (should be 81)\n",*first_ptr);

        printf ("my function returned %i\n", myfunc(first_ptr));
        return 0;
}

組み立てプログラム:

.section .data

msg:
  .asciz "the value is %i\n"

.section .bss
.lcomm str, 8

.section .text
.type myfunc, @function
.globl myfunc
myfunc:

  # save stack
  pushl %ebp
  movl  %esp, %ebp

  # save string arg from stack to "str"
  movl  8(%esp), %ecx
  movl  %ecx, str

  # let's try printing the ecx dereference

  pushl (%ecx)
  pushl $msg
  call printf

  # put the value of str on the stack 
  # and call printf

  pushl (str)
  pushl $msg
  call printf

  # now return the character at pos1
  movl  (str), %eax

  # restore the stack
  movl  %ebp, %esp
  popl  %ebp

  ret
4

1 に答える 1

8

オペランドの少なくとも 1 つがcmpレジスタでなければなりません。2 つのメモリ位置の内容を比較しようとしている場合は、そのうちの 1 つをレジスタに入れる必要があります。あなたが尋ねるレジスターにそれを入れる方法は?サンプルコードですでにそれを行っています。この行:

movl  8(%esp), %ecx

%esp+8 で 4 バイトを取得し、それらを %ecx に入れます。C ライクな疑似コードでは、次のようになります。

ecx = *(esp + 8);

うまくいけば、それは理にかなっています。同様の操作を実行して、ポインターをスタックからレジスターに取得し、それらを逆参照したり、逆参照された値を比較したりすることができます。ご不明な点がございましたら、お気軽にお問い合わせください。

編集 - 分割された質問:

  1. ポインターを渡し、ポインターとして保持する方法は?

    あなたはすでにそれを行っており、あなたのmovl 8(%esp), %ecx指示、またはそれに似たもので、必要なことはすべて行われます。

  2. アセンブリでそのポインタの値を使用する方法は? (例: C の *ptr のように)

    上記の命令から()ポインターの最初のバイトをロードするには、再度 -を使用する必要があります。次に例を示します。%ecx

    movb (%ecx), %edx
    

    上記の使用方法と同様の C ライクな擬似コードでは、この命令は次のようになります。

    edx = *(unsigned char *)ecx;
    
  3. LEA オペランドを見たいですか?

    あなたが提供した問題の説明に基づいて、おそらくそうではありません。ただし、常に可能です。 leaCの演算子のように機能&します。例として、この命令は次のとおりです。

    lea 12(%ecx), %edx
    

    次のように擬似コードに変換できます。

    edx = &(*(ecx + 12))
    

    またはもっと簡単に:

    edx = ecx + 12
    

    比較的単純なアドレス指定モードを使用しているため、この例は少しばかげていますが、次のようなものはどうでしょうか。

    lea 1(%edx,%ecx,4), %eax
    

    つまり:

    eax = &(edx[ecx * 4] + 1)
    

多くの場合、この種の問題に対する最も簡単な解決策は、ルーチンを C で記述し、それをコンパイルして結果を逆アセンブルすることです。

編集2:

サンプル プログラムはほぼ正しいように見えますが、メモリ内のポインターを逆参照しようとしています。最初にそれらのポインターをレジスターに取得すれば問題ありません。

于 2011-03-04T00:21:14.410 に答える