vmlinuzからvmlinuxへ変換する
convert vmlinuz to vmlinux !
以前、vmlinuzからvmlinuxを抽出する方法を調べたのだが、ここにメモしていなかったため、今回また最初から調べなければならなかった。今回はメモしておく。
具体的な方法は“convert vmlinuz to vmlinux”に書いてあったのだが、実はこの方法、ちょっと難がある。vmlinuzを16進ダンプしてgzipのヘッダである'1f 8b 08 00'がある個所を見つけだしているのだが(その場所から後ろがgzipで圧縮されたvmlinuxの本体)、この4バイトがダンプの1行に入っているとは限らない。2行に渡っていることだってある。実際、今回Chrome OSのvmlinuxを調べようとしたら、
0012912 00 01 8d 83 40 1a 29 00 ff e0 be 19 29 00 1f 8b 0012928 08 00 ad b3 05 4b 02 03 ec 3b 6d 74 53 c7 95 23
となっていて、最初見つからなかった。ちょっとハマって来て“Vmlinux - Wikipedia”に“since Linux 2.6.30, using LZMA or BZIP2”とか書いてあり、Chrome OSのカーネルはまさに2.6.30だからgzip圧縮ではなくbzip2圧縮なのかとbzip2のヘッダを探したりして時間を喰った。(しかし、Chrome OSのvmlinuzは結局gzip圧縮だった。また、Ubuntu 9.10で使っている2.6.31でもgzip圧縮だった。)
さて、このような問題を回避するために、次のようにコマンドを組み合わせた。
dd if=vmlinuz bs=1 skip=`grep -abo -e \`printf "\x1f\x8b\x08\x00"\` vmlinuz | awk -F: '{print $1}'` | zcat -d > vmlinux
grepの引数で16進や8進でパターンを指定できないので、こういったちょっとヤヤコシイ組み合わせとなってしまった。
【変更:2010/06/23】
dd if=vmlinuz bs=1 skip=`grep -abo -e \`echo | sed -e "s/^/\x1f\x8b\x08\x00/"\` vmlinuz | awk -F: '{print $1}'` | zcat -d > vmlinux から変更
【追記:2010/06/23】
“grep-2.5.1だと、-oを付けても-bは行頭のオフセットを出してしまうので、これだとダメぽ” というツブヤキを見つけたので試したみた。2.5.1は随分前のバージョン(とっていも2年位前?)なのだが、2.5.1ではgrepの仕様なのかバグなのか、確かに-bオプションがパターンマッチしたオフセットではなく、パターンマッチした行の行頭のオフセットを出してしまうので、この方法は使えない。(たまたま私の使っているプロバイダのサーバがgrep 2.5.1だったので確認できた。)2.5.3以降であれば大丈夫。(2.5.2は手元に無かったので試していない。)
実際に動かすと次のようになる。
root@ubuntu:/tmp# cp /vmlinuz vmlinuz root@ubuntu:/tmp# dd if=vmlinuz bs=1 skip=`grep -abo -e \`printf "\x1f\x8b\x08\x00"\` vmlinuz | awk -F: '{print $1}'` | zcat -d > vmlinux 2708962+0 records in 2708962+0 records out 2708962 bytes (2.7 MB) copied, 2.65972 s, 1.0 MB/s gzip: stdin: decompression OK, trailing garbage ignored root@ubuntu:/tmp# file vmlinux vmlinux: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, stripped root@ubuntu:/tmp# hd vmlinux | head -2 00000000 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 |.ELF............| 00000010 02 00 03 00 01 00 00 00 00 00 00 01 34 00 00 00 |............4...| root@ubuntu:/tmp# readelf -h vmlinux ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: Intel 80386 Version: 0x1 Entry point address: 0x1000000 Start of program headers: 52 (bytes into file) Start of section headers: 5201520 (bytes into file) Flags: 0x0 Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 3 Size of section headers: 40 (bytes) Number of section headers: 36 Section header string table index: 35 root@ubuntu:/tmp#
■ おや?
Chrome OSのvmlinuzは正常にvmlinuxに変換できたが、普段使っている Ubuntu 8.04.3のマシンで実行するちょっと変だ。カーネルはvmlinuz-2.6.24-25-server。
root@vmhost:/tmp# cp /vmlinuz . root@vmhost:/tmp# dd if=vmlinuz bs=1 skip=`grep -abo -e \`printf "\x1f\x8b\x08\x00"\` vmlinuz | awk -F: '{print $1}'` | zcat -d > vmlinux 1921388+0 records in 1921388+0 records out 1921388 bytes (1.9 MB) copied, 1.26134 s, 1.5 MB/s gzip: stdin: decompression OK, trailing garbage ignored root@vmhost:/tmp# file vmlinux vmlinux: data root@vmhost:/tmp# hd vmlinux | head -2 00000000 48 8d 2d f9 ff ff ff 48 81 ed 00 00 20 00 48 89 |H.-....H.... .H.| 00000010 e8 25 ff ff 1f 00 85 c0 0f 85 9a 01 00 00 48 8d |.%............H.| root@vmhost:/tmp# readelf -h vmlinux readelf: Error: Unable to read in 0x4800 bytes of section headers readelf: Error: Not an ELF file - it has the wrong magic bytes at the start root@vmhost:/tmp#
問題なくgunzipは出来るのだが、解凍されたファイルがELF、COFF、a.outの何れの形式でない。カーネル2.6.24ではx86もamd64でも同じような結果になる。
ただ、vmlinuzをvmlinuxに変換した目的はvmlinuxのオブジェクトの内部にある文字列を調べたかっただけで、例えば、
root@vmhost:/tmp# strings vmlinux | grep "Linux version" Linux version 2.6.24-25-server (buildd@crested) (gcc version 4.2.4 (Ubuntu 4.2.4-1ubuntu3)) #1 SMP Tue Oct 20 07:20:02 UTC 2009 (Ubuntu 2.6.24-25.63-server) root@vmhost:/tmp#
とカーネル内の文字列を抽出はできるので、これ以上の深追いは止めておこう。