零、文档 【1】 http://lxr.linux.no/linux+v3.3.6/arch/x86/boot/header.S header.S位置 【2】 http://lxr.linux.no/linux+v3.3.6/Documentation/x86/boot.txt header.S的文档 【3】 http://lxr.linux.no/linux+v3.3.6/arch/x86/include/asm/bootparam.hsetup_header等数据结构定义 【4】 http://lxr.linux.no/linux+v3.3.6/arch/x86/boot/setup.ld setup部分链接文件,描述了相关的符号 一、概述 在2.6以后,系统启动由header.S开始,功能上它是老版本中bootsect.S和setup.S的结合。不过一般情况下,系统都会有loader,bootsect是用不上的。现在的loader会做很多事。这个文件中bootsect的功能已经不支持了,如果从这个文件中的bootsect(开始512字节)启动,则直接提示按任意键重启电脑。真正还有意义的是setup部分,它会进行:磁盘复位,设置堆栈,检查setup.elf的安装,清bss段,跳转到boot/main.c等。 在header.S中有一个核心的数据结构setup_header,它定义在【3】中。它的位置牌bootsect尾部和setup前部。
在setup.ld中有如下两句,说明setup.bin结束位置一定不会超过X+0x8000;hdr位置一定是0x1f1。根据header.S中的分析可知,栈底一定不会超过X+10000,溢出时最大相距X偏移为0xfffc(4字节对齐了)
. = ASSERT(_end <= 0x8000, "Setup too big!"); /*若表达式不为真,打印msg,结束链接*/
. = ASSERT(hdr == 0x1f1, "The setup header has the wrong offset!");
# vmlinux # ^ # | # +-< $(vmlinux-init) # | +--< init/version.o + more # | # +--< $(vmlinux-main) # | +--< driver/built-in.o mm/built-in.o + more # | # +-< kallsyms.o (see description in CONFIG_KALLSYMS section) vmlinux-init := $(head-y) $(init-y) vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y) vmlinux-all := $(vmlinux-init) $(vmlinux-main) vmlinux-lds := arch/$(SRCARCH)/kernel/vmlinux.lds 在http://lxr.linux.no/linux+v3.3.6/arch/x86/Makefile中: head-y := arch/x86/kernel/head_$(BITS).o head-y += arch/x86/kernel/head$(BITS).o head-y += arch/x86/kernel/head.o head-y += arch/x86/kernel/init_task.o 在arch/x86/kernel目录下存在一些head开头的汇编或c语言源代码的。其中BITS指出是32位还是64位。 head.c,head32.c,head64.c,head_32.S,head_64.S,init_task.c (在顶层makefile)而其它子目标都是指向具体的根目录下的子目录了: init-y := init/ drivers-y := drivers/ sound/ firmware/ net-y := net/ libs-y := lib/ core-y := usr/ 在http://lxr.linux.no/linux+v3.3.6/arch/x86/boot/Makefile中: bzImage: $(obj)/setup.bin $(obj)/vmlinux.bin $(obj)/tools/build FORCE 说明bzImage是依赖于setup.bin vmlinux.bin 和tools的。tools最终会把setup.bin和vmlinux.bin结合成一个内核文件。 在http://lxr.linux.no/linux+v3.3.6/arch/x86/boot/Makefile文件中 $(obj)/setup.bin: $(obj)/setup.elf FORCE $(obj)/setup.elf: $(src)/setup.ld$(SETUP_OBJS) FORCE SETUP_OBJS = $(addprefix $(obj)/,$(setup-y)) setup-y += a20.o bioscall.o cmdline.o copy.o cpu.o cpucheck.o setup-y += early_serial_console.o edd.o header.o main.o mca.o memory.o setup-y += pm.o pmjump.o printf.o regs.o string.o tty.o video.o setup-y += video-mode.o version.o setup-$(CONFIG_X86_APM_BOOT) += apm.o setup-y += video-vga.o setup-y += video-vesa.o setup-y += video-bios.o 所以,setup.bin是由上面这些文件构成,并最终和vmlinux结合起来。由此可见,setup.bin是我们最先执行的部分 在http://lxr.linux.no/linux+v3.3.6/arch/x86/boot/setup.ld中指出 ENTRY(_start) 说明_start是setup.bin最终的入口,而这个符号正是来源于header.S中的setup部分(第二个512字节)
二、setup相关
在setup部分有一个核心的数据结构,setup_header(【3】)。它各字段描述在【2】中。这个结构中,有些字段同内核(kernel)给加载器(bootloader)用的,一些是加载器给内核的,一些是加载器读完后再写进去。这三种行为分别称为"读","写","修改"。一个通用的加载器应该将有标志"强制"(obligatory)标志的字段,如果某些加载器想将内核加载到‘非标准’地址,应该改写有"重定位"(reloc)的字段,其它类型的加载器可以忽略这些字段。具体内容参见文档【2】。
三、几个标志
loadflags:bit 0:LOADED_HIGH,=1表示加载内核到0x100000,=0加载到0x10000
bit 5:QUIET_FLAG,=1不显示早期msg,否则显示之
bit 6:KEEP_SEGMENTS,=1在32bit入口时不重新加载段寄存器,假设都是以0开始的段,否则重加载之
bit 7:CAN_USE_HEAP,=1说明heap_end_ptr 字段中输入的值是可用的,否则不可用
四、代码分析
/* * header.S * * Copyright (C) 1991, 1992 Linus Torvalds * * Based on bootsect.S and setup.S * modified by more people than can be counted * * Rewritten as a common file by H. Peter Anvin (Apr 2007) * * BIG FAT NOTE: We're in real mode using 64k segments. Therefore segment * addresses must be multiplied by 16 to obtain their respective linear * addresses. To avoid confusion, linear addresses are written using leading * hex while segment addresses are written as segment:offset. * */ #include <asm/segment.h> #include <generated/utsrelease.h> #include <asm/boot.h> #include <asm/e820.h> #include <asm/page_types.h> #include <asm/setup.h> #include "boot.h" #include "voffset.h" #include "zoffset.h" BOOTSEG = 0x07C0 /* original address of boot-sector */ SYSSEG = 0x1000 /* historical load address >> 4 */ #ifndef SVGA_MODE #define SVGA_MODE ASK_VGA #endif #ifndef RAMDISK #define RAMDISK 0 #endif #ifndef ROOT_RDONLY #define ROOT_RDONLY 1 #endif /* For COFF targets, the .section directive is used in one of the following ways: .section name [, "flags"] .section name [, subsegment] If the optional argument is quoted, it is taken as flags to use for the sectio n. Each flag is a single character. The following flags are recognized: b bss section (uninitialized data) n section is not loaded w writable section d data section r read-only section x executable section s shared section (meaningful for PE targets) */ /* * 主要功能:磁盘复位,设置堆栈,检查setup.elf的安装,清0bss段,中转到boot/main.c * 主要数据结构:setup_header */ .code16 /*这是16位汇编*/ .section ".bstext", "ax" /*可分配内存(a,allocatable)、可执行的一个段*/ .global bootsect_start /*blobal使此标号对ld可见*/ bootsect_start: #ifdef CONFIG_EFI_STUB /*EFI启动相关*/ # "MZ", MS-DOS header .byte 0x4d .byte 0x5a #endif # Normalize the start address ljmp $BOOTSEG, $start2 /*跳转到start2标号处,BOOTSEG=0x7c0见前面定义*/ start2: /*它位于0x7c00处*/ movw %cs, %ax /*ax,ds,es,ss都等于cs*/ movw %ax, %ds movw %ax, %es movw %ax, %ss xorw %sp, %sp /*栈指针sp设置为0*/ sti /*开中断*/ cld /*清除方向,告诉si,di递增,std告诉它们递减*/ /* * 下面这段代码实际上是显示bugger_off_msg * 标号处的几句话,调用的int 10h中断 */ movw $bugger_off_msg, %si /*bugger_off_msg标号地址放入si*/ msg_loop: lodsb /*Load Accumulator from string 将DS:SI处内容取一字节放入al,由上可知 DS=CS */ andb %al, %al /*看al是不是0,如果是那个0的话,会提示重启*/ jz bs_die /*如果al==0,跳转到bs_die,表示显示结束*/ movb $0xe, %ah /*设置int 10h中断参数,0x0E表示显示字符,光标前移*/ movw $7, %bx /*AL=字符,BL=颜色*/ int $0x10 /*int 10中断/ jmp msg_loop /*跳回去显示下一个字符*/ bs_die: # Allow the user to press a key, then reboot xorw %ax, %ax int $0x16 /*从键盘读一字符,AL=字符码,AH=扫描码*/ int $0x19 /*寻找并加载MBR到0x7c00,并跳去执行(重启了)*/ # int 0x19 should never return. In case it does anyway, # invoke the BIOS reset code... /* * ljmp section,offset,0xffff0是bois位置,即cpu上电后执行的第一句 * 上面返回的话,跳转到0xffff0去,相当于重启 * 两处重启代码,理论上第一处重启代码不会返回 */ ljmp $0xf000,$0xfff0 #ifdef CONFIG_EFI_STUB /*EFI启动相关*/ .org 0x3c # # Offset to the PE header. # .long pe_header #endif /* CONFIG_EFI_STUB */ .section ".bsdata", "a" /*定义一个节*/ bugger_off_msg: .ascii "Direct booting from floppy is no longer supported.\r\n" .ascii "Please use a boot loader program instead.\r\n" .ascii "\n" .ascii "Remove disk and press any key to reboot . . .\r\n" .byte 0 #ifdef CONFIG_EFI_STUB pe_header: .ascii "PE" .word 0 coff_header: #ifdef CONFIG_X86_32 .word 0x14c # i386 #else .word 0x8664 # x86-64 #endif .word 2 # nr_sections .long 0 # TimeDateStamp .long 0 # PointerToSymbolTable .long 1 # NumberOfSymbols .word section_table - optional_header # SizeOfOptionalHeader #ifdef CONFIG_X86_32 .word 0x306 # Characteristics. # IMAGE_FILE_32BIT_MACHINE | # IMAGE_FILE_DEBUG_STRIPPED | # IMAGE_FILE_EXECUTABLE_IMAGE | # IMAGE_FILE_LINE_NUMS_STRIPPED #else .word 0x206 # Characteristics # IMAGE_FILE_DEBUG_STRIPPED | # IMAGE_FILE_EXECUTABLE_IMAGE | # IMAGE_FILE_LINE_NUMS_STRIPPED #endif optional_header: #ifdef CONFIG_X86_32 .word 0x10b # PE32 format #else .word 0x20b # PE32+ format #endif .byte 0x02 # MajorLinkerVersion .byte 0x14 # MinorLinkerVersion # Filled in by build.c .long 0 # SizeOfCode .long 0 # SizeOfInitializedData .long 0 # SizeOfUninitializedData # Filled in by build.c .long 0x0000 # AddressOfEntryPoint .long 0x0000 # BaseOfCode #ifdef CONFIG_X86_32 .long 0 # data #endif extra_header_fields: #ifdef CONFIG_X86_32 .long 0 # ImageBase #else .quad 0 # ImageBase #endif .long 0x1000 # SectionAlignment .long 0x200 # FileAlignment .word 0 # MajorOperatingSystemVersion .word 0 # MinorOperatingSystemVersion .word 0 # MajorImageVersion .word 0 # MinorImageVersion .word 0 # MajorSubsystemVersion .word 0 # MinorSubsystemVersion .long 0 # Win32VersionValue # # The size of the bzImage is written in tools/build.c # .long 0 # SizeOfImage .long 0x200 # SizeOfHeaders .long 0 # CheckSum .word 0xa # Subsystem (EFI application) .word 0 # DllCharacteristics #ifdef CONFIG_X86_32 .long 0 # SizeOfStackReserve .long 0 # SizeOfStackCommit .long 0 # SizeOfHeapReserve .long 0 # SizeOfHeapCommit #else .quad 0 # SizeOfStackReserve .quad 0 # SizeOfStackCommit .quad 0 # SizeOfHeapReserve .quad 0 # SizeOfHeapCommit #endif .long 0 # LoaderFlags .long 0x1 # NumberOfRvaAndSizes .quad 0 # ExportTable .quad 0 # ImportTable .quad 0 # ResourceTable .quad 0 # ExceptionTable .quad 0 # CertificationTable .quad 0 # BaseRelocationTable # Section table section_table: .ascii ".text" .byte 0 .byte 0 .byte 0 .long 0 .long 0x0 # startup_{32,64} .long 0 # Size of initialized data # on disk .long 0x0 # startup_{32,64} .long 0 # PointerToRelocations .long 0 # PointerToLineNumbers .word 0 # NumberOfRelocations .word 0 # NumberOfLineNumbers .long 0x60500020 # Characteristics (section flags) # # The EFI application loader requires a relocation section # because EFI applications are relocatable and not having # this section seems to confuse it. But since we don't need # the loader to fixup any relocs for us just fill it with a # single dummy reloc. # .ascii ".reloc" .byte 0 .byte 0 .long reloc_end - reloc_start .long reloc_start .long reloc_end - reloc_start # SizeOfRawData .long reloc_start # PointerToRawData .long 0 # PointerToRelocations .long 0 # PointerToLineNumbers .word 0 # NumberOfRelocations .word 0 # NumberOfLineNumbers .long 0x42100040 # Characteristics (section flags) #endif /* CONFIG_EFI_STUB */ /* * 以下是内核头部数据结构的第1部分,这部分位于第一个扇区(512)中 * 因为bootsect代码只提供的重启功能,代码量很小,后面这块位置还需要放 * setup相关数据结构。所以,内核头结构是从第一扇区最后跨到第二扇区: * setup_header part1-_start-jmp code-kernel header part2... * 在http://lxr.linux.no/linux+v3.3.6/arch/x86/include/asm/bootparam.h * 中定义了setup_header结构。 */ # Kernel attributes; used by setup. This is part 1 of the # header, from the old boot sector. .section ".header", "a" .globl hdr /*导出这个符号,实模式内核头部,参考doc/x86/boot.txt*/ hdr: setup_sects: .byte 0 /* Filled in by build.c */ root_flags: .word ROOT_RDONLY syssize: .long 0 /* Filled in by build.c */ ram_size: .word 0 /* Obsolete */ vid_mode: .word SVGA_MODE root_dev: .word 0 /* Filled in by build.c */ boot_flag: .word 0xAA55 /*MBR最后两字节的特征标志*/ # offset 512, entry point /*上面的代码量为512字节的bootsect,下是代码是setup的开始*/ .globl _start _start: # Explicitly enter this as bytes, or the assembler # tries to generate a 3-byte jump here, which causes # everything else to push off to the wrong offset. /* * 位于0x200处是一个jmp,会直接跳到start_of_setup处 * 实际就是越过内核头部数据结构,跳到代码处 */ .byte 0xeb # short (2-byte) jump off:0x200 .byte start_of_setup-1f #off:0x201,1f是指下面的标号'1' /*以下是内setup_header数据结构的第二部分*/ 1: # Part 2 of the header, from the old setup.S .ascii "HdrS" # header signature .word 0x020a # header version number (>= 0x0105) # or else old loadlin-1.5 will fail) .globl realmode_swtch realmode_swtch: .word 0, 0 # default_switch, SETUPSEG start_sys_seg: .word SYSSEG # obsolete and meaningless, but just # in case something decided to "use" it .word kernel_version-512 # pointing to kernel version string # above section of header is compatible # with loadlin-1.5 (header v1.5). Don't # change it. type_of_loader: .byte 0 # 0 means ancient bootloader, newer # bootloaders know to change this. # See Documentation/x86/boot.txt for # assigned ids # flags, unused bits must be zero (RFU) bit within loadflags loadflags: LOADED_HIGH = 1 # If set, the kernel is loaded high CAN_USE_HEAP = 0x80 # If set, the loader also has set # heap_end_ptr to tell how much # space behind setup.S can be used for # heap purposes. # Only the loader knows what is free .byte LOADED_HIGH setup_move_size: .word 0x8000 # size to move, when setup is not # loaded at 0x90000. We will move setup # to 0x90000 then just before jumping # into the kernel. However, only the # loader knows how much data behind # us also needs to be loaded. code32_start: # here loaders can put a different # start address for 32-bit code. .long 0x100000 # 0x100000 = default for big kernel ramdisk_image: .long 0 # address of loaded ramdisk image # Here the loader puts the 32-bit # address where it loaded the image. # This only will be read by the kernel. ramdisk_size: .long 0 # its size in bytes bootsect_kludge: .long 0 # obsolete heap_end_ptr: .word _end+STACK_SIZE-512 # (Header version 0x0201 or later) # space from here (exclusive) down to # end of setup code can be used by setup # for local heap purposes. ext_loader_ver: .byte 0 # Extended boot loader version ext_loader_type: .byte 0 # Extended boot loader type cmd_line_ptr: .long 0 # (Header version 0x0202 or later) # If nonzero, a 32-bit pointer # to the kernel command line. # The command line should be # located between the start of # setup and the end of low # memory (0xa0000), or it may # get overwritten before it # gets read. If this field is # used, there is no longer # anything magical about the # 0x90000 segment; the setup # can be located anywhere in # low memory 0x10000 or higher. ramdisk_max: .long 0x7fffffff # (Header version 0x0203 or later) # The highest safe address for # the contents of an initrd # The current kernel allows up to 4 GB, # but leave it at 2 GB to avoid # possible bootloader bugs. kernel_alignment: .long CONFIG_PHYSICAL_ALIGN #physical addr alignment #required for protected mode #kernel #ifdef CONFIG_RELOCATABLE relocatable_kernel: .byte 1 #else relocatable_kernel: .byte 0 #endif min_alignment: .byte MIN_KERNEL_ALIGN_LG2 # minimum alignment pad3: .word 0 cmdline_size: .long COMMAND_LINE_SIZE-1 #length of the command line, #added with boot protocol #version 2.06 hardware_subarch: .long 0 # subarchitecture, added with 2.07 # default to 0 for normal x86 PC hardware_subarch_data: .quad 0 payload_offset: .long ZO_input_data payload_length: .long ZO_z_input_len setup_data: .quad 0 # 64-bit physical pointer to # single linked list of # struct setup_data pref_address: .quad LOAD_PHYSICAL_ADDR # preferred load addr #define ZO_INIT_SIZE (ZO__end - ZO_startup_32 + ZO_z_extract_offset) #define VO_INIT_SIZE (VO__end - VO__text) #if ZO_INIT_SIZE > VO_INIT_SIZE #define INIT_SIZE ZO_INIT_SIZE #else #define INIT_SIZE VO_INIT_SIZE #endif init_size: .long INIT_SIZE # kernel initialization size # End of setup header ##################################################### .section ".entrytext", "ax" /*再来一个段*/ start_of_setup: #ifdef SAFE_RESET_DISK_CONTROLLER # Reset the disk controller. movw $0x0000, %ax # Reset disk controller movb $0x80, %dl # All disks int $0x13 /*ah=0,磁盘系统复位*/ #endif # Force %es = %ds movw %ds, %ax movw %ax, %es cld # Apparently some ancient versions of LILO invoked the kernel with %ss != %ds, # which happened to work by accident for the old code. Recalculate the stack # pointer if %ss is invalid. Otherwise leave it alone, LOADLIN sets up the # stack behind its own code, so we can't blindly put it directly past the heap. movw %ss, %dx cmpw %ax, %dx # %ds == %ss?,ax=ds->ds==ss? movw %sp, %dx # dx设置为栈顶 je 2f # -> assume %sp is reasonably set # Invalid %ss, make up a new stack,栈不对,整理之 /* * 根据setup.ld,从本代码生成setup.elf * 一些符号可从setup.ld中看到 */ movw $_end, %dx /* * _end指向setup.elf结尾处,如果下面这个test不是0,说明 * CAN_USE_HEAP被设置。表示可以使用heap,那么就要使用 * 加载器设置的heap结尾值heap_end_ptr,并放入dx中去。 * 若CAN_USE_HEAP标志没设置,说明不能使用堆,则dx是本句 * 的_end标志,指向setup.bin结尾处(此时heap大小为0)。 */ testb $CAN_USE_HEAP, loadflags jz 1f movw heap_end_ptr, %dx /* * CAN_USE_HEAP已设置,heap_end_ptr=_end+STACK_SIZE-512 * 在boot.h中,STACK_SIZE=512,所以heap_end_ptr=_end(setup结尾) * 此字段是lbligatory的,说明是要loader强制写入的。 * 而此字段要配合CAN_USE_HEAP使用,如果此字段被设置 * 则heap_end_ptr可用,指向堆尾,默认情况下此值与_end相同 * 表示可以使用heap,堆从_end开始,并heap_end_ptr指向堆尾 */ 1: addw $STACK_SIZE, %dx /* * 栈是由高地址向低地址方向生长的,栈指针最远指向heap结尾处; * 所以栈底得抬高,给栈创造出一个空间-STACK_SIZE。由_end标志 * 向上,依次是堆开始(_end标志处),堆空间,堆结束(heap_end_ptr) * 栈开始(heap_end_ptr),栈空间(大小STACK_SIZE),栈结束 * (heap_end_ptr+STACK_SIZE) */ jnc 2f /* 如果dx溢出,则设置dx=0*/ xorw %dx, %dx # Prevent wraparound 2: # Now %dx should point to the end of our stack space andw $~3, %dx # dword align (might as well...) jnz 3f /* * 与1111 1111 1111 1100进行and,并且结果为0的值是<=3的, * 包括标志1处的xorw,它的结果是0。就是说只要标志1处jnc 2f * 没跳转,此处(标志2)的jnz也不会跳,因为dx=0<=3。所以给dx一 * 个固定值0xfffc * 总结一下:如果标志1溢出dx=0xfffc,其它<=3的情况应该没有, * 因为STACK_SIZE=512 */ movw $0xfffc, %dx /* Make sure we're not zero*/ 3: movw %ax, %ss /*ax=ss=ds*/ movzwl %dx, %esp /* Clear upper half of %esp*/ /* * esp高端清0,低端设置为dx,即栈顶 * dx=堆尾+STACK_SIZE * 上述标志1,2都顺利跳转的话,堆栈应该是: * * |esp | * |栈 | * |heap_end_ptr| * |堆 | * |_end | * * 如果真的溢出了,说明堆尾太高了 * 默认会把栈底设置为0xfffc,加载器应该会知道这一点。同时应该注 * 意,实模式代码段寄存器cs不等于0,cs是header.S的加载地址(boot.txt * 中的X处)。这里其它通用寄存器都是相对cs的一个偏移。结合setup.ld * 结尾处可知栈底是不会大于X+10000的,这就是此处为什么设置dx(栈底) * 为0xfffc的原因(4字节对齐,0xffff+1=0x10000) */ sti # Now we should have a working stack,开中断 # We will have entered with %cs = %ds+0x20, normalize %cs so # it is on par with the other segments. pushw %ds pushw $6f lretw /* * 远跳转会影响%cs的值: * 假设内核被加载到了地址X,应该类似远跳转jmp SEG+0x20:0,跳进header.S偏移512处, * 也就是setup开始的部分(第2个512)。进来前,所有段寄存器都相同=X,进来后,%cs多加 * 了0x20。而这里的lretw 会从栈中弹出一个值作为%cs,弹出第二个值作为* %ip。所以, * 这句执行完成后,cs:ip=%ds:标志6。这样的话,cs恢复到了跳转前的位置,即内核加载基 * 址---X。进入main后,也会以X作为基址执行。 */ 6: # Check signature at end of setup,setup.elf签名检查 cmpl $0x5a5aaa55, setup_sig jne setup_bad # Zero the bss movw $__bss_start, %di movw $_end+3, %cx xorl %eax, %eax subw %di, %cx shrw $2, %cx rep; stosl /*因al中是0,所以将0放di中,cx为计数*/ # Jump to C code (should not return) calll main /*boot/main.c*/ # Setup corrupt somehow... setup_bad: movl $setup_corrupt, %eax calll puts /*boot/tty.c*/ # Fall through... .globl die .type die, @function die: hlt jmp die .size die, .-die .section ".initdata", "a" setup_corrupt: .byte 7 .string "No setup signature found...\n" .data dummy: .long 0 .section .reloc reloc_start: .long dummy - reloc_start .long 10 .word 0 reloc_end:
五、setup.ld
setup链接脚本 这个文件的功能是负责链接后的内存布局的。它规定了符号在内存中出现的位置。 /* * setup.ld * * Linker script for the i386 setup code */ OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")/*输出格式命令*/ OUTPUT_ARCH(i386) ENTRY(_start) /* * ENTRY()命令的意思是将某符号定们输出文件入口点 * ld对入口点处理有一定顺序:1)ld的-e选项,2)链接脚本的ENTRY()命令, * 3)已定义的start符号,4).text区段的第一字节,5)使用0为入口点 */ SECTIONS /* * SECTIONS命令告诉ld如何将输入文件的各区段映射到输出文件的区段, * 并规定了区段的位置 */ { . = 0; /* * '.'(句点)代表是当前位置,也叫定位器,这里是说将紧后面的符号 * 定位到offset=0的位置 */ .bstext : { *(.bstext) } /* 由于是面定位器原因,这个符号将被加载到offset=0的位置*/ .bsdata : { *(.bsdata) } /* 这个符号将在bstext后面出现,位于offset=.bstext的size处, * 与上面一句一样,意思是将所有(*,星号)输入文件的".bsdata"段 * 合并成一个总的(在输出文件中的)".bsdata"段。 */ . = 497; /* * 意思是.header区段是从offset497位置开始的,前文说过, * setup_header的第一部分在第一扇区的最后。有setup_sects等字段 * 到boot_flag共15字节,15+497=512,就是说到boot_flag结束,刚好 * 是512字节(占第一个扇区)。 */ .header : { *(.header) } /* header符号位于offset=497*/ .entrytext : { *(.entrytext) } .inittext : { *(.inittext) } .initdata : { *(.initdata) } __end_init = .; /* * 句点代表当前位置,这里是将当前位置记录到__end_init符号中, * 其它的"符号=句点"也表示记录当前位置的意思 */ .text : { *(.text) } .text32 : { *(.text32) } . = ALIGN(16); /* 16字节对齐*/ .rodata : { *(.rodata*) } .videocards : { video_cards = .; *(.videocards) video_cards_end = .; } . = ALIGN(16); .data : { *(.data*) } .signature : { /*setup的签名标志*/ setup_sig = .; LONG(0x5a5aaa55) } . = ALIGN(16); .bss : /*bss段的开始和结束*/ { __bss_start = .; *(.bss) __bss_end = .; } . = ALIGN(16); _end = .; /*全部结束*/ /DISCARD/ : { *(.note*) } /* * The ASSERT() sink to . is intentional, for binutils 2.14 compatibility: */ . = ASSERT(_end <= 0x8000, "Setup too big!"); /*若表达式不为真,打印msg,结束链接*/ . = ASSERT(hdr == 0x1f1, "The setup header has the wrong offset!"); /* Necessary for the very-old-loader check to work... */ . = ASSERT(__end_init <= 5*512, "init sections too big!"); }
linux boot 学习
最新推荐文章于 2025-05-26 13:33:57 发布