hello,我是杰瑞。最近在读《深入Linux内核架构》,为此花了好几天的时间,找了若干资料搭建了一下Linux 2.6.24的内核调试环境。权且做个笔记记录一下吧。
环境准备
- 虚拟机镜像:ubuntu-14.04.6-desktop-amd64.iso
- QEMU版本:2.0.0
- gcc版本:4.8.4
- gdb版本:7.7.1
- Linux 2.6.24内核源码:mirror.bjtu.edu.cn/kernel/linu…
- Busybox 1.21.1源码:busybox.net/downloads/b…
【注】建议是使用上面的版本搭建环境,否则会踩到很多不兼容性的坑 (不要问我踩了多少,都是泪啊)
搭建基础环境
- 这里我使用的是Vmware Workstation 16,在之上搭建了一个Ubuntu 14.04的64位虚拟机。用32位系统也是可以的 (当然,如果你手头有物理环境那更好咯)
# 1. 更新apt源
cp /etc/apt/sources.list /etc/apt/sources.list.bak
cat > /etc/apt/sources.list << EOF
deb http://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-proposed main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-proposed main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-backports main restricted universe multiverse
EOF
apt-get update
# 2. 安装编译环境
apt-get install -y libncurses5-dev build-essential
apt-get install -y lib32readline-gplv2-dev # 编译32位系统
apt-get install -y wget vim gdb
# 3. 安装QEMU
apt-get install -y qemu-system-x86
编译内核
这里编译的是32位的Linux 2.6.24内核,步骤如下:
1. 下载内核源码
wget --no-check-certificate https://mirror.bjtu.edu.cn/kernel/linux/kernel/v2.6/linux-2.6.24.tar.xz
tar xvJf linux-2.6.24.tar.xz
cd linux-2.6.24
2. 为内核打补丁。早期的内核和高版本系统有诸多不兼容,可能遇到的编译错误有:
(1) 编译报错
Makefile:434: *** mixed implicit and normal rules: deprecated syntax
Makefile:1503: *** mixed implicit and normal rules: deprecated syntax
- 问题原因:系统的make工具太新,make的旧版规则已经无法兼容新版
- 解决方法:修改
Makefile脚本,第434行和1503行
config %config: scripts_basic outputmakefile FORCE
/ %/: prepare scripts FORCE
# 修改为
%config: scripts_basic outputmakefile FORCE
%/: prepare scripts FORCE
(2) 编译报错
gcc: error: elf_x86_64: No such file or directory
gcc: error: unrecognized command line option ‘-m’
make[1]: *** [arch/x86/vdso/vdso.so.dbg] Error 1
- 问题原因:由于gcc 4.6不再支持linker-style架构
- 解决方法:修改
arch/x86/vdso/Makefile如下:将-m elf_x86_64改为-m64
cmd_syscall = $(CC) -m elf_x86_64 -nostdlib $(SYSCFLAGS_$(@F)) \
-Wl,-T,$(filter-out FORCE,$^) -o $@
# 修改为
cmd_syscall = $(CC) -m64 -nostdlib $(SYSCFLAGS_$(@F)) \
-Wl,-T,$(filter-out FORCE,$^) -o $@
(3) 编译报错
/tmp/ccrzv34H.s: Assembler messages:
/tmp/ccrzv34H.s: Error: .size expression for copy_user_generic_c does not evaluate to a constant
- 修改步骤:修改
arch/x86/lib/copy_user_64.S,与前面的ENTRY(copy_user_generic_string)对应
END(copy_user_generic_c)
# 修改为
END(copy_user_generic_string)
(4) 编译报错
kernel/built-in.o: In function `mutex_lock':
/root/linux-2.6.24/kernel/mutex.c:92: undefined reference to `__mutex_lock_slowpath'
kernel/built-in.o: In function `mutex_unlock':
/root/linux-2.6.24/kernel/mutex.c:118: undefined reference to `__mutex_unlock_slowpath'
- 修改步骤:修改
kernel/mutex.c,增加gcc属性扩展名__used
# 修改前
static void fastcall noinline __sched
__mutex_lock_slowpath(atomic_t *lock_count);
static void fastcall noinline __sched
__mutex_unlock_slowpath(atomic_t *lock_count);
# 修改后
static __used void fastcall noinline __sched
__mutex_lock_slowpath(atomic_t *lock_count);
static __used void fastcall noinline __sched
__mutex_unlock_slowpath(atomic_t *lock_count);
(5) 编译报错(Linux 2.6.26内核不会报错)
arch/x86/kernel/irq_32.c: At top level:
arch/x86/kernel/irq_32.c:69:23: error: conflicting types for ‘do_IRQ’
fastcall unsigned int do_IRQ(struct pt_regs *regs)
- 修改步骤:修改
arch/x86/kernel/irq_32.c,去掉fastcall
fastcall unsigned int do_IRQ(struct pt_regs *regs)
# 修改后
unsigned int do_IRQ(struct pt_regs *regs)
(6) 编译报错(Linux 2.6.26内核不会报错)
gcc: error: elf_i386: No such file or directory
gcc: error: unrecognized command line option ‘-m’
make[1]: *** [arch/x86/kernel/vsyscall-int80_32.so] Error 1
make: *** [arch/x86/kernel] Error 2
- 修改步骤:修改
arch/x86/kernel/Makefile_32,将-m elf_i386改为-m32
cmd_syscall = $(CC) -m elf_i386 -nostdlib $(SYSCFLAGS_$(@F)) \
-Wl,-T,$(filter-out FORCE,$^) -o $@
# 修改后
cmd_syscall = $(CC) -m32 -nostdlib $(SYSCFLAGS_$(@F)) \
-Wl,-T,$(filter-out FORCE,$^) -o $@
3. 设置编译选项
make ARCH=i386 defconfig
make ARCH=i386 menuconfig
# 选择以下编译选项,使用空格键选择。然后退出
Kernel hacking --->
[*] Compile the kernel with debug info
[*] KGDB: kernel debugging with remote gdb --->
4. 开始编译
# -jN 代表多任务并行化,数字一般为cpu核数*2
make ARCH=i386 -j4
编译成功后显示,内核文件位于:arch/x86/boot/bzImage
Root device is (8, 1)
Setup is 11288 bytes (padded to 11776 bytes).
System is 2858 kB
Kernel: arch/x86/boot/bzImage is ready (#1)
制作根文件系统initrd.img
- 这里使用busybox制作根文件系统。这里编译为32位根文件系统
# 1. 下载busybox
wget --no-check-certificate https://busybox.net/downloads/busybox-1.21.1.tar.bz2
tar jxvf busybox-1.21.1.tar.bz2
cd busybox-1.21.1
# 2. 修改Makefile,在292行最后加上-m32
CC = $(CROSS_COMPILE)gcc -m32
# 3. 设置busybox编译选项。注意使用静态链接,关闭Job Control
make defconfig
make menuconfig
Busybox Settings --->
Build Options --->
[*] Build BusyBox as a static binary (no shared libs)
Shells --->
[ ] Job control
# 4. 制作initrd.img
dd if=/dev/zero of=initrd.img count=1024 bs=4096
mkfs.ext2 initrd.img
mkdir rootfs
mount -o loop initrd.img rootfs
# 5. 编译busybox
make -j4
make CONFIG_PREFIX=./rootfs install
# 6. 安装完成,补充一些必要的文件和目录
cd rootfs
mkdir etc proc dev home mnt tmp sys
mkdir etc/init.d
cat > etc/fstab << EOF
proc /proc proc defaults 0 0
tmpfs /tmp tmpfs defaults 0 0
none /tmp ramfs defaults 0 0
sysfs /sys sysfs defaults 0 0
EOF
cat > etc/init.d/rcS << EOF
#!/bin/sh
/bin/mount -a
mount -o remount,rw /
mkdir -p /dev/pts
mount -t devpts devpts /dev/pts
EOF
cat > etc/inittab << EOF
::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
::askfirst:-/bin/sh
::ctrlaltdel:/bin/umount -a -r
EOF
cd dev
mknod null c 1 3
mknod console c 5 1
mknod ram b 1 0
# 7. 卸载rootfs
cd ..
chmod -R 777 rootfs/
umount ./rootfs
- 编译出来的initrd.img内容目录结构如下:
# ls
bin dev etc home linuxrc lost+found mnt proc sbin sys tmp usr
启动内核
- 终于,我们可以使用QEMU工具启动内核了:
qemu-system-x86_64 -kernel ./arch/x86/boot/bzImage -initrd ./initrd.img -append "root=/dev/ram" -vnc :0
- 这里
-vnc :0表示VNC端口为5900,可以用VNC工具连接到内核
- 当然,也可以使用
-nographic参数,在tty0启动内核
qemu-system-x86_64 -kernel ./arch/x86/boot/bzImage -initrd ./initrd.img -append "root=/dev/ram console=ttyS0" -nographic
调试内核
- 用gdb调试内核时,QEMU增加
-s -S
qemu-system-x86_64 -kernel ./arch/x86/boot/bzImage -initrd ./initrd.img -append "root=/dev/ram init=/linuxrc config=ttyS0" -s -S -vnc :0
- 在另一个终端开启gdb
# gdb --dir=./linux-2.6.24
(gdb) file linux-2.6.24/vmlinux
(gdb) target remote :1234
(gdb) hb start_kernel
(gdb) focus
(gdb) c
参考资料