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

自定义OS开发中,ISR0函数地址转int后值为0的问题求助

问题描述

我正在开发自定义操作系统,在设置IDT(中断描述符表)时,尝试通过&isr0获取ISR0函数的地址,核心代码如下:

// 示例函数
void isr0() {
    volatile uint8_t* ptr1 = (uint8_t*)(0xB8000);
    ptr1[0] = 'A';
}
void main(){
    int ptr = (int)&isr0;
    volatile uint8_t* ptr1 = (uint8_t*)0xB8000;
    int offset2 = 40;
    int value = (int)ptr;
    // 将地址以十进制形式输出到VGA缓冲区
    ptr1[0 + offset2] = 48 + (value/100000) % 10;
    ptr1[2 + offset2] = 48 + (value/10000) % 10;
    ptr1[4 + offset2] = 48 + (value/1000) % 10;
    ptr1[6 + offset2] = 48 + (value/100) % 10;
    ptr1[8 + offset2] = 48 + (value/10) % 10;
    ptr1[10+ offset2] = 48 + (value/1) % 10;
    while(1);
    ...
}

运行后Qemu窗口显示结果为000000,但在常规Linux机器上运行该代码能正常显示内存地址。此外发现调用该函数指针能正常工作,但将其转换为int类型时却得到0。

编译步骤:

i686-elf-gcc -nostdlib -O3 -ffreestanding -Wall -Wextra -m32 -c kernel.c -o bin/stage4.bin
objcopy -O binary bin/stage4.bin bin/stage5.bin

引导加载相关文件为手写汇编的Stage1和Stage2,编译命令:

nasm stage1.asm -f bin -o bin/stage1.bin

镜像合并与运行命令:

cat bin/stage1.bin bin/stage2.bin bin/stage5.bin > bin/stage3.bin
qemu-system-x86_64 -fda bin/stage3.bin -vga vmware -d guest_errors -m 64M >> bin/a 2>&1
原因分析与解决方案

核心原因

  1. 链接地址不匹配:引导加载程序(Stage2)会将内核加载到物理地址0x100000处执行,但编译时使用了i686-elf-gcc的默认链接脚本,默认链接地址为0x0。编译器生成的函数地址是基于链接地址的偏移量,而非实际运行时的物理地址,因此&isr0被解析为链接时的偏移(若isr0是代码段第一个函数,偏移即为0)。
  2. 类型转换不安全:用int存储指针地址存在风险,虽然32位环境下int通常为32位,但标准并未保证指针类型与int的长度一致,可能导致地址截断或解析错误。
  3. 优化导致的异常:开启-O3优化后,编译器可能认为ptr和value未参与关键逻辑(仅用于显示),进而将其优化为0,但函数指针能正常调用说明函数本身的相对跳转逻辑未被破坏。

解决办法

  1. 编写自定义链接脚本
    创建一个linker.ld文件,指定代码段的运行地址为引导加载程序加载内核的地址(即0x100000),示例内容如下:

    ENTRY(main)
    SECTIONS
    {
        . = 0x100000;
        .text ALIGN(4) : { *(.text) }
        .rodata ALIGN(4) : { *(.rodata) }
        .data ALIGN(4) : { *(.data) }
        .bss ALIGN(4) : { *(COMMON) *(.bss) }
    }
    

    编译时指定链接脚本:

    i686-elf-gcc -nostdlib -O3 -ffreestanding -Wall -Wextra -m32 -T linker.ld kernel.c -o bin/stage4.bin
    
  2. 使用正确的指针存储类型
    替换代码中的int为uintptr_t(需包含头文件<stdint.h>),确保指针地址被正确存储:

    #include <stdint.h>
    ...
    uintptr_t ptr = (uintptr_t)&isr0;
    uintptr_t value = ptr;
    
  3. 验证链接地址与加载地址一致性
    可以通过objdump查看ELF文件的代码段地址,确认链接地址是否正确:

    i686-elf-objdump -h bin/stage4.bin
    

    检查.text段的Addr字段是否为0x100000。

  4. 临时关闭优化排查问题
    若怀疑是优化导致的异常,可暂时将-O3改为-O0重新编译,观察地址是否能正常显示,待问题解决后再开启优化。

内容的提问来源于stack exchange,提问作者Stigl

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 05:35:10