3

逆アセンブルprintfは多くの情報を提供しません:

(gdb) disas printf
Dump of assembler code for function printf:
0x00401b38 <printf+0>:  jmp    *0x405130
0x00401b3e <printf+6>:  nop
0x00401b3f <printf+7>:  nop
End of assembler dump.


(gdb) disas 0x405130
Dump of assembler code for function _imp__printf:
0x00405130 <_imp__printf+0>:    je     0x405184 <_imp__vfprintf+76>
0x00405132 <_imp__printf+2>:    add    %al,(%eax)

内部でどのように実装されていますか?

なぜ分解しても役に立たないのですか?

*以前はどういう意味ですか0x405130

4

6 に答える 6

8

これが1つの特定の実装、http://ftp.fr.openbsd.org/pub/OpenBSD/src/lib/libc/stdio/printf.cおよび http://ftp.fr.openbsd.org/pub/OpenBSD/srcです。 /lib/libc/stdio/vfprintf.c

于 2010-04-01T11:03:08.930 に答える
3

間接メモリ参照用の*AT&Tアセンブラ構文です。つまり

jmp *<addr>

「に保存されているアドレスにジャンプする」という意味<addr>です。

これは、次のIntel構文と同等です。

jmp [addr]

レジスタまたはメモリオペランドを使用した分岐アドレス指定には、プレフィックスとして「*」を付ける必要があります。

ソース

于 2010-04-04T03:08:36.677 に答える
2

事実上すべてのCコンパイラは、オープンソースコンパイラだけでなく、ソースにランタイムライブラリを提供します。残念ながら、それらは多くの場合、従うのがかなり難しい形式で書かれており、通常、設計根拠のドキュメントは付属していません。

したがって、この問題に対処するための非常に優れたリソースは、PJ Plaugerの「標準Cライブラリ」です。これは、ライブラリ実装のソースだけでなく、その設計方法やそのようなライブラリが必要とする可能性のある特別な状況についての詳細も提供します。検討。

この本の「中古」バージョンのいくつかが提供されている価格で、それは盗みであり、真面目なCプログラマーの本棚にあるはずです。

Plaugerには、C ++ライブラリを対象とした同様の本があり、同様の価値があると思います。

于 2010-04-01T17:39:20.150 に答える
1

ここでは逆アセンブルは問題なく機能し、printfはvfprintfを使用して「内部」で実装されます。これはほぼ期待どおりです。アセンブラは通常、Cよりもはるかに冗長であり、注釈付きのソースがない場所を理解するのに時間がかかることに注意してください。コンパイラ出力は、アセンブラを自分で教えるための優れた方法でもありません。

于 2010-04-01T11:45:39.520 に答える
1

はどうかと言うと

0x405130より前の*はどういう意味ですか?

私はgdbの逆アセンブラに精通していませんがjmp *0x405130、ポインタを介した間接ジャンプのようです。0x405130にあるものを逆アセンブルする代わりに、そこに4バイトのメモリをダンプする必要があります。そこに別のアドレスが見つかることは間違いありません。その場所を逆アセンブルすると、printf()のコードが見つかります(逆アセンブルがどれほど読みやすいかは別の話です)。

つまり、は、それ自体ではなく_imp__printf、へのポインタです。printf()printf()


以下のコメントの詳細については、後で編集してください。

少し突っついているのは、Intelアセンブリ構文を使用する場合の命令jmp *0x405130のGAS / AT&Tアセンブリ構文であることを示しています。jmp [0x405130]

