FreeBSD の libgcc_s シンボリックリンクを作り直す2017年07月09日 13時30分59秒

/usr/lib からのシンボリックリンクが変わった為に、一般ユーザからファイルが読めなくなっている。そこで、リンクを針直してみた。

ここは root で作業。 まずは、print で実行コマンドの点検。

$ cd /usr/lib
$ ls -l *.so | nawk '$NF ~ /..\/..\/lib/{cmd="ln -sf " substr($NF, 6) " " $(NF-2);print cmd}
ln -sf /lib/lib80211.so.1 lib80211.so
ln -sf /lib/libalias.so.7 libalias.so
ln -sf /lib/libavl.so.2 libavl.so
ln -sf /lib/libbegemot.so.4 libbegemot.so
ln -sf /lib/libbsdxml.so.4 libbsdxml.so
ln -sf /lib/libcam.so.7 libcam.so
print を system コマンドに変えて、再度実行。
$ ls -l *.so | nawk '$NF ~ /..\/..\/lib/{cmd="ln -sf " substr($NF, 6) " " $(NF-2);system(cmd)}'
$
この後、/usr/bin/ld: cannot find -gcc_s のエラーは出なくなった。

前回次回

/usr/lib/libgcc_s.so へのアクセス2017年07月07日 11時55分49秒

libgcc_s.so が見付けられないのは /usr/lib からのシンボリックリンクが変わったからの様に見られる。
% ls -sld /usr/lib/libgcc_s.so /lib/libgcc_s.so.1
46 -r--r--r--  1 root  wheel  45468 Jul  3 20:51 /lib/libgcc_s.so.1
 0 lrwxr-xr-x  1 root  wheel     23 Jul  3 20:51 /usr/lib/libgcc_s.so -> ../../lib/libgcc_s.so.1

head でどうしたらファイルが読めるか実験をする。

% head -c5 /usr/lib/libgcc_s.so
head: /usr/lib/libgcc_s.so: Permission denied
% head -c5 `realpath /usr/lib/libgcc_s.so`
ELF%
head -c5 `ls -l /usr/lib/libgcc_s.so| nawk '{print $NF}'`
head: ../../lib/libgcc_s.so.1: No such file or directory
realpath で展開した後ならファイルを読めるが、シンボリックリンクを辿ると、駄目な様だ。realpath は realpath ライブラリ関数を呼ぶだけのコマンド。シンボリックリンクを展開し、実ファイルに展開できる。

まあ、最後のコマンドではリンクを手元で展開しては正しいファイルのパスでは無くなるが…。

stat と lstat はシステム関数で、こちらもそれに対応する stat コマンドが存在する。-L をつけると lstat の動作になり、シンボリックリンクを表示する。

% stat -L /usr/lib/libgcc_s.so
stat: /usr/lib/libgcc_s.so: stat: Permission denied

前回次回

libgcc_s が見付けられない理由を truss で追跡2017年07月05日 12時07分56秒

root では見付けられる /lib/libgcc_s.so.1 をなぜ uyota アカウントでは見付けられないのかを更に追跡。truss を使ってシステムコールを探る。
% truss /usr/bin/ld --eh-frame-hdr -dynamic-linker /libexec/ld-elf.so.1 --hash-style=both
--enable-new-dtags -m elf_i386_fbsd -o a.out /usr/lib/crt1.o /usr/lib/crti.o
/usr/lib/crtbegin.o -L/usr/lib a.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc
--as-needed -lgcc_s --no-as-needed /usr/lib/crtend.o /usr/lib/crtn.o | & grep gcc_s
open("/usr/lib/libgcc_s.so",O_RDONLY,0666)       ERR#13 'Permission denied'
open("/usr/lib/libgcc_s.a",O_RDONLY,0666)        ERR#2 'No such file or directory'
open("/usr/bin/../libdata/libgcc_s.so",O_RDONLY,0666) ERR#2 'No such file or directory'
open("/usr/bin/../libdata/libgcc_s.a",O_RDONLY,0666) ERR#2 'No such file or directory'
open("//lib/libgcc_s.so",O_RDONLY,0666)          ERR#2 'No such file or directory'
open("//lib/libgcc_s.a",O_RDONLY,0666)           ERR#2 'No such file or directory'
open("//usr/lib/libgcc_s.so",O_RDONLY,0666)      ERR#13 'Permission denied'
open("//usr/lib/libgcc_s.a",O_RDONLY,0666)       ERR#2 'No such file or directory'
-lgcc_swrite(2,"-lgcc_s",7)                              = 7 (0x7)
libgcc_s が読み込みように開けない。

