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

自定义RISC-V CPU模拟器中malloc返回错误内存区域的问题

RISC-V自定义CPU模拟器malloc地址异常问题分析

我开发了一款自定义RISC-V CPU模拟器,其余功能均正常,但malloc()及同类内存分配函数存在异常——返回的指针指向FLASH内存区域,未落在RAM的起始地址范围内。

内存映射配置

MEMORY
{
    FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 16384K  /* KiB */
    RAM (rw)   : ORIGIN = 0x20000000, LENGTH = 512K    /* KiB */
}

测试代码与现象

基础测试代码

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>

extern uint32_t _sidata; /* Start of .data in FLASH */
extern uint32_t _sdata;  /* Start of .data in RAM */
extern uint32_t _edata;  /* End of .data in RAM */

extern uint32_t _sbss;  /* Start of .bss in RAM */
extern uint32_t _ebss;  /* End of .bss in RAM */

void initialize_memory(void) {
    // Copy initialized variables from FLASH to RAM (.data section)
    uint32_t *src = &_sidata;
    uint32_t *dest = &_sdata;

    while (dest < &_edata) {
        *dest++ = *src++;
    }

    // Zero-initialize the .bss section in RAM
    dest = &_sbss;

    while (dest < &_ebss) {
        *dest++ = 0;
    }
}


int main(void){

    initialize_memory();

    int *ptr1 = (int*)malloc(4*sizeof(int));

    __asm__("nop");               // 用于定位汇编位置
    __asm__("lui t0, 0x80000");   // 输出地址
    __asm__("mv t1, a5");         
    __asm__("sw t1, 0(t0)");      // 存储malloc返回值
    __asm__("nop");
}

测试结果

通过内嵌汇编观测到,0x80000000地址存储的值为8,即malloc返回的地址是0x8,而非预期的0x20000008这类RAM区域地址。二次分配测试中,第二次malloc返回值为0x20,依然落在FLASH地址空间。

对应RISC-V汇编片段

000000bc <main>:
  bc:   fe010113            addi    sp,sp,-32
  c0:   00112e23            sw  ra,28(sp)
  c4:   00812c23            sw  s0,24(sp)
  c8:   02010413            addi    s0,sp,32
  cc:   f4dff0ef            jal 18 <initialize_memory>
  d0:   01000513            li  a0,16
  d4:   038000ef            jal 10c <malloc>
  d8:   00050793            mv  a5,a0
  dc:   fef42623            sw  a5,-20(s0)
  e0:   00000013            nop
  e4:   800002b7            lui t0,0x80000
  e8:   00078313            mv  t1,a5
  ec:   0062a023            sw  t1,0(t0) # 80000000 <_estack+0x5ff80004>
  f0:   00000013            nop
  f4:   00000793            li  a5,0
  f8:   00078513            mv  a0,a5
  fc:   01c12083            lw  ra,28(sp)
 100:   01812403            lw  s0,24(sp)
 104:   02010113            addi    sp,sp,32
 108:   00008067            ret

二次分配测试代码

...
    int *ptr1 = (int*)malloc(4*sizeof(int));

    __asm__("nop");
    __asm__("lui t0, 0x80000");
    __asm__("mv t1, a5");
    __asm__("sw t1, 0(t0)"); 
    __asm__("nop");

    int *ptr2 = (int*)malloc(4*sizeof(int));

    __asm__("nop");
    __asm__("lui t0, 0x80000");
    __asm__("mv t1, a5");
    __asm__("sw t1, 0(t0)"); 
    __asm__("nop");
}

当前链接脚本片段

.data : 
  {
    . = ALIGN(4);
    _sdata = .;
    *(.data)
    *(.data*)
    *(.*data*)
    
    . = ALIGN(4);
    _edata = .;
  } >RAM AT> FLASH
  /* } AT> FLASH */
  /* } >FLASH AT>RAM */
 
 _sibss = LOADADDR(.bss);
  

  . = ALIGN(4);
  .bss :
  {
    _sbss = .; 
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss*)
    *(COMMON)

    . = ALIGN(4);
    _ebss = .;
    __bss_end__ = _ebss;
  /* } >RAM */
  /* } >RAM */
  } >RAM AT> FLASH

  ._user_heap_stack :
  {
        . = ALIGN(8);
    PROVIDE ( __heap_start__ = . );
    . = . + 0x1000;
    PROVIDE ( __heap_end__ = . );
  } >RAM

问题原因分析

malloc的底层实现依赖链接脚本中定义的__heap_start__和__heap_end__符号确定堆的地址范围。从链接脚本可以看出:

  • .data和.bss段虽然指定了>RAM AT> FLASH(加载到RAM,存储在FLASH),但链接器默认使用FLASH的起始地址0x00000000作为链接地址基准。
  • ._user_heap_stack段中的.(当前地址)是基于链接地址计算的,即.bss段结束后的偏移地址,而非RAM的物理起始地址0x20000000加上偏移。
  • 最终__heap_start__被设置在FLASH的地址区间,导致malloc从该地址开始分配,返回的地址自然落在FLASH范围内。

地址正确性判断与冲突风险

  1. 地址正确性判断:正确的malloc返回地址必须落在RAM的地址区间0x20000000到0x20080000(512KiB)内。当前返回的0x8、0x20均属于FLASH地址空间,明显错误。
  2. 冲突风险:这些地址会直接与FLASH中预加载的程序代码、.data段镜像数据冲突。若模拟器允许写FLASH,写入操作会覆盖原有内容导致程序崩溃;若FLASH为只读,malloc分配的地址无法写入,直接触发异常。

修复方案

修改链接脚本,将.data、.bss和堆的链接地址基准切换到RAM的物理起始地址:

SECTIONS
{
    . = ORIGIN(FLASH);
    /* 代码段等FLASH区域的定义 */
    .text : { *(.text*) } >FLASH

    /* 切换链接地址基准到RAM起始地址 */
    . = ORIGIN(RAM);

    .data : 
    {
        . = ALIGN(4);
        _sdata = .;
        *(.data)
        *(.data*)
        *(.*data*)
        
        . = ALIGN(4);
        _edata = .;
    } >RAM AT> FLASH

    .bss :
    {
        . = ALIGN(4);
        _sbss = .; 
        __bss_start__ = _sbss;
        *(.bss)
        *(.bss*)
        *(COMMON)

        . = ALIGN(4);
        _ebss = .;
        __bss_end__ = _ebss;
    } >RAM AT> FLASH

    ._user_heap_stack :
    {
        . = ALIGN(8);
        PROVIDE ( __heap_start__ = . );
        . = . + 0x1000;
        PROVIDE ( __heap_end__ = . );
    } >RAM
}

此修改后,链接器会将.data、.bss和堆的链接地址都设置在RAM的物理地址范围内,malloc即可正确返回RAM中的地址。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 10:30:55