内核编译

原文muhe,有些地方有改动

0x01: 环境说明

ubuntu 14.04 x86
qemu
使用的内核版本2.6.32.1

busybox版本1.19.4

使用busybox是因为文件添加方便.

0x02: 内核编译并测试

1
2
$ wget https://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.32.1.tar.gz -O linux-2.6.32.1.tar.gz
$ tar -xvf linux-2.6.32.1.tar.gz

首先要安装一些依赖库以及qemu。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ cd linux-2.6.32.1/
$ sudo apt-get install libncurses5-dev
$ sudo apt-get install qemu qemu-system
$ make menuconfig
$ make
$ make all
make bzImage
$ make modules
64位安32位


make ARCH=i386 menuconfig
make ARCH=i386
make ARCH=i386 modules_install
make ARCH=i386 install

0x03:增加syscall

增加syscall的方式和之前文章写的差不多,
只是这次内核版本更低,所以更简单一点。我这里添加了两个系统调用进去。

1. 在syscall table中添加信息

文件 arch/x86/kernel/syscall_table_32.S中添加自己的调用

1
2
.long sys_muhe_test
.long sys_hello

2. 定义syscall的宏

文件arch/x86/include/asm/unistd_32.h中添加

1
2
3
4
#define __NR_hello 337
#define __NR_muhe_test 338
#ifdef __KERNEL__
#define NR_syscalls 339

要注意NR_syscalls要修改成现有的调用数目,
比如原来有0~336一共337个调用,
现在增加了两个,那就改成339。

3. 添加函数定义

文件include/linux/syscalls.h

1
2
asmlinkage long sys_muhe_test(int arg0);
asmlinkage long sys_hello(void);

4. 编写syscall代码

新建目录放自定义syscall的代码

1
2
# muhe @ ubuntu in ~/linux_kernel/linux-2.6.32.1/linux-2.6.32.1/muhe_test [2:43:06] 
$ cat muhe_test.c
1
2
3
4
5
6
7
8
9
10
#include <linux/kernel.h>
asmlinkage long sys_muhe_test(int arg0){
printk("I am syscall");
printk("syscall arg %d",arg0);
return ((long)arg0);
}
asmlinkage long sys_hello(void){
printk("hello my kernel worldn");
return 0;
}
1
2
3
# muhe @ ubuntu in ~/linux_kernel/linux-2.6.32.1/linux-2.6.32.1/muhe_test [2:43:12] 
$ cat Makefile
obj-y := muhe_test.o

5. 修改Makefile

1
2
3
# muhe @ ubuntu in ~/linux_kernel/linux-2.6.32.1/linux-2.6.32.1 [2:44:59] 
$ cat Makefile| grep muhe
core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ muhe_test/

6. 编译

1
make -j2

0x04: busybox编译配置

下载 busybox-1.20.1.tar.bz2:

wget http://www.busybox.net/downloads/busybox-1.20.1.tar.bz2

编译

busybox用常规中的二进制编译

1
2
3
$ make menuconfig
$ make
$ make install

编译完成之后如下配置

1
2
3
4
5
6
7
8
9
10
#!/bin/sh
#将 proc 文件系统挂载到 /proc 目录中
mount -t proc none /proc/
#用于对内存控制与对驱动操作
#将 sys 文件系统挂载到 /sys 的目录上
mount -t sys none /sys
#mdev 是 busybox 自带的一个 udev ,它是用于系统启动和
#热插拔或是动态加载驱动程序的时候,而自动产生设别节点的,
#这句话如果不加上的话,这需要手动的 mknod 来挂载设备节点
/sbin/mdev -s

nano /etc/init

实际只要examples/inittab注释掉一些tty2::askfirst:-/bin/sh类似的行就可以了,因为我们只要一个控制台就可以了,文件内容只如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
::sysinit:/etc/init.d/rcS

::askfirst:-/bin/sh

::restart:/sbin/init

::ctrlaltdel:/sbin/reboot

::shutdown:/bin/umount -a -r

::shutdown:/sbin/swapoff -a


