Как распаковать vmlinuz и вытащить оттуда версию компилятора
Опубликовано 05.09.2008
Иногда бывает необходимо узнать полную версию ядра Linux, не имея возможности или желания грузиться с него. Например, сегодня мне нужно было перекомпилировать ядро с незначительными изменениями в .config и самым тщательным образом убедиться, что новый vmlinuz ничем не отличается от старого. Один из ключевых моментов — это строчка vermagic, которая должна совпадать у ядра и загружаемых модулей. Эта строчка выводится в начале загрузки ядра, например:
Linux version 2.6.24-19-generic (buildd@terranova) (gcc version 4.2.3 (Ubuntu 4.2.3-2ubuntu7)) #1 SMP Fri Jul 11 23:41:49 UTC 2008 (Ubuntu 2.6.24-19.36-generic)
Интересующихся смыслом vermagic отправляю к и описанию опции --force-vermagic
в man modprobe.
Vermagic незагруженного модуля, находящегося в текущем каталоге, можно узнать по команде modinfo ./module_name.ko
(работает, даже если модуль собран для другой архитектуры, например, если мы работаем в i386, а модуль — для x86_64, и наоборот). С ядром такой фокус не пройдет: во-первых, ядро, как правило, находится в сжатом виде, а исходники ядра не всегда доступны; во-вторых, сжатое ядро представляет собой загрузчик и распаковщик, за которым следует собственно сжатый алгоритмом zlib vmlinux (вопреки распространенному мнению, bzImage — это не ядро, сжатое bzip’ом, а сжатое ядро, которое можно загружать в несвязанные между собой области памяти), и необходимо отделить сжатое ядро от загрузчика.
нашлось на форуме .
Заголовок gzip начинается с байт 1f 8b 08 00
, их и будем искать. По ссылке выше рекомендуют использовать команду od -A d -t x1 vmlinuz|grep "1f 8b 08 00"
, и в некоторых случаях это действительно помогает, однако, во-первых, остаётся много трудноалгоритмизируемой ручной работы (необходимо отсчитать количество байт от смещения первого байта в строке до начала собственно заголовка gzip); во-вторых, две части искомого фрагмента могут находиться в разных строках, как, например, в текущей версии generic-ядра Ubuntu:
0011376 00 fd f3 a4 fc 5e 81 c5 ff ff 0f 00 81 e5 00 00 0011392 f0 ff 8d 83 90 01 1d 00 ff e0 01 01 1d 00 1f 8b 0011408 08 00 26 a1 ac 48 02 03 ec 3a 6d 74 14 55 96 af 0011424 3b d5 49 77 e8 58 15 a7 5b 5b b6 19 1a 2d 30 19
В данном случае, чтобы вычислить актуальное смещение заголовка gzip, необходимо к смещению первого байта в строке (11392) прибавить еще 14 байт. Поэтому мы пойдём немного другим путём.
Как я уже писал ранее, в состав исходников ядра Linux входит программа binoffset
, с помощью которой можно находить сколь угодно длинные последовательности байт в бинарном файле. Ей и воспользуемся, благо она присутсвует также и в пакете linux-headers в Ubuntu:
$ gcc -o binoffset /usr/src/linux/scripts/binoffset.c $ /binoffset /vmlinuz 0x1f 0x8b 0x08 00 2>/dev/null 11406
Теперь «вытащим» из vmlinuz сжатое ядро и распакуем его:
$ dd if=/vmlinuz skip=11406 bs=1|gzip -d >vmlinux
В vmlinux уже можно искать нужные строки:
$ strings vmlinux |grep gcc Linux version 2.6.24-19-generic (buildd@terranova) (gcc version 4.2.3 (Ubuntu 4.2.3-2ubuntu7)) #1 SMP Wed Aug 20 22:56:21 UTC 2008 (Ubuntu 2.6.24-19.41-generic) %s version %s (buildd@terranova) (gcc version 4.2.3 (Ubuntu 4.2.3-2ubuntu7)) %s
Как видно, получили почти то, что нам нужно. Теперь сократим все вышенаписанное в одну строчку:
(dd if=/vmlinuz skip=`./binoffset /vmlinuz 0x1f 0x8b 0x08 0x00` bs=1|gzip -d |strings|grep "^Linux version") 2>/dev/null
Оптимизацию и рефакторинг этой команды оставляю в качестве самостоятельной работы для читателей.
P.S. Примерно то же самое делает скрипт /usr/src/linux/scripts/extract-ikconfig
, только нужно слегка его модифицировать, чтобы он выдавал не конфиг, а распакованное ядро, но мне было интересно изобрести очередной велосписед и разобраться самостоятельно :-)