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

Windows下用NASM+GCC(MinGW)开发OS:C与汇编结合优化方案咨询

问题描述

我正在开发一个简单内核,目前在Windows环境下使用GCC(MinGW)编译器结合C语言进行开发,但当前C与汇编的结合方式存在问题。我的操作流程如下:

:: 首先将汇编文件编译为纯二进制文件   
nasm -f bin Assembly/asm.asm -o asm.bin

:: 对C文件执行相同操作
gcc -c C/c.c     
objcopy -O binary -j .text c.o c.bin

:: 按预期顺序合并所有二进制文件
COPY /B asm.bin + c.bin + asm1.asm ..... os.bin

这种方式虽能运行,但十分繁琐:需将代码拆分到不同段(因二进制文件是叠加合并),需手动查看原始二进制文件计算地址,且无法跨文件调用函数。

请问我该如何操作?在Windows下进行OS开发时,结合C与NASM汇编的最佳方式是什么?我猜测应该使用链接器之类的工具。

注:我还需要实现C中调用汇编函数、汇编中调用C函数的功能。


解决方案

核心思路是用链接器替代手动合并二进制文件,通过生成目标文件而非直接输出纯二进制,让链接器自动处理地址解析、函数调用绑定等问题,具体步骤如下:

1. 编译汇编文件为兼容目标文件

NASM要生成和MinGW GCC兼容的ELF格式目标文件,不要直接输出bin:

# 32位内核用这个命令
nasm -f elf32 Assembly/asm.asm -o asm.o
# 64位内核替换为 -f elf64

汇编代码中要做两个关键声明:

  • 用global暴露给C调用的函数名
  • 用extern声明要调用的C函数名

示例汇编代码(asm.asm):

global my_asm_func   ; 让C能调用这个汇编函数
extern my_c_func     ; 声明要调用的C函数

section .text
my_asm_func:
    ; 示例逻辑:调用C函数后返回
    call my_c_func
    ret

2. 编译C文件为目标文件

用MinGW GCC编译时,要加上内核开发的必要参数,避免依赖系统标准库:

# 32位内核用这个命令
gcc -m32 -ffreestanding -c C/c.c -o c.o
# 64位内核替换为 -m64

参数说明:

  • -m32/-m64:指定生成对应位数的代码
  • -ffreestanding:告诉编译器当前是无OS的独立环境,不依赖标准库
  • -c:仅编译生成目标文件,不进行链接

示例C代码(c.c):

// 声明要调用的汇编函数
extern void my_asm_func(void);

// 暴露给汇编调用的C函数
void my_c_func(void) {
    // 自定义逻辑
}

// 内核入口函数,链接时会指定为启动点
void kernel_main(void) {
    my_asm_func();  // 调用汇编函数
    // 内核主逻辑
}

3. 用链接器合并目标文件

使用MinGW的ld链接器,结合自定义的链接脚本,把汇编和C的目标文件链接成ELF可执行文件:

# 32位内核用这个命令
ld -m elf_i386 -T linker.ld -o kernel.elf asm.o c.o
# 64位内核替换为 -m elf_x86_64

参数说明:

  • -m elf_i386:指定32位ELF目标格式
  • -T linker.ld:指定链接脚本,定义内核的内存布局(代码段、数据段的加载地址等)

链接脚本示例(linker.ld)

链接脚本用来告诉链接器如何安排各个段的内存地址,以32位内核为例:

ENTRY(kernel_main)  ; 指定内核的入口函数

SECTIONS {
    . = 0x100000;  ; 内核加载到内存0x100000(1MB位置,避开BIOS占用区域)

    .text ALIGN(4) : {
        *(.text)   ; 合并所有目标文件的.text代码段
    }

    .data ALIGN(4) : {
        *(.data)   ; 合并所有目标文件的.data数据段
    }

    .bss ALIGN(4) : {
        *(.bss)    ; 合并所有目标文件的.bss未初始化数据段
    }
}

4. 生成最终内核二进制文件

用objcopy把ELF文件转换成引导加载器能识别的纯二进制文件:

objcopy -O binary kernel.elf os.bin

跨语言调用的关键注意事项

  • 调用约定统一:MinGW默认使用cdecl调用约定(参数从右到左入栈,调用者负责清理栈),汇编代码必须遵循这个规则。
  • 函数名修饰:32位C语言会给函数名加下划线前缀(比如my_c_func会变成_my_c_func),汇编中要对应使用extern _my_c_func;如果觉得麻烦,可以在编译C时加-fno-leading-underscore取消前缀。
  • 段对齐:链接脚本中指定的对齐值(比如4字节)要符合CPU的内存访问要求,避免出现错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 17:58:28