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で型情報付きオブジェクトをロードしてやればよさそうです。