感謝這篇文章的作者: http://www.cnblogs.com/strugglesometimes/p/4231359.html
編譯是個很蛋疼的事情,本想把linux0.12在bochs上跑起來然后就可以各模塊的學習,沒想各種問題。
問題1:
1 gas -c -o boot/head.o boot/head.s 2 make: gas: Command not found
gas已過時,將所有Makfile里gas -> as
具體解決方法
1
|
msed gas as
|
msed 是個簡單的shell 函數,具體定義見下面的傳送門。
傳送門:http://www.cnblogs.com/strugglesometimes/p/4231348.html
問題2:
1 boot/head.s:43: Error: unsupported instruction `mov' 2 boot/head.s:47: Error: unsupported instruction `mov' 3 boot/head.s:59: Error: unsupported instruction `mov' 4 boot/head.s:61: Error: unsupported instruction `mov' 5 boot/head.s:136: Error: invalid instruction suffix for `push' 6 boot/head.s:137: Error: invalid instruction suffix for `push' 7 boot/head.s:138: Error: invalid instruction suffix for `push' 8 boot/head.s:139: Error: invalid instruction suffix for `push' 9 boot/head.s:140: Error: invalid instruction suffix for `push' 10 boot/head.s:151: Error: invalid instruction suffix for `push' 11 boot/head.s:152: Error: invalid instruction suffix for `push' 12 boot/head.s:153: Error: invalid instruction suffix for `push' 13 boot/head.s:154: Error: operand type mismatch for `push' 14 boot/head.s:155: Error: operand type mismatch for `push' 15 boot/head.s:161: Error: invalid instruction suffix for `push' 16 boot/head.s:163: Error: invalid instruction suffix for `pop' 17 boot/head.s:165: Error: operand type mismatch for `pop' 18 boot/head.s:166: Error: operand type mismatch for `pop' 19 boot/head.s:167: Error: invalid instruction suffix for `pop' 20 boot/head.s:168: Error: invalid instruction suffix for `pop' 21 boot/head.s:169: Error: invalid instruction suffix for `pop' 22 boot/head.s:214: Error: unsupported instruction `mov' 23 boot/head.s:215: Error: unsupported instruction `mov' 24 boot/head.s:217: Error: unsupported instruction `mov'
這是由於在64位機器上編譯的原因,需要告訴編譯器,我們要編譯32位的code,在所有Makefile的AS后面添加 --32,CFLAGS中加-m32
具體解決方法
msed as$ as\ --32
msed -O -O\ -m32
問題3:
boot/head.s: Assembler messages: boot/head.s:231: Error: alignment not a power of 2 make: *** [boot/head.o] Error 1
把align n -> align 2^n
具體解決方法
sed -i 's/align 2/align 4/g' boot/head.s sed -i 's/align 3/align 8/g' boot/head.s
問題4:
gcc: error: unrecognized command line option ‘-fcombine-regs’ gcc: error: unrecognized command line option ‘-mstring-insns’
把這兩個刪掉即可,現在GCC已經不支持了
具體解決方法
msed -fcombine-regs \
msed -mstring-insns \
問題5:
額。。。。為了更快的找到Error的地方,我把所有的warning 都關掉了即在CFLAGS 中加-w
具體解決方法
msed -Wall -w
問題6:
In file included from init/main.c:8:0: init/main.c:23:29: error: static declaration of ‘fork’ follows non-static declaration static inline _syscall0(int,fork) ^ include/unistd.h:151:6: note: in definition of macro ‘_syscall0’ type name(void) \ ^ init/main.c:24:29: error: static declaration of ‘pause’ follows non-static declaration static inline _syscall0(int,pause) ^ include/unistd.h:151:6: note: in definition of macro ‘_syscall0’ type name(void) \ ^ include/unistd.h:241:5: note: previous declaration of ‘pause’ was here int pause(void); ^ init/main.c:26:29: error: static declaration of ‘sync’ follows non-static declaration static inline _syscall0(int,sync) ^ include/unistd.h:151:6: note: in definition of macro ‘_syscall0’ type name(void) \ ^ include/unistd.h:252:5: note: previous declaration of ‘sync’ was here int sync(void);
這里是由於include/unistd.h 中聲明了一次pause() sync() fork(), 而在main.c 中通過宏又定義了這三個函數,但定義時多了static 限定,與聲明不同,所以出錯。所以直接把unistd.h中的聲明去掉。
問題7:
init/main.c:179:12: error: static declaration of ‘printf’ follows non-static declaration static int printf(const char *fmt, ...)
這個問題困擾了好久,網上的解決方案都是把static去掉,但是這樣做,后面在鏈接的時候會出現另一個錯誤undefined reference to '_put'. 新的問題是由於GCC會對printf進行優化,把無參的printf優化成put,而linux0.12的libc中又沒有實現put才會導致新的問題。那么現在回到這個問題上,猜測應該也是由於GCC本身對printf這個函數名有特別的關照所致,所以把printf稍微改下名,printf -> printw。發現果然就編譯通過了。
具體解決方案:
sed -i 's/ printf/ printw/g' init/main.c
問題8:
init/main.c: In function ‘main’: init/main.c:176:3: error: ‘asm’ operand has impossible constraints __asm__("int $0x80"::"a" (__NR_pause):"ax");
類似的問題在后面編譯中出現好多,C內嵌匯編的格式__asm__(匯編語句:輸入寄存器:輸出寄存器:可能被修改的寄存器),最新的GCC規定輸入或輸出寄存器不能出現在可能被修改的寄存器中,目前看到網上的方法是把所有類似問題的可能被修改的寄存器全部刪掉。
具體解決方法
find -type f -exec sed -i 's/:\"\w\{2\}\"\(,\"\w\{2\}\"\)*)/:) /g' {} \;
問題9:
make[1]: gld: Command not found
同gas, 把gld -> ld
具體解決方法
msed gld ld
問題10:
ld -r -o kernel.o sched.o sys_call.o traps.o asm.o fork.o panic.o printk.o vsprintf.o sys.o exit.o signal.o mktime.o ld: Relocatable linking with relocations from format elf32-i386 (sched.o) to format elf64-x86-64 (kernel.o) is not supported
同問題2,告訴ld以32位鏈接,在ld命令后面加 -m elf_i386
具體解決方法
msed ld$ ld\ -m\ elf_i386
問題11:
../include/asm/segment.h: Assembler messages:
../include/asm/segment.h:27: Error: bad register name `%sil'
去segment.h 的第27行找,沒找到sil相關的東西,根據網上的方法,把=r或r 改成=q或q,果然就好了,這里應該是編譯器造成的,r表示任意寄存器,在編譯的時候就用了sil這個寄存器,可為什么無效還會被用到呢。q表示使用eax,ebx,ecx,edx中任意一個。
具體解決方法
sed -i s'/r"/q"/g' include/asm/segment.h
問題12:
exec.c: In function ‘copy_strings’: exec.c:162:44: error: lvalue required as left operand of assignment !(pag = (char *) page[p/PAGE_SIZE] =
if (!(pag = (char *) page[p/PAGE_SIZE]) && !(pag = (char *) page[p/PAGE_SIZE] = (unsigned long *) get_free_page())) return 0;
以上是原始code,以下是OK的code
if ((!page[p/PAGE_SIZE]) &&
!(page[p/PAGE_SIZE] = (unsigned long *) get_free_page())) return 0; else pag = (char *) page[p/PAGE_SIZE];
問題13:
In file included from floppy.c:42:0: blk.h:90:6: error: #elif with no expression #elif
這里把第90行的#elif -> #else
問題14:
make[1]: gar: Command not found
老問題了,gar -> ar
msed gar ar
問題15:
malloc.c: In function ‘malloc’: malloc.c:156:46: error: lvalue required as left operand of assignment bdesc->page = bdesc->freeptr = (void *) cp = get_free_page();
和問題12一樣
bdesc->page = bdesc->freeptr = (void *) cp = get_free_page();
上面是原始的,下面是OK的,把代碼拆分下就好
cp = get_free_page(); bdesc->page = bdesc->freeptr = (void *) cp;
========================================================================================
9、ld: warning: cannot find entry symbol _start; defaulting to 0000000008048098
ld -m elf_i386 -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 0000000008048098
這是因為ld在將所有目標文件鏈接起來時,不知道程序的入口點在哪里。由內核的啟動過程知其從head.s中開始執行,因此給head.s的 .text 段添加一句 .globl startup_32,然后給 ./Makefile 中的ld加上選項 -e startup_32 以指定入口點。
另外注意,僅指定入口點的標號還不夠,后續使用tools/build構建Image仍會出錯,因為此時程序入口點的地址仍是0x8048098(見上方出錯信息的最后一行),而在tools/build.c中處理system模塊時,認定的合法入口點地址為0x0:
tools/build.c: 157 if ((id=open(argv[3],O_RDONLY,0))<0) 158 die("Unable to open 'system'"); 159 if (read(id,buf,GCC_HEADER) != GCC_HEADER) 160 die("Unable to read header of 'system'"); 161 if (((long *) buf)[5] != 0) //判斷入口點地址是否為0x0 162 die("Non-GCC header of 'system'");
因此還需添加 -Ttext 0 選項使startup_32標號對應的地址為0x0(更詳細的說明見ld的手冊,另有一個討論見這里)。
還有一個問題是,上述代碼中第161行上執行的檢查是buf[]中第24-27的四個字節的內容,因為程序假設在這個位置上保存着ELF頭中的程序入口地址,然而事實上對於本機的GCC編譯出的目標文件,使用readelf命令查看其ELF頭格式如下:
~/Src/LinuxKernel/0.11/linux-0.11-deb$ readelf -h tools/system
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x0
Start of program headers: 52 (bytes into file)
...
再結合od命令的結果:
~/Src/LinuxKernel/0.11/linux-0.11-deb$ od -w4 -N 80 -x tools/system
0000000 457f 464c
0000004 0101 0001
0000010 0000 0000
*
0000020 0002 0003
0000024 0001 0000
0000030 0000 0000
0000034 0034 0000
...
可以看出入口點地址事實上位於ELF頭中第28-30字節的位置上,也就是((long*)buf)[6]處,所以應對tools/build.c作對應的修改。(關於ELF頭的格式,更詳細的討論見這里)
10.
gcc -m32 -Wall -O -fstrength-reduce -fomit-frame-pointer -mtune=i386 -o tools/build tools/build.c In file included from /usr/include/features.h:395:0, from /usr/include/stdio.h:27, from tools/build.c:23: /usr/include/gnu/stubs.h:7:27: fatal error: gnu/stubs-32.h: No such file or directory # include <gnu/stubs-32.h>
很顯然這又是一個因64位系統上缺少32位庫導致的問題(更多細節見這里),從源里裝個32位庫即可:
sudo aptitude install libc6-dev-i386
11.
tools/build.c: In function ‘main’: tools/build.c:72:4: warning: implicit declaration of function ‘MAJOR’ [-Wimplicit-function-declaration] major_root = MAJOR(sb.st_rdev); tools/build.c:73:4: warning: implicit declaration of function ‘MINOR’ [-Wimplicit-function-declaration] minor_root = MINOR(sb.st_rdev); /tmp/cctAdnmd.o: In function `main': build.c:(.text+0xc7): undefined reference to `MAJOR' build.c:(.text+0xe1): undefined reference to `MINOR' collect2: error: ld returned 1 exit status
build.c中包含的是標准庫的頭文件 /usr/include/linux/fs.h ,但是這個頭文件里並沒有實現MAJOR和MINOR宏。解決方法很簡單,從include/linux/fs.h中把這兩個宏復制到build.c中即可:
#define MAJOR(a) (((unsigned)(a))>>8)
#define MINOR(a) ((a)&0xff)
12.
tools/build boot/bootsect boot/setup tools/system /dev/hd6 > Image /dev/hd6: No such file or directory Couldn't stat root device.
這是因為在源代碼頂層目錄的Makefile中所指定的根設備為/dev/hd6(代表第二個硬盤的第一個分區), 而本機上並不存在這個設備所致。Linus當年之所以指定根設備為/dev/hd6, 是因為他把Linux 0.11安裝在了機子的第二塊硬盤上。我們這里打算通過在bochs中模擬軟盤來啟動編譯好的系統,故在頂層目錄Makefile中設定根設備為軟盤:
ROOT_DEV=FLOPPY
tools/build.c使用Makefile中指定的ROOT_DEV對應的設備號覆蓋Image文件中的第509、510字節(即地址508、509處),這兩個字節所保存的根設備號將被bootsect.s使用。
tools/build.c 115 buf[508] = (char)minor_root 116 buf[509] = (char)major_root 117 i = write(1, buf, 512); //注意標准輸出已經被重定向至Image文件
3.總結
基本上編譯中會遇到的問題就是這些,另推薦這篇博文, 主要介紹的是在32位Ubuntu 11系統下編譯並調試運行linux 0.11的過程。編譯成功源代碼只是第一步,接下來通過調試源代碼弄清內核的工作細節才是最艱苦的,不過那將是另一個故事了:-)
============================================================================
http://chfj007.blog.163.com/blog/static/173145044201132523034138/
Linux 環境下編譯 0.11版本內核 kernel
系統環境:Fedora 13 + gcc-4.4.5
最近在看《linux內核0.11完全注釋》一書,由於書中涉及匯編語言的地方眾多,本人在大學時匯編語言學得一塌糊塗,所以實在看不下去了,頭都大了只好匆匆看了個頭尾(前面幾章和最后一章)。看來即使有《九陰真經》這樣的武功秘籍,內功不夠也是修煉不出來神馬來的。於是索性下了個0.11版本的kernel下來嘗試編譯一把。
linux-0.11.tar.gz 下載地址:ftp://ftp.kernel.org/pub/linux/kernel/Historic/old-versions/
下面開始工作:
1、 tar xvfz linux-0.11.tar.gz
2、 cd linux-0.11
3、 make
make: as86: Command not finded
make: ***
出錯原因:as86 匯編器未安裝
解決辦法:
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版本內核的時候也遇到了這些問題。這樣我想起了高中解數學難題的時候,高手解體時總是省略了一些因式分解的過程,而對於菜鳥來說這些省略的過程是非常重要的。
===================================================================================================
bochs下安裝linux-0.11
首先非常感謝趙炯老師給我們提供了豐富的資源,包括他寫的《Linux內核完全注釋》和他維護的網站www.oldlinux.org
一、准備工作
軟件:bochs-20060202.tar.gz
軟盤映像:http://www.oldlinux.org/Linux.old/images/bootimage-0.11-20040305
http://www.oldlinux.org/Linux.old/images/rootimage-0.11-20040305
二、.bochsrc文件(注釋已刪掉)
ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15
ata2: enabled=0, ioaddr1=0x1e8, ioaddr2=0x3e0, irq=11
ata3: enabled=0, ioaddr1=0x168, ioaddr2=0x360, irq=9
ata0-master: type=disk, path=minix203.img, cylinders=940, heads=6, spt=17, translation=none
boot: a
floppy_bootsig_check: disabled=0
log: bochsout.txt
panic: action=ask
error: action=report
info: action=report
debug: action=ignore
debugger_log: -
parport1: enabled=1, file="parport.out"
vga_update_interval: 300000
keyboard_serial_delay: 250
keyboard_paste_delay: 100000
mouse: enabled=1
private_colormap: enabled=0
ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:00, ethmod=linux, ethdev=eth0
keyboard_mapping: enabled=0, map=
三、制作軟盤映像
mshost:/usr/local/bochs # ./bximage
========================================================================
bximage
Disk Image Creation Tool for Bochs
$Id: bximage.c,v 1.31 2005/11/20 20:26:35 vruppert Exp $
========================================================================
Do you want to create a floppy disk image or a hard disk image?
Please type hd or fd. [hd]
What kind of image should I create?
Please type flat, sparse or growing. [flat]
Enter the hard disk size in megabytes, between 1 and 129023
[10]
mshost:/usr/local/bochs # ./bximage
========================================================================
bximage
Disk Image Creation Tool for Bochs
$Id: bximage.c,v 1.31 2005/11/20 20:26:35 vruppert Exp $
========================================================================
Do you want to create a floppy disk image or a hard disk image?
Please type hd or fd. [hd] fd
Choose the size of floppy disk image to create, in megabytes.
Please type 0.16, 0.18, 0.32, 0.36, 0.72, 1.2, 1.44, 1.68, 1.72, or 2.88.
[1.44]
I will create a floppy image with
cyl=80
heads=2
sectors per track=18
total sectors=2880
total bytes=1474560
What should I name the image?
[a.img] a.img
Writing: [] Done.
I wrote 1474560 bytes to a.img.
The following line should appear in your bochsrc:
floppya: image="a.img", status=inserted
四、制作啟動盤和根文件系統盤
首先按照步驟三再作一張b.img,然后運行下列命令:
dd if=bootimage-0.11-20040305 of=a.img
dd if=rootimage-0.11-20040305 of=b.img
五、啟動
mshost:/usr/local/bochs # ./bochs
啟動后會出現
Insert root floppy and press ENTER
之后cp b.img a.img(注意備份a.img),最后回車,linux-0.11就啟動起來了!
========================================================
questions:
1、用bochs調試遇到"Unable to mount root"
把啟動盤的Image文件(bootimage)的第509,510字節修改成0x1d, 0x02。這兩個字節是根文件系統所在設備號。0x021d表示第2個軟盤(b:)設備。
2、為什么在轉到B盤后一直顯示的是Reset-floppy called
請使用Bochs 2.1.1版本再試試。