いったい何の役に立つねんシリーズ。技術の無駄遣い担当の平野です。
今回は、CentOS の/lib64 のシンボリックリンクをどう書き換えるかをご紹介します。
はじめに
とあることから、システムの標準ライブラリ(.so)をすべて置き換えたくなりました。
/usr/lib64 にあるようなライブラリを全部一括で、です。
CentOS7 や 8 の場合、システムのライブラリは/lib64 から実体/usr/lib64 へのシンボリックリンクになっています。
新しいライブラリを置いたディレクトリへ/lib64 のシンボリックリンクを付け替えるのが楽そうです。
やってみる
まず思いつくのは、以下のような方法です。
最初は気にせず、これを実行しました。
# ln -sf /my-lib64 /lib64
しかし、あれれ。書き換わっていないようです。
# ls -ld /lib64
lrwxrwxrwx 1 root root 9 May 11 2019 /lib64 -> usr/lib64
システムの部分なので、変更途中でエラーになって変えられないのかなぁ、とか考えながら、とりあえず、別の方法を試してみます。
※お急ぎの方は解決方法 ④ へどうぞ。
削除して作成 (失敗)
上書きができないのなら、簡単に思いつくのは、削除して、新たに作成するという方法です。
やってみましょう。
# rm /lib64
# ln -s /my-lib64 /lib64
sh: /usr/bin/ln: /lib64/ld-linux-x86-64.so.2: bad ELF interpreter: No such file or directory
半分予想してはいましたが、システムのライブラリを削除してしまったので、ln コマンドが実行できなくなってしまいました。
では、LD_LIBRARY_PATH で指定してみたらどうでしょうか。
# LD_LIBRARY_PATH=/usr/lib64 ln -s /my-lib64 /lib64
sh: /usr/bin/ln: /lib64/ld-linux-x86-64.so.2: bad ELF interpreter: No such file or directory
効かないですね。
もはや、ls コマンドさえ、もう実行できません。
# ls
sh: /usr/bin/ls: /lib64/ld-linux-x86-64.so.2: bad ELF interpreter: No such file or directory
さようなら。。。
/lib64 がない状態でコマンドを実行する
システムのライブラリが使えない状態でコマンドが実行できるようにするには、static link でコンパイルしたコマンドを利用することを、まず思いつきます。
しかし、残念ながら CentOS7 の ln コマンドは static link にはなっておらず、いくつかの外部ライブラリを必要としていました。
# ldd /usr/bin/ln
linux-vdso.so.1 => (0x00007ffeaa98e000)
libc.so.6 => /lib64/libc.so.6 (0x00007f48229de000)
/lib64/ld-linux-x86-64.so.2 (0x00007f4822dac000)
自分で static link されたコマンドをコンパイルするのは負けた気分がするので、これは、最後の手段とすることで、少し調査してみることにしました。
まずは、他力本願で、全世界の人々に頼ろうと、自分の Facebook で軽くつぶやいてみました。すると、すぐに会社の同僚から反応が来ました。
LiveCD でやってみたら?
いやいやいやいやいやいや、それはダメです。最後です。負けです。ていうか、環境は Docker コンテナです。却下。
しばらくすると、予想もしていなかった、自転車仲間方面から反応がありました。
ld-linux でライブラリ指定すれば?
ほぉぉぉ。なんですかね、それは。さっそく試してみましょう。
解決方法 ① ld-linux を使う
ld-linux はさっきから No such file or directory と怒られている張本人です。
それで、ライブラリを指定するとは、どういうことだろう、と少し調べてみると、なんと、そのまま実行できるようです。
# /usr/lib64/ld-linux-x86-64.so.2
Usage: ld.so [OPTION]... EXECUTABLE-FILE [ARGS-FOR-PROGRAM...]
... 長い説明 ...
--list list all dependencies and how they are resolved
--verify verify that given object really is a dynamically linked
object we can handle
--inhibit-cache Do not use /etc/ld.so.cache
--library-path PATH use given PATH instead of content of the environment
variable LD_LIBRARY_PATH
--inhibit-rpath LIST ignore RUNPATH and RPATH information in object names
in LIST
--audit LIST use objects named in LIST as auditors
おーーー。どうやら、--library-path というのでライブラリの場所を指定しつつ、コマンドを実行できるようです。
LD_LIBRARY_PATH はだめでしたが、こちらはどうでしょうか。
# /usr/lib64/ld-linux-x86-64.so.2 --library-path /usr/lib64 /usr/bin/ln -s /my-lib64 /lib64
# ls -ld /lib64
lrwxrwxrwx 1 root root 9 Mar 25 02:42 /lib64 -> /my-lib64
すばらしい!!!
できました!!
解決方法 ② static link されたコマンドを作る
さて、もう、今なら負けた気分はしません。
勝ち誇った気持ちで、自作 static link コマンドバージョンも試しておきます。
こんなコードです。
ld-linux なんちゃらを調べるより速く書けます。
#include <unistd.h>
int main() {
symlink("/my-lib64", "/lib64");
return 0;
}
コンパイルします。
# yum install -y gcc glibc-static
# gcc -Wall -static -o my-ln my-ln.c
試します。
# rm /lib64
# ls -ld /lib64
sh: /usr/bin/ls: /lib64/ld-linux-x86-64.so.2: bad ELF interpreter: No such file or directory
# ./my-ln
# ls -ld /lib64
lrwxrwxrwx 1 root root 9 Mar 25 02:58 /lib64 -> /my-lib64
できました!!!
解決方法 ③ 用意された static link されたコマンドを使う
実は、悔しいので隠していたのですが、調べてる途中で OS に static link された ln コマンドが付属していることがわかりました。
90 年代頃は、sh やメンテに必要なコマンドが、/bin や/sbin の下に同名で static link された状態で置かれてしました。
最近は/usr/bin や/usr/sbin へのシンボリックリンクなので気づかなかったのですが、/sbin/sln というのがありました。
# rm /lib64
# ls -ld /lib64
sh: /usr/bin/ls: /lib64/ld-linux-x86-64.so.2: bad ELF interpreter: No such file or directory
# sln /my-lib64 /lib64
# ls -ld /lib64
lrwxrwxrwx 1 root root 9 Mar 25 03:05 /lib64 -> /my-lib64
素晴らしい!
解決方法 ④ そもそも ln だけでできたのだ
そもそも、ことの発端は
# ln -sf /my-lib64 /lib64
がなぜか効かなかったことでした。
問題があるのならエラーが出るはずで、エラーもなく静かに終わるのが少し気になります。
ということで、詳細を strace で追いかけてみることにしました。
何かエラーが出てるかもしれません。
# strace ln -s /my-lib64 /lib64
execve("/usr/bin/ln", ["ln", "-s", "/my-lib64", "/lib64"], [/* 8 vars */]) = 0
... snip ...
symlink("/my-lib64", "/lib64/my-lib64") = 0
... snip ...
+++ exited with 0 +++
え゛っ???
/lib64 の下に my-lib64 のリンクを作ってるじゃないですか!!
# ls -ld /usr/lib64/my-lib64
lrwxrwxrwx 1 root root 9 Mar 25 03:11 /usr/lib64/my-lib64 -> /my-lib64
確かにあります。。。
よく考えたら、cp も mv もこの動きですね。
対象がディレクトリへのシンボリックリンクだったので、置き換えではなく、そのディレクトリ配下にリンクを作ってしまっていた、と。
こういうとき、cp や mv コマンドには、対象がディレクトリの場合にもファイルのように扱うオプション「-T」があります。
ln コマンドもにもあるでしょうか。
# ln --help
... snip ...
-T, --no-target-directory treat LINK_NAME as a normal file always
... snip ...
ありますねぇ。
試してみます。
# ls -ld /lib64
lrwxrwxrwx 1 root root 9 Oct 1 01:15 /lib64 -> usr/lib64
# ln -sfT /my-lib64 /lib64
# ls -ld /lib64
lrwxrwxrwx 1 root root 9 Mar 25 03:18 /lib64 -> /my-lib64
なんと、簡単にできてしまいました。
終わりに
わかってみれば、簡単な話でした。。。
だいぶ、遠回りをしました。。。
こうして、技術の無駄遣い、いったい何の役に立つねんシリーズができあがっていくのです。
*本記事は @qualitia_cdevの中の一人、@hirachanさんに作成して頂きました。