LD_PRELOAD を Mac で

昨日、大学の図書館で偶然 BINARY HACKS という以前から読んでみたかった本を見つけた。

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

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

ただ、残念なことに GNU/Linux での話が前提になってしまっていて、Mac OS X などの他のプラットホームでの話がほとんどなかった。
Valgrind とか strace, ltrace とかも Mac OS X で使いたいなー。


で、HACK #60 に「LD_PRELOAD で共有ライブラリを差し換える」というのがあったんだけど、これと同様なことを Mac OS X でやる方法がわかったから、ここに書いておく。
gethostname(3) を差し換えて、hostname(1) の挙動を変えようというストーリー。
差し換える gethostname のコードは原本のまんまで。

/* gethostname.c */
#include <stdlib.h>
#include <string.h>

int gethostname(char *name, size_t len)
{
  char *p = getenv("FAKE_HOSTNAME");
  if (p == NULL)
    p = "localhost";
  strncpy(name, p, len-1);
  name[len-1] = '\0';
  return 0;
}

何はともあれ、共有ライブラリとしてコンパイル

$ gcc -shared -fPIC -o gethostname.dylib gethostname.c

GNU/Linux の場合、

$ LD_PRELOAD=./gethostname.dylib FAKE_HOSTNAME=foo hostname
foo

となるらしいのだけど、Mac の場合は

$ DYLD_INSERT_LIBRARIES=gethostname.dylib DYLD_FORCE_FLAT_NAMESPACE=YES FAKE_HOSTNAME=bar hostname
bar

とする。
DYLD_INSERT_LIBRARIES が LD_PRELOAD にあたると考えていいと思う。
その次の DYLD_FORCE_FLAT_NAMESPACE ってのは、どうやら Mac ではデフォルトで two-level namespace という環境でビルドされているがために必要な環境変数らしい。
http://developer.apple.com/documentation/Porting/Conceptual/PortingUnix/compiling/chapter_4_section_7.html を読んでみたけど、一体 two-level namespace がどういうものかは分からなかった。
とにかく、two-level namespace だと DYLD_INSERT_LIBRARIES で指定したにもかかわらず、オリジナルの gethostname を呼んでしまうらしい。
そこで、シンボルの解決方法を強制的に flat namespace にするのが DYLD_FORCE_FLAT_NAMESPACE=YES というわけだ。
うぅむ、GNU/Linux の場合と違ってなかなかめんどくさいな。タイプ数的に。

もう一つの差し換える方法

あまり実践的な方法じゃないけど。
それは、プログラムをコンパイルするときに、リンカに -flat_namespace という引数を与えてやるという方法。

/* myhostname.c */
#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
  char name[128];
  gethostname(name, sizeof(name));
  puts(name);
  return 0;
}

こんな、hostname(1) もどきのプログラムを書いてみる。
そして、

$ cc -Wl,-flat_namespace -o myhostname myhostname.c
$ DYLD_INSERT_LIBRARIES=gethostname.dylib FAKE_HOSTNAME=baz ./myhostname
baz

こうやってコンパイル&リンクすることで、DYLD_FORCE_FLAT_NAMESPACE=YES といちいち指定する必要がなくなる。
そのバイナリが flat_namespace かどうか判定する方法はあるのかなぁ?otool で見れたりするんだろうか。


参考:
http://developer.apple.com/documentation/Darwin/Reference/ManPages/man1/dyld.1.html
http://developer.apple.com/documentation/Darwin/Reference/ManPages/man1/ld.1.html#//apple_ref/doc/man/1/ld