👻

ラズパイでSDLライブラリ無しでmomoを動かす(邪道)

2022/04/23に公開

動機

ラズパイにディスプレイをつながずに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_InitXOpenDisplayはこれが呼ばれたときに明示的に強制終了するようにしておきます。

fakesdl.c
#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