Мы активно пользуемся WSL2 для того, чтобы открывать линуксовые коры в Visual Studio. Для обеспечения консистентности символов и коры мы монтируем squashfs образ, созданный в целевой системе. После очередного апдейта целевого дистрибутива, у нас всё сломалось - squash монтировался, но мог посередине файла выдать ошибку чтения, записав в dmesg что-то типа
[ 17.157892] SQUASHFS error: xz decompression failed, data probably corrupt
[ 17.158705] SQUASHFS error: Failed to read block 0x5c3cb5e: -5
Естественно, точно такой же сквош на целевой системе работал как часы и никаких ошибок не выдавал.
По мере исследования выяснилось, что ядро WSL не включает опции ядра, связанные с BCJ фильтрацией в XZ.
BCJ фильтр
Алгоритм XZ это на самом деле LZMA плюс интервальное кодирование как разновидность арифметического. В свою очередь, LZMA можно очень упрощенно описать как замену одинаковых участков сжимаемого текста на ссылки 'назад' - сжатие по словарю, а интервальное кодирование - как выбор самого короткого числа, описывающего сжимаемый текст с точки зрения вероятностей отдельных символов в месте их появления.
Поскольку используется словарное сжатие LZMA, важно иметь много одинакового текста. А в исполняемых файлах много вызовов библиотечных функций. Эти вызовы выглядят примерно так:
0x000000000040112a <+4>: bf 10 20 40 00 mov $0x402010,%edi
0x000000000040112f <+9>: e8 fc fe ff ff call 0x401030 <puts@plt>
0x0000000000401134 <+14>: bf 1c 20 40 00 mov $0x40201c,%edi
0x0000000000401139 <+19>: e8 f2 fe ff ff call 0x401030 <puts@plt>
Два вызова puts() в листинге выше используют инструкцию call relative to next instruction но на самом деле указывают на одно и то же место. Если бы мы нашли все такие вызовы puts() и поменяли их с относительного адреса на абсолютный, то у нас было бы очень много одинаковых пятибайтных кусочков. То же самое справедливо для инструкций ветвления (Branch) и безусловных переходов (Jump) - отсюда и название BCJ фильтра - Branch/Call/Jump.
Естественно, такой способ подходит только для исполняемого кода и замедляет обработку - например, при тестировании сборки Fedora Linux за 30 мб cжатия от 1.7 Гб образа заплатили 18 минутами распаковки (примерно в два раза медленнее). Но кому-то и такое может зайти. Дефолт такой дефолт.
Впрочем, раз squashfs по дефолту собирается с BCJ фильтром, надо соответствующие опции включить в ядре. А для этого ядро надо пересобрать.
Сборка ядра
В качестве подопытного кролика будет выступать AlmaLinux 9 - но не так сложно найти гайды по пересборке ядра в базовой для WSL Ubuntu. В общем и целом процесс отличается от "обычной" сборки ядра для линукса только файлом настроек.
Внимание!
В отличие от линукса, NTFS регистронезависим, и поэтому могут быть проблемы с доступом к исходникам во время компиляции! Все действия этого раздела проводятся внутри WSL2 на собственной файловой системе!
Поставим всё, что для сборки понадобится:
sudo dnf install --enablerepo=crb kernel-devel bc \
git dwarves ncurses-devel binutils{,-gold} rsync kmod
kernel-devel это немножко перебор - но зато ставит большую часть инструментария для сборки ядра.
ncurses-devel это для menuconfig - текстового интерфейса конфигурации ядра.
Скачаем исходники ядра с настройками "от Микрософт"
git clone https://github.com/microsoft/WSL2-Linux-Kernel.git --depth=1 -b linux-msft-wsl-5.15.y
cd WSL2-Linux-Kernel
На момент публикации основная ветка - linux-msft-wsl-5.15.y, следующий кандидат - linux-msft-wsl-6.1.y.
Если нужно конкретное ядро - есть таги вида linux-msft-wsl-<maj>.<min>.<x>.<y> - например, linux-msft-wsl-6.1.21.2
Файл с настройками ядра можно найти в Microsoft/config-wsl
.
Теперь нужно изменить эти настройки - руками в файле или через menuconfig
make menuconfig KCONFIG_CONFIG=Microsoft/config-wsl
Конкретно для работы BCJ было достаточно добавить
CONFIG_XZ_DEC_X86=y
но в ядре есть и опции для BCJ фильтрации кода других архитектур.
Для точного определения какое ядро используется можно поменять суффикс локальной версии (General setup -> Local Version) и/или добавить git sha к суффиксу (General setup -> Automatically append version information).
Дальше - сборка ядра и установка модулей.
Базовая настройка от Микрософта не имеет настроек компиляции в модули, но вдруг именно вам надо?
Заголовки ядра тоже вещь очень на любителя.
make -j $(nproc) KCONFIG_CONFIG=Microsoft/config-wsl
make modules_install headers_install
Предупреждение - мне ни модули ни хедеры не пригодились, поэтому привожу команду только для полноты картины. Возможно, потребуется что-то ещё чтобы модули загружались автоматически.
Установка и проверка нового ядра
Если make закончился успешно, то новое ядро можно найти в arch/x86/boot/bzImage
. Для использования в WSL его надо вытащить наружу
mkdir -p /mnt/c/Users/<USER>/.wsl-kernels/
cp arch/x86/boot/bzImage /mnt/c/Users/<USER>/.wsl-kernels/
WSL2 использует конфигурационный файл .wslconfig для юзероспецифичных настроек WSL.
Для использования своего ядра надо туда записать
[wsl2]
kernel=C:\\Users\\<USER>\\.wsl-kernels\\bzImage
потом перезапустить подсистему WSL
wsl --shutdown
wsl
и потом изнутри WSL проверить какое ядро запущено
uname -r
Дополнительные ссылки
Ядерная реализация XZ в линуксе взята из https://github.com/tukaani-project/xz-embedded
LZMA - https://neerc.ifmo.ru/wiki/index.php?title=Алгоритм_LZMA, https://www.7-zip.org/sdk.html
интервальное кодирование - https://ru.wikipedia.org/wiki/Интервальное_кодирование
Другие маны по сборке ядра для WSL2:
https://github.com/Zhoneym/WSL-Docs/blob/main/WSL/compile-wsl2-kernel.md
https://alexkaouris.medium.com/run-your-own-kernel-with-wsl2-21e3143e014e
fshp
В NTFS можно включить case sensitive для отдельных директорий
win32asm Автор
Спасибо, не знал, попробую.