Monday, April 20, 2009

opaque typeの表示

今朝は hariさん、jinmeiさんとWunderlich County ParkにHikingへいきました。7:15amにFirst Lutheran Churchに集合してそこから車で Wunderlich County Parkへ。雑談しつつてくてく1時間半ほど山歩き。
Menlo Parkに戻ってCafe Borroneで ブランチを食べました。そこで「opaque typeをdebuggerで見たい時ってどうするのがいいんですかね?」という話がでたので、ちょっと試してみました。
libraryなどで、内部では具体的な型が定義されているけど、使う側にはgenericなpointer(void *)としてしか見せていない場合です。例えばこんなかんじ。
# library側
% cat a.h
void* create_foo();
void set_foo(void* p, int i);
% cat liba.c
#include <stdlib.h>
#include <string.h>
#include "a.h"

struct foo {
  int i;
};

void* create_foo()
{
   void *p = malloc(sizeof(struct foo));
   memset(p, 0, sizeof(struct foo));
   return p;
}
void set_foo(void* p, int i)
{
   struct foo* fp = (struct foo*)p;
   fp->i = i;
}
% gcc -fPIC -o liba.o -c liba.c
% gcc -shared -o liba.so liba.o -lc
# libraryを使う側
% cat b.c
#include <stdlib.h>
#include "a.h"

int main(int argc, char *argv[])
{
  void* p = craete_foo();
  set_foo(p, argc);
  exit(0);
}
% gcc -g -o b.o -c b.c
% gcc -g -o b b.o -L. -la
% LD_LIBRARY_PATH=. gdb b
...
(gdb) break main
Breakpoint 1 at 0x400657: file b.c, line 6.
(gdb) run
Starting program: /tmp/g/b 

Breakpoint 1, main (argc=1, argv=0x7fffb44a6e08 "?\213J??\177") at b.c:6
6   void *p = create_foo();
(gdb) n
7         set_foo(p, argc);
(gdb) print p
$1 = (void *) 0x601010
(gdb) whatis p
type = void *
(gdb) whatis *p
type = void
(gdb) ptype p
type = void *
(gdb) ptype *p
type = void
(gdb) print *(struct foo*)p
No struct type named foo.
(gdb)
というかんじで p の中身をみることができません。defaultではset opaque-type-resolution onですが、liba.soのデバッグ情報がないのでどうしようもないのです。
単純な場合なら examine でダンプしちゃうのが簡単ですね。
(gdb) x/dw p
0x601010  0
(/dwでword(w)を10進(d)で表示)
もしliba.soのデバッグ情報つきが手にはいれば次のようにロードしてやればよいでしょう。
# デバッグ情報つき
% gcc -fPIC -g -o liba.g.o -c liba.c
% gcc -shared -g -o liba.g.so liba.g.o -lc
...
(gdb) info share
From                To                  Syms Read   Shared Object Library
0x00007f3ba9cd3a60  0x00007f3ba9ce9d44  Yes         /lib64/ld-linux-x86-64.so.2
0x00007f3ba9ad2520  0x00007f3ba9ad2648  Yes         ./liba.so
0x00007f3ba978de30  0x00007f3ba987ee88  Yes         /lib/libc.so.6
(gdb) add-symbol-file liba.g.so 0x00007f3ba9ad2520
add symbol table from file "liba.g.so" at
 .text_addr = 0x7f3ba9ad2520
(y or n) y
Reading symbols from /tmp/g/liba.g.so...done.
(gdb) print *(struct foo*)p
$2 = {i = 0}
(gdb) s
set_foo (p=0x601010, i=1) at a.c:18
18  struct foo *fp = (struct foo *)p;
(gdb) s
19  fp->i = i;
(gdb) s
20 }
(gdb) s
main (argc=1, argv=0x7fffb1ef1858 "?\033??\177") at b.c:8
8   exit(0);
(gdb) print *p
Attempt to dereference a generic pointer.
(gdb) print *(struct foo*)p
$4 = {i = 1}
このように p のさしている先を struct foo として解釈できるようになります。またstep実行もできますね。
デバッグ情報つきライブラリがつくれない/入手できない、でもopaque typeの中をみたいという場合は、型情報を含むオブジェクトをつくってロードしてみるとよいかと
% cat atype.c
struct foo {
  int i;
};
struct foo *fooptr;
% gcc -fPIC -g -o atype.o -c atype.c
% gcc -shared -g -o atype.so atype.o -lc
% LD_LIBRARY_PATH=. gdb b
...
(gdb) break main
Breakpoint 1 at 0x400657: file b.c, line 6.
(gdb) run
Starting program: /tmp/g/b 

Breakpoint 1, main (argc=1, argv=0x7fff8bd03668 "?;?\213?\177") at b.c:6
6   void *p = create_foo();
(gdb) n
7   set_foo(p, argc);
(gdb) info share
From                To                  Syms Read   Shared Object Library
0x00007fde83ae5a60  0x00007fde83afbd44  Yes         /lib64/ld-linux-x86-64.so.2
0x00007fde838e4520  0x00007fde838e4648  Yes         ./liba.so
0x00007fde8359fe30  0x00007fde83690e88  Yes         /lib/libc.so.6
(gdb) add-symbol-file atype.so 0x00007fde838e4520
add symbol table from file "atype.so" at
 .text_addr = 0x7fde838e4520
(y or n) y
Reading symbols from /tmp/g/atype.so...done.
(gdb) print *(struct foo*)p
$1 = {i = 0}
(gdb) s
8   exit(0);
(gdb) print *(struct foo*)p
$2 = {i = 1}
(gdb)
この場合はstep実行はできせんがstruct fooとして見ることはできるようになりました。
static linkされている場合だとどうでしょう。
# static library
% ar ruv liba.a liba.o
ar: creating liba.a
a - liba.o
% gcc -g -o b.static b.o liba.a
% gdb b.static
...
(gdb) break main
Breakpoint 1 at 0x400547: file b.c, line 6.
(gdb) run
Starting program: /tmp/g/b.static

Breakpoint 1, main (argc=1, argv=0x7fffedb324a8 "?+???\177") at b.c:6
6   void *p = create_foo();
(gdb) n
7   set_foo(p, argc);
(gdb) print p
$1 = (void *) 0x601010
(gdb) whatis p
type = void *
(gdb) whatis *p
type = void
(gdb) ptype p
type = void *
(gdb) ptype *p
type = void
(gdb) print *(struct foo*)p
No struct type named foo.
(gdb) info share
From                To                  Syms Read   Shared Object Library
0x00007f49e5914a60  0x00007f49e592ad44  Yes         /lib64/ld-linux-x86-64.so.2
0x00007f49e55cfe30  0x00007f49e56c0e88  Yes         /lib/libc.so.6
(gdb) add-symbol-file atype.so 0x800000000000
add symbol table from file "atype.so" at
 .text_addr = 0x800000000000
(y or n) y
Reading symbols from /tmp/g/atype.so...done.
(gdb) print *(struct foo*)p
$2 = {i = 0}
(gdb) n
8   exit(0);
(gdb) print *(struct foo*)p
$3 = {i = 1}

というかんじで適当なアドレスにadd-symbol-fileで型情報付きオブジェクトをロードしてやればよさそうです。

1 comment:

  1. なるほど、add-symbol-fileを使えばよかったんですね。gdbのマニュアルを見てこれが使えるかもとは思ったのですが、アドレスをどう指定するといいのかがよくわからなくて素通りしてしまってました。

    勉強になりました。ありがとうございます。

    ReplyDelete