void main(void)
{
/* First, copy the boot header into the "zeropage" */
copy_boot_params();
/* Initialize the early-boot console */
console_init();
if (cmdline_find_option_bool("debug"))
puts("early console in setup code\n");
/* End of heap check */
init_heap();
/* Make sure we have all the proper CPU support */
if (validate_cpu()) {
puts("Unable to boot - please use a kernel appropriate "
"for your CPU.\n");
die();
}
/* Tell the BIOS what CPU mode we intend to run in. */
set_bios_mode();
/* Detect memory layout */
detect_memory();
/* Set keyboard repeat rate (why?) */
keyboard_set_repeat();
/* Query MCA information */
query_mca();
/* Query Intel SpeedStep (IST) information */
query_ist();
/* Query APM information */
#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
query_apm_bios();
#endif
/* Query EDD information */
#if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE)
query_edd();
#endif
/* Set the video mode */
set_video();
/* Do the last things and invoke protected mode */
go_to_protected_mode();
}
注意header.S中的代码是16位的,通过伪指令.code16指示
其中调用main的指令是
calll main
call后面的后缀是l,代表32位操作模式
目前gcc不支持生成16位代码,只能是32位或64位。
因此main.c编译后的指令将是32位。
我们知道,如果不考虑模式,指令的字节码都是一样的。
例如
.cdoe16
push %bp
和
.code32
push %ebp
经过as汇编后的字节指令都是55
在16位模式下,执行32位代码,需要加指令前缀66和67,反过来也是
指令前缀0x66可以用来选择非默认值的操作数大小;前缀0x67可用来选择非默认值的地址大小
这样,虽然gcc生成的汇编指令是32位的,但可以通过指示当前模式是16位,通知汇编器在所有的使用32位模式的指令前面增加66,67前缀,使得指令能够正确执行
我们看看main.c编译命令
gcc -Wp,-MD,arch/x86/boot/.main.o.d -nostdinc -isystem /usr/local/lib/gcc/x86_64-unknown-linux-gnu/4.6.0/include -I/home/zws/linux-3.0/arch/x86/include -Iarch/x86/include/generated -Iinclude -include include/generated/autoconf.h -D__KERNEL__ -I/home/zws/linux-3.0/arch/x86/include -Iarch/x86/include/generated -Iinclude -include include/generated/autoconf.h -g -Os -D_SETUP -D__KERNEL__ -DDISABLE_BRANCH_PROFILING -Wall -Wstrict-prototypes -march=i386 -mregparm=3 -include /home/zws/linux-3.0/arch/x86/boot/code16gcc.h -fno-strict-aliasing -fomit-frame-pointer -ffreestanding -fno-toplevel-reorder -fno-stack-protector -m32 -D"KBUILD_STR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(main)" -D"KBUILD_MODNAME=KBUILD_STR(main)" -c -o arch/x86/boot/main.o arch/x86/boot/main.c
注意-include /home/zws/linux-3.0/arch/x86/boot/code16gcc.h
/*
* code16gcc.h
*
* This file is -include'd when compiling 16-bit C code.
当编译16位C代码时这个文件被包含
* Note: this asm() needs to be emitted before gcc emits any code.
在gcc发射任何指令前这个asm命令需要被发射
* Depending on gcc version, this requires -fno-unit-at-a-time or
* -fno-toplevel-reorder.
依赖gcc版本,需要-fno-unit-at-a-time或者-fno-toplevel-reorder选项
*
* Hopefully gcc will eventually have a real -m16 option so we can
* drop this hack long term.
希望gcc能够支持-m16选项支持实模式,这样我们可以不需要长期地这样非常规实现
*/
#ifndef __ASSEMBLY__
asm(".code16gcc");
#endif
通过发射.code16gcc伪指令,指示as加前缀66和67到32位代码前,以实现16位下的32位操作
还有一个相关选项no-toplevel-reorder
-fno-toplevel-reorder
Do not reorder top-level functions, variables, and "asm" statements. Output them in the same order that they appear in the input file. When this option is used, unreferenced static variables will not be removed. This option is intended to support existing code which relies on a particular ordering. For new code, it is better to use attributes.
不要重组顶层函数,变量和asm语句。以他们在文件中出现的顺序输出。当这个选项被使用时,未必引用的静态变量不会被移除。这个选项用于支持现存的依赖特定顺序的代码,对于新代码,最好使用编译属性。
具备这些知识后,我们再查询main.o的汇编代码就一目了然了
[zws@ssq_pppoe boot]$ objdump -Mi8086 -d main.o
main.o: file format elf32-i386
Disassembly of section .text.startup:
00000000
0: 66 55 push %ebp
2: 66 89 e5 mov %esp,%ebp
5: 66 57 push %edi
7: 66 56 push %esi
9: 66 83 e4 f0 and $0xfffffff0,%esp
d: 66 81 ec b0 00 00 00 sub $0xb0,%esp
14: 66 b8 f1 01 00 00 mov $0x1f1,%eax
1a: 66 be 00 00 00 00 mov $0x0,%esi
20: 66 b9 67 00 00 00 mov $0x67,%ecx
26: 66 89 c7 mov %eax,%edi
29: f3 a4 rep movsb %ds:(%si),%es:(%di)
2b: 66 83 3e 28 02 00 cmpl $0x0,0x228
31: 75 2d jne 60
33: 81 3e 20 00 3f a3 cmpw $0xa33f,0x20
39: 75 25 jne 60
3b: 8b 16 22 00 mov 0x22,%dx
3f: 66 b8 00 90 ff ff mov $0xffff9000,%eax
45: 3b 16 12 02 cmp 0x212,%dx
49: 73 02 jae 4d
4b: 8c d8 mov %ds,%ax
4d: 66 0f b7 c0 movzwl %ax,%eax
51: 66 c1 e0 04 shl $0x4,%eax
55: 66 0f b7 d2 movzwl %dx,%edx
59: 66 01 d0 add %edx,%eax
5c: 66 a3 28 02 mov %eax,0x228
60: 66 e8 fc ff ff ff calll 62
66: 66 ba 00 00 00 00 mov $0x0,%edx
6c: 66 a1 28 02 mov 0x228,%eax
70: 66 e8 fc ff ff ff calll 72
76: 66 85 c0 test %eax,%eax
79: 74 0c je 87
7b: 66 b8 06 00 00 00 mov $0x6,%eax
81: 66 e8 fc ff ff ff calll 83
87: 80 3e 11 02 00 cmpb $0x0,0x211
8c: 79 26 jns b4
8e: 67 66 8d 84 24 00 fe addr32 lea -0x200(%esp),%eax
95: ff ff
97: 66 0f b7 16 24 02 movzwl 0x224,%edx
9d: 66 81 c2 00 02 00 00 add $0x200,%edx
a4: 66 89 16 00 00 mov %edx,0x0
a9: 66 39 c2 cmp %eax,%edx
ac: 76 12 jbe c0
ae: 66 a3 00 00 mov %eax,0x0
b2: eb 0c jmp c0
b4: 66 b8 23 00 00 00 mov $0x23,%eax
ba: 66 e8 fc ff ff ff calll bc
c0: 66 e8 fc ff ff ff calll c2
c6: 66 85 c0 test %eax,%eax
c9: 74 12 je dd
cb: 66 b8 64 00 00 00 mov $0x64,%eax
d1: 66 e8 fc ff ff ff calll d3
d7: 66 e8 fc ff ff ff calll d9
dd: 66 89 e0 mov %esp,%eax
e0: 66 e8 fc ff ff ff calll e2
e6: 67 c7 44 24 1c 00 ec addr32 movw $0xec00,0x1c(%esp)
ed: 67 c7 44 24 10 02 00 addr32 movw $0x2,0x10(%esp)
f4: 66 31 c9 xor %ecx,%ecx
f7: 66 89 e2 mov %esp,%edx
fa: 66 b8 15 00 00 00 mov $0x15,%eax
100: 66 e8 fc ff ff ff calll 102
106: 66 e8 fc ff ff ff calll 108
10c: 67 66 8d 44 24 2c addr32 lea 0x2c(%esp),%eax
112: 66 e8 fc ff ff ff calll 114
118: 67 c7 44 24 48 05 03 addr32 movw $0x305,0x48(%esp)
11f: 66 31 c9 xor %ecx,%ecx
122: 67 66 8d 54 24 2c addr32 lea 0x2c(%esp),%edx
128: 66 b8 16 00 00 00 mov $0x16,%eax
12e: 66 e8 fc ff ff ff calll 130
134: 66 e8 fc ff ff ff calll 136
13a: 66 83 3e 00 00 05 cmpl $0x5,0x0
140: 7e 6c jle 1ae
142: 67 66 8d 44 24 58 addr32 lea 0x58(%esp),%eax
148: 66 e8 fc ff ff ff calll 14a
14e: 67 c7 44 24 74 80 e9 addr32 movw $0xe980,0x74(%esp)
155: 67 66 c7 44 24 6c 43 addr32 movl $0x47534943,0x6c(%esp)
15c: 49 53 47
15f: 67 66 8d 8c 24 84 00 addr32 lea 0x84(%esp),%ecx
166: 00 00
168: 67 66 8d 54 24 58 addr32 lea 0x58(%esp),%edx
16e: 66 b8 15 00 00 00 mov $0x15,%eax
174: 66 e8 fc ff ff ff calll 176
17a: 67 66 8b 84 24 a0 00 addr32 mov 0xa0(%esp),%eax
181: 00 00
183: 66 a3 60 00 mov %eax,0x60
187: 67 66 8b 84 24 94 00 addr32 mov 0x94(%esp),%eax
18e: 00 00
190: 66 a3 64 00 mov %eax,0x64
194: 67 66 8b 84 24 9c 00 addr32 mov 0x9c(%esp),%eax
19b: 00 00
19d: 66 a3 68 00 mov %eax,0x68
1a1: 67 66 8b 84 24 98 00 addr32 mov 0x98(%esp),%eax
1a8: 00 00
1aa: 66 a3 6c 00 mov %eax,0x6c
1ae: 66 e8 fc ff ff ff calll 1b0
1b4: 66 e8 fc ff ff ff calll 1b6
注意
第一 -Mi8086选项,使用16位模式反汇编
第二main.o中只有一个main函数,其他函数都内联了
第三main没有ret,因为最后一个函数go_to_protected_mode有noreturn属性
第三66和67前缀被使用,因此是32位操作模式
第四header.S中的calll main也是32位调用
第五,使用了-fomit-frame-pointer选项,但是使用建立了栈帧,不知为何?