mkdir dev
sudo mknod dev/ttyAMA0 c 204 64
sudo mknod dev/null c 1 3
sudo mknod dev/console c 5 1
$ find . | cpio -o --format=newc > ./rootfs.img
gzip -c rootfs.img > rootfs.img.gz

$ qemu-system-i386 -kernel bzImage -initrd rootfs.img.gz -append "root=/dev/ram rdinit=/sbin/init"
http://p0.qhimg.com/t016ecb6e221f21933d.png

0x05: 测试系统调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# muhe @ ubuntu in ~/linux_kernel/linux-2.6.32.1/linux-2.6.32.1 [2:45:04] 
$ cd muhe_test_syscall_lib
# muhe @ ubuntu in ~/linux_kernel/linux-2.6.32.1/linux-2.6.32.1/muhe_test_syscall_lib [2:51:48]
$ cat muhe_test_syscall_lib.c
#include <stdio.h>
#include <linux/unistd.h>
#include <sys/syscall.h>
int main(int argc,char **argv)
{
printf("n Diving to kernel levelnn");
syscall(337,1337);
return 0;
}
# muhe @ ubuntu in ~/linux_kernel/linux-2.6.32.1/linux-2.6.32.1/muhe_test_syscall_lib [2:51:51]
$ gcc muhe_test_syscall_lib.c -o muhe -static

一定要静态链接,因为你进busybox链接库那些是没有的。
这里要注意,每次拷贝新文件到busybox的文件系统中去,
都要执行find . | cpio -o –format=newc > ../rootfs.img去生成新的rootfs。

然后qemu起系统

1
2
3
# muhe @ ubuntu in ~/linux_kernel/linux-2.6.32.1/linux-2.6.32.1 [2:53:33] 
$ qemu-system-i386 -kernel arch/i386/boot/bzImage -initrd ../busybox-1.19.4/rootfs.img -append "root=/dev/ram rdinit=/sbin/init"
http://p2.qhimg.com/t019e8e38063f5ebdac.png

制作initrd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[cpp] view plain copy
/*hello.c*/
#include <stdio.h>


void main()
{
printf("Hello World\n");
printf("Hello World\n");
printf("Hello World\n");
  /*强制刷新输出,不然可能打印不出来*/
fflush(stdout);
while(1);
}

initrd吧,全称是initial ramdisk,在内核启动的时候会先去加载的一种文件系统.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[plain] view plain copy
$ cd ..
# 使用静态编译链接.
$ gcc -static -o helloworld hello.c
# 将helloworld制作成cpio
$ echo helloworld | cpio -o --format=newc > rootfs
1776 blocks
$ ls -la rootfs
-rw-rw-r-- 1 seijia seijia 909312 12月 21 13:15 rootfs
# 使用qemu进行启动
$ qemu-system-x86_64 \
-kernel ./bzImage \
-initrd ./rootfs \
-append "root=/dev/ram rdinit=/helloworld"
qemu的-kernel 和-initrd能够绕过bootload直接
对指定的kernel和ramdisk进行加载.
用-append进行额外的选项配置,
在这里我们把根目录直接设置成内存,
启动的init程序设置成放进去的helloworld.
用来检测是否编译成功能输出helloworld

内核编译遇到的问题

问题2

