首页 > 代码库 > linux0.12 链接过程

linux0.12 链接过程

终于编译OK了。。可链接就是一大堆错误

问题1:

boot/head.o: In function `startup_32:(.text+0x10): undefined reference to `_stack_startboot/head.o: In function `startup_32:(.text+0x2e): undefined reference to `_stack_startboot/head.o: In function `after_page_tables:(.text+0x540c): undefined reference to `_mainboot/head.o: In function `ignore_int:(.text+0x5440): undefined reference to `_printk

一堆的undefined reference to xxxxx,stack_start 定义在kernel/sched.c中,main 定义在init/main.c中,实际上这些符号是存在的,可为什么链接的时候找不到呢。最终找到原因,是由于现在GCC在编译C时不会在函数前加"_"了,即以前GCC编译一个C函数,例如:fun() 编译后会变成_fun(), 而现在还是fun(),所以就导致了符号找不到,这个叫name magling,有兴趣的可以自行google下。

解决的思路有两种:

1. 网上的是把把有引用C符号的.s 和 .S中的变量前的"_"全部删掉,这个用正则不太好弄,得一个个删。

2. 在查name magling的时候,有幸让我看到了-fleading-underscore这个option, 可以让让GCC在编译的时候强制加上"_",这种只需要在CFLAGS中加-fleading-underscore即可,第二种方法更省事。

方案2具体解决方法

msed -O -O\ -fleading-underscore

请记得在改完后,make clean,后再make。

问题2:

main.c:(.text+0x6c): undefined reference to `_printfmain.c:(.text+0x87): undefined reference to `_printfmain.c:(.text+0x142): undefined reference to `_putsmain.c:(.text+0x1e9): undefined reference to `_printf

额,之前编译的时候似乎正则没全部匹配到,只好手动改下了,把init/main.c 里的printf 全改成 printw

问题3:

vsprintf.c:(.text+0x18cb): undefined reference to `___stack_chk_failfs/fs.o: In function `_do_execve:(.text+0x3554): undefined reference to `___stack_chk_failkernel/chr_drv/chr_drv.a(tty_ioctl.o): In function `_tty_ioctl:tty_ioctl.c:(.text+0x51d): undefined reference to `___stack_chk_fail

直接google到的答案,这是由于ubuntu的GCC默认是加-fstack-protector的,这里在CFLAGS中去掉这个option,加入-fno-stack-protector,至于这个option到底有什么作用,请有兴趣的自行google

msed -O -O\ -fno-stack-protector

问题4:

build.c:(.text+0x1c): undefined reference to `_stderrbuild.c:(.text+0x24): undefined reference to `___fprintf_chk/tmp/cctqTbXN.o: In function `_main:build.c:(.text+0x7d): undefined reference to `_strcmpbuild.c:(.text+0x99): undefined reference to `___xstatbuild.c:(.text+0xab): undefined reference to `_perror

这个问题是由问题1的解决方案引入的,build.c链接时用的是glibc,而glibc在是没-fleading-underscore这个option的,所以在编译build.c时我们 把这个option去掉。。。,目前没找到什么好方法,我只好手动把CFLAGS展开,然后去掉-fleading-underscore了

修改linux0.12根目录下的Makefile第50行

tools/build: tools/build.c     $(CC) $(CFLAGS)      -o tools/build tools/build.c

上面是原来的,下面是展开后的

tools/build: tools/build.c    $(CC) -w -O -fno-stack-protecto -m32 -fstrength-reduce -fomit-frame-pointer     -o tools/build tools/build.c

 问题5:

build.c:(.text+0xcb): undefined reference to `MAJORbuild.c:(.text+0xe3): undefined reference to `MINORbuild.c:(.text+0x16c): undefined reference to `MAJORbuild.c:(.text+0x184): undefined reference to `MINOR

缺少两个宏定义,将以下两个定义加入到build.c中即可

#define MAJOR(x) (((unsigned)(x))>>8)                                                                                                           #define MINOR(x) ((x)&0xff)

问题6:

/dev/hd6: No such file or directoryCouldnt stat root device.

这个是在linux0.12根目录下的Makefile中定义的

ROOT_DEV=/dev/hd6SWAP_DEV=/dev/hd2

但我们没这东西,阅读build.c 代码,了解到这里可以写成FLOPPY代替/dev/hd6

if (argc > 4) {        if (strcmp(argv[4], "FLOPPY")) {            if (stat(argv[4], &sb)) {                perror(argv[4]);                die("Couldn‘t stat root device.");            }            major_root = MAJOR(sb.st_rdev);            minor_root = MINOR(sb.st_rdev);        } else {            major_root = 0;            minor_root = 0;        }    } else {        major_root = DEFAULT_MAJOR_ROOT;        minor_root = DEFAULT_MINOR_ROOT;    }

问题6:

/dev/hd2: No such file or directoryCouldnt stat root device.make: *** [Image] Error 1

与上个问题类似,相同的地方,阅读build.c,可以用NONE代替/dev/hd2

if (argc == 6) {        if (strcmp(argv[5], "NONE")) {            if (stat(argv[5], &sb)) {                perror(argv[5]);                die("Couldn‘t stat root device.");            }            major_swap = MAJOR(sb.st_rdev);            minor_swap = MINOR(sb.st_rdev);        } else {            major_swap = 0;            minor_swap = 0;        }    } else {        major_swap = DEFAULT_MAJOR_SWAP;        minor_swap = DEFAULT_MINOR_SWAP;    }

问题7:

Non-GCC header of systemmake: *** [Image] Error 1

我们先看代码

if ((id=open(argv[3],O_RDONLY,0))<0)    die("Unable to open ‘system‘");if (read(id,buf,GCC_HEADER) != GCC_HEADER)    die("Unable to read header of ‘system‘");if (((long *) buf)[5] != 0)    die("Non-GCC header of ‘system‘");for (i=0 ; (c=read(id,buf,sizeof buf))>0 ; i+=c )     if (write(1,buf,c)!=c)         die("Write call failed");    

大致分4步,1. 打开第4个参数的文件,即system,2. 读system的头1024个字节;3. 判断程序入口地址是不是0x0;4. 读取剩下的部分写入Image中。错在第三步。我们用readelf -h system来看下

~/workspace/blog/linux-0.12/tools$ readelf -h system ELF Header:  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00   Class:                             ELF32  Data:                              2s 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:               0x80480a0  Start of program headers:          52 (bytes into file)  Start of section headers:          133532 (bytes into file)  Flags:                             0x0  Size of this header:               52 (bytes)  Size of program headers:           32 (bytes)  Number of program headers:         3  Size of section headers:           40 (bytes)  Number of section headers:         8  Section header string table index: 7

这里可以看到程序的入口是0x80480a0,不是0x0,所以要告诉链接器,把text段放到0x0上,在linux0.12根目录下面的Makefile,修改ld命令,在其后面加上-Ttext 0x0 -e startup_32, 同时在head.s 的.global中加入startup_32以便外部访问。除此而外,GCC头的第24个字节是version,第26个字节开始才是程序入口。。。额,索性直接把判断GCC头那段给干掉。。。

然后链接也OK了,生成了Image 进入调试阶段。。

 

linux0.12 链接过程