これを不思議に思うのは、gdbコマンドx/xw 0x405130がそのアドレスに含まれていることを示しているということです0x00005274(これは、0x405130を分解したときに取得したものと一致しているようです)。ただし、それはjmp [0x405130]アドレスにジャンプしようとすることを意味します0x00005274が、これは正しくないようです(そして、そのアドレスを分解しようとすると、gdbは同じように言いました。

_imp_printfエントリが、最初の実行が0x405130をジャンプするときに、OSがトラップをフィールドして動的リンクを修正する0x00005274アドレスにヒットする、ある種の遅延バインディング手法を使用している可能性があります。修正後、OSは0x405130の正しいリンクアドレスで実行を再開します。しかし、これは私の側のまったくの当て推量です。使用しているシステムがこのようなことをするかどうかはわかりませんが(実際、どのシステムで実行されているかさえわかりません)、技術的には可能です。このようなことが起こっている場合、最初の呼び出しprintf()が行われるまで、0x405130に正しいアドレスが表示されません。

printf()実際に何が起こっているのかを確認するには、アセンブリレベルでの呼び出しを1ステップ実行する必要があると思います。


GDBセッションで更新された情報:

これがあなたが直面している問題です-あなたはシステムがDLLをロードしてDLLへのリンクを修正する前のプロセスを見ています。これは、GDBでデバッグされたMinGWでコンパイルされた単純な「helloworld」プログラムのデバッグセッションです。

C:\temp>\mingw\bin\gdb test.exe
GNU gdb (GDB) 7.1
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "mingw32".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from C:\temp/test.exe...done.

(gdb) disas main
Dump of assembler code for function main:
   0x004012f0 <+0>:     push   %ebp
   0x004012f1 <+1>:     mov    %esp,%ebp
   0x004012f3 <+3>:     sub    $0x8,%esp
   0x004012f6 <+6>:     and    $0xfffffff0,%esp
   0x004012f9 <+9>:     mov    $0x0,%eax
   0x004012fe <+14>:    add    $0xf,%eax
   0x00401301 <+17>:    add    $0xf,%eax
   0x00401304 <+20>:    shr    $0x4,%eax
   0x00401307 <+23>:    shl    $0x4,%eax
   0x0040130a <+26>:    mov    %eax,-0x4(%ebp)
   0x0040130d <+29>:    mov    -0x4(%ebp),%eax
   0x00401310 <+32>:    call   0x401850 <_alloca>
   0x00401315 <+37>:    call   0x4013d0 <__main>
   0x0040131a <+42>:    movl   $0x403000,(%esp)
   0x00401321 <+49>:    call   0x4018b0 <printf>
   0x00401326 <+54>:    mov    $0x0,%eax
   0x0040132b <+59>:    leave
   0x0040132c <+60>:    ret
End of assembler dump.

逆アセンブルprintf()すると、同様の間接ジャンプが発生することに注意してください。

(gdb) disas printf
Dump of assembler code for function printf:
   0x004018b0 <+0>:     jmp    *0x4050f8     ; <<-- indirect jump
   0x004018b6 <+6>:     nop
   0x004018b7 <+7>:     nop
End of assembler dump.

そして、その_imp__printfシンビオールはコードとしては意味がありません...

(gdb) disas 0x4050f8
Dump of assembler code for function _imp__printf:
   0x004050f8 <+0>:     clc                 ; <<-- how can this be printf()?
   0x004050f9 <+1>:     push   %ecx
   0x004050fa <+2>:     add    %al,(%eax)
End of assembler dump.

またはポインタとして...

(gdb) x/xw 0x4050f8
0x4050f8 <_imp__printf>:        0x000051f8  ; <<-- 0x000051f8 is an invalid pointer

main()それでは、にブレークポイントを設定して、それに実行してみましょう。

(gdb) break main
Breakpoint 1 at 0x40131a: file c:/temp/test.c, line 5.

(gdb) run
Starting program: C:\temp/test.exe
[New Thread 11204.0x2bc8]
Error while mapping shared library sections:
C:\WINDOWS\SysWOW64\ntdll32.dll: No such file or directory.

Breakpoint 1, main () at c:/temp/test.c:5
5           printf( "hello world\n");

printf()同じように見えます:

(gdb) disas printf
Dump of assembler code for function printf:
   0x004018b0 <+0>:     jmp    *0x4050f8
   0x004018b6 <+6>:     nop
   0x004018b7 <+7>:     nop
End of assembler dump.

しかし、_imp__printf見た目は異なります-動的リンクが修正されました:

(gdb) x/xw 0x4050f8
0x4050f8 <_imp__printf>:        0x77bd27c2

そして、現在指しているものを分解する_imp__printfと、あまり読みにくいかもしれませんが、明らかに今はコードです。これはprintf()、MSVCRT.DLLに実装されているとおりです。

(gdb) disas _imp__printf
Dump of assembler code for function printf:
   0x77bd27c2 <+0>:     push   $0x10
   0x77bd27c4 <+2>:     push   $0x77ba4770
   0x77bd27c9 <+7>:     call   0x77bc84c4 <strerror+554>
   0x77bd27ce <+12>:    mov    $0x77bf1cc8,%esi
   0x77bd27d3 <+17>:    push   %esi
   0x77bd27d4 <+18>:    push   $0x1
   0x77bd27d6 <+20>:    call   0x77bcca49 <msvcrt!_lock+4816>
   0x77bd27db <+25>:    pop    %ecx
   0x77bd27dc <+26>:    pop    %ecx
   0x77bd27dd <+27>:    andl   $0x0,-0x4(%ebp)
   0x77bd27e1 <+31>:    push   %esi
   0x77bd27e2 <+32>:    call   0x77bd400d <wscanf+3544>
   0x77bd27e7 <+37>:    mov    %eax,-0x1c(%ebp)
   0x77bd27ea <+40>:    lea    0xc(%ebp),%eax
   0x77bd27ed <+43>:    push   %eax
   0x77bd27ee <+44>:    pushl  0x8(%ebp)
   0x77bd27f1 <+47>:    push   %esi
   0x77bd27f2 <+48>:    call   0x77bd3330 <wscanf+251>
   0x77bd27f7 <+53>:    mov    %eax,-0x20(%ebp)
   0x77bd27fa <+56>:    push   %esi
   0x77bd27fb <+57>:    pushl  -0x1c(%ebp)
   0x77bd27fe <+60>:    call   0x77bd4099 <wscanf+3684>
   0x77bd2803 <+65>:    add    $0x18,%esp
   0x77bd2806 <+68>:    orl    $0xffffffff,-0x4(%ebp)
   0x77bd280a <+72>:    call   0x77bd281d <printf+91>
   0x77bd280f <+77>:    mov    -0x20(%ebp),%eax
   0x77bd2812 <+80>:    call   0x77bc84ff <strerror+613>
   0x77bd2817 <+85>:    ret
   0x77bd2818 <+86>:    mov    $0x77bf1cc8,%esi
   0x77bd281d <+91>:    push   %esi
   0x77bd281e <+92>:    push   $0x1
   0x77bd2820 <+94>:    call   0x77bccab0 <msvcrt!_lock+4919>
   0x77bd2825 <+99>:    pop    %ecx
   0x77bd2826 <+100>:   pop    %ecx
   0x77bd2827 <+101>:   ret
   0x77bd2828 <+102>:   int3
   0x77bd2829 <+103>:   int3
   0x77bd282a <+104>:   int3
   0x77bd282b <+105>:   int3
   0x77bd282c <+106>:   int3
End of assembler dump.

適切な記号が使用可能かどうか(またはGDBがそれらの記号を適切に読み取ることができるかどうか)がわからないため、おそらくあなたが望むよりも読みにくいでしょう。

ただし、別の回答で述べたように、オープンソースであるかどうかに関係なく、通常、コンパイラでCランタイムルーチンのソースを取得できます。MinGWにはWindowsのものであるためMSVDRT.DLLのソースは付属していませんが、Visual StudioディストリビューションでMSVDRT.DLLのソース(またはそれにかなり近いもの)を入手できます-無料のVC++Expressでさえ付属していると思いますランタイムソース(しかし、私はそれについて間違っているかもしれません)。

于 2010-04-02T07:33:24.903 に答える
0

printf()ほとんどの場合、動的共有ライブラリにあります。ダイナミックリンカは、インポートされた関数のアドレスでテーブルを埋めます。そのため、間接的に電話をかける必要があります。

これがどのように機能するかはよく覚えていません。最適化によってプロセスが複雑になる可能性があります。しかし、あなたはその考えを理解します。

于 2010-04-01T11:26:58.750 に答える