问题描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
arch/x86/kernel/ptrace.c:1472:17: error: conflicting types for ‘syscall_trace_enter’
asmregparm long syscall_trace_enter(struct pt_regs *regs)
^
In file included from /home/muhe/linux_kernel/linux-2.6.32.1/arch/x86/include/asm/vm86.h:130:0,
from /home/muhe/linux_kernel/linux-2.6.32.1/arch/x86/include/asm/processor.h:10,
from /home/muhe/linux_kernel/linux-2.6.32.1/arch/x86/include/asm/thread_info.h:22,
from include/linux/thread_info.h:56,
from include/linux/preempt.h:9,
from include/linux/spinlock.h:50,
from include/linux/seqlock.h:29,
from include/linux/time.h:8,
from include/linux/timex.h:56,
from include/linux/sched.h:56,
from arch/x86/kernel/ptrace.c:11:
/home/muhe/linux_kernel/linux-2.6.32.1/arch/x86/include/asm/ptrace.h:145:13: note: previous declaration of ‘syscall_trace_enter’ was here
extern long syscall_trace_enter(struct pt_regs *);
^
arch/x86/kernel/ptrace.c:1517:17: error: conflicting types for ‘syscall_trace_leave’
asmregparm void syscall_trace_leave(struct pt_regs *regs)
^
In file included from /home/muhe/linux_kernel/linux-2.6.32.1/arch/x86/include/asm/vm86.h:130:0,
from /home/muhe/linux_kernel/linux-2.6.32.1/arch/x86/include/asm/processor.h:10,
from /home/muhe/linux_kernel/linux-2.6.32.1/arch/x86/include/asm/thread_info.h:22,
from include/linux/thread_info.h:56,
from include/linux/preempt.h:9,
from include/linux/spinlock.h:50,
from include/linux/seqlock.h:29,
from include/linux/time.h:8,
from include/linux/timex.h:56,
from include/linux/sched.h:56,
from arch/x86/kernel/ptrace.c:11:
/home/muhe/linux_kernel/linux-2.6.32.1/arch/x86/include/asm/ptrace.h:146:13: note: previous declaration of ‘syscall_trace_leave’ was here
extern void syscall_trace_leave(struct pt_regs *);
^
make[2]: *** [arch/x86/kernel/ptrace.o] 错误 1
make[1]: *** [arch/x86/kernel] 错误 2
make: *** [arch/x86] 错误 2

解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
--- linux-2.6.32.59/arch/x86/include/asm/ptrace.h
+++ fix_ptrace.o_compile_error/arch/x86/include/asm/ptrace.h
@@ -130,6 +130,7 @@
#ifdef __KERNEL__

#include <linux/init.h>
+#include <linux/linkage.h>

struct cpuinfo_x86;
struct task_struct;
@@ -142,8 +143,8 @@
int error_code, int si_code);
void signal_fault(struct pt_regs *regs, void __user *frame, char *where);

-extern long syscall_trace_enter(struct pt_regs *);
-extern void syscall_trace_leave(struct pt_regs *);
+extern asmregparm long syscall_trace_enter(struct pt_regs *);
+extern asmregparm void syscall_trace_leave(struct pt_regs *);

static inline unsigned long regs_return_value(struct pt_regs *regs)
{}

3.3 问题3

问题描述

1
2
gcc: error: elf_i386: 没有那个文件或目录
gcc: error: unrecognized command line option ‘-m’

解决方案

1
2
3
arch/x86/vdso/Makefile
VDSO_LDFLAGS_vdso.lds = -m elf_x86_64 -Wl,-soname=linux-vdso.so.1 -Wl,-z,max-page-size=4096 -Wl,-z,common-page-size=4096 把"-m elf_x86_64" 替换为 "-m64"
VDSO_LDFLAGS_vdso32.lds = -m elf_i386 -Wl,-soname=linux-gate.so.1中的 "-m elf_i386" 替换为 "-m32"

问题4

问题描述

1
2
3
4
5
6
7
drivers/net/igbvf/igbvf.h15: error: duplicate member ‘page’
struct page page;
^
make[3]: ** [drivers/net/igbvf/ethtool.o] 错误 1
make[2]: [drivers/net/igbvf] 错误 2
make[1]: [drivers/net] 错误 2
make: * [drivers] 错误 2

解决方案

1
2
3
4
5
6
7
8
//修改名字重复
struct {
struct page *_page;
u64 page_dma;
unsigned int page_offset;
};
};
struct page *page;

busybox编译问题

2.1 问题一以及解决方案

错误

1
2
  loginutils/passwd.c:188:12: error: ‘RLIMIT_FSIZE’ undeclared (first use in this function)
setrlimit(RLIMIT_FSIZE, &rlimit_fsize);

解决

1
2
3
4
5
$  vim include/libbb.h
$ add a line #include <sys/resource.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/socket.h>

问题二以及解决方案

错误

1
linux/ext2_fs.h: 没有那个文件或目录

解决

1
2
3
Linux System Utilities --->
[ ] mkfs_ext2
[ ] mkfs_vfat