问题描述

在用高版本gcc编译 xv6 时,会出现以下错误(这里用的是 12.1.1 ):

make qemu
...
user/sh.c: In function 'runcmd':
user/sh.c:58:1: error: infinite recursion detected [-Werror=infinite-recursion]
    58 | runcmd(struct cmd *cmd)
       | ^~~~~~
...

这是因为gcc高版本加入了对无限递归函数的检测,在默认设置下会对这种行为发出警告。而 xv6 中的编译选项有-Werror,因此该警告会被视为错误。但是在操作系统中,很多功能都是不返回的,如exec更换进程上下文后正常执行是不会返回的,而sh在正常情况下在子进程结束后也将返回到本身,继续接收下一条命令。因此,不应将sh.c的这种写法视为错误。

解决方案

1. 为runcmd加上noreturn标签,这样编译器知道它是永不返回的,因而不会将它的无限递归调用视为错误。具体而言,可以在runcmd函数定义前面加上该标签,如

...
// Execute cmd.  Never returns.
__attribute__((noreturn))
void
runcmd(struct cmd *cmd)
...

或者直接加上一个带有noreturn标签的函数原型,如

...
struct cmd *parsecmd(char*);
void runcmd(struct cmd*) __attribute__((noreturn));

// Execute cmd.  Never returns.
void
runcmd(struct cmd *cmd)
...

2. 告知编译器,忽略无限递归的错误。在Makefile中找到以下行

CFLAGS = -Wall -Werror -O -fno-omit-frame-pointer -ggdb

将其改为

CFLAGS = -Wall -Werror -Wno-error=infinite-recursion -O -fno-omit-frame-pointer -ggdb

参考资料

https://github.com/mit-pdos/xv6-riscv/pull/126

鸣谢

感谢 Eastonman 为我提供的 GitHub pull request 链接。