最近在看《linux內 核0.11徹底註釋》一書,因爲書中涉及彙編語言的地方衆多,本人在大學時彙編語言學得一塌糊塗,因此實在看不下去了,頭都大了只好匆匆看了個頭尾(前面 幾章和最後一章)。看來即便有《九陰真經》這樣的武功祕籍,內功不夠也是修煉不出來神馬來的。因而索性下了個0.11版本的kernel下來嘗試編譯一 把。linux
下面開始工做:工具
1、 tar xvfz linux-0.11.tar.gz學習
2、 cd linux-0.11優化
3、 makeui
make: as86: Command not findedspa
make: ***debug
出錯緣由:as86 彙編器未安裝code
解決辦法:內存
yum install dev86* (請務必保證網絡暢通,若是使用Debian Linux系統命令改成 apt-get install dev86*)
或
下載dev86-0.16.3-8.i386.rpm安裝 下載地址 http://www.oldlinux.org/Linux.old/study/tools/
4、 make
gas -c -o boot/head.o boot/head.s
make: gas: Command not finded
make: *** [boot/head.o] Error 127
出錯緣由:gas 彙編器未安裝
解決辦法:
yum install binutils* (請確保網絡正常)
或
下載binutils-2.20.tar.gz安裝 下載地址http://ftp.gnu.org/gnu/binutils/
tar xvfz binutils-2.20.tar.gz
./configure
make
make install
確認 Gnu assembler 已經安裝
which as
/usr/local/bin/as
5、 make
gas -c -o boot/head.o boot/head.s
make: gas: Command not found
make: *** [boot/head.o] Error 127
出錯緣由:gas、gld 的名稱已通過時,如今GNU assembler的名稱是 as
解決辦法:
修改主 Makefile 文件
將 AS =gas 修改成 AS =as
將 LD =gld 修改成 LD =ld
6、make
as -c -o boot/head.o boot/head.s
as: unrecognized option '-c'
make: *** [boot/head.o] Error 1
出錯緣由:as 語法和1991年的時候有了一些變化
解決辦法:
修改 Makefile 文件
將 $(AS) -c -o $*.o $< 修改成 $(AS) -o $*.o $<
7、make
as -o boot/head.o boot/head.s
boot/head.s: Assembler messages:
boot/head.s:231: Error: alignment not a power of 2
make: *** [boot/head.o] Error 1
出錯緣由:.align 2 是彙編語言指示符,其含義是指存儲邊界對齊調整;
「2」表示把隨後的代碼或數據的偏移位置調整到地址值最後2比特位爲零的位置(2^2),即按4字節對齊內存地址。
不過如今GNU as直接是寫出對齊的值而非2的次方值了。
.align 2 應該改成 .align 4
.align 3 應該改成 .align 8
解決辦法:
修改 boot/head.s 文件
將 .align 2 應該改成 .align 4
.align 3 應該改成 .align 8
8、make
cc1: error: unrecognized command line option "-mstring-insns"
cc1: error: unrecognized command line option "-fcombine-regs"
make: *** [init/main.o] Error 1
解決辦法:
修改 Makefile 文件
將 -fcombine-regs -mstring-insns 刪除或者註釋掉
9、make
In file include from init/main.c:9:
include/unistd.h:207: warning: function return types not compatible due to 'volatile'
include/unistd.h:208: warning: function return types not compatible due to 'volatile'
init/main.c:24: error: static declaration of 'fork' follows non-static declaration
init/main.c:26: error: static declaration of 'pause' follows non-static declaration
include/unistd.h:224: note: previous declaration of 'pause' was here
init/main.c:30: error: static declaration of 'sync' follows non-static declaration
include/unistd.h:235: note: previous declaration of 'sync' was here
init/main.c:108: warning: return type of 'main' is not 'int'
make: *** [init/main.o] Error 1
解決辦法:
修改 init/main.c 文件
將 static inline _syscall0(int,fork) 修改成 inline _syscall0(int,fork)
static inline _syscall0(int,pause) 修改成 inline _syscall0(int,pause)
static inline _syscall1(int,setup,void *,BIOS) 修改成 inline _syscall1(int,setup,void *,BIOS)
static inline _syscall0(int,sync) 修改成 inline _syscall0(int,sync)
10、make
(cd kernel; make)
make[1]: Entering directory '***/linux-0.11/kernel'
gcc -Wall -O -fstrength-reduce -fomit-frame-pointer -fcombine-regs -finline-functions -mstring-insns -nostdinc -I../include \
-c -o sched.o sched.c
cc1: error: unrecognized command line option "-mstring-insns"
cc1: error: unrecognized command line option "-fcombine-regs"
make[1]: *** [sched.o] Error 1
make[1]: Leaving directiory '***/linux-0.11/kernel'
make: *** [kernel/kernel.o] Error 2
解決辦法:
修改 kernel目錄下 Makefile 文件
將 -fcombine-regs -mstring-insns 刪除或者註釋掉
11、make
gas -c -o system_call.o system_call.s
make[1]: gas: Command not found
make[1]: *** [system_call.o] Error 127
make[1]: Leaving directory '***/linux-0.11/kernel'
make: *** [kernel/kernel.o] Error 2
解決辦法:
修改 kernel目錄下 Makefile 文件
將 AS =gas 修改成 AS =as
將 LD =gld 修改成 LD =ld
將 $(AS) -c -o $*.o $< 修改成 $(AS) -o $*.o $<
12、make
fork.c:In function 'copy_process':
fork.c:121: warning: suggest parentheses around assignment used as truth value
fork.c:In function 'copy_mem':
fork.c:54: error: can't find a register in class 'DREG' while reloading 'asm'
fork.c:55: error: can't find a register in class 'DREG' while reloading 'asm'
fork.c:46: error: 'asm' operand has impossible constraints
fork.c:47: error: 'asm' operand has impossible constraints
fork.c:54: error: 'asm' operand has impossible constraints
fork.c:55: error: 'asm' operand has impossible constraints
make[1]: *** [fork.o] Error 1
make[1]: Leaving directory '***/linux-0.11/kernel'
make: *** [kernel/kernel.o] Error 2
在 kernel/fork.c 第 54 行:
能夠看到這樣的調用 set_base(p->ldt[1],new_code_base);
在 include/linux/sched.h 第 188 —— 211 行:
能夠看到 set_base 的實現
#define set_base(ldt,base) _set_base( ((char *)&(ldt)) , base )
#define _set_base(addr,base) \
__asm__("movw %%dx,%0\n\t" \
"rorl $16,%%edx\n\t" \
"movb %%dl,%1\n\t" \
"movb %%dh,%2" \
::"m" (*((addr)+2)), \
"m" (*((addr)+4)), \
"m" (*((addr)+7)), \
"d" (base) \
:"dx")
因 爲這裏涉及到彙編的知識,哥卡在這裏一直沒解決而且鬱悶了很久,最後只好看回《linux內核0.11徹底註釋》一書。趙炯博士在 http://www.oldlinux.org/Linux.old/kernel/0.1x/ 這裏提供了修改 linux-0.11-060618-gcc4.tar.gz 好的 0.11版本的內核。
因而這個時候比較工具 Beyond Compare 就派上用場了。
將 #define _set_base(addr,base) \ 修改成 #define _set_base(addr,base) \
__asm__("movw %%dx,%0\n\t" \ __asm__("push %%edx\n\t" \
"rorl $16,%%edx\n\t" \ "movw %%dx,%0\n\t" \
"movb %%dl,%1\n\t" \ "rorl $16,%%edx\n\t" \
"movb %%dh,%2" \ "movb %%dl,%1\n\t" \
::"m" (*((addr)+2)), \ "movb %%dh,%2\n\t" \
"m" (*((addr)+4)), \ "pop %%edx" \
"m" (*((addr)+7)), \ ::"m" (*((addr)+2)), \
"d" (base) \ "m" (*((addr)+4)), \
:"dx") "m" (*((addr)+7)), \
"d" (base) )
將 #define switch_to(n) {\
struct {long a,b;} __tmp; \
__asm__("cmpl %%ecx,_current\n\t" \
"je 1f\n\t" \
"movw %%dx,%1\n\t" \
"xchgl %%ecx,_current\n\t" \
"ljmp %0\n\t" \ ------------------------------這裏修改成 "ljmp *%0\n\t" \
"cmpl %%ecx,_last_task_used_math\n\t" \
"jne 1f\n\t" \
"clts\n" \
"1:" \
::"m" (*&__tmp.a),"m" (*&__tmp.b), \
"d" (_TSS(n)),"c" ((long) task[n])); \
}
將 #define _get_base(addr) ({\ 修改成 static inline unsigned long _get_base(char * addr)
unsigned long __base; \ {
__asm__("movb %3,%%dh\n\t" \ unsigned long __base;
movb %2,%%dl\n\t" \ __asm__("movb %3,%%dh\n\t" \
shll $16,%%edx\n\t" \ "movb %2,%%dl\n\t" \
movw %1,%%dx" \ "shll $16,%%edx\n\t" \
"=d" (__base) \ "movw %1,%%dx" \
"m" (*((addr)+2)), \ :"=&d" (__base) \
"m" (*((addr)+4)), \ :"m" (*((addr)+2)), \
"m" (*((addr)+7))); \ "m" (*((addr)+4)), \
__base;}) "m" (*((addr)+7)));
return __base;
}
注意:
因爲GNU as彙編器不斷進化的緣由,須要將 *.s 文件中
相似
.globl _idt,_gdt,_pg_dir,_tmp_floppy_area
修改成
.globl idt,gdt,pg_dir,tmp_floppy_area
否則雖然編譯經過,可是鏈接的時候將出現相似這樣的錯誤
boot/head.o: In function 'setup_idt':
(.text+0x87): undefined reference to '_idt'
boot/head.o: In function 'idt_descr':
(.text+0x54ac): undefined reference to '_idt'
kernel/kernel.o: In function 'sched_init':
(.text+0x37c): undefined reference to '_idt'
GCC中基本的內聯彙編:__asm____volatile__("InstructionList");
__asm__(
"movl $1,%eax\r\t"
"xor %ebx,%ebx\r\t"
"int $0x80"
);
帶有C/C++表達式的內聯彙編:
__asm__ __volatile__("InstructionList"
:Output
:Input
:Clobber/Modify);
這4個部分都不是必須的,任何一個部分均可覺得空,其規則爲:
1、若是Clobber/Modify 爲空,則其前面的冒號(:)必須省略。
2、若是Output,Input,Clobber/Modify都爲空,Output,Input以前的冒號(:)既能夠省略,也能夠不省略。
3、若是Input,Clobber/Modify爲空,但Output不爲空,Input前的冒號(:)既能夠省略,也能夠不省略。
4、若是後面的部分不爲空,而前面的部分爲空,則前面的冒號(:)都必須保留,不然沒法說明不爲空的部分到底是第幾部分。
每個Input和Output表達式都必須指定本身的操做約束Operation Constraint,這裏將討論在80386平臺上所可能使用的操做約束。
當前的輸入或輸出須要藉助一個寄存器時,須要爲其指定一個寄存器約束,能夠直接指定一個寄存器的名字。
經常使用的寄存器約束的縮寫
約束 意義
r 表示使用一個通用寄存器,由 GCC 在%eax/%ax/%al,%ebx/%bx/%bl,%ecx/%cx/%cl,%edx/%dx/%dl中選取一個GCC認爲合適的。
g 表示使用任意一個寄存器,由GCC在全部的可使用的寄存器中選取一個GCC認爲合適的。
q 表示使用一個通用寄存器,和約束r的意義相同。
a 表示使用%eax/%ax/%al
b 表示使用%ebx/%bx/%bl
c 表示使用%ecx/%cx/%cl
d 表示使用%edx/%dx/%dl
D 表示使用%edi/%di
S 表示使用%esi/%si
f 表示使用浮點寄存器
t 表示使用第一個浮點寄存器
u 表示使用第二個浮點寄存器
若是一個Input/Output 操做表達式的C/C++表達式表現爲一個內存地址,不想借助於任何寄存器,則可使用內存約束。好比:
__asm__("lidt%0":"=m"(__idt_addr));
__asm__("lidt%0"::"m"(__idt_addr));
修飾符 輸入/輸出 意義
= O 表示此Output操做表達式是Write-Only的。
+ O 表示此Output操做表達式是Read-Write的。
& O 表示此Output操做表達式獨佔爲其指定的寄存器。
% I 表示此Input 操做表達式中的C/C++表達式能夠和下一 個Input操做表達式中的C/C++表達式互換
--------------------------------------------------------------------------------------------------------------------------------------------------------------
13、make
In file included from stat.c:13:
../include/asm/segment.h: Assembler messages:
../include/asm/segment.h:27: Error: bad register name '%sil'
make[1]: *** [stat.o] Error 1
make[1]: Leaving directory '***/linux-0.11/fs'
make: *** [fs/fs.o] Error 2
出錯緣由:
fs 目錄下的 Makefile 中編譯選項使用了 -O 優化選項致使寄存器錯誤
解決方法:
將fs目錄下的Makefile 文件中的
CFLAGS =-Wall -O -fstrength-reduce -fomit-frame-pointer \
修改成
CFLAGS =-Wall -fstrength-reduce -fomit-frame-pointer \
14、make
tools/build.c: In function 'main':
tools/build.c:75: warning: implicit declaration of function 'MAJOR'
tools/build.c:76: warning: implicit declaration of function 'MINOR'
tmp/ccsMKTAS.o: In function 'main':
build.c:(.text+0xe1): undefined reference to 'MAJOR'
build.c:(.text+0xf7): undefined reference to 'MINOR'
collect2: ld returned 1 exit status
出錯緣由:'MAJOR' 和 'MINOR' 未定義
解決辦法:
咱們能夠在 include/linux/fs.h 文件中找到
#define MAJOR(a) (((unsigned)(a))>>8)
#define MINOR(a) ((a)&0xff)
而在 tools/build.c 中也有包含 #include <linux/fs.h>
那麼再看第一層目錄中的主 Makefile 文件
tools/build: tools/build.c
$(CC) $(CFLAGS) \
-o tools/build tools/build.c
好象確實沒有引用頭文件
簡單的添加 -Iinclude
從新編譯後出現一堆報標準C庫頭文件的錯誤
再添加 -nostdinc
又報 stderr fprintf 之類的錯誤
沒折,只好將
#define MAJOR(a) (((unsigned)(a))>>8)
#define MINOR(a) ((a)&0xff)
添加到 tools/build.c 文件中,而後刪除 #include <linux/fs.h>
15、make
make[1]: Leaving directory '***/linux-0.11/lib'
ld -s -x -M boot/head.o init/main.o \
kernel/kernel.o mm/mm.o fs/fs.o \
kernel/blk_drv/blk_drv.a kernel/chr_drv/chr_drv.a \
kernel/math/math.a \
lib/lib.a \
-o tools/system > System.map
ld: warning: cannot find entry symbol _start; defaulting to 08048a0
gcc -Wall -O -fstrength-reduce -fomit-frame-pointer \
-o tools/build tools/build.c
tools/build boot/bootsect boot/setup tools/system /dev/hd6 > Image
/dev/hd6: No such file or directory
Couldn't stat root device.
make: *** [Image] Error 1
解決辦法:
將第一層主 Makefile 文件中的
tools/system: boot/head.o init/main.o \
$(ARCHIVES) $(DRIVERS) $(MATH) $(LIBS)
$(LD) $(LDFLAGS) boot/head.o init/main.o \
$(ARCHIVES) \
$(DRIVERS) \
$(MATH) \
$(LIBS) \
-o tools/system > System.map
修改成
tools/system: boot/head.o init/main.o \
$(ARCHIVES) $(DRIVERS) $(MATH) $(LIBS)
$(LD) $(LDFLAGS) boot/head.o init/main.o \
$(ARCHIVES) \
$(DRIVERS) \
$(MATH) \
$(LIBS) \
-o tools/system
nm tools/system | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aU] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)'| sort > System.map
nm命令將目標文件中的各類符號列出來。
ROOT_DEV=/dev/hd6 修改成 ROOT_DEV=
16、make
/DISCARD/
*(.note.GNU-stack)
*(.gnu_debuglink)
*(.gnu.lto_*)
OUTPUT(tools/system elf32-i386)
nm tools/system | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aU] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)'| sort > System.map
nm: tools/system: no symbols
gcc -Wall -O -fstrength-reduce -fomit-frame-pointer \
-o tools/build tools/build.c
tools/build boot/bootsect boot/setup tools/system > Image
Root device is (3, 6)
Boot sector 512 bytes.
Setup is 312 bytes.
Non-Gcc header of 'system'
make: *** [Image] Error 1
解決辦法:
將第一層主 Makefile 文件中的
LDFLAGS =-s -x -M
修改成
LDFLAGS =-m elf_i386 -Ttext 0 -e startup_32
Image: boot/bootsect boot/setup tools/system tools/build
tools/build boot/bootsect boot/setup tools/system $(ROOT_DEV) > Image
sync
修改成
Image: boot/bootsect boot/setup tools/system tools/build
objcopy -O binary -R .note -R .comment tools/system tools/kernel
tools/build boot/bootsect boot/setup tools/kernel $(ROOT_DEV) > Image
rm tools/kernel -f
sync
objcopy命令能複製和轉化目標文件
objcopy -O binary -R .note -R .comment tools/system tools/kernel
-O binary tools/system tools/kernel將 tools/system 生成二進制文件 tools/kernel
-R .note -R .comment 刪除 .note段 和 .comment 段
將 tools/build.c 文件中的
if (((long *) buf)[5] != 0)
die("Non-GCC header of 'system'");
這段代碼註釋掉
//if (((long *) buf)[5] != 0)
// die("Non-GCC header of 'system'");
17、make
ld -m elf_i386 -Ttext 0 -e startup_32 boot/head.o init/main.o \
kernel/kernel.o mm/mm.o fs/fs.o \
kernel/blk_drv/blk_drv.a kernel/chr_drv/chr_drv.a \
kernel/math/math.a \
lib/lib.a \
-o tools/system
nm tools/system | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aU] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)'| sort > System.map
gcc -Wall -O -fstrength-reduce -fomit-frame-pointer \
-o tools/build tools/build.c
objcopy -O binary -R .note -R .comment tools/system tools/kernel
tools/build boot/bootsect boot/setup tools/system > Image
Root device is (3, 6)
Boot sector 512 bytes.
Setup is 312 bytes.
System is 128323 bytes.
rm tools/kernel -f
sync
終於編譯 linux 內核 0.11 版本成功了!
最 後也能夠利用趙炯博士在http://www.oldlinux.org/Linux.old/kernel/0.1x/ 這裏提供了修改 linux-0.11-060618-gcc4.tar.gz 好的 0.11版本的內核進行編譯,只要修改如下 Makefile 裏 -mcpu=i386 爲 -march=i386 還須要將 kernel/blk_drv/blk.h 文件第87行 將 #elif 修改成 #else 就能夠編譯經過了。
總結:編譯須要一個過程,學習也是一樣須要一個過程。雖然能夠利用趙博士修改好的 kernel-0.11 版快速的編譯內核,可是那樣就不會遇到太多有價值的編譯問題,而解決這些問題就是一個學習過程,相信趙博士在編譯0.11版本內核的時候也遇到了這些問 題。這樣我想起了高中解數學難題的時候,高手解體時老是省略了一些因式分解的過程,而對於菜鳥來講這些省略的過程是很是重要的。