ラズパイでSDLライブラリ無しでmomoを動かす(邪道)
動機
ラズパイにディスプレイをつながずにmomoを使ってカメラの映像をWebRTCで送信しています。OSにはRaspberry Pi OS Liteを使用しています。
ディスプレイをつないでいないので、GUIを使うことはありません。システムをシンプルに保つためにGUIのライブラリは入れたくありませんが、momoは起動時にオプションで指定するとSDLを使って画面表示をする機能を持っています。そのため、momoを起動するにはSDLのライブラリをインストールしておく必要があります。
$ ./momo
./momo: error while loading shared libraries: libSDL2-2.0.so.0: cannot open shared object file: No such file or directory
というエラーが出たときに
$ sudo apt install libsdl2-2.0-0
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
libasyncns0 libgbm1 libopus0 libpulse0 libsndfile1 libvorbisenc2 libwayland-client0 libwayland-cursor0
libwayland-egl1 libwayland-server0 libxcursor1 libxfixes3 libxi6 libxinerama1 libxkbcommon0 libxrandr2
libxrender1 libxss1 libxxf86vm1 x11-common
Suggested packages:
opus-tools pulseaudio xdg-utils
The following NEW packages will be installed:
libasyncns0 libgbm1 libopus0 libpulse0 libsdl2-2.0-0 libsndfile1 libvorbisenc2 libwayland-client0
libwayland-cursor0 libwayland-egl1 libwayland-server0 libxcursor1 libxfixes3 libxi6 libxinerama1 libxkbcommon0
libxrandr2 libxrender1 libxss1 libxxf86vm1 x11-common
0 upgraded, 21 newly installed, 0 to remove and 0 not upgraded.
Need to get 1,830 kB of archives.
After this operation, 5,139 kB of additional disk space will be used.
Do you want to continue? [Y/n]
ここでYを押せば済むことですが、そうすると使う予定のないたくさんのライブラリが芋づる式に山盛りでインストールされてしまいます。とはいえ、マイクロSDの容量は十分な空きがあるので、これをインストールせずに済まそうとするのはただの意地っ張りです。
この記事ではその意地を貫きます。
SDLライブラリをインストールせずにmomoを使うには以下の2つの方法があります。
(1) ソースコードを修正し、SDLライブラリを使っているところをコメントアウトしてビルドしたものを使用する。
(2) SDLライブラリの偽物を作り、それを使う。
(1)が正攻法ですが、この記事ではあえて邪道である(2)を選択しました。
必要なライブラリの調査
現時点でmomoを動かすのに足りないライブラリを調べます。
以下の4つであるとわかりました。
$ ldd ./momo |grep not
libSDL2-2.0.so.0 => not found
libXtst.so.6 => not found
libGLESv2.so.2 => not found
libEGL.so.1 => not found
次に、momoの実行ファイルの中の未解決のシンボルのうち、標準ライブラリ以外で解決されるものを調べました。
$ nm momo |grep ' U ' |grep -v @@
U bcm_host_init
U mmal_buffer_header_release
U mmal_component_create
U mmal_component_destroy
U mmal_component_disable
U mmal_component_enable
U mmal_connection_create
U mmal_connection_destroy
U mmal_connection_enable
U mmal_event_format_changed_get
U mmal_format_copy
U mmal_format_full_copy
U mmal_pool_create
U mmal_pool_destroy
U mmal_port_disable
U mmal_port_enable
U mmal_port_format_commit
U mmal_port_parameter_set
U mmal_port_parameter_set_boolean
U mmal_port_parameter_set_uint32
U mmal_port_pool_create
U mmal_port_pool_destroy
U mmal_port_send_buffer
U mmal_queue_get
U SDL_CreateRenderer
U SDL_CreateRGBSurfaceFrom
U SDL_CreateTextureFromSurface
U SDL_CreateThread
U SDL_CreateWindow
U SDL_Delay
U SDL_DestroyRenderer
U SDL_DestroyTexture
U SDL_DestroyWindow
U SDL_FreeSurface
U SDL_GetError
U SDL_GetTicks
U SDL_GetWindowFlags
U SDL_GetWindowID
U SDL_Init
U SDL_PollEvent
U SDL_Quit
U SDL_RenderClear
U SDL_RenderCopy
U SDL_RenderPresent
U SDL_SetRenderDrawColor
U SDL_SetWindowFullscreen
U SDL_ShowCursor
U SDL_WaitThread
U XCloseDisplay
U XOpenDisplay
U XQueryKeymap
GUIが関連しているのは SDL_
で始まるものと X
で始まるものです。
これらを差し替えます。
空のライブラリを作る
シンボルを何も含まない共有ライブラリは以下のようにして作ることができます。
$ mkdir libfake
$ cd libfake
$ touch empty.c
$ gcc -shared -o libempty.so empty.c
$ ln -s libempty.so libGLESv2.so.2
$ ln -s libempty.so libEGL.so.1
$ ln -s libempty.so libXtst.so.6
偽物のライブラリを作る
未定義のシンボルを定義します。
SDL_Init
とXOpenDisplay
はこれが呼ばれたときに明示的に強制終了するようにしておきます。
#include <stdlib.h>
#include <stdio.h>
SDL_Init()
{
fprintf(stderr, "SDL_Init: Not implemented\n");
abort();
}
XOpenDisplay()
{
fprintf(stderr, "XOpenDisplay: Not implemented\n");
abort();
}
SDL_CreateRenderer(){}
SDL_CreateRGBSurfaceFrom(){}
SDL_CreateTextureFromSurface(){}
SDL_CreateThread(){}
SDL_CreateWindow(){}
SDL_Delay(){}
SDL_DestroyRenderer(){}
SDL_DestroyTexture(){}
SDL_DestroyWindow(){}
SDL_FreeSurface(){}
SDL_GetError(){}
SDL_GetTicks(){}
SDL_GetWindowFlags(){}
SDL_GetWindowID(){}
SDL_PollEvent(){}
SDL_Quit(){}
SDL_RenderClear(){}
SDL_RenderCopy(){}
SDL_RenderPresent(){}
SDL_SetRenderDrawColor(){}
SDL_SetWindowFullscreen(){}
SDL_ShowCursor(){}
SDL_WaitThread(){}
XCloseDisplay(){}
XQueryKeymap(){}
$ gcc -shared -o libfakesdl.so fakesdl.c
$ ln -s libfakesdl.so libSDL2-2.0.so.0
作ったファイルは以下の通り。
$ cd ..
$ ls -l libfake/
total 28
-rw-r--r-- 1 koba koba 0 Apr 23 21:50 empty.c
-rw-r--r-- 1 koba koba 725 Apr 23 21:47 fakesdl.c
lrwxrwxrwx 1 koba koba 11 Apr 23 21:52 libEGL.so.1 -> libempty.so
-rwxr-xr-x 1 koba koba 7028 Apr 23 21:51 libempty.so
-rwxr-xr-x 1 koba koba 8264 Apr 23 21:53 libfakesdl.so
lrwxrwxrwx 1 koba koba 11 Apr 23 21:52 libGLESv2.so.2 -> libempty.so
lrwxrwxrwx 1 koba koba 13 Apr 23 21:54 libSDL2-2.0.so.0 -> libfakesdl.so
lrwxrwxrwx 1 koba koba 11 Apr 23 21:52 libXtst.so.6 -> libempty.so
うまくリンクされるか確認
$ LD_LIBRARY_PATH=$PWD/libfake ldd ./momo
linux-vdso.so.1 (0x7ef6b000)
/usr/lib/arm-linux-gnueabihf/libarmmem-${PLATFORM}.so => /usr/lib/arm-linux-gnueabihf/libarmmem-v7l.so (0x76f02000)
libSDL2-2.0.so.0 => /home/koba/momo/current/libfake/libSDL2-2.0.so.0 (0x76ef0000)
libX11.so.6 => /lib/arm-linux-gnueabihf/libX11.so.6 (0x76dca000)
libXau.so.6 => /lib/arm-linux-gnueabihf/libXau.so.6 (0x76db7000)
libXdmcp.so.6 => /lib/arm-linux-gnueabihf/libXdmcp.so.6 (0x76da2000)
libXtst.so.6 => /home/koba/momo/current/libfake/libXtst.so.6 (0x76d90000)
libxcb.so.1 => /lib/arm-linux-gnueabihf/libxcb.so.1 (0x76d5f000)
libplds4.so => /lib/arm-linux-gnueabihf/libplds4.so (0x76d4c000)
libXext.so.6 => /lib/arm-linux-gnueabihf/libXext.so.6 (0x76d2d000)
libexpat.so.1 => /lib/arm-linux-gnueabihf/libexpat.so.1 (0x76cf9000)
libdl.so.2 => /lib/arm-linux-gnueabihf/libdl.so.2 (0x76ce5000)
libnss3.so => /lib/arm-linux-gnueabihf/libnss3.so (0x76bb5000)
libnssutil3.so => /lib/arm-linux-gnueabihf/libnssutil3.so (0x76b81000)
libplc4.so => /lib/arm-linux-gnueabihf/libplc4.so (0x76b6d000)
libnspr4.so => /lib/arm-linux-gnueabihf/libnspr4.so (0x76b2c000)
librt.so.1 => /lib/arm-linux-gnueabihf/librt.so.1 (0x76b14000)
libbcm_host.so.0 => /lib/arm-linux-gnueabihf/libbcm_host.so.0 (0x76af0000)
libcontainers.so.0 => /lib/arm-linux-gnueabihf/libcontainers.so.0 (0x76acd000)
libvcos.so.0 => /lib/arm-linux-gnueabihf/libvcos.so.0 (0x76ab3000)
libvcsm.so.0 => /lib/arm-linux-gnueabihf/libvcsm.so.0 (0x76a98000)
libvchiq_arm.so.0 => /lib/arm-linux-gnueabihf/libvchiq_arm.so.0 (0x76a81000)
libmmal.so.0 => /lib/arm-linux-gnueabihf/libmmal.so.0 (0x76a6d000)
libmmal_core.so.0 => /lib/arm-linux-gnueabihf/libmmal_core.so.0 (0x76a4e000)
libmmal_components.so.0 => /lib/arm-linux-gnueabihf/libmmal_components.so.0 (0x76a32000)
libmmal_util.so.0 => /lib/arm-linux-gnueabihf/libmmal_util.so.0 (0x76a13000)
libmmal_vc_client.so.0 => /lib/arm-linux-gnueabihf/libmmal_vc_client.so.0 (0x769f7000)
libm.so.6 => /lib/arm-linux-gnueabihf/libm.so.6 (0x76988000)
libpthread.so.0 => /lib/arm-linux-gnueabihf/libpthread.so.0 (0x7695c000)
libstdc++.so.6 => /lib/arm-linux-gnueabihf/libstdc++.so.6 (0x767d4000)
libgcc_s.so.1 => /lib/arm-linux-gnueabihf/libgcc_s.so.1 (0x767a7000)
libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0x76653000)
/lib/ld-linux-armhf.so.3 (0x76f17000)
libbsd.so.0 => /lib/arm-linux-gnueabihf/libbsd.so.0 (0x76631000)
libmd.so.0 => /lib/arm-linux-gnueabihf/libmd.so.0 (0x76616000)
これでlibSDL2-2.0.so.0
などは自分で作った方のライブラリを参照するようになりました。
実行結果
$ LD_LIBRARY_PATH=$PWD/libfake ldd ./momo
momoが起動して、ヘルプメッセージが出力されました。いい感じです。
$ LD_LIBRARY_PATH=$PWD/libfake ./momo --no-audio-device test
正しく動作しました!! カメラの映像をブラウザ画面で確認することができました。
そして、--use-sdl
のオプションをつけたときには、想定通りに強制終了することも確認できました。
$ LD_LIBRARY_PATH=$PWD/libfake ./momo --no-audio-device --use-sdl test
SDL_Init: Not implemented
Aborted
終わりに
これをやってみたおかげで、共有ライブラリのしくみの理解が深まりました。
私と同じようなmomoの使い方をしている方で、momoをコンテナに入れて依存性を分離して運用している方は、このテクニックを使うとコンテナのサイズをかなり減らせて嬉しいかもしれません。
2022/05/10 追記
この修正でうまく動くのは --no-audio-device
のオプションをつけているときだけと気がつきました。
あとでまた別の記事に書きます。
Discussion