川の流れのように‥(Eiji Ota's Weblog)

金曜日 6 03, 2005

mdbの強力な助っ人CTF (でも、誰もみてくれない可哀想なやつ)‥の話

Solaris10の新機能ではありませんけれど、今日はSolaris9で導入されたmdbのCTF (Compact ANSI-C Type Format)についてお話したいと思います。 日頃お世話になっているツールですものね。クラフトマンは、ツールを大事にするものですもの。(^-^) 率直に言って、このCTFは、カーネルのデバックには相当な強力な味方になっていて、 一度これにはまると、CTF無しでデバックしたくなくなるほど、便利です。 またデバック時間もかなり節約してくれる、やりくり上手。開発者には必須のアイテムって言っても 良いくらいなんですね。でも、これが黒子、縁の下の力持ちなんですね。 そんな、日頃から陰で支えてくれているのに、陽の当たるところには出ない、CTFについて ちょっと紹介してあげたいって今回は思いました。^_^ oO (Yoroshiku!) CTF以前は、デバック情報を残すためにはコンパイラ・オプションで-gを指定するなどしなくて はなりませんでした。その結果、コンパイルされたカーネルは肥大化してしまい、 32bitの890Kバイト程のunixカーネルが、7Mバイト強まで膨れ上がってしまう有様で、 とても製品版のカーネルに使える状態ではありませんでした。 こんな大きいカーネルではロードにも時間がかかるし、格納媒体も増えてしまいますよね。 (でも、DVDだったら一枚で収まるのかな?) それに、CTF以前のデバックでは、adbのマクロや自分で作成したmdbのコマンドなどを使用して データ構造をクラッシュダンプから出力してやらなくてはならず、ソースが変更されたり、 別のバージョンのカーネルが使用されたりしてデータ構造に変更があったりすると、 表示された結果が妥当なものかすら分からないことがありました。 開発者としては、こっちも相当大きいですよね。adbのマクロやmdbのコマンドを その都度作成していくのは、大変手間暇がかかって、うんざりすることもままありました。 (今だから言える! えへへへへ‥) 結果、各開発者は各々お手製のコマンド、ライブラリを持つことになるんですけれど‥。 (まぁ、自然な流れとして‥) こういう問題を解決するため、C言語のシンボル情報、静的変数、大域変数の情報、リターン値、 引数情報などを効率良くカーネルに残すことを目的に、CTFが作られたわけです。 偉い、さすがっ! って一応エールを送りませう。これを開発したのは、Mike Shaprioさんと Matt Simmonsさんですね。Mikeさんは、元々mdbの開発者です。 そして、MattさんはSolaris10でkmdbを開発しています。(パチパチ "-") CTFの情報に加えて、不要な情報の削除、重複情報の削除、圧縮などを施した結果、 平均して1-2%の増加でデバック情報をカーネルに含ませることができました。 これなら、いつもカーネルに含ませることができますね。(うん、グットだぁ) CTF情報はコンパイル時に作成され、ELFセクションに一度格納された後、 リンカーでロードされる時に、各々のモジュールの持つ情報はマージされて、メモリーにロードされます。 パニックが発生した場合に、自動的にこれらの情報がクラッシュダンプに入るので、 デバック時には動作中のカーネルそのものの情報が得られます。 この仕組みにより、ソース変更や、カーネル・バージョンを気にせずデバックが可能になりました。 そして、Solaris9からmdbはこのCTF情報を使用しており、次のコマンドが導入されています。 ・sizeコマンド ・offsetofコマンド ・printコマンド それでは、ちょっとこれらのコマンドを使うとどういうことになるか、見てみましょうね。(うんうん) まず、psコマンドで、パニック発生時に動いていたプロセスをリストアップさせて、 この中のautomoutdのプロセス構造体を表示させてあげましょう。

> ::ps 
S    PID   PPID   PGID    SID    UID      FLAGS             ADDR NAME
R      0      0      0      0      0 0x00000001 0000000001842d30 sched
R      3      0      0      0      0 0x00020001 0000030005089b90 fsflush
R      2      0      0      0      0 0x00020001 000003000508a778 pageout
R      1      0      0      0      0 0x42004000 000003000508b360 init
R 101898      1 101898 101898      0 0x42000000 0000030019763398 automountd
R 101881      1 101881 101881      0 0x42000000 000003000f2197f8 nscd
                     :
automountdのプロセス構造体のアドレスは0x30019763398ですから、 このアドレスをprintコマンドに指定させてあげるだけで、プロセス構造体の中身を見ることができます。
> 0000030019763398::print proc_t
{
    p_exec = 0x3001280bd00
    p_as = 0x300070617a8
    p_lockp = 0x300063f2a00
    p_crlock = {
        _opaque = [ 0 ]
    }
    p_cred = 0x300127cf350
    p_swapcnt = 0
    p_stat = '\002'
    p_wcode = '\0'
    p_pidflag = 0
    p_wdata = 0
    p_ppid = 0x1
    p_link = 0
    p_parent = 0x3000508b360
    p_child = 0
    p_sibling = 0x3000f2197f8
              :
また、プロセス構造体のサイズが知りたい時は、sizeofコマンドを使用して、
> ::sizeof proc_t
sizeof (proc_t) = 0xbd0
とやってやります。 例えば、プロセス構造体の中にあるas構造体のオフセットが知りたければ、offsetコマンドを使用して、
> ::offsetof proc_t p_as
offsetof (proc_t, p_as) = 8
とやってやります。 これらのコマンドは、皆クラッシュダンプに格納されているCTF情報を参照してデータ構造(上の例だとプロセス構造体)を把握しているわけですね。実に便利っ! このprintコマンドは、多彩なオプションを実はもっていて、非常に便利。このprintコマンドだけでもかなりデバックがサクサク進む‥。ありがたや、ありがたや。
> ::help print

NAME
  print - print the contents of a data structure

SYNOPSIS
  [ addr ] ::print [-aCdhiLptx] [-c lim] [-l lim] [type] [member|offset ...]

DESCRIPTION
  
  -a         show address of object
  -c limit   limit the length of character arrays
  -C         unlimit the length of character arrays
  -d         output values in decimal
  -h         print holes in structures
  -l limit   limit the length of standard arrays
  -L         unlimit the length of standard arrays
  -n            don't print pointers as symbol offsets
  -p         interpret address as a physical memory address
  -t         show type of object
  -i         interpret address as data of the given type
  -x         output values in hexadecimal
  
  type may be omitted if the C type of addr can be inferred.
  
  Members may be specified with standard C syntax using the
  array indexing operator "[index]", structure member
  operator ".", or structure pointer operator "->".
  
  Offsets must use the $[ expression ] syntax

ATTRIBUTES

  Target: kvm
  Module: mdb
  Interface Stability: Evolving
例えば、-taオプションを指定してやると、先の例は次のようになります。
> 0000030019763398::print -ta proc_t
{
    30019763398 struct vnode *p_exec = 0x3001280bd00
    300197633a0 struct as *p_as = 0x300070617a8
    300197633a8 struct plock *p_lockp = 0x300063f2a00
    300197633b0 kmutex_t p_crlock = {
        300197633b0 void *[1] _opaque = [ 0 ]
    }
    300197633b8 struct cred *p_cred = 0x300127cf350
    300197633c0 int p_swapcnt = 0
    300197633c4 char p_stat = '\002'
    300197633c5 char p_wcode = '\0'
    300197633c6 ushort_t p_pidflag = 0
    300197633c8 int p_wdata = 0
    300197633cc pid_t p_ppid = 0x1
    300197633d0 struct proc *p_link = 0
    300197633d8 struct proc *p_parent = 0x3000508b360
    300197633e0 struct proc *p_child = 0
    300197633e8 struct proc *p_sibling = 0x3000f2197f8
    300197633f0 struct proc *p_psibling = 0
                    :
このprintコマンドだけを見ても、CTFが如何に便利か分かりますよね。 でも、普段は誰からも意識されない存在。ちょっと可哀想。^^; 皆さん、mdbを使った時に、是非CTF君のことをちょっと思い出してあげて下さいね。(^-^)v それではよい週末を。

Comments:

Post a Comment:
  • HTML Syntax: NOT allowed

Calendar

Feeds

Search

Links

Navigation

Referrers