为什么 xv6-riscv 编译的时候需要 -mcmodel=m

2023-05-19  本文已影响0人  虾饺的开发手记

xv6-riscv 有这样一行代码:

// kernel/start.c
void start() {
  ...

  // requires gcc **-mcmodel=medany**
  w_mepc((uint64)main)

  ...
}

对应的,Makefile 里有这个:

CFLAGS += -mcmodel=medany

然后我就不禁要问了,我拿个 main 函数的地址,关这个 cmodel 啥事?网上翻了一圈后,就有了这篇文章。


关于 -mcmodel=medany,简单地说,他是指定编译器如何生成访问全局变量指令的命令,cmodel 即是 code model 的意思。相对的,还有另一种模式是 medlow(默认的模式):

具体可以参考这篇文章

理解了 code model 以后,我们就可以确信地说,拿个 main 函数的地址,跟 code model 其实没半毛钱关系。或者说,这不是使用 medany 的根本原因。

那么,要不我们试试把这个 flag 去掉?遗憾的是,如果你真的去掉了这个 flag,链接的时候你会发现有一堆错误:

kernel/start.o: in function `timerinit':
xv6-riscv/kernel/start.c:76:(.text+0x34): relocation truncated to fit: R_RISCV_HI20 against `.LANCHOR0'
kernel/console.o: in function `consoleread':
xv6-riscv/kernel/console.c:87:(.text+0x84): relocation truncated to fit: R_RISCV_HI20 against `.LANCHOR0'
......

意思是,函数 xxx 里面,某个需要重定位的地址超过了 R_RISCV_HI20 能够支持的范围,即超过了 2G(因为默认是 medlow 模式)。原因是,我们在链接的时候,把 text 段放在了 0x80000000(= 2^31 = 2G),我们所有的代码都超过了 2G:

# kernel/kernel.ld
SECTIONS
{
  /*
   * ensure that entry.S / _entry is at 0x80000000,
   * where qemu's -kernel jumps.
   */
  . = 0x80000000;

  .text : {
    *(.text .text.*)
    . = ALIGN(0x1000);
    _trampoline = .;
    *(trampsec)
    . = ALIGN(0x1000);
    ASSERT(. - _trampoline == 0x1000, "error: trampoline larger than one page");
    PROVIDE(etext = .);
  }

...

也因此,我们才需要用 medany

你可以简单验证一下,比方说,把 kernel.ld 里的 0x80000000 改成 0x40000000。这样即使用默认的 medlow,也能够编译成功(但是运行就不行了,qemu 默认会执行 0x80000000 处的代码)。

上一篇 下一篇

猜你喜欢

热点阅读