ライブラリの中で cgo が使われていて Go のバイナリが別環境で実行できなかった事案

事案

Go アプリケーションをビルド&デプロイする際、 Circle CI 上でテストしてビルド -> S3 にアップロード -> サーバはS3 からダウンロードして起動というフローにしている。今まで golang:1.10 の dockerイメージでビルドしていたのを Go のバージョンアップに伴って golang:1.12 でビルドするようにしてデプロイしたら、下記のエラーに直面して起動できなかった。

xxxx: /lib/x86_64-linux-gnu/libc.so.6: version GLIBC_2.28 not found (required by xxxx)

で実行時エラーになった。

調査

file で見てみると、今回ビルドしたものは下記のようになり、

$ file xxxx
xxxx: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=67540d7247528874fca3131e686cc24782486904, not stripped

いままで動いてたものは下記のような出力が得られた。

$ file xxxx
xxxx: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=8130beb7e01b4efde5d7def420f361b7c6e8ac50, with debug_info, not stripped

リンクされたライブラリのバージョンが違う模様。あと思い出したんだが Go 1.4 あたりから動的リンクになるんだった。続いて ldd と objdump で調査する。

$ ldd xxxx
./xxxx: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.28' not found (required by ./xxxx)
        linux-vdso.so.1 =>  (0x00007ffc5816c000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f59dd670000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f59dd46c000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f59dd0a2000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f59dd88d000)


$ objdump -T xxxx
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.28  fcntl64
〜略〜
0000000000c14cd0 g    DF .text  0000000000000028  Base        sqlite3_set_last_insert_rowid
000000000045d9c0 g    DF .text  0000000000000005  Base        main

どうやら go-sqlite3 で cgo が使われているのでそれが libc に依存していて fcntl64@@GLIBC_2.28 のシンボルを参照している模様。

対応

go-sqlite3 に手を入れるか libc のバージョンを変えるなどが対応として考えられるが、 go-sqlite3 はこのプロジェクトではもう使っていなかったので go-sqlite3 を削除してビルドしなおして対応完了。

$ ldd xxxx
        linux-vdso.so.1 =>  (0x00007ffd075fa000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f4ef81dc000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4ef7e12000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f4ef83f9000)


$ file xxxx
xxxx: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, not stripped

まとめ

Go をビルドしたあとにバイナリ配布している場合は注意する。

おわり