mmap(2)
こちらとあちら
前回は、各所で使われるdlopenについて調べた。そして、それを支えているのが、mmapって事が分かった。これで十分じゃんと言う内部の声と、それだけじゃ話半分はおろか、やっと入り口に達しただけじゃんと言う声もある。
どうも後者が優勢のようなんで。。
こちら側ってかユーザーが存在する側を、ユーザーランド側と言う。普通にアプリを書いて、その中ににdlopenが有ったりする世界。
あちら側ってのはkernelね。何だか恐そう。間違った事をすると、すぐに怒られたりして。でも、正しいお願いをしてる限りは、忠実な執事のように、正確に動いてくれる。
正しくないお願いって、例えば、他人のアプリの実行状態を許可なく盗み見するとか、改変するとか、アプリを停止させてしまうとか、色々ある。ようするに、悪いウィルスの振る舞いね。
カーネルってのは、サーバーと思っていれば間違いない。サーバーだから、呼び出されるのをずっと待ってる。呼び出しの合図がシステムコールだ。ソフトウェアの割り込みを使って実現されている。
dlopenが、kernelに理解出来るようなシステムコールに変換されて、その結果がカーネルに伝わるのだ。システムコールには色々な種類があるが、今回はmmapって訳だ。
だから、両者を調べない限り、調べたうちに入らない。
こちら側のdlopen追跡準備
前回はコピペしたプログラムでlibm.soなんてファイルを使ってた。これを、いたずらでxxx.cなんていうテキストファイルに変更してみた。実行結果は
File not an ELF object
そうか、dlopenで受け付けてくれるのは、ELFファイルだけって理解出来る。こういうブラックボックスなチェックも、たまには良いものだ。エラーの数だけ、賢くなるからね。
さて、libm.soばかりを使っていると、自由度が上がらないので、自前のやつを用意しておく。 myadd.c
int myadd(int x){ return(x + x); }
これ以上簡単にならないってぐらい、簡単なやつ。
vbox$ cc -shared -fPIC -o myadd.o myadd.c
こんなオプションを付けて、コンパイルする。共有ファイル形式で、ロード位置は自由な形式ってやつだ。
vbox$ file myadd.o myadd.o: ELF 32-bit LSB shared object, Intel 80386, version 1 vbox$ nm myadd.o 20000018 c _DYNAMIC : 00001460 T myadd
一応、出来上がったオブジェクトファイルを確認。なお、同種のファイルでlibm.so なんてのがある。これは、複数のオブジェクトファイルを一つにまとめてしまったものだ。
soファイルがどんなファイルから作られているかは、下記のようにすれば分かる。
vbox$ nm /usr/lib/libm.so.10.1|grep ' F' 00000000 F b_exp__D.c 00000000 F b_log__D.c 00000000 F b_tgamma.c :
先程作ったやつも確認。余計なファイルも一緒にコンパイルされて、合成されてる。
vbox$ nm myadd.o | grep ' F' 00000000 F crtbeginS.c 00000000 F crtendS.c 00000000 F myadd.c
次は、dlopenを含んだ、カーネルへのお願いプログラムだ。
#include <unistd.h> // for sleep #include <dlfcn.h> int main(int argc, char **argv) { void *fd; int (*fp)(int), rv=0; sleep(1); fd = dlopen("./myadd.o", RTLD_LAZY); fp = dlsym(fd, "myadd"); rv = (*fp)(12); dlclose(fd); return (rv); }
出来上がったやつを確認。やっぱり、コンパイラーが勝手にファイルを追加してコンパイルしてる。オイラーが用意したのは、z.cだけだ。普通は真面目にファイル名を考えて、それらしい名前にするんだろうけど(コンパイル済のファイルも同様だろう)、そんな面倒な事は最初から放棄してる。デフォで出来上がるファイルはa.outなんで、なら入力はz.cぐらいでいいだろうって言う安易な考えです。(ヨイ子は真似しないでね)
vbox$ nm a.out | grep ' F' 00000000 F crt0.c 00000000 F crtbegin.c 00000000 F crtend.c 00000000 F z.c
そして、一緒に働いて欲しいsharedライブラリーの列挙。デフォのものしか、設定されていない。
vbox$ ldd ./a.out ./a.out: Start End Type Open Ref GrpRef Name 18c1e000 38c20000 exe 1 0 0 ./a.out 01442000 21453000 rlib 0 1 0 /usr/lib/libc.so.96.0 07a18000 07a18000 ld.so 0 1 0 /usr/libexec/ld.so
dlopenのやってる事
勿論、gdbにかけられるように、コンパイル時に-gをつけます(cc -g z.c)。 ハード屋さんだと、回路図を眺めて動きが分からない場合、通電して、オシロスコープで波形を追うのが常です。
ソフト屋さんだと、ソースをにらめっこ(だけ)して、動きを理解する? そういいう人は、きっと頭脳明晰な人でしょう。凡人は、ソフトウェア業界のオシロスコープであるgdbを使います。
Reading symbols from a.out...done. (gdb) b dlopen Function "dlopen" not defined. Breakpoint 1 (dlopen) pending. (gdb) r Starting program: /tmp/a.out Breakpoint 1, dlopen (libname=0x160af3a6 "./myadd.o", flags=1) at /usr/src/libexec/ld.so/dlfcn.c:57 57 if (flags & ~(RTLD_TRACE|RTLD_LAZY|RTLD_NOW|RTLD_GLOBAL)) {
見たい所にbreakpointをセットしてから、走らせます。なんか変な所にあるソースの部位が表示されました。ld.soですって。ですって言うけど、作ったアプリに、これ含まれていましたよ。
とりあえずステップ実行してくと
=> DL_DEB(("dlopen: %s: done (%s).\n", libname, failed ? "failed" : "success"));
こんな風景も現れてきました。なにせ楽しい観光旅行ですからね。
ここまでの調査で、ld.soとかに飛び込んで処理してる事が判明。ld.soって有名そうだから、manに有るかな?
NAME ld.so – run-time link-editor DESCRIPTION ld.so is a self-contained, position independent program image providing run-time support for loading and link-editing shared objects into a process's address space. It uses the data structures (see elf(5)) contained within dynamically linked programs to determine which shared libraries are needed and loads them at a convenient virtual address using the mmap(2) system call. ld.so is itself a shared object that is initially loaded by the kernel. LD_DEBUG When set, be verbose about what ld.so does. This variable is ignored for set-user-ID and set-group-ID executables.
run-time xxx ってのは、goのプログラムをコンパイルした時にもくっついてきたな。色々な裏方の仕事をやってくれる奴だった。普通のC語をコンパイルして出来た奴には、リンカーが裏方として付属するのか。
考えてみれば当然だな。今の場合ならmyadd.oをアプリが要求してるんだから、それを何とかしてカーネルに伝える必要が有る。それから、dlopenばかりでなく、libcなんかも、読み込み要求を起動時に出す必要がある。
なに、libcなんて、あらかじめアプリに組み込んでおけ? それじゃ図体が大きくなりすぎて無駄。だから、名前だけをアプリに登録しておいて、起動時に読み込む。そのために、カーネルは、アプリの起動時にld.soだけは、特別扱いして、読み込んで(起動も)してくれるんだな。
何やら LD_DEBUG
を、セットすると、詳細が見らるっぽい。
vbox$ LD_DEBUG=t ./a.out ld.so loading: 'a.out' exe load offset: 0x181f4000 flags ./a.out = 0x0 head ./a.out obj ./a.out has ./a.out as head examining: './a.out' loading: libc.so.96.0 required by ./a.out : dlopen: loading: ./myadd.o flags ./myadd.o = 0x0 head ./myadd.o obj ./myadd.o has ./myadd.o as head linking ./myadd.o as dlopen()ed head [./myadd.o] examining: './myadd.o' tail ./myadd.o protect RELRO [0x2483f000,0x24840000) in ./myadd.o doing ctors obj 0x51ad7e00 @0x4840480: [./myadd.o] dlopen: ./myadd.o: done (success). dlsym: myadd in ./myadd.o: 0x4840460 doing dtors obj 0x51ad7e00 @0x4840490: [./myadd.o] unload_shlib called on ./myadd.o unload_shlib unloading on ./myadd.o doing dtors
lisperっぽく変数をtに設定して、アプリを走らせたら、ld.soの挙動が表示された。先ほど観光してて、チラ見した風景も出てきた。こういう楽しみが有るから、病みつきになるな。
OpenBSD 宣伝モード 始まり
これもそれも、無色透明、底までクッキリ見えるOpenBSDのおかげです。リナだとこうはいかないでしょう。大体あちら様は、ライブラリーの類にgdbで潜り込むなんて、うんと鍛えたプロが、それなりの準備をしないといけませんから。OpenBSDだと、気が向いた時にtar玉を一つ展開するだけ。だれでも、素晴らしい観光が出来ます。
宣伝にはURLが付きもの。ソースのインストール方法は、例えば Notes about the source code (ページの最後の所)に説明が有る。実際のソースは、理研のわかめならぬ、 理研のソース からでもいいし、越前方面なら、越前蟹は高いので、 越前のソース でも良い。(こちら、いつの間にか復旧してた。)
宣伝モード終了
mmapを呼んでる所
上で見たように、ld.soは、システムコールmmapを呼び出しているって説明が有った。でも、今の所、その現場には遭遇していない。ld.soをガサ入れするか。ああ、家宅捜索ね。つい、察の小説を読みすぎなもので。
mmapを頼りにソースを探したら、如何にもって _dl_mmap
なんてのが引っかかってきたので、デカ(刑事)の勘で、張り込みしてみる。
Breakpoint 1, dlopen (libname=0x15a3d3a6 "./myadd.o", flags=1) at /usr/src/libexec/ld.so/dlfcn.c:57 57 if (flags & ~(RTLD_TRACE|RTLD_LAZY|RTLD_NOW|RTLD_GLOBAL)) { (gdb) b _dl_mmap Breakpoint 2 at 0x9eec874: _dl_mmap. (8 locations) (gdb) c Continuing. Breakpoint 2, map (d=0x5f8a7010, sz=703500580, zero_fill=0) at /usr/src/libexec/ld.so/malloc.c:457 457 p = MMAP(sz);
dlopenにBPを置いてから走らせる。これで頭出しする。次に目当ての場所にBPを置く。へぇ、ld.so用のmallocなんてのが有るのか。デカの定めで、ここまでの足取りを確認。
(gdb) bt #0 map (d=0x5f8a7010, sz=703500580, zero_fill=0) at /usr/src/libexec/ld.so/malloc.c:457 #1 0x09eecb7d in omalloc (sz=<optimized out>, zero_fill=<optimized out>) at /usr/src/libexec/ld.so/malloc.c:809 #2 0x09eecaf6 in _dl_malloc (size=8192) at /usr/src/libexec/ld.so/malloc.c:885 #3 0x09ef02d7 in _dl_opendir (name=0x47a80bc0 ".") at /usr/src/libexec/ld.so/dir.c:73 #4 0x09ef54e0 in _dl_find_shlib (sodp=0xcf7f31a0, searchpath=<optimized out>, nohints=0) at /usr/src/libexec/ld.so/library_subr.c:173 #5 0x09ef5aa0 in _dl_load_shlib (libname=0x15a3d3a6 "./myadd.o", parent=0x50a07e00, type=4, flags=0) at /usr/src/libexec/ld.so/library_subr.c:344 #6 0x09eebc1d in dlopen (libname=0x15a3d3a6 "./myadd.o", flags=1) at /usr/src/libexec/ld.so/dlfcn.c:83 #7 0x15a3e694 in main (argc=1, argv=0xcf7f3284) at z.c:9
更に詳細って事で、s する。ああ、sはgdbのコマンドね。決して察用語のSではありません(察の隠語でスパイの事をSと言います)。
static inline void * _dl_mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset) { B => return (void *)_dl___syscall(SYS_mmap, addr, len, prot, flags, fd, 0, offset); }
syscall.hです。機種依存の場所にあります。今使ってるのはi386なんで、そこを見ます 。同じ場所に、SYS.hが有り、それはアセンブラでした。そう、システムコールはC語では書けないんです。
#define DL_SYSCALL(n) \ .section ".text" ;\ .align 16,0xcc ;\ .global __CONCAT(_dl_,n) ;\ .type __CONCAT(_dl_,n),@function ;\ __CONCAT(_dl_,n): ;\ movl $__CONCAT(SYS_, n),%eax; ;\ int $0x80 ;\ jb .L_cerr ;\ ret
第一引数に、システムコール番号が入るみたい。で、その番号は
vbox$ grep SYS_mmap /sys/sys/syscall.h #define SYS_mmap 197
ついにやりましたね。こちら側でのシステムコール発行現場を同定しました。 後は、同じ要領で追うだけ。
それより、他の機種ではどんな風にシステムコールを発行してるのだろう。最近リナスが、新しいMACに搭載されたM1ってCPUでlinuxが動けばとか言って話題になってた。
M1の元となったARMな石 aarch64のそれを見物してみる。
#define DL_SYSCALL(n) \ .global __CONCAT(_dl_,n) ;\ .type __CONCAT(_dl_,n)%function ;\ __CONCAT(_dl_,n): ;\ RETGUARD_SETUP(__CONCAT(_dl_,n), x15) ;\ SYSTRAP(n) ;\ cneg x0, x0, cs /* r0 = -errno */ ;\ RETGUARD_CHECK(__CONCAT(_dl_,n), x15) ;\ ret
SYSTRAPって言う、非常に分かり易いマシン語表記になってる。INTEL遅れてるーーで、アプルが見切りを付けたのも分かる気がしますよ。あちらは意味不な int $0x80 ですからね。おまけに、呼び出し方が洗練されていない。
mmapの復習
改めてMMAPの仕様を復習しておく。manから引数を取り出してきた
void * 実際にマッピングされたアドレス mmap(void *addr, マッピングの開始アドレス(要求) size_t len, マッピングの長さ(通常は4096byteの倍数) int prot, マッピングのメモリー保護(R/W/E) int flags, マッピングが他のプロセスに見えるか等 int fd, ファイル指示子 off_t offset ファイルの先頭からのオフセット )
メモリーに対する要求と、ファイルに対する要求が出会いましたって趣。ファイルの内容を、メモリーに反映させる。
ファイル指示子で指定されるファイルの先頭からoffsetだけ進んだ所から、メモリーに張り付けろ。メモリー関係の諸元は、希望のアドレスから、指定した長さを使ってファイル内容を張り付ける(コピーされるイメージかな)。
そのメモリーエリアの保護は、読み・書き・実行のうちのどれ(複数の権利指定可)。そのエリアは、よそ様のプロセスが参照とかしても良い?あるいは、自分だけにしとく?
複雑だけど、分解して考えれば、納得ものです。
あちら側のmmap
さて、いよいよあちら側(kernel)の挙動を観察する時がやって来た。 まずは、起動時のごたごたを避ける為に、sleepのシステムコールであるnanosleepにBPを置いて、同期を取る。他にもやり方があるけど、面倒なのでこれが一番だ。オシロスコープを上手に使いこなせる人が、外部同期の方法を知ってる人だ。
(gdb) b sys_nanosleep Breakpoint 1 at 0xd068bb25: file /usr/src/sys/kern/kern_time.c, line 268. (gdb) c Continuing. Breakpoint 1, sys_nanosleep (p=0xd17c3008, v=0xf1d13aa4, retval=0xf1d13a9c) at /usr/src/sys/kern/kern_time.c:268 268 } */ *uap = v; (gdb) b sys_mmap Breakpoint 2 at 0xd0acd835: file /usr/src/sys/uvm/uvm_mmap.c, line 217. (gdb) c Continuing.
nanosleepでbreakしたら、今回の主役mmapにBPを置いてから継続する。
Breakpoint 2, sys_mmap (p=0xd17c3008, v=0xf1d13aa4, retval=0xf1d13a9c) at /usr/src/sys/uvm/uvm_mmap.c:217 217 } */ *uap = v;
やってきましたね。ソースが置いてあるのは、 uvm/uvm_mmap.c
って所に注目です。主要なカーネル機能は kern/ の中に置いてあるけど、mmapは、どちらかと言うとデバイスのコントロールっぽいので、幹部扱いされていないのね。
/* first, extract syscall args from the uap. */ addr = (vaddr_t) SCARG(uap, addr); size = (vsize_t) SCARG(uap, len); prot = SCARG(uap, prot); flags = SCARG(uap, flags); fd = SCARG(uap, fd); pos = SCARG(uap, pos);
mpamの中で、最初にやる事は、ユーザーランド側から送られてきた引数を荷ほどきする事です。 下記は、荷ほどきが終わった状態をモニターしたものです。
(gdb) p addr $9 = 170549248 (gdb) p size $10 = 4096 (gdb) p prot $8 = 5 (gdb) p flags $11 = 2066 (gdb) p fd $12 = 3 (gdb) p pos $13 = 4096
注目部分はprotの値ですかね。sys/mman.hにある
#define PROT_NONE 0x00 /* no permissions */ #define PROT_READ 0x01 /* pages can be read */ #define PROT_WRITE 0x02 /* pages can be written */ #define PROT_EXEC 0x04 /* pages can be executed */
と照らし合わせれば、読み込みと実行をお願いされてる事が分かります。
細かい部分は大いにすっ飛ばしてしまったけど、dlopenにまつわる、こちら側の挙動とあちら側の挙動を観察出来た。オイラーの一番の収穫は、ld.soと言う縁の下の力持ちの存在を知った事です。
次の希望は、カーネル内の殺人現場の検証ですかね。アプリがSEGVになると、カーネルがそのアプリを殺し、遺体をこちら側に放置するって挙動になる。
実世界だと、殺人及び死体遺棄事件として、大々的にに取り上げられる事件です。そんな事件が、パソコンの中で、時々発生しています。勿論、非はユーザー側にあるのがほとんどですけど、デカになった積りで、紐解いてみたいですな。
こちら側のごたごた
ここまでで大体dlopenの挙動が分かった。実験中で起動時のごたごたを見て見ぬふりをしてた。最後に、起動時のごたごたを調べておきたい。 前回やったktraceの結果からCALLしてる所を抜き出してを再掲する。
CALL execve(0xcf7e15c7,0xcf7e151c,0xcf7e1524) CALL mmap(0,0x3000,0<PROT_NONE>,0x1002<MAP_PRIVATE|MAP_ANON>,-1,0) CALL mmap(0,0x1000,0x3<PROT_READ|PROT_WRITE>,0x1002<MAP_PRIVATE|MAP_ANON>,-1,0) CALL mmap(0,0x1000,0x3<PROT_READ|PROT_WRITE>,0x1002<MAP_PRIVATE|MAP_ANON>,-1,0) : CALL mmap(0xe02f000,0x8f000,0x5<PROT_READ|PROT_EXEC>,0x812<MAP_PRIVATE|MAP_FIXED|__MAP_NOREPLACE>,3,0x20000) CALL mmap(0x2e00e000,0x2000,0x3<PROT_READ|PROT_WRITE>,0x812<MAP_PRIVATE|MAP_FIXED|__MAP_NOREPLACE>,3,0xaf000) : CALL nanosleep(0xcf7f9170,0xcf7f9160) CALL mmap(0,0x3000,0x3<PROT_READ|PROT_WRITE>,0x1002<MAP_PRIVATE|MAP_ANON>,-1,0) CALL mmap(0x533d000,0x1000,0x1<PROT_READ>,0x812<MAP_PRIVATE|MAP_FIXED|__MAP_NOREPLACE>,3,0) CALL mmap(0x533e000,0x1000,0x5<PROT_READ|PROT_EXEC>,0x812<MAP_PRIVATE|MAP_FIXED|__MAP_NOREPLACE>,3,0)
システムコールをのexecveが実行され、いよいよアプリが動き出す。その中で沢山mmapが実行されてる。アドレスが0からかつファイル指定の無いリクエストは、カーネルに適当な場所を確保してねってお願いだ。その後、確保したエリアにファイル内容が配置されている。 ここまでが、いわゆる起動時のごたごた部分。
その後は、sleep部分があってdlopenによるmmapの呼び出しが続いている。
じゃ、ごたごたは、どうやって実行されてる? 既に DL_DEBUG
のログ取得で結果が出てしまっているが、自分で考えてみる。
dlopenとそれ以外の起動時に暗黙のうちに呼び出されている(であろう)部分があるけど、きっとそれをデスパッチ(選り分け)してる部分が、ld.soの中に有るはず。それらしい部分を列挙させよう。
オシロスコープのプローブを当てて波形観測するにも、いわゆる当たりを回路図から読み取るのと一緒だ。更に極論すれば、ハードもソフトも一緒な事よ。戯言になるけど、聞いとくれ。
昔初めてデジタルICに触れた時、ICは2種類しか無かった(有名なテキサスインスツルメンツの74シリーズICが出る前の頃です)。NOR回路が2つ入ったICとRS-FLIPFLOPのIC。これを組み合わせて、時計とか作っていた。何桁ものカウンターが必要。それで、一桁の回路を収納するプリント板を起こした。後はそのプリント版を結合するだけ。
そう、プリント板が、ソフトウェアで言うサブルーチンに相当するんだなって気づいたよ。 それで視野が大いに広がったね。ただ、決定的なハードとソフトの違いは、ハードは間違う(いわゆるBUGね)と、煙は出るは火がでるわで大変な目に合うと言う事。その点、ソフトは楽なものだ。そんな事を言ってたら、ソフト業界の大変さを訴える企画がスタートしてた。 今年もスタート、「本番環境でやらかした失敗談」を毎日1人ずつさらけ出す人気企画
vbox$ grep dlopen -l *.c dlfcn.c library_subr.c loader.c resolve.c sod.c
loader.cが怪しそうなんで眺める。
(gdb) b _dl_dtors Function "_dl_dtors" not defined. Make breakpoint pending on future shared library load? (y or [n]) y Breakpoint 1 (_dl_dtors) pending. (gdb) r Starting program: /tmp/dlset/a.out Breakpoint 1, _dl_dtors () at /usr/src/libexec/ld.so/loader.c:198 198 _dl_thread_kern_stop(); (gdb) bt #0 _dl_dtors () at /usr/src/libexec/ld.so/loader.c:198 #1 0x0c2d0039 in _libc___cxa_finalize (dso=0x0) at /usr/src/lib/libc/stdlib/atexit.c:177 #2 0x0c2ce05f in _libc_exit (status=24) at /usr/src/lib/libc/stdlib/exit.c:54 #3 0x181f14c5 in ___start () #4 0x181f13da in _start ()
これかな?
(gdb) b _dl_boot Function "_dl_boot" not defined. Make breakpoint pending on future shared library load? (y or [n]) y Breakpoint 1 (_dl_boot) pending. (gdb) r Starting program: /tmp/dlset/a.out Breakpoint 1, _dl_boot (argv=0xcf7d8224, envp=0xcf7d822c, dyn_loff=60502016, dl_data=0xcf7d81dc) at /usr/src/libexec/ld.so/loader.c:468 468 if (dl_data[AUX_pagesz] != 0) (gdb) bt #0 _dl_boot (argv=0xcf7d8224, envp=0xcf7d822c, dyn_loff=60502016, dl_data=0xcf7d81dc) at /usr/src/libexec/ld.so/loader.c:468 #1 0x039b5abf in _dl_start () at /usr/src/libexec/ld.so/i386/ldasm.S:69 #2 0xcf7d8224 in ?? () #3 0xcf7d82cc in ?? ()
それとも、こちらが本流かな? 国土交通省に聞いてみようか。
そもそも、何でこんなに苦労しなくちゃいけないか? それは、ライブラリーをダイナミックロードする機構になってるからーー。
libcとか、みんなが使うやつは、アプリ毎に入れておく必要が無い。現地調達すれば十分。行ってQの定番、ジャングル探検で夕食は現地のゲテモノを食べるみたいなもの。
go言語が吐き出すバイナリーは、自分が使うものを一切喝采抱え込んでいる。自衛隊方式ね。戦場では都合よく食料等を調達出来るとは限らない。そんな都合良くいくなんて、はなから眼中にない。だから、一つの独立社会を丸ごと抱え込んでいる。よくニュースに登場するよね。災害時に、自衛隊の風呂屋が開設されるの。
go言語で作ったアプリは、それに内包されてるランタイムライブラリが、直接システムコールを行うようになってる。ソースが公開されてるので、眺めてみると、これまた楽しいぞ。
Linuxをお使いの皆さまへ
上での実験はOpenBSDで行った。世間を席巻してるリナにも言及しておく必要が有るな。
debian:tmp$ ldd ./a.out linux-gate.so.1 (0xb7fce000) libdl.so.2 => /lib/i386-linux-gnu/libdl.so.2 (0xb7f91000) libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7db3000) /lib/ld-linux.so.2 (0xb7fd0000)
こんな具合にライブラリーの要求リストが表示された。ld.soに相当するのは、ld-linux.so.2になる。manすると、
NAME ld.so, ld-linux.so - dynamic linker/loader
こんな具合に出て来た。つらつら見て行くと、このオプションはglibc x.x.xから使えるようになったとかって具合に、歴史を感じさせる記述がみられる。25年のひずみが凝縮されてるんですね。それはそうと、残念ながら、kernelとの連携であるmmapの事について、ほとんど触れていない。視点が違うのだな(あくまで、こちら側向けの記述だ)。 そして、意味不なlinux-gateは、カーネルに組み込まれたやつらしい。何処を探しても見つからない不思議な奴。システムコールを作るメカニズムをカーネルが公開してるものらしい。怪しい雰囲気がプンプンしますね、迷宮に迷い込まない事。
それから、ライブラリーが、何から作られたかと言う説明が無い。
debian:tmp$ nm myadd.o | grep ' F'
これはもう、そういう仕様なんだろうけど、後追いの手段を放棄してるとも言えるな。と思ったら、
debian:tmp$ strings myadd.o | grep myadd myadd myadd.c myadd
埋め込まれていた。探すのに苦労しそうだけど。
debian:tmp$ strings -n 2 a.out | grep '\.c$' crtstuff.c z.c