赵炯;《Linux 内核完全注释 0.11 修正版 V3.0》
Linux 内核源代码中的 tools 目录中包含一个生成内核磁盘映像文件的工具程序 build.c,该程序将单独编译成可执行文件,在 linux/ 目录下的 Makefile 文件被调用运行,用于将所有内核编译代码链接和合并成一个可运行的内核映像文件 Image。具体方法是对 boot/ 中的 bootsect.s、setup.s 使用 8086 汇编器进行编译,分别生成各自的执行模块,再对源代码中的其他所有程序使用 GNU 的编译器 gcc/gas 进行编译,并连接成模块 system,然后使用 build 工具将这三块组合成一个内核映像文件 Image。
linux/Makefile 中的命令行:tools/build boot/bootsect boot/setup tools/system $(ROOT_DEV) > Image
build 程序有 4 个参数,分别是 bootsect 文件名、setup 文件名、system 文件名和可选的根文件系统设备文件名。前二者是由 as86 和 ld86 编译链接产生,它们具有 MINIX 执行文件格式,而 system 是由源代码各个子目录中编译产生的模块链接而成,具有 GNU a.out 执行文件格式。build 程序的主要工作就是去掉 bootsect 和 setup 的 MINIX 执行文件头结构信息、去掉 system 文件中的 a.out 头结构信息,只保留它们的代码和数据部分,然后把它们顺序组合在一起写入名为 Image 的文件中。
- 程序首先检查命令行上最后一个根设备文件名可选参数,若其存在,则读取设备文件的状态信息结构(stat),取出设备号。若命令行上不带该参数,则使用默认值。
- 然后对 bootsect 文件进行处理,读取该文件的 minix 执行头部信息,判断其有效性,然后读取随后 512 字节的引导代码数据,判断其是否具有可引导标志 0xAA55,并将前面获取的根设备号写入到 508,509 位移处,最后将该512 字节代码数据写到 stdout 标准输出,由 Make 文件重定向到 Image 文件。
- 接下来以类似的方法处理 setup 文件,若该文件长度小于 4 个扇区,则用 0 将其填满为 4 个扇区的长度,并写到标准输出 stdout 中。
- 最后处理 system 文件。该文件是使用 GCC 编译器产生的,所以执行头部格式是 GCC 类型的,与 linux 定义的 a.out 格式一样,在判断执行入口点是 0 后,就将数据写到标准输出 stdout 中,若其代码数据长度超过 128KB,则显示出错信息,最终形成的内核 Image 文件是:
- 第 1 个扇区上存放的是 bootsect 代码,长度正好是 512 字节;
- 第 2 个扇区开始的 4 个扇区,存放着 setup 代码,长度不超过 4 个扇区大小;
- 从第 6 个扇区开始存放 system 模块代码,长度不超过 build.c 第 35 行上定义的大小(#define SYS_SIZE 0x2000);
/*
 *  linux/tools/build.c
 *
 *  (C) 1991  Linus Torvalds
 */
/*
 * This file builds a disk-image from three different files:
 *
 * - bootsect: max 510 bytes of 8086 machine code, loads the rest
 * - setup: max 4 sectors of 8086 machine code, sets up system parm
 * - system: 80386 code for actual system
 *
 * It does some checking that all files are of the correct type, and
 * just writes the result to stdout, removing headers and padding to
 * the right amount. It also writes some system data to stderr.
 */
/*
 * Changes by tytso to allow root device specification
 */
/* fprintf */
/* contains exit */
/* unistd.h needs this */
/* contains read/write */
/* max nr of sectors of setup: don't change unless you also change
 * bootsect etc */
void die(char * str)
{
  fprintf(stderr,"%s\n",str);
  exit(1);
}
void usage(void)
{
  die("Usage: build bootsect setup system [rootdev] [> image]");
}
int main(int argc, char ** argv)
{
  int i,c,id;
  char buf[1024];
  char major_root, minor_root;
  struct stat sb;
  if ((argc != 4) && (argc != 5))
    usage();
  if (argc == 5) {
    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;
  }
  fprintf(stderr, "Root device is (%d, %d)\n", major_root, minor_root);
  if ((major_root != 2) && (major_root != 3) &&
      (major_root != 0)) {
    fprintf(stderr, "Illegal root device (major = %d)\n",
      major_root);
    die("Bad root device --- major #");
  }
  for (i=0;i<sizeof buf; i++) buf[i]=0;
  if ((id=open(argv[1],O_RDONLY,0))<0)
    die("Unable to open 'boot'");
  if (read(id,buf,MINIX_HEADER) != MINIX_HEADER)
    die("Unable to read header of 'boot'");
  if (((long *) buf)[0]!=0x04100301)
    die("Non-Minix header of 'boot'");
  if (((long *) buf)[1]!=MINIX_HEADER)
    die("Non-Minix header of 'boot'");
  if (((long *) buf)[3]!=0)
    die("Illegal data segment in 'boot'");
  if (((long *) buf)[4]!=0)
    die("Illegal bss in 'boot'");
  if (((long *) buf)[5] != 0)
    die("Non-Minix header of 'boot'");
  if (((long *) buf)[7] != 0)
    die("Illegal symbol table in 'boot'");
  i=read(id,buf,sizeof buf);
  fprintf(stderr,"Boot sector %d bytes.\n",i);
  if (i != 512)
    die("Boot block must be exactly 512 bytes");
  if ((*(unsigned short *)(buf+510)) != 0xAA55)
    die("Boot block hasn't got boot flag (0xAA55)");
  buf[508] = (char) minor_root;
  buf[509] = (char) major_root; 
  i=write(1,buf,512);
  if (i!=512)
    die("Write call failed");
  close (id);
  
  if ((id=open(argv[2],O_RDONLY,0))<0)
    die("Unable to open 'setup'");
  if (read(id,buf,MINIX_HEADER) != MINIX_HEADER)
    die("Unable to read header of 'setup'");
  if (((long *) buf)[0]!=0x04100301)
    die("Non-Minix header of 'setup'");
  if (((long *) buf)[1]!=MINIX_HEADER)
    die("Non-Minix header of 'setup'");
  if (((long *) buf)[3]!=0)
    die("Illegal data segment in 'setup'");
  if (((long *) buf)[4]!=0)
    die("Illegal bss in 'setup'");
  if (((long *) buf)[5] != 0)
    die("Non-Minix header of 'setup'");
  if (((long *) buf)[7] != 0)
    die("Illegal symbol table in 'setup'");
  for (i=0 ; (c=read(id,buf,sizeof buf))>0 ; i+=c )
    if (write(1,buf,c)!=c)
      die("Write call failed");
  close (id);
  if (i > SETUP_SECTS*512)
    die("Setup exceeds " STRINGIFY(SETUP_SECTS)
      " sectors - rewrite build/boot/setup");
  fprintf(stderr,"Setup is %d bytes.\n",i);
  for (c=0 ; c<sizeof(buf) ; c++)
    buf[c] = '\0';
  while (i<SETUP_SECTS*512) {
    c = SETUP_SECTS*512-i;
    if (c > sizeof(buf))
      c = sizeof(buf);
    if (write(1,buf,c) != c)
      die("Write call failed");
    i += c;
  }
  
  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");
  close(id);
  fprintf(stderr,"System is %d bytes.\n",i);
  if (i > SYS_SIZE*16)
    die("System is too big");
  return(0);
}                
                










