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に賛同して、寄贈されたんかな。

etc

jonesforthのgasによるコンパイルと -build-id オプションなど

Porting Richard Jones' FORTH to ARM