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

This year's Index

Home