zoukankan      html  css  js  c++  java
  • <摘录>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 "
     "rorl $16,%%edx "
     "movb %%dl,%1 "
     "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 "                  __asm__("push %%edx "    
           "rorl $16,%%edx "                        "movw %%dx,%0 "        
           "movb %%dl,%1 "                          "rorl $16,%%edx "       
           "movb %%dh,%2"                              "movb %%dl,%1 "         
           ::"m" (*((addr)+2)),                           "movb %%dh,%2 "             
             "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 "
           "je 1f "
           "movw %%dx,%1 "
           "xchgl %%ecx,_current "
           "ljmp %0 " ------------------------------这里修改为 "ljmp *%0 "
           "cmpl %%ecx,_last_task_used_math "
           "jne 1f "
           "clts "
           "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 "                        unsigned long __base;                                               
       movb %2,%%dl "                                     __asm__("movb %3,%%dh "
       shll $16,%%edx "                                    "movb %2,%%dl "
       movw %1,%%dx"                                          "shll $16,%%edx "
       "=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 "
    "xor %ebx,%ebx "
    "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版本内核的时候也遇到了这些问 题。这样我想起了高中解数学难题的时候,高手解体时总是省略了一些因式分解的过程,而对于菜鸟来说这些省略的过程是非常重要的。

  • 相关阅读:
    (21)tar打包命令详解
    (19)ln命令:在文件之间建立链接(硬链接和软链接)
    (20)打包(归档)和压缩概念
    (18)文件和目录管理类的常用命令3
    SSH 登录警告:WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
    (17)文件和目录管理类的常用命令2
    (16)文件和目录管理类的常用命令1
    (15)Linux命令基本格式
    (14)Linux绝对路径和相对路径
    (13)Linux文件系统的优缺点
  • 原文地址:https://www.cnblogs.com/hummersofdie/p/3645218.html
Copyright © 2011-2022 走看看