strings
普段何気なく使っている、stringと言う英語。プログラミングをする人なら 今更何を言うって感じだけど、普通の人は、どう取るのか?
辞書を調べたら、糸、ひも、弦とか繊維って意味らしい。そこから転じて、 文字が連なったのを意味する言葉に、stringなんてのを借りてきたのね。
弦楽器の事をストリングなんて言うね。今頃になって思い出したって事は、 狭い領域に閉じ込められていたって事か。ストリングスって複数形になると、 ギターとか三味線とか琴とかの合奏になるのかな。兄はそっち方面が得意 だから、聞いてみるか。
後は、弦と言ったら、弓の弦ですかね。幼少の頃、おじいちゃんに弓を 作ってもらって、遊んだ覚えがあるぞ。竹で本体が出来ていたのは記憶に あるけど、弦は何で作られていたのかなあ。麻かな、バイオリンの弓宜しく 馬の尻尾?
最近お目にかかった弦と言えば、 超弦理論 という恐ろしくむずかしいやつ。科学雑誌のニュートンで、1年に1回は取り上げ られる、人気題材。手を変え品を変えて説明してくれてるけど、いまだに ピンとこない。空想の世界だからかなあ。
strings
前回、オブジェクトファイル(実行ファイル)中に潜む、ASCII文字列を炙り出すのに stringなんてコマンドを使った。特殊?なエリアに潜む文字列も検索対象にするのに aオブションが必要だった。
改めて、manを眺めてみると、-a の意味は、オブジェクトファイルから初期化・ロードされたセクションのみをスキャンす るのではなく、ファイル全体をスキャンする って事らしい。ここまで、ちゃんと理解してたかと言うと、そうではなく、条件反射 である。
改めて、このあたりをどうやってるか、ソース嫁してみる。FreeBSDだとGNUの toolchainの一部なので、portsから入れないと、見られない。今回は幸いな事に armのクロス環境を作ったんで、/home/sakae/cross2/toolchain/binutils-2.21.1/binutils の中に入っていた。
Written by Richard Stallman <rms@gnu.ai.mit.edu> and David MacKenzie <djm@gnu.ai.mit.edu>. */
冒頭から、コメントで、簡単なman相当の説明が有り、最後に、なんとあの人が 書いたとなってた。まだ、mitに居た、若かりし頃の作品? それとも、偉大なる人に敬意を表して、メアドは永久使用可ってなってるの?
彼は1953年生まれ、MITを1975年に中退、1983年にGNUプロジェクトを始めているんで メアド使用は、MITの好意なんだろうね。
static bfd_boolean strings_object_file (const char *file) { filename_and_size_t filename_and_size; bfd *abfd; abfd = bfd_openr (file, target); : got_a_section = FALSE; filename_and_size.filename = file; filename_and_size.filesize = 0; bfd_map_over_sections (abfd, strings_a_section, & filename_and_size); : } static bfd_boolean strings_file (char *file) { : /* If we weren't told to scan the whole file, try to open it as an object file and only look at initialized data sections. If that fails, fall back to the whole file. */ if (!datasection_only || !strings_object_file (file)) { FILE *stream; stream = fopen (file, FOPEN_RB); if (stream == NULL) { fprintf (stderr, "%s: ", program_name); perror (file); return FALSE; } print_strings (file, stream, (file_ptr) 0, 0, 0, (char *) 0); :
こんな風に、構造を持ったものと、まっ平らなファイルとして扱うかを、より分けて 検索してるんだな。
ちょっと面白い所を見て行く前に、stringsの使い方。
$ arm-elf-strings -30 -tx a.out 1194c 0000000000000000bug in vfprintf: bad base 11b0c 0000000000000000
これ、-aが付いていないので、a.outファイルを構造を持ったものとして扱ってる。 30文字以上の表示可能文字列を検索。見つかったら、その位置をファイルの冒頭からの オフセットを付けて、16進数で表示しなさい。次は、-aを付けてみる。
1194c 0000000000000000bug in vfprintf: bad base 11b0c 0000000000000000 138c1 ../../../../../../../../toolchain/gcc-3.4.6/newlib/libc/sys/arm/crt0.S 13908 /home/sakae/cross2/build/gcc/arm-elf/arm-elf/newlib/libc/sys/arm 30f70 ../../../../toolchain/gcc-3.4.6/gcc/config/arm/lib1funcs.asm 30fad /home/sakae/cross2/build/gcc/arm-elf/gcc 30ffe ../../../../toolchain/gcc-3.4.6/gcc/config/arm/lib1funcs.asm 3103b /home/sakae/cross2/build/gcc/arm-elf/gcc :
使い方が分かった所で、本命の bfd_map_over_sections を追ってみる。
(gdb) r -30 a.out Starting program: /usr/local/cross2/bin/arm-elf-strings -30 a.out Breakpoint 1, bfd_map_over_sections (abfd=abfd@entry=0x288030c0, operation=operation@entry=0x804a8a0 <strings_a_section>, user_storage=user_storage@entry=0xbfbfe728) at ../../../../toolchain/binutils-2.21.1/bfd/section.c:1269 1269 {
どうやらセクション毎に指定された、オペレーションをやっているようだ。 どんなセクションが有るか、先回りして、arm-elf-readelf -S で、調べて みると、
Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .init PROGBITS 00008000 008000 00001c 00 AX 0 0 4 [ 2] .text PROGBITS 0000801c 00801c 0098f4 00 AX 0 0 4 [ 3] .fini PROGBITS 00011910 011910 000018 00 AX 0 0 4 [ 4] .rodata PROGBITS 00011928 011928 000204 00 A 0 0 4 [ 5] .eh_frame PROGBITS 00019b2c 011b2c 000004 00 WA 0 0 4 [ 6] .ctors PROGBITS 00019b30 011b30 000008 00 WA 0 0 4 [ 7] .dtors PROGBITS 00019b38 011b38 000008 00 WA 0 0 4 [ 8] .jcr PROGBITS 00019b40 011b40 000004 00 WA 0 0 4
アドレスオフセットの値を考慮すると、.rodataに有るっぽい。
#0 print_strings (filename=0xbfbfe956 "a.out", stream=stream@entry=0x0, address=71976, magiccount=magiccount@entry=516, magic=magic@entry=0x28818080 "Hello ARM, input pls.\n", stop_point=0) at ../../../../toolchain/binutils-2.21.1/binutils/strings.c:550 #1 0x0804a989 in strings_a_section (abfd=0x288030c0, sect=0x28811230, arg=0xbfbfe728) at ../../../../toolchain/binutils-2.21.1/binutils/strings.c:349
大体分かってきたぞ。hexdumpして確かめてみる。
$ hd -C -s 0x1194c a.out 0001194c 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 | | 0001194c 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 | | 0001195c 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000| 0001195c 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000| 0001196c 62 75 67 20 69 6e 20 76 66 70 72 69 6e 74 66 3a |bug in vfprintf:| 0001196c 62 75 67 20 69 6e 20 76 66 70 72 69 6e 74 66 3a |bug in vfprintf:| 0001197c 20 62 61 64 20 62 61 73 65 00 00 00 69 6e 66 00 | bad base...inf.| 0001197c 20 62 61 64 20 62 61 73 65 00 00 00 69 6e 66 00 | bad base...inf.|
構造を持ったファイルと言うと、昔のMacの、リソース・フォークとか データ・フォークなんてのを思い出すな。ResEditでバイナリーパッチするの 大好きだったよ。
結局、strings.cで大事な事は全部やってた。
/* Scan section SECT of the file ABFD, whose printable name is in ARG->filename and whose size might be in ARG->filesize. If it contains initialized data set `got_a_section' and print the strings in it. FIXME: We ought to be able to return error codes/messages for certain conditions. */ static void strings_a_section (bfd *abfd, asection *sect, void *arg) { filename_and_size_t * filename_and_sizep; bfd_size_type *filesizep; bfd_size_type sectsize; void *mem; if ((sect->flags & DATA_FLAGS) != DATA_FLAGS) return; : if (bfd_get_section_contents (abfd, sect, mem, (file_ptr) 0, sectsize)) { got_a_section = TRUE; print_strings (filename_and_sizep->filename, NULL, sect->filepos, 0, sectsize, (char *) mem); }
これ以上無いってぐらい適切なコメントが書いてあって、コードは、これだけ 見れば十分って事ですね。
/* The BFD section flags that identify an initialized data section. */ #define DATA_FLAGS (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS)
おまけで、これを見ておけ。このフラグが立ってるセクションだけが、検査 対象だからなって、冒頭の特等席に書いてあったよ。
更に、コードが書いてあるより先に、
static void strings_a_section (bfd *, asection *, void *); static bfd_boolean strings_object_file (const char *); static bfd_boolean strings_file (char *file); static void print_strings (const char *, FILE *, file_ptr, int, int, char *); static void usage (FILE *, int); static long get_char (FILE *, file_ptr *, int *, char **);
こんな先行宣言がしてあった。適切な名前が付いているんで、見るべき関数も 予測出来るってもんです。 rmsさん、めちゃくちゃ親切!
BFD
上で、BFDってのが出てきたけど、これは何? 図書館の司書宜しく自前で調べる。 手がかりを得るため、世界の百科事典を当ると、 BFDライブラリ - Wikipediaって事らしい。
GNUの根幹ライブラリィーっぽい。ファイルを構造を持ったものとして扱う ツール類か。使用実例が無いかと思って探してみたら、 BFDでデバッグ情報の取得 なんてのが、引っかかってきた。懐かしい所だ。この作者さん、ググルに拉致されて から、とんと消息を聞かないな。あそこは、ブラックホールだから、余り情報が 漏れ出さないのかな。
このライブラリィーは、gdbとかでも使われているとの事。今丁度ウブを立ち上げて いて、そこでarm用のgdbを作った残骸が残ってる。本当に有るか、残骸に潜って みる。
目指すは、上で出てきた、セクションのフラグの意味を知る事。 もろに、section.cに説明が有ったぞ。
. {* Tells the OS to allocate space for this section when loading. . This is clear for a section containing debug information only. *} .#define SEC_ALLOC 0x001 . . {* Tells the OS to load the section from the file when loading. . This is clear for a .bss section. *} .#define SEC_LOAD 0x002 . . {* The section contains data still to be relocated, so there is . some relocation information too. *} .#define SEC_HAS_CONTENTS 0x100
こんなのがソースに書かれていた。これって、man用の原稿ではなかろうか。 何やら、構文っぽい臭いがするぞ。
sakae@ub:/usr/share/man/man5$ for f in *gz > do > zcat $f | grep SEC_HAS_CONTENTS > done
説明が有りそうな、ファイルフォーマットのセクション(5)について、探索したけど 出てこなかった。他に有りそうな所は何処だ? そんなのは、ソース嫁ですかい?
こういう時は、ドキュメントがしっかりしてる、OpenBSDを当ればいいんだな。 見たけど、無かった。どうでもいいけど、OpenBSDのmanファイルってベタな ファイルなのね。リナみたいに、バイナリー文化は受け付けない、昔かたぎの unixをきちんと継承してるのね。(こう書くと、何時の時代に生きてるんねんと、 嘲笑されそう)
見方を変えるんだ。strigs.cのヘッダーファイルから、それらしいのを当たれ! それらしいったら、bfd.hだな。今度は、FreeBSDで調べてみる。
$ locate bfd.h /usr/local/include/bfd.h /usr/src/contrib/binutils/bfd/elf-bfd.h /usr/src/contrib/binutils/bfd/libbfd.h /usr/src/gnu/usr.bin/binutils/libbfd/bfd.h $ lv /usr/local/include/bfd.h : /* Tells the OS to allocate space for this section when loading. This is clear for a section containing debug information only. */ #define SEC_ALLOC 0x001 /* Tells the OS to load the section from the file when loading. This is clear for a .bss section. */ #define SEC_LOAD 0x002
なる程、ヘッダーファイルに出力するための原稿だった訳ね。そして、こんな 特殊なフラグは、世界中で10人しか使わないから、わざわざmanに載せる事は 必要ない。必要な人はヘッダー見るだろうしって事ね。やっぱりソース嫁重要。
libbfdを使ってバイナリから情報を吸い出す方法の調査とかも有るなあ。という事は、バイナリー八苦本 にも、何か載ってるかな?
調べたら、同じような例が載ってた。bfdの和訳がsraのサイトに有るって事だったけど、 既にリンク切れだった。バイナリー屋さんのあんちょこマニュアルを見つけた。 GNU Binary Utilities
strip
バイナリー本に、stripの例も出てたので、ソースとご対面しよう。
$ find . -name strip.c $
あれれ、無いぞ。そなら、
$ find . -name '*.c' | xargs grep strip : ./objcopy.c: {"strip-all", no_argument, 0, 's'}, ./objcopy.c: {"strip-debug", no_argument, 0, 'S'}, :
なんとなく、stripのオプションの説明っぽいのが書かれているから、きっと ここに収納されているんだろう。 でも、どうせなら、gdbに教えて貰おう。
$ gdb -q arm-elf-strip Reading symbols from arm-elf-strip...done. (gdb) b main Breakpoint 1 at 0x804b490: file ../../../../toolchain/binutils-2.21.1/binutils/objcopy.c, line 3969.
program_name = argv[0]; xmalloc_set_program_name (program_name); START_PROGRESS (program_name, 0); : if (is_strip) => strip_main (argc, argv); else copy_main (argc, argv); END_PROGRESS (program_name);
こんな風になってた。前処理と後処理は共通だから、共通処理をするとして、 strppとobjcopy、どちらで呼ばれるかで、分離してる訳ね。確かに、両者供 やってる事は、セクションの切り貼りだから、共通にしたくなるよね。 こういうのを、カメレオン・プログラミングって言うんだっけ?
ソースをざっと見、copy_fileが主役っぽい。必要ないセクションを飛ばして、 一時ファイルにコピー。最後に、renameしてる。bfdのライブラリィーを フルに活用してるな。
カメレオンは、その場の雰囲気で色を変えるけど、本体は一緒。今回のstripと objcopyも同じ事か確認。 ls -l すれば、ファイルサイズ同一で、リンク数が 2になってるんで、まあ、同じ物だわな。lsに頼らない、別の検証方法は?
$ arm-elf-size arm-elf-objcopy arm-elf-strip text data bss dec hex filename 756208 5424 17468 779100 be35c arm-elf-objcopy 756208 5424 17468 779100 be35c arm-elf-strip
まあ、同じと思っていいな。このsizeコマンドのオプションを眺めていたら、 あれそっくりの表示も出来る事を発見。
$ arm-elf-size --format=SysV arm-elf-objcopy arm-elf-strip arm-elf-objcopy : section size addr .interp 21 134512948 .note.tag 48 134512972 .hash 860 134513020 : .debug_ranges 57712 0 Total 3476535 arm-elf-strip : section size addr .interp 21 134512948 .note.tag 48 134512972 .hash 860 134513020 : .debug_ranges 57712 0 Total 3476533
これはもう、元祖bfdライブラリィーを余す所なく、使って位相。
$ cmp -x arm-elf-objcopy arm-elf-strip 000034e2 0c d0 000034e3 26 19 00003535 0c d0 00003536 26 19 : 000bba76 ad ac 000bba8f c3 c2 000bbaf3 c0 bf 000bbb03 a7 a6 000bbbbd 43 42 000bbbd7 fc fb 000bbc0b 83 82 000bbc25 fe fd :
外面は似てるけど、内面を探ると、異なっているなあ。(微妙にずれてるのも 観測出来るし)何でかな?
折角、良いライブラリィーが有るんだから、一般の人にも使えるように しておこう。そうすれば、組み込み屋さんから感謝されるだろうって寸法。
bfd/の下を覗いてみると、pdp11の時代のファイル構造が見えたり、Win用の coffフォーマットも扱えたりするみたい。そして山程のcpuも扱えるぞ。 まさに、意地で集めてみましたって感じがする。ああ、集めたんじゃなくて、 rmsに賛同して、寄贈されたんかな。