Strlcpyがない (gcc imageに)
docker でCファイルをコンパイルしようとしたら、strlcpyがなかった。
(標準なんじゃないんか!!)
GNU libcライブラリをインストールすれば、何とかなるっぽい。
公式サイト:https://sourceware.org/glibc/started.html
を回ったら、git clone https://sourceware.org/git/glibc.git
でクローンしてインストールするらしい
make install
でできる?英語読めぬ・・・ぐぬぬ。
make install
したら、よくわからんエラー出た。
Makeconfig:43: *** objdir must be defined by the build-directory Makefile. Stop.
ネットで検索する限り、設定忘れらしい。適当に調べた感じ設定の仕方は見つからんかったー。
クローンしたファイルに、README
があったため、閲覧。
INSTALL
ファイルにインストール方法が書いているらしい。助かる。
ん?そういえば、さっきのmake install
する前のセクションに設定がうんたらって書いてあるページがあったような。
うん、あった。設定方法がないんじゃなくて読み飛ばしておりました。
うーん設定がよーわからん。
ビルド用の空のディレクトリを作って、そのディレクトリをカレントディレクトリにして、クローンしたディレクトリの'configure'を実行する必要があるらしい。
また、エラー・・・なんぞ?
*** These critical programs are missing or too old: gawk bison
*** Check the INSTALL file for required versions.
必要なプログラムがないって怒られた。
今度は下に、書いてあった。
gawkとbisonをインストールした後、ふたたび、configureを実行。
またエラー。
*** On GNU/Linux systems the GNU C Library should not be installed into
*** /usr/local since this might make your system totally unusable.
*** We strongly advise to use a different prefix. For details read the FAQ.
*** If you really mean to do this, run configure again using the extra
*** parameter `--disable-sanity-checks'.
今度は--prefix=/usr
オプションを付けて実行。
うまくいったみたい。
config.status: creating config.make
config.status: creating Makefile
config.status: creating config.h
config.status: executing default commands
root@ef1964b9a3b0:/app/test/glibc-build# ls
Makefile bits config.h config.log config.make config.status
ここで、make install
すればいいのかにゃ?
make install
きたくさい。
待機。
・・・数分後
と思ったら、数時間後・・・。
(途中でmake install -j8
に変更)
はい、エラーw
make[2]: *** [Makefile:1332: /app/test/glibc-build/elf/ld.so] Error 1
make[2]: Leaving directory '/app/test/glibc/elf'
make[1]: *** [Makefile:484: elf/subdir_lib] Error 2
make[1]: Leaving directory '/app/test/glibc'
make: *** [Makefile:12: install] Error 2
ネットで調べた感じ、windowsのディレクトリにバインドした下でコンパイルしたことで、大文字と小文字の区別がなくなってエラーが出てるっぽいorz
以下のURLで紹介されている通りにやればよさそう。
fsutil.exe file setCaseSensitiveInfo <path> enable
コマンドを実行して大文字と小文字の区別を有効に。。。
エラー。
エラー: ディレクトリが空ではありません。
つまりやり直し。
きついから、一旦コンテナ削除して、別コンテナでglibcをインストールした後、別イメージで固めようかな、、、タハハ。
バインドしなければ、大文字小文字も関係ないはず。
とりあえずコンテナの起動。
docker run -dit --init --rm --name gcc gcc cat
docker exec -it gcc bash
で接続
さっきの依存しているプログラムをインストール
apt-get update && apt-get install -y \
gawk \
bison
今度は、wget
をつかって、なるべく簡潔にインストールしたい。そのあと、手順をDockerfileに固めたい。公式サイトに現在の安定バージョンは2.39
って書いてあるので、今回はそれを選択。今回は特にgzipとして保存する。ftpサーバーから対象のファイルを選択して、リンクのコピーを行い張り付けた。
wget <url>
https://ftp.gnu.org/gnu/glibc/glibc-2.39.tar.gz
(なぜか<url>を https://ftp.gnu.org/gnu/glibc/glibc-2.39.tar.gz で置き換えると投稿できなかった。よーわからんぬ。)
2.39
バージョンのglibがダウンロードされる。
tar xzf glibc-2.39.tar.gz
ダウンロードしたファイルを解凍。
mkdir glib-build
ビルド用のディレクトリを作成。
../glibc-2.39/configure --prefix=/usr
システム全体にインストールするするための設定を行う。
make install -j8
を実行。
なんか今回はめっちゃ早い。さっきは、3秒に1ファイルぐらいだったのに・・・。
さっきは、途中で電源設定いじったり、一回ctrl+c
で中断したり、したのが問題だったのか・・・?
それか、ルーターをIPv6対応のにしたからか?バインドしてないから?ポートフィルタリング溶かしてるから?なんでかはよくわからんな。とりますぐ終わりそうでうれしい。あ、あと配信見ながらコンパイルしていたからだろうか・・・?
なんてことを打ち込んでいるうちに終了・・・。さっきまでのはいったい何だったんだ。
と思ったら、またエラー。なんぞ?
make[2]: *** No rule to make target '/root/glib-build/mathvec/libmvec.so.1', needed by '/usr/lib64/libm.so'. Stop.
make[2]: *** Waiting for unfinished jobs....
make[2]: Leaving directory '/root/glibc-2.39/math'
make[1]: *** [Makefile:484: math/subdir_install] Error 2
make[1]: Leaving directory '/root/glibc-2.39'
make: *** [Makefile:12: install] Error 2
ネットで調べたら、make
してなくね?ということらしい。
はい、してませんでした。すみません。マニュアルにも書いてありました。わかりづらいよね?ね?
https://sourceware.org/glibc/manual/latest/html_node/Running-make-install.html#:~:text=You must first build the library (‘make’)%2C optionally check it (‘make check’)%2C switch the include directories and then install (‘make install’).
とりあえず、make
またエラー。読むの疲れたから、make check
してみる。
うーん、なんか遅いので一旦ディレクトリ削除して、
configure
make -j8
make -j8 check
を実行。
すると、makeは通ったものの、checkの時点で、エラーが。
修正方法が見つからなかったので一旦スルーして、
make -j8 install
を実行。
するとしばらくすると、*** stack smashing detected *** terminated
というエラーが出て終了した。
そのあとはls
してもecho
しても同じエラー。
コンテナから離れたら接続できなくなっちゃった。
調べたら、スタックオーバーランで強制終了しているらしい。けどずっと強制終了ってどういうコト?ナニモ調査ができぬ・・・。
おとなしく、make -j8 check
で止まっておけば・・・。
とりあえず、再実行してみる。
結果変わらず。
今更ながら、GNU libc ではなく、BSD libcを入れないといけないようである。orz
よく探したら、gccのイメージにBSDライブラリが入ってた。
ヘッダーファイルをインストールして、動的ライブラリへの検索パスを通せば晴れてstrlcpyが使えるようになりそう。
けど、どうやって?
動的ライブラリのパスについては、/etc/ld.so.conf
にあるみたい。
中身を見るとinclude /etc/ld.so.conf.d/*
のようになってたので、/etc/ld.so.conf.d
以下の設定ファイルをすべて読み込むようである。
読み込みたいライブラリlibbsd.so.0
は、/usr/lib/x86_64-linux-gnu/
にあった。
設定ファイルの一つに/usr/lib/x86_64-linux-gnu
の行があったので、ライブラリの検索には引っかかってるように見える。
find / -name "string.h"
を実行したら、3件見つかったが、
cat $(find / -name "string.h") | grep 'strlcpy'
で、strlcpy
の宣言を探したが見つからなかった。
apt search libbsd
で見つかったパッケージをそれっぽいものをいくつかインストールして確認したところ、libbsd-dev
がヘッダーファイルをインストールするものらしい。
インストールを実行apt install -y libbsd-dev
インクルードパスを通す前に、ちゃんと動的ライブラリが使えるかどうかをチェックする。
次のようにして、`main.c`を作成。
echo '
#include <stdio.h>
#include <string.h>
size_t strlcpy(char *dst, const char *src, size_t siz);
int main(void) {
strlcpy(NULL, NULL, 0);
return 0;
}
' > main.c
gcc main.c
を実行。コンパイルエラー。
/usr/bin/ld: /tmp/cc550dlV.o: in function `main':
main.c:(.text+0x14): undefined reference to `strlcpy'
collect2: error: ld returned 1 exit status
さっきの設定ファイルがちゃんと設定されていなかったのかと思い確認したが問題なさそう。
引数で指定してコンパイル。
gcc main.c -L /usr/lib/x86_64-linux-gnu/ -l bsd
コンパイルエラーはなかった。
生成されたファイルを実行./a.out
セグフォは起きたが、実行はできた。
つまり、ライブラリへのパスが通ってないように見える。
gcc main.c -l bsd
でも実行。問題なくコンパイルができた。
検索パスは指定されているが、libbsdを自動的に読み込む設定になっていないようである。
libbsdを自動的に読み込む設定はよくわからなかったからやーめた!
pkg-config とか使うと割と簡単にはなるっぽいけど、省略するような設定は見つけられなかった。
apt update && apt install -y libbsd-dev
を実行すると、ヘッダーファイル類がインストールされる。
#include <bsd/string.h>
のようにして、読み込むとstrlcpy
をコンパイルできるようになる。
実際のサンプルコード(main.c)
#include <stdio.h>
#include <bsd/string.h>
int main(void) {
strlcpy(NULL, NULL, 0);
return 0;
}
コンパイル
gcc -l bsd main.c
実行
./a.out
結果
Segmentation fault
コンテナ立ち上げるたびに、インストールするのは面倒なので、Dockerfileを作成
apt update
時にダウンロードしたファイルは削除したい。
find / -not -path "/proc/**" -fprint old.files
でapt update
前のファイル一覧をold.files
に書き出した後、同様に次のコマンドを実行して保存する。/proc/**
は関係ないので無視する。
find / -not -path "/proc/**" -fprint new.files
その後、diffして何が違うか見る。
diff old.files new.files
/var/lib/apt/lists
にファイルが増えていたため、これを削除して、apt install libbsd-dev
ができるかどうか、apt update
ができるかどうかを確認したところ問題なかった。
ので、rm -rf /var/lib/apt/lists/*
を最後に実行する。
もしもapt isntall libbsd-dev
をした後にも不要なファイルが増えるなら削除したいので、同様に確かめる。
消しても問題ない(gcc を使ってコンパイルできる)と判断できるファイルがlog
ファイルとman
ファイルぐらいしかなかったため、削除するのはやーめた。
DockerfileのベストプラクティスにCMDは毎回書けよ見たいなことが書いてあったような気がするので、一応書いておく。なぜ、cat
にしているかは、bash
だとSIGTERMを受け取っても終了しないため。
FROM gcc
RUN apt update && apt install -y \
libbsd-dev && \
rm -rf /var/lib/apt/lists/*
CMD [ "cat" ]
インデントおかしい気もするけど、面倒なのでスキップ。
docker image build . -t libbsd:latest
で、適当に名前とタグをつけてビルド。
docker run -it --init libbsd bash
で、起動。
再び、さっきのファイルをコンパイルして、動作確認。
次のようなdocker-compose.ymlファイルを作成。(例)
services:
gcc:
container_name: hoge
image: libbsd:latest
build:
context: ./docker/libbsd
volumes:
- type: bind
source: ./
target: /app
init: true
tty: true
command: "cat"
docker compose up -d
で立ち上げて、
docker compose -it exec gcc bash
で接続。
さっきコンパイルできることは確認したので、同じイメージかどうかをざっくりと検証。apt list | grep 'libbsd-dev'
これで、何とかstrlcpy
が使えるようになった。
ただ、bsd/string.h
のように読み込まないといけないので、移植性は低いような気もする。
bsdlibがインストールできない環境が本番環境だったら大変そうなので気を付けないと・・・。
今の環境はただの趣味だけど。
ldd
で生成した実行ファイルがどんな共有ライブラリとリンクしているかを確認したら、libcや他のものも混じっていた。ならば、どうにかして、-l bsd
などのオプションを設定しなくてもコンパイルできるようになるだろうと思った。
gcc -v
でコンパイルの詳細が見れた。
中に、-lc
の指定があった。つまり、どうにかして設定する方法がある・・・はず。
他にも Configured with
を発見。パスを確認したがソースコードは見つからなかった。
man gcc
を見る感じ特にそれらしいオプションは見つけられなかった。
多分、gcc
をインストールする際に設定したのだと思われる。
gcc の公式サイトから、Installationを踏んだら、それらしきページを見つけた。-lc
を指定するオプションがあるので、そこで一緒に-lbsd
をしようと思う。
お!次の関数を作成したら、gccコマンドをオーバーライドできた!!
gcc() {
command gcc $(pkg-config --cflags libbsd-overlay) $* $(pkg-config --libs libbsd-overlay);
}
/etc/profileにこの関数を書き込んでみたけど、うまく反映されなかった。なぜに?
とりあえず、ちゃんと反映された、/root/.bashrcに書き込んだ。
FROM gcc
RUN apt update && apt install -y \
libbsd-dev && \
rm -rf /var/lib/apt/lists/*
RUN echo ' \
function gcc() { \
command gcc $(pkg-config --cflags libbsd-overlay) $* $(pkg-config --libs libbsd-overlay); \
}; \
' >> /root/.bashrc
CMD [ "cat" ]
やったー!コンパイルされた―!
いぇええいい!
だがしかし・・・makeファイルでは反映されず・・・どうしてだよおおおおお!
まあ、ちょっとは楽になったということで良しとしましょうかねぇ・・・
むむ、もしや--with-stage1-ldflags
らへんを使うとライブラリを指定できる感じか?!
pkg-config --libs --cflags libbsd-overlay
を実行すると
-DLIBBSD_OVERLAY -isystem /usr/include/bsd -lbsd
のように取得できる。
この内容より、システムディレクトリを/usr/include/bsd
に
リンカーのオプションを-lbsd
にして、-DLIBBSD_OVERLAY
をおそらくプリプロセス時に指定すればいいと思われる。ちなみに、通常時に同じオプションを指定してコンパイルできることは確認済み。
--with-native-system-header-dir
が使えそう。
gccのコマンドを参考に、以下の行を追加してビルドしてみる。
extraConfigureArgs=''
extraConfigureArgs="$extraConfigureArgs --with-native-system-header-dir=/usr/include/bsd"
extraConfigureArgs="$extraConfigureArgs --with-boot-ldflags="-lbsd -static-libstdc++ -static-libgcc""
どうしてもうまくいかない。
まだ早かったようなので、また別の機会にでも頑張るとする。