Open13

ギリギリまでstatic linkされたWindows版qemuをビルドしたい会

okuokuokuoku

ちょっと依存関係が多くて配付が面倒なので。。

  • mingw-w64-ucrt-x86_64-libslirp
  • mingw-w64-ucrt-x86_64-glib2
  • mingw-w64-ucrt-x86_64-pixman
  • mingw-w64-ucrt-x86_64-virglrenderer
  • mingw-w64-ucrt-x86_64-angleproject
  • mingw-w64-ucrt-x86_64-SDL2

ANGLEはヘッダを他所から持ってくれば問題ないはず。SDL2はまぁ普通にCMakeで良いよな。残りは絶妙に難しい。

okuokuokuoku

glib2

Windows向けのinstructionは一応 https://gitlab.gnome.org/GNOME/glib/-/blob/main/docs/win32-build.md に有るが、大したことは書かれていない。 https://download.gnome.org/sources/glib/2.83/glib-2.83.2.tar.xz からDLして展開する。

tar: glib-2.83.2/COPYING: Cannot create symlink to ‘LICENSES/LGPL-2.1-or-later.txt’: No such file or directory

まぁsymlinkは気にしないって事で。。

ERROR: This python3 seems to be msys/python on MSYS2 Windows, but you are in a MinGW environment
ERROR: Please install it via https://packages.msys2.org/base/mingw-w64-python

mesonって専用版が要るのか。。 mingw-w64-ucrt-x86_64-meson をインストール。

WARNING: failed to download with error: could not get https://github.com/PCRE2Project/pcre2/releases
/download/pcre2-10.44/pcre2-10.44.tar.bz2 is the internet available?. Trying after a delay...
<urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issue
r certificate (_ssl.c:1000)>

ca-certificates ってデフォルトインストールじゃないのか。。 mingw-w64-ucrt-x86_64-ca-certificates をインストール。

subprojectsはWebやGitから拾ってくるため、 git も必要になる。

最終的に /ucrt64/bin/meson .. --prefix=/opt/qemudeps --buildtype=debug -Ddefault_library=static でconfigureした。

okuokuokuoku

virglrenderer

https://gitlab.freedesktop.org/virgl/virglrenderer/-/releases/1.1.0 からかな。。今年の6月 https://zenn.dev/link/comments/6f6e07f28d9be2 に見つけたバグは治っているようだ。

libepoxy

libepoxyが依存のようだ。 https://github.com/anholt/libepoxy/releases/tag/1.5.10 からDLしてくる。ヘッダ類もepoxyが提供するものを使っているのかな。

Windows上では何故かEGLをビルドしない。

/ucrt64/bin/meson .. --prefix=/opt/qemudeps --buildtype=debug -Ddefault_library=static -Degl=yes

mingw-w64-ucrt-x86_64-egl-headersmingw-w64-ucrt-x86_64-gles-headers が必要。

okuokuokuoku

SDL2

これはmesonではなくCMakeなので mingw-w64-ucrt-x86_64-cmake が必要。

/ucrt64/bin/cmake -G Ninja -DSDL_STATIC=ON -DSDL_SHARED=OFF -DSDL_TEST=OFF -DSDL_OPENGL=OFF -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=/opt/qemudeps .. でconfigureしてみる。

... ビルドはできるけど /lib/libSDL2d.a になっちゃうな。。まぁ手でコピーすれば良いか。

okuokuokuoku

qemu (一周目)

おい誰だよ dllimport してる奴。。

E:/msys64_qemu/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/14.2.0/../../../../x86_64-w64-mingw32/bin/ld
.exe: libcommon.a.p/net_slirp.c.obj: in function `net_slirp_state_load':
E:\msys64_qemu\home\oku\qemu\build/../net/slirp.c:404:(.text+0x1d3): undefined reference to `__imp_s
lirp_state_load'

半年前とか何十年歴史あるライブラリなのに修正されたの最近すぎるだろ。。

https://gitlab.freedesktop.org/slirp/libslirp/-/merge_requests/134

一旦手動で修正。

