_x/s
_のような印刷関数でGDBに追加の逆参照を行わせるにはどうすればよいですか?
_x/
_で明示的な逆参照を試みると、「汎用ポインタを逆参照しようとしました」というエラーが表示されます。 _x/
_を複数回使用すると機能します。使用するたびに暗黙的な逆参照が含まれるためですが、各中間結果をコピーして貼り付ける必要があるため、これは面倒です。
非常に便利なCプログラム_example.c
_を考えてみます。
_#include <stdio.h>
int main(int argc, char **argv) {
printf("argv[0] = %s\n", argv[0]);
}
_
これをビルドしてGDBにロードすると、argv
が0xc(%ebp)
に格納されていることがわかります。これは、その2つのdererenceが2番目の引数としてprintf
(つまり、26行目の0x4(%esp)
)内:
_$ gcc -o example example.c
$ gdb example
(gdb) disass main
Dump of assembler code for function main:
0x080483e4 <+0>: Push %ebp
0x080483e5 <+1>: mov %esp,%ebp
0x080483e7 <+3>: and $0xfffffff0,%esp
0x080483ea <+6>: sub $0x10,%esp
0x080483ed <+9>: mov 0xc(%ebp),%eax
0x080483f0 <+12>: mov (%eax),%edx
0x080483f2 <+14>: mov $0x80484e0,%eax
0x080483f7 <+19>: mov %edx,0x4(%esp)
0x080483fb <+23>: mov %eax,(%esp)
0x080483fe <+26>: call 0x8048300 <printf@plt>
0x08048403 <+31>: leave
0x08048404 <+32>: ret
End of assembler dump.
_
printf
で中断し、引数first
およびsecond
を指定してプログラムを実行します。
_(gdb) break *main + 26
Breakpoint 1 at 0x80483fe
(gdb) run first second
Starting program: /var/tmp/SO-attempt-to-dereference-generic-pointer/example first second
_
GDBで_argv[0]
_を出力しようとしましたが、「汎用ポインタ」エラーが発生します。
_Breakpoint 1, 0x080483e5 in main ()
(gdb) x/s **(0xc + $ebp)
Attempt to dereference a generic pointer.
_
ただし、「x/xw」を使用して手動で数回逆参照すると、最終的に_argv[0]
_(および_argv[1]
_)を出力できます。
_(gdb) x/xw 0xc + $ebp
0xbfffeba4: 0xbfffec34
(gdb) x/xw 0xbfffec34
0xbfffec34: 0xbfffedc8
(gdb) x/s 0xbfffedc8
0xbfffedc8: "/var/tmp/SO-attempt-to-dereference-generic-pointer/example"
(gdb) x/xw 0xbfffec34 + 4
0xbfffec38: 0xbfffee03
(gdb) x/s 0xbfffee03
0xbfffee03: "first"
(gdb)
_
しかし、これは面倒で間接的です(ポインタプログラミングはそうならないのですか?)
解決策は、ポインターを逆参照する前にポインターをキャストすることです。
たとえば、上記で中断したところから始めます。
(gdb) x/s **((char ***) (0xc + $ebp))
0xbfffedc8: "/var/tmp/SO-attempt-to-dereference-generic-pointer/example"
(gdb) x/s *(*((char ***) (0xc + $ebp)) + 1)
0xbfffee03: "first"
(gdb) x/s *(*((char ***) (0xc + $ebp)) + 2)
0xbfffee09: "second"
スタックアドレス0xc + $ebp
は、それ自体がそのスタックの場所の内容へのポインターであるため、char ***
ではなくchar **
が必要であることに注意してください。