或阿呆のブログ

Pythonを好んで使っているプログラマです。Ruby,Perl,PowerShell,VBAなどでもたまに書いています。最近はスロット放浪記やってます。。

C言語のオブジェクトファイルのシンボルを覗いてみる。

Hello,World!よりも重要なこと?2 Makefile - oneshotlife_tomの日記

以前、Makefileを作って簡単なプログラムを作ったけど、もう一歩レベルの高いプログラムを書きたくなった。

Makefile

$cat Makefile 
program:main.o sayhello.o
	gcc -o program main.o sayhello.o
main.o:main.c
	gcc -c main.c
sayhello.o:sayhello.c
	gcc -c sayhello.c

clean:
	 rm -f *.o *~ program

簡単に説明すると、

  1. programってのがターゲットとなる実行形式のファイル。
  2. main.cってのがmain関数があるソース
  3. sayhello.cは"Hello,World!"と出力する関数。
  4. sayhello.hはsayhello.cのヘッダ。
  5. xxx.oはそれと同名のソースをコンパイルしたオブジェクト。

makeコマンドを叩けば、programがソースをコンパイルし、オブジェクトが生成され、リンクしてターゲットが生成される。

$ll
total 16
-rw-r--r-- 1 tom tom 163  130 22:55 Makefile
-rw-r--r-- 1 tom tom 108  130 22:56 main.c
-rw-r--r-- 1 tom tom  78  130 22:51 sayhello.c
-rw-r--r-- 1 tom tom  40  130 22:53 sayhello.h
$make
gcc -c main.c
gcc -c sayhello.c
gcc -o program main.o sayhello.o
$ll
total 32
-rw-r--r-- 1 tom tom  163  130 22:55 Makefile
-rw-r--r-- 1 tom tom  108  130 22:56 main.c
-rw-r--r-- 1 tom tom 1068  130 23:06 main.o
-rwxr-xr-x 1 tom tom 7416  130 23:06 program
-rw-r--r-- 1 tom tom   78  130 22:51 sayhello.c
-rw-r--r-- 1 tom tom   40  130 22:53 sayhello.h
-rw-r--r-- 1 tom tom 1036  130 23:06 sayhello.o

make前後で、オブジェクトとターゲットが生成されているのがわかる。

make cleanコマンドを叩けば、オブジェクトやメインプログラムが削除される。makeの逆だね。といっても、cleanを定義しているからそういう動きになるわけだけどね。慣習的にcleanはロードモジュールやオブジェクトを削除するのに使われるようだ。

$ll
total 32
-rw-r--r-- 1 tom tom  163  130 22:55 Makefile
-rw-r--r-- 1 tom tom  108  130 22:56 main.c
-rw-r--r-- 1 tom tom 1068  130 23:06 main.o
-rwxr-xr-x 1 tom tom 7416  130 23:06 program
-rw-r--r-- 1 tom tom   78  130 22:51 sayhello.c
-rw-r--r-- 1 tom tom   40  130 22:53 sayhello.h
-rw-r--r-- 1 tom tom 1036  130 23:06 sayhello.o
$make clean
rm -f *.o *~ program
$ll
total 16
-rw-r--r-- 1 tom tom 163  130 22:55 Makefile
-rw-r--r-- 1 tom tom 108  130 22:56 main.c
-rw-r--r-- 1 tom tom  78  130 22:51 sayhello.c
-rw-r--r-- 1 tom tom  40  130 22:53 sayhello.h

オブジェクトの中を覗いてみよう。

ここで疑問がある。
main.c:

#include <stdio.h>
#include "sayhello.h"
int main(void)
{
	printf("[log]main\n");
	sayhello();
	return 0;
}

sayhello.h

#include <stdio.h>
void sayhello(void);

sayhello.c

#include "sayhello.h"
void sayhello(void)
{
	printf("[log]Hello,World!\n");
}

mainから呼ばれるのは、あくまでもsayhello.oの中にある関数なわけだ。sayhello.oが後進されても、ヘッダとシンボルの整合性が取れていれば呼び出せる。

んで、シンボルの中身がどうなっているんだ?と気になると思う。C言語だけのプログラムだとそれほど意識しないかもしれないが、C++からCを呼び出していたりすると、ネームスペースがあったりなかったりと、仕様が違うから、衝突回避のために、シンボルにも勘数名以外の情報が付いていたりする。

今回はC言語のみのプログラムなので、あまり重要性が伝わらないかもしれないが、一応シンボルを除いてみる。どうせだからターゲットである、programの中身を覗いてみようか?!

と言いつつも、まずは、
sayhello.o

$nm sayhello.o
         U puts
00000000 T sayhello

見方わからないんだけど、アドレス00000000んとこでsayhelloがあるってことか?!

sayhelloが呼ばれているところを覗いてみる。
main.o

$nm main.o
00000000 T main
         U puts
         U sayhello

推して察するべし・・・。何か呼ばれているねぇくらいしかわからない。

最後に大物の、ターゲットを覗いてみる。最初は、オプション無しで表示してみたんだけど、流れがわからなかったので、アドレスでソートして出力してみた。理解不能で説明しにくいところもあるが、いろいろ呼ばれているのがわかる。もちろん、mainもsayhelloも呼ばれている。
program

$nm --numeric-sort program
         w _ITM_deregisterTMCloneTable
         w _ITM_registerTMCloneTable
         w _Jv_RegisterClasses
         w __gmon_start__
         U __libc_start_main@@GLIBC_2.0
         U puts@@GLIBC_2.0
080482b0 T _init
08048320 T _start
08048350 t deregister_tm_clones
08048380 t register_tm_clones
080483c0 t __do_global_dtors_aux
080483e0 t frame_dummy
0804840c T main
08048430 T sayhello
08048450 T __libc_csu_init
080484c0 T __libc_csu_fini
080484c2 T __i686.get_pc_thunk.bx
080484c8 T _fini
080484e0 R _fp_hw
080484e4 R _IO_stdin_used
08048620 r __FRAME_END__
08049f08 t __frame_dummy_init_array_entry
08049f08 t __init_array_start
08049f0c t __do_global_dtors_aux_fini_array_entry
08049f0c t __init_array_end
08049f10 d __JCR_END__
08049f10 d __JCR_LIST__
08049f14 d _DYNAMIC
0804a000 d _GLOBAL_OFFSET_TABLE_
0804a018 D __data_start
0804a018 W data_start
0804a01c D __dso_handle
0804a020 D __TMC_END__
0804a020 B __bss_start
0804a020 D _edata
0804a020 b completed.6382
0804a024 B _end

Binary Hacks

Binary Hacks ―ハッカー秘伝のテクニック100選

Binary Hacks ―ハッカー秘伝のテクニック100選

直接は関係のない書籍だけど、かなり低いレイヤーの情報を扱った書籍。上位レイヤーは流行り廃りなどがあって、今やっていることが廃れる可能性はあるが、Cとかその辺の仕組みは当分変わらないだろう。だから、当書籍に掲載されていることや、シンボルの読み方もいつか役に立つはず。プログラムの移植とか、リファクタリングするときとかに。