Linux 2.6.24内核调试环境搭建

1,988 阅读2分钟

hello,我是杰瑞。最近在读《深入Linux内核架构》,为此花了好几天的时间,找了若干资料搭建了一下Linux 2.6.24的内核调试环境。权且做个笔记记录一下吧。

环境准备

【注】建议是使用上面的版本搭建环境,否则会踩到很多不兼容性的坑 (不要问我踩了多少,都是泪啊)

搭建基础环境

  • 这里我使用的是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工具连接到内核

image.png

  • 当然,也可以使用-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

image-20221031002146382.png

参考资料