You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 12:52:49