同じコマンドを root で実行。

# truss /usr/bin/ld --eh-frame-hdr -dynamic-linker /libexec/ld-elf.so.1 --hash-style=both
--enable-new-dtags -m elf_i386_fbsd -o a.out /usr/lib/crt1.o /usr/lib/crti.o
/usr/lib/crtbegin.o -L/usr/lib a.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc
--as-needed -lgcc_s --no-as-needed /usr/lib/crtend.o /usr/lib/crtn.o | & grep gcc_s
open("/usr/lib/libgcc_s.so",O_RDONLY,0666)       = 9 (0x9)
open("/usr/lib/libgcc_s.so",O_RDONLY,0666)       = 3 (0x3)
こちらでは問題なく開いている。

最初に libgcc_s を調べたときもそうだったが、読み出しの権限は与えられている。

% ls -sld /usr/lib/libgcc_s.so /lib/libgcc_s.so.1
46 -r--r--r--  1 root  wheel  45468 Jun 30 02:51 /lib/libgcc_s.so.1
 0 lrwxr-xr-x  1 root  wheel     23 Jun 30 02:51 /usr/lib/libgcc_s.so -> ../../lib/libgcc_s.so.1
% head /usr/lib/libgcc_s.so
head: /usr/lib/libgcc_s.so: Permission denied
上記は、FreeBSD 11.1-RELEASE と、11.0-RELEASE のシンボリックリンクだった。

気になったので、10.2-RELEASE でも ls。

% ls -sld /usr/lib/libgcc_s.so
 0 lrwxr-xr-x  1 root  wheel     18 Feb 21 2016 /usr/lib/libgcc_s.so -> /lib/libgcc_s.so.1
% head /usr/lib/libgcc_s.so
?ELF...
10.x-RELEASE の時は、絶対パスだったのが、11.x-RELEASE になって、相対パスに変わっている。

前回次回

FreeBSD で root は gcc_s を拾え uyota は失敗2017年07月01日 11時35分12秒

一般ユーザで「/usr/bin/ld: cannot find -lgcc_s」のためリンク出来ない問題で悩んでいる。ちょっと横道にそれたが、libgcc について調べた。他の FreeBSD で報告されている問題とは若干違うようだ。

まずは、コンパイルとリンクを切り分ける。現在の環境は、もうすぐリリースされる FreeBSD 11.1-RELEASE相当。PRERELEASE だったり、RC だったするが、大した違いは無い。

% cat a.c 
int main(){ return 0; }
% cc -c a.c 
コンパイル自体に問題は無い。
% cc a.o
/usr/bin/ld: cannot find -lgcc_s
cc: error: linker command failed with exit code 1 (use -v to see invocation)
リンク時に失敗。
% cc -v a.o
FreeBSD clang version 4.0.0 (tags/RELEASE_400/final 297347) (based on LLVM 4.0.0)
Target: i386-unknown-freebsd11.1
Thread model: posix
InstalledDir: /usr/bin
 "/usr/bin/ld" --eh-frame-hdr -dynamic-linker /libexec/ld-elf.so.1 --hash-style=both --enable-new-dtags
-m elf_i386_fbsd -o a.out /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtbegin.o -L/usr/lib a.o -lgcc --as-needed
-lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/crtend.o /usr/lib/crtn.o
/usr/bin/ld: cannot find -lgcc_s
cc: error: linker command failed with exit code 1 (use -v to see invocation)

不思議なことに、root で実行すると全く同じコマンドなのに成功する。

$ cc -v a.o
FreeBSD clang version 4.0.0 (tags/RELEASE_400/final 297347) (based on LLVM 4.0.0)
Target: i386-unknown-freebsd11.1
Thread model: posix
InstalledDir: /usr/bin
 "/usr/bin/ld" --eh-frame-hdr -dynamic-linker /libexec/ld-elf.so.1 --hash-style=both --enable-new-dtags
-m elf_i386_fbsd -o a.out /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtbegin.o -L/usr/lib a.o -lgcc --as-needed
-lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/crtend.o /usr/lib/crtn.o
$  ls -l a.out
-rwxr-xr-x  1 root  uyota  5355 Jun 30 01:24 a.out
二つのアカウントのライブラリ関連の環境変数も調べてみたが、違いは見当たらない。

前回次回

FreeBSD での -lgcc_s の続きを調べる2017年06月13日 12時43分49秒

一般ユーザでリンク出来ない問題で悩んでいる。検索を続けると、lgcc_s は結構見付かる。大まかに分けると、Linux 関連の物、FreeBSD 関連だが gcc と clang が共存するもの。FreeBSD 関連だが、amd64 環境で、i386 版の作成に失敗する物、FreeBSD 関連で、base では問題無いようだが、ports でうまくいっていないもの等がある。

現状では 11.0-RELEASE から起きていた事。また、root ユーザでは問題ないこと。root ユーザと非 root ユーザの ld コマンドが同一な事。base でも ports からも gcc は一つも入っていない事は確認した。

libgcc は GCC の名がついているが、The GCC low-level runtime library によると、GCC が提供している低レベルライブラリみたいだ。GCC の実行環境を提供しているわけではない。

前回次回

FreeBSD で /usr/bin/ld: cannot find -lgcc_s2017年06月01日 12時42分58秒

cc がなぜだか動かなくなっている。11.0-RELEASE に更新した後からこうなっていた様だが、ながいこと気がつかなかった。
% cat a.c 
int main(){ return 0; }
% cc a.c 
/usr/bin/ld: cannot find -lgcc_s
cc: error: linker command failed with exit code 1 (use -v to see invocation)
gcc_s のライブラリ自体は見付けられる。
% ls -L /usr/lib/libgcc*so
/usr/lib/libgcc_s.so
% ls -l /usr/lib/libgcc*so
lrwxr-xr-x  1 root  wheel  23 May 23 02:51 /usr/lib/libgcc_s.so -> ../../lib/libgcc_s.so.1
なおも奇妙なことに、root だと、問題なくリンクできる。-v の表示を比べてみたが、root も自分も同じ出力。

次回

static 関数や static 変数をヘッダーに2015年09月19日 12時40分29秒

何故だか知らないが、チョコチョコ見付ける摩訶不思議な関数と変数。static 関数や static 変数が .h のヘッダファイルに書かれているのを良く見付ける。あちこちで見たことがあるし、消しても消しても誰かが復活させる。

static 関数や static 変数をヘッダファイルに書くのは大間違い。

C++ ではあれこれと新しい意味も増えた。基本の C 言語由来の static はこうだ。static 修飾子を付けると、関数や変数がコンパイル単位内でしか、見えなくなるのだ。extern を付けて、広域で使い回すのと逆になる。

さて、問題はここからだ。ヘッダーファイルのコンパイル単位はそれを #include する各々のファイルだ。何が起きるのかというと、ヘッダーファイルに書かれた static 関数と static 変数は各々のオブジェクトファイルに生成される。

つまり、ヘッダーファイルに書かれた static 関数と static 変数はそれを読み込むファイルの数だけ生成される事になる。同じ関数がいくつも定義されているのに何故エラーにならないかと言うと static 関数だからだ。staic 関数なので、各々の .c や .cpp を越えてリンカーが参照させることは無い。そのため、シンボルの重複が起きないのだ。ヘッダファイルの内容と .c や .cpp ファイルの内容はプリプロセッサに処理されてからコンパイラに渡される。そのため、コンパイラが見るのは大きなファイルが一つだけ。プリプロセッサにどのファイルからどこの行が読まれたかの情報は組み込まれているが、それらを元に、static 関数がヘッダーファイルにあるのを警告するコンパイラは、知る限りでは無い。

static 関数や static 変数をヘッダファイルに見付けたら、即刻修正の対象だ。厄介なことに、コンパイラやリンカーはこの問題を検出しない。また、間違ってはいても、問題の無い動作をすることが多いのも特徴だ。具体的な問題としては、コンパイルに掛かる時間が増える。リンカーも時間が掛かる。実行形式のファイルの大きさが増える。実行時のメモリの利用量も増えるなど多岐にわたる。

Purify でプログラムが起動しない2015年08月04日 11時10分12秒

Purify はオブジェクトファイルに検査の為のコードを埋め込み、実行時の各種問題の検査をしてくれる。プログラムのリンクの時間とプログラムの実行時間がとてつもなく遅いのが珠に傷なのだが、その検知力は他を追随しない。ゆっくりと時間を掛けてプログラムを生成し、時間に余裕をもってテストを行なう。

さて、久方に、Purify を使った。しかし、Purify を組み込んだプログラムが起動しない。エラーメッセージがいかの様に出る。

the two libraries might interfere with each other, leading to unpredictable results. This happens occasionally when you share the cache between multiple machines running the same version of the OS.
要約すると、「プログラムが同名の複数の実装のライブラリを組み込んでいる。」となる。

Purify を用いないとプログラムは起動する。Purify だと駄目。

ldd などを用いて詳しく調べると、libm が SunStudio とシステムの両方に参照されていた。プログラムは起動こそするものの、どちらの実装が使われるのかはそれこそ運次第。libm は数学系のライブラリなので枯れてはいるので、おそらく問題が表面化しないのだろう。

もし上記のエラーで困ったのだったら、-L にて指定されているライブラリのパスを調べたら良いだろう。

Solaris の fatal: relocation error:2013年10月04日 13時56分24秒

Solaris でプログラムを実行時に以下のエラーを残して急に落ちることがある。

ld.so.1: XXX fatal: relocation error: XXX: XXX: referenced symbol not found

XXX の部分は見付からなかった関数名だ。これが起こる原因はおそらくこの二つ。

まずは、リンク時と実行時のライブラリに不整合がある時になる。リンクされたバイナリを他のホストに移したが、同じライブラリが無かった場合に起こる。良く起こる場合としては、他所から共有ライブラリを使っているが、うっかり全てのホストにはインストールされていなかったなど。

そしてもう一つは、-lznodefs をリンカに渡して、解決仕切れなかったシンボルが存在したときに、起きる可能性がある。なぜ、起きる可能性があるかと遠回しな言い方をするかというと、実行時にこの関数を呼ばなければ、落ちることは無いからだ。

-lznodefs は名前から察する通り、解決仕切れないシンボルがあっても、プログラムのリンクを正しく終了できた事にしてしまうオプションだ。この事からも、本番環境に用いるプログラムでは、絶対に避けるべきオプションである。このオプションを使わないと、一つでも見付からないシンボルがあるとプログラムの生成は成功しない。

それではいつ使うかと言うと、やはり開発環境になる。共有ライブラリを用いたり、相互依存の醜いライブラリ群があると、いちいち再リンクをするのに時間と手間がかかる。開発環境で部分的な試験をしている時は、全部のコードが実行される事はほとんど無いので、業と呼ばれない部分を無視してしまうのだ。どうせ、本番環境では使わない邪道を用いて開発効率を上げているのだから、いちいち関係の無い細部に拘る必要もない。

しかし、時として見付からないシンボルがあって、実行時に落ちることがある。そんなときは、ldd -d を使って、どのシンボルが解決できていないかを探る。

-qlinedebug はあまり役にたたない2013年09月10日 12時31分51秒

AIX のコンパイラには -qlinedebug というオプションがある。デバッグオプションの -g とは別途用いる。

IBM のサイトをみると以下のように説明されている。

目的 デバッガー用に行番号およびソース・ファイル名の情報のみを生成する。 -qlinedebug が有効な場合、コンパイラーは最小限のデバッグ情報しか生成しないため、 結果として得られるオブジェクト・サイズは、 -g デバッグ・オプションを指定した場合に生成されるオブジェクトよりも 小さくなります。 デバッガーを使用してソース・コードをステップスルーすることができますが、変数情報を表示したり照会することはできません。 トレースバック・テーブルを生成させると、 行番号が組み込まれます。

つまり、デバッガで各々の行を実行できるが変数は見ることは出来ない。コードにも依るのだが、-qlinedebug-g に比べると、一割から二割程度のバイナリサイズを削減できる。なお、strip を使うと、七割から九割ぐらいのぜい肉をそぎ落とすことが出来る。実行ファイルのほとんどは余分な情報なのだ。

core ファイルを開くと、最後のトレースは見られるが変数が一切見られないので、確かに落ちた辺りは見られるが、既に痕跡は無くなっているのだ。core を調べる時に役に立つのがやはり、変数の値。最適化によって、直には見られなくても、大きな手がかりになるので、これが無くなってしまうのは痛い。

AIX では strip された実行ファイルからの core であっても、strip する前の実行ファイルを使えるらしい。もし、それが真実だとすると、linedebug を使うよりも strip した方が core は見やすいことになる。まだ、自身での経験はあまりないので、もっと見極める必要がある。