余計なものを探す

        libffi-8.dll => /ucrt64/bin/libffi-8.dll (0x7ffd7b160000)
        libintl-8.dll => /ucrt64/bin/libintl-8.dll (0x7ffd66430000)
        libbz2-1.dll => /ucrt64/bin/libbz2-1.dll (0x7ffd79070000)
        libiconv-2.dll => /ucrt64/bin/libiconv-2.dll (0x7ffcf7d60000)
        libwinpthread-1.dll => /ucrt64/bin/libwinpthread-1.dll (0x7ffd782f0000)
        libcurl-4.dll => /ucrt64/bin/libcurl-4.dll (0x7ffcf7110000)
        zlib1.dll => /ucrt64/bin/zlib1.dll (0x7ffd66410000)
        libncursesw6.dll => /ucrt64/bin/libncursesw6.dll (0x7ffd657b0000)
        libzstd.dll => /ucrt64/bin/libzstd.dll (0x7ffcf5d70000)
        libidn2-0.dll => /ucrt64/bin/libidn2-0.dll (0x7ffd65760000)
        libbrotlidec.dll => /ucrt64/bin/libbrotlidec.dll (0x7ffd65740000)
        libnghttp2-14.dll => /ucrt64/bin/libnghttp2-14.dll (0x7ffd4a930000)
        libnghttp3-9.dll => /ucrt64/bin/libnghttp3-9.dll (0x7ffd469a0000)
        libpsl-5.dll => /ucrt64/bin/libpsl-5.dll (0x7ffd60870000)
        libcrypto-3-x64.dll => /ucrt64/bin/libcrypto-3-x64.dll (0x7ffceb6d0000)
        libssl-3-x64.dll => /ucrt64/bin/libssl-3-x64.dll (0x7ffd423d0000)
        libssh2-1.dll => /ucrt64/bin/libssh2-1.dll (0x7ffd42880000)
        libbrotlicommon.dll => /ucrt64/bin/libbrotlicommon.dll (0x7ffd55090000)
        libunistring-5.dll => /ucrt64/bin/libunistring-5.dll (0x7ffcf2c90000)
        libunistring-5.dll => /ucrt64/bin/libunistring-5.dll (0x1598cf80000)

そもそもnghttpとかどうやってビルド環境に紛れこんだんだよ。。(たぶんgccとかpython類だとは思うけど)

Dependenciesでねっとりと確認したところ:

  • libffi → gio が gobject をリンクしている関係
  • libintl
  • libbz2
  • libcurl
  • libiconv
  • libncursesw6 → これが intl と iconv をpull in しているっぽい
  • zlib1
  • libzstd

これで勝負:

../configure --enable-whpx --enable-system --enable-slirp --enable-vnc \
--target-list=aarch64-softmmu,arm-softmmu,avr-softmmu,riscv32-softmmu,riscv64-softmmu,x86_64-softmmu \
--prefix=e:/qemutmp \
--disable-gio --disable-curl --disable-zstd --disable-bzip2 --disable-curses
okuokuokuoku

qemu (二周目)

まだ libintl が居る。。

glib-2.0.pc:Libs: -L${libdir} -lglib-2.0 -lintl -latomic -lm -lws2_32 -lwinmm

glibだった。libintl外せるのかな。。 configureに --static を渡すと、

E:/msys64_qemu/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/14.2.0/../../../../x86_64-w64-mingw32/bin/ld
.exe: E:/msys64_qemu/ucrt64/lib/libintl.a(dcigettext.o):(.text+0x6d9): undefined reference to `libic
onv_open'

iconvがリンクされずに失敗してしまう。

okuokuokuoku

qemu(三周目)

とりあえず glib-2.0.pc

Libs: -L${libdir} -lglib-2.0 -liconv -lintl -latomic -lm -lws2_32 -lwinmm

みたいに -liconv を入れてみたけど、そもそもqemu自体にiconvサポートがあるんだからそっちを有効にすれば良いよね。。とりあえずこれでリンクに成功した。

okuokuokuoku

qemu(四周目)

../configure --enable-whpx --enable-system --enable-slirp --enable-vnc --target-list=aarch64-softmmu,arm-softmmu,avr-softmmu,riscv32-softmmu,riscv64-softmmu,x86_64-softmmu --prefix=e:/qemutmp --disable-gio --disable-curl --disable-zstd --disable-bzip2 --disable-curses --static --extra-ldflags=-liconv

libsilrp 以外は手動パッチなしになった。...が、起動しない。。

起動しないのはGLESが無いせいだった。 https://github.com/mmozeiko/build-angle からDLしたらちゃんと起動した。

okuokuokuoku

ビルドスクリプトを書く

結局のところ、ビルドツールは全てNinja、SDL以外はconfigureに meson という構成だった。subprojectは後で処理するとして、一括でビルドできるようにスクリプト化するのが良いかな。

今回は Docker コンテナで作業することにした。

volumeの生成

tmplibsdist の3つのvolumeを用意する。

$ docker volume create tmp
$ docker volume create libs
$ docker volume create dist

ライブラリのconfigure / compile / install

ライブラリは glib → libslirp → pixman → libepoxy → virglrenderer の順でビルドする。

docker run --isolation process -t \
  -vtmp:c:\\objs -vlibs:c:\\libs -ve:/repos/qemubuild:c:\\srcs \
  qemubuild c:\\msys64\\msys2_shell.cmd -here -no-start -ucrt64  -defterm \
  -c "PKG_CONFIG_PATH=c:/libs/lib/pkgconfig /ucrt64/bin/meson setup  --prefix=c:/libs --buildtype=release -Ddefault_library=static c:/srcs/deps/libslirp c:/objs/libslirp"

コンパイルとインストールはどちらも meson がwrap しているのでそれを使う。後で destdir を一旦変更してからインストールする方式にしよう。。

yaml モジュールが無くて失敗した

ModuleNotFoundError: No module named 'yaml'

virglrendererのgitソースのビルドには要るのか。。

docker run --isolation process --rm -vtmp:c:\\objs -vlibs:c:\\libs -ve:/repos/qemubuild:c:\\srcs \
  qemubuild c:\\msys64\\msys2_shell.cmd -here -no-start -ucrt64  -defterm \
  -c "pacman --noconfirm -S mingw-w64-ucrt-x86_64-python-yaml && PKG_CONFIG_PATH=c:/libs/lib/pkgconfig /ucrt64/bin/meson compile -C c:/objs/virglrenderer"

後でDockerイメージに足しておく。

SDL2

SDL2はCMakeプロジェクトなのでconfigure方法が違う。

docker run --isolation process --rm -vtmp:c:\\objs -vlibs:c:\\libs -ve:/repos/qemubuild:c:\\srcs \
  qemubuild c:\\msys64\\msys2_shell.cmd -here -no-start -ucrt64  -defterm \
  -c "/ucrt64/bin/cmake -G Ninja -DSDL_STATIC=ON -DSDL_SHARED=OFF -DSDL_OPENGL=OFF -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=c:/libs -B c:/objs/SDL2 -S c:/srcs/deps/SDL2"

CMakeにも --build <dir>--install <dir> がある。

qemu

満を持して dist をマウント。

docker run --isolation process --rm -vtmp:c:\\objs -vlibs:c:\\libs -vdist:c:\\dist -ve:/repos/qemubuild:c:\\srcs qemubuild \
  c:\\msys64\\msys2_shell.cmd -here -no-start -ucrt64  -defterm \
  -c "mkdir -p c:/objs/qemu && cd c:/objs/qemu && PKG_CONFIG_PATH=c:/libs/lib/pkgconfig c:/srcs/qemu/configure --enable-whpx --enable-system --enable-slirp --enable-vnc --target-list=aarch64-softmmu,arm-softmmu,avr-softmmu,riscv32-softmmu,riscv64-softmmu,x86_64-softmmu --prefix=c:/dist --disable-gio --disable-curl --disable-zstd --disable-bzip2 --disable-curses --static --extra-ldflags=-liconv"

内部的にはmesonなんだけど一応 ninja -C で代用する。

-Werror に当たる

C:/srcs/qemu/net/slirp.c: In function 'net_slirp_poll_notify':
C:/srcs/qemu/net/slirp.c:366:9: error: 'slirp_pollfds_fill' is deprecated [-Werror=deprecated-declarations]
  366 |         slirp_pollfds_fill(s->slirp, &poll->timeout,
      |         ^~~~~~~~~~~~~~~~~~
In file included from C:/srcs/qemu/net/slirp.c:41:
c:/libs/include/slirp/libslirp.h:273:6: note: declared here
  273 | void slirp_pollfds_fill(Slirp *slirp, uint32_t *timeout,
      |      ^~~~~~~~~~~~~~~~~~
cc1.exe: all warnings being treated as errors

漢は黙って --disable-werror 。一旦ABIを破壊する変更が入り、その後追加のコミットで復活 + deprecateされたようだ。 ...というかqemuみたいな大口顧客を無視してアップデートするのは良いんだろうか。。

https://gitlab.freedesktop.org/slirp/libslirp/-/commit/c0916cf0710ae72fabf131c18a4a922f3cb19702

qemu側はリリース待ち。

https://gitlab.com/qemu-project/qemu/-/issues/2603

さらにstatic buildのために --extra-cflags=-DLIBSLIRP_STATIC が要るのか。。

ファイルの取り出し

普通にシェルを使うのが簡単かな。。

docker run --isolation process --rm -vdist:c:\\dist -ve:/repos/qemubuild/out:c:\\out qemubuild \
  c:\\msys64\\msys2_shell.cmd -here -no-start -ucrt64  -defterm \
  -c "cp -rp c:/dist/* c:/out/"
okuokuokuoku

EGLを有効にするの忘れた

qemu-system-x86_64.exe: OpenGL support was not enabled in this build of QEMU

確かにqemuのconfigureの時に No って出てるな。。

Run-time dependency epoxy found: YES 1.5.11
Has header "epoxy/egl.h" with dependency epoxy: NO

mesonに -Degl=yes を渡す必要がある。

docker run --isolation process --rm -vtmp:c:\\objs -vlibs:c:\\libs -ve:/repos/qemubuild:c:\\srcs qemubuild \
  c:\\msys64\\msys2_shell.cmd -here -no-start -ucrt64  -defterm \
  -c "PKG_CONFIG_PATH=c:/libs/lib/pkgconfig /ucrt64/bin/meson setup  --prefix=c:/libs --buildtype=release -Ddefault_library=static -Degl=yes c:/srcs/deps/libepoxy c:/objs/libepoxy"