Linux内核2.6.11启动代码中protected_mode_jump的ABI疑问
关于Linux Kernel 2.6.11中protected_mode_jump的ABI疑问
在研读Linux Kernel 2.6.11代码时,在路径arch/x86/boot/下发现了切换到保护模式的相关代码,但不清楚其中protected_mode_jump使用的ABI类型及声明位置。该函数仅声明了__attribute__((noreturn)),未显式修改ABI,浏览Makefile并通过grep搜索后仍未找到相关声明,具体代码如下:
protected_mode_jump函数声明
/* pmjump.S */ void __attribute__((noreturn)) protected_mode_jump(u32 entrypoint, u32 bootparams);
调用代码
/* * Actual invocation sequence */ void go_to_protected_mode(void) { /* Hook before leaving real mode, also disables interrupts */ realmode_switch_hook(); /* Move the kernel/setup to their final resting places */ move_kernel_around(); /* Enable the A20 gate */ if (enable_a20()) { puts("A20 gate not responding, unable to boot...\n"); die(); } /* Reset coprocessor (IGNNE#) */ reset_coprocessor(); /* Mask all interrupts in the PIC */ mask_all_interrupts(); /* Actual transition to protected mode... */ setup_idt(); setup_gdt(); protected_mode_jump(boot_params.hdr.code32_start, (u32)&boot_params + (ds() << 4)); }
汇编实现代码
/* * The actual transition into protected mode */ #include <asm/boot.h> #include <asm/processor-flags.h> #include <asm/segment.h> .text .globl protected_mode_jump .type protected_mode_jump, @function .code16 /* * void protected_mode_jump(u32 entrypoint, u32 bootparams); */ protected_mode_jump: movl %edx, %esi # Pointer to boot_params table xorl %ebx, %ebx movw %cs, %bx shll $4, %ebx addl %ebx, 2f jmp 1f # Short jump to serialize on 386/486 1: movw $__BOOT_DS, %cx movw $__BOOT_TSS, %di movl %cr0, %edx orb $X86_CR0_PE, %dl # Protected mode movl %edx, %cr0 # Transition to 32-bit mode .byte 0x66, 0xea # ljmpl opcode 2: .long in_pm32 # offset .word __BOOT_CS # segment .size protected_mode_jump, .-protected_mode_jump .code32 .type in_pm32, @function in_pm32: # Set up data segments for flat 32-bit mode movl %ecx, %ds movl %ecx, %es movl %ecx, %fs movl %ecx, %gs movl %ecx, %ss # The 32-bit code sets up its own stack, but this way we do have # a valid stack if some debugging hack wants to use it. addl %ebx, %esp # Set up TR to make Intel VT happy ltr %di # Clear registers to allow for future extensions to the # 32-bit boot protocol xorl %ecx, %ecx xorl %edx, %edx xorl %ebx, %ebx xorl %ebp, %ebp xorl %edi, %edi # Set up LDTR to make Intel VT happy lldt %cx jmpl *%eax # Jump to the 32-bit entrypoint .size in_pm32, .-in_pm32
分析与解答
1. ABI类型判断
从汇编实现可以看出,protected_mode_jump遵循的是x86 16位实模式下的自定义调用约定,本质是适配启动环境的寄存器传递变种:
- 第一个参数
entrypoint通过%eax传递(最后跳转指令jmpl *%eax直接使用该寄存器); - 第二个参数
bootparams通过%edx传递(汇编开头movl %edx, %esi直接读取该参数)。
这种规则和标准32位cdecl的栈传递不同,是因为这段代码运行在16位实模式下,为了启动阶段的执行效率和环境适配而设计。
2. ABI的声明位置
这个ABI并没有通过__attribute__显式声明,原因在于:
arch/x86/boot/目录下的代码属于内核引导阶段的16位代码,编译时使用了特殊选项(比如-m16),gcc在-m16模式下会默认采用适配16位环境的调用约定,结合汇编中的.code16伪指令,编译器会自动处理参数传递规则,无需显式指定ABI属性;- 该函数是C调用汇编的场景,两者的参数传递是通过手动约定实现的:汇编代码直接读取指定寄存器,而C编译器在编译调用代码时,会按照当前16位编译环境的隐式规则,把参数放入对应寄存器中。
内容的提问来源于stack exchange,提问作者0xDkXy
相关产品推荐
相关产品推荐

