昨天晚上 Jactry 使用 Rescue for Lemote2f 打包龙芯笔记本上的操作系统,发现 Ext4 分区挂载不了,检查发现是内核没有编译进 Ext4 文件系统的支持(当时太粗心,忘记了 :))。
准备重新编译这个 Rescue System,但是当时制作的 InitRamFs 已经早就没有了,在 Wuzhangjin 的帮助下成功的从 Rescue System 中提取出了映像文件。
Rescue System
这是为 Lemote2f 机器编译制作的紧急修复系统,用于在操作系统不能正常启动的情况下修复硬盘中的系统,也可以用于打包硬盘中的操作系统等等。
Rescue System 的实质就是一个 Linux 内核且编译进了一个 RamDisk 文件系统的映像文件,运行时解压到内存盘中用作根文件系统。它是一个 ELF 格式的文件,可以使用 readelf 程序读取其中的信息。
检查是否压缩
readelf -S rescue-lemote2f |
There are 26 section headers, starting at offset 0x668610: Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .text PROGBITS ffffffff80de0000 00010000 0000000000000b10 0000000000000000 AX 0 0 16 [ 2] .text.zlib_update PROGBITS ffffffff80de0b10 00010b10 0000000000000190 0000000000000000 AX 0 0 8 [ 3] .text.nofill PROGBITS ffffffff80de0ca0 00010ca0 0000000000000018 0000000000000000 AX 0 0 8 [ 4] .text.error PROGBITS ffffffff80de0cb8 00010cb8 0000000000000038 0000000000000000 AX 0 0 8 [ 5] .text.memcpy PROGBITS ffffffff80de0cf0 00010cf0 0000000000000048 0000000000000000 AX 0 0 8 [ 6] .text.zlib_inflat PROGBITS ffffffff80de0d38 00010d38 0000000000000978 0000000000000000 AX 0 0 8 [ 7] .text.inflate_fas PROGBITS ffffffff80de16b0 000116b0 0000000000000c80 0000000000000000 AX 0 0 8 [ 8] .text.zlib_inflat PROGBITS ffffffff80de2330 00012330 0000000000000018 0000000000000000 AX 0 0 8 [ 9] .text.zlib_inflat PROGBITS ffffffff80de2348 00012348 0000000000000098 0000000000000000 AX 0 0 8 [10] .text.zlib_inflat PROGBITS ffffffff80de23e0 000123e0 00000000000000e8 0000000000000000 AX 0 0 8 [11] .text.zlib_inflat PROGBITS ffffffff80de24c8 000124c8 00000000000018a8 0000000000000000 AX 0 0 8 [12] .text.zlib_inflat PROGBITS ffffffff80de3d70 00013d70 0000000000000038 0000000000000000 AX 0 0 8 [13] .text.zlib_inflat PROGBITS ffffffff80de3da8 00013da8 0000000000000308 0000000000000000 AX 0 0 8 [14] .text.decompress_ PROGBITS ffffffff80de40b0 000140b0 00000000000004f0 0000000000000000 AX 0 0 8 [15] .text.putc PROGBITS ffffffff80de45a0 000145a0 0000000000000018 0000000000000000 AX 0 0 8 [16] .text.puts PROGBITS ffffffff80de45b8 000145b8 0000000000000078 0000000000000000 AX 0 0 8 [17] .text.puthex PROGBITS ffffffff80de4630 00014630 0000000000000120 0000000000000000 AX 0 0 8 [18] .text.main PROGBITS ffffffff80de4750 00014750 0000000000000018 0000000000000000 AX 0 0 8 [19] .rodata.str1.8 PROGBITS ffffffff80de4768 00014768 00000000000002f8 0000000000000001 AMS 0 0 8 [20] .data PROGBITS ffffffff80de4a60 00014a60 0000000000653a0d 0000000000000000 WA 0 0 16 [21] .bss NOBITS ffffffff81438470 0066846d 0000000000402030 0000000000000000 WA 0 0 16 [22] .gnu.attributes LOOS+ffffff5 0000000000000000 0066846d 0000000000000010 0000000000000000 0 0 1 [23] .shstrtab STRTAB 0000000000000000 0066847d 000000000000018e 0000000000000000 0 0 1 [24] .symtab SYMTAB 0000000000000000 00668c90 0000000000002a00 0000000000000018 25 417 8 [25] .strtab STRTAB 0000000000000000 0066b690 0000000000000a70 0000000000000000 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings) I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific) |
从上面我们看出有几个代码段包含 zlib 关键词,说明内核编译进了压缩支持,多数是压缩内核。
提取映像
readelf -s rescue-lemote2f | grep image |
424: ffffffff81438480 8 OBJECT GLOBAL DEFAULT 21 zimage_start 425: ffffffff8143846d 0 NOTYPE GLOBAL DEFAULT 20 __image_end 429: ffffffff80de4a60 0 NOTYPE GLOBAL DEFAULT 20 __image_begin |
从上面的数据看到 __image_begin 和 __image_end 两个符号没有类型且没有长度,应该是静态常量用于标记映像文件的位置地址,结合上面的 Section Headers 发现标记的这段数据正好处于 .data 段,而 zimage_start 则是一个 .bss 段的变量,一定不是我们需要的信息。
我们看到 __image_begin 的内存地址是 ffffffff80de4a60,__image_end 内存地址是 ffffffff8143846d,计算出长度是 ffffffff8143846d – ffffffff80de4a60 = 653a0d,从 Section Headers 检查发现整个 .data 段就是这个映像文件。从 Section Headers 我们找到了 .data 段在文件中的偏移地址 00014a60。有了这些信息我们就可以使用 dd 命令提取出映像文件了。
全部转换成十进制数
偏移地址 00014a60 = 84576
长度 653a0d = 6633997
dd if=rescue-lemote2f of=vmlinux skip=84576 bs=1 count=6633997 |
映像类型检查
file vmlinux |
vmlinux: gzip compressed data, from Unix, last modified: Tue May 25 20:59:46 2010, max compression |
获取 gz 文件头
获取文件头用于后面查找 ramdisk.cpio.gz 文件的开始地址
hexdump vmlinux | grep 0000000 |
0000000 8b1f 0008 c9c2 4bfb 0302 5cec 740d 5514 |
我们看到当时打包的 gz 文件前四个字节是 8b1f 0008
解压 vmlinux 文件
从上面我们得知 vmlinux 实际是 gzip 压缩文件,我们解压它,得到了新的 vmlinux 文件。
mv vmlinux vmlinux.gz gunzip vmlinux.gz |
查找 ramdisk.cpio.gz 的开始地址
我们先使用 hexdump 将整个文件都 dump 出来保存到一个文本文件中,便于后面的查找。
hexdump vmlinux > vmlinux.txt vim vmlinux.txt |
通过搜索,我们在这个文件中找到了两处 8b1f 0008,分别是
03b4520 4d50 8028 ffff ffff 47f8 8028 ffff ffff 03b4530 4b80 8028 ffff ffff 4810 8028 ffff ffff 03b4540 4b49 4643 5f47 5453 8b1f 0008 c8c2 4bfb |
04b6c80 0000 0000 0000 0000 0000 0000 0000 0000 * 04b8000 8b1f 0008 6c56 4bd5 0302 5ab4 707d 555b |
我猜测后者的可能性比较大,也可以每个都 dd 出解压看看。
提取 ramdisk.cpio.gz
从上面我们得到了ramdisk.cpio.gz在文件中的偏移地址 04b8000 = ,但是没有长度,实际也不需要长度,因为 gunzip 肯定能够从 gz 文件中得知需要读取多少个字节,就让它交给 gunzip 处理吧。我们从这个地址开始取到文件结束。
dd if=vmlinux of=ramdisk.cpio.gz skip=4947968 bs=1 |
解压 ramdisk.cpio.gz
gunzip ramdisk.cpio.gz |
我们得到了完整的 ramdisk.cpio 文件了!
Over!
运行时解压到内存盘中用作根文件系统,这个就跟安装盘一样了呀!
差不多,再在里面放一个安装程序就可以作为网络安装盘了。
楼主,我用你的resuce 运行起来了,但挂不上硬盘 (dev 下面没有那些设备文件)
fdisk -l 没东西输出,然后手动建了几个设备文件
mount /dev/sda1 /mnt //挂却上的是U盘
我今天也做了一个resuce,就是在编译内核的时候把文件系统(用busybox1.18.1做的,在龙芯电脑上放在sda2可以跑)编译进去了,用pmon load 之后
pmon> g console=tty
提示VFS:unknow “root=” or unknow block(0,0)
换成:
pmon> g console=tty root=ram
情况同上
不清楚。