OpenBMC 构建 systemd 出错
构建 devtool modify checkout 出来的 systemd 时出现了静态断言,稍微分析了一下导致错误的原因。

问题

❯ devtool modify systemd
❯ devtool build systemd

| In file included from ../../../../../../workspace/sources/systemd/src/fundamental/macro-fundamental.h:5,
|                  from ../../../../../../workspace/sources/systemd/src/basic/macro.h:13,
|                  from ../../../../../../workspace/sources/systemd/src/basic/fd-util.h:10,
|                  from ../../../../../../workspace/sources/systemd/src/udev/cdrom_id/cdrom_id.c:14:
| ../../../../../../workspace/sources/systemd/src/fundamental/macro-fundamental.h:109:25: error: static assertion failed: "STRLEN(__FILE__) > STRLEN(RELATIVE_SOURCE_PATH) + 1"
|   109 | #define assert_cc(expr) static_assert(expr, #expr)
|       |                         ^~~~~~~~~~~~~
| ../../../../../../workspace/sources/systemd/src/basic/log.h:83:1: note: in expansion of macro 'assert_cc'
|    83 | assert_cc(STRLEN(__FILE__) > STRLEN(RELATIVE_SOURCE_PATH) + 1);
|       | ^~~~~~~~~
| ninja: build stopped: subcommand failed.

分析

RELATIVE_SOURCE_PATH 宏在如下位置定义:

cd systemd/oe-workdir

❯ grep -rn RELATIVE_SOURCE_PATH
build/config.h:521:#define RELATIVE_SOURCE_PATH "../git"
systemd-254.4/config.h:521:#define RELATIVE_SOURCE_PATH "../../../../../../workspace/sources/systemd"

其中 build/config.h 是 bitbake 构建生成的(严格来说应该是 devtool modify 之前的构建),而 systemd-254.4/config.h 是 devtool 构建生成的(严格来说应该是 devtool modify 之后的构建,因为 devtool modify 之后再使用 bitbake 也是在新目录进行构建)。

meson.build 里 RELATIVE_SOURCE_PATH 定义如下:

project_source_root = meson.current_source_dir()
project_build_root = meson.current_build_dir()
relative_source_path = run_command('realpath',
                                   '--relative-to=@0@'.format(project_build_root),
                                   project_source_root,
                                   check : true).stdout().strip()
conf.set_quoted('RELATIVE_SOURCE_PATH', relative_source_path)

devtool 构建的 meson 日志如下:

Source dir: /home/runsisi/working/bmc/openbmc/build/romulus/workspace/sources/systemd
Build dir: /home/runsisi/working/bmc/openbmc/build/romulus/tmp/work/arm1176jzs-openbmc-linux-gnueabi/systemd/254.4/systemd-254.4

Running command: /home/runsisi/working/bmc/openbmc/build/romulus/tmp/hosttools/realpath --relative-to=/home/runsisi/working/bmc/openbmc/build/romulus/tmp/work/arm1176jzs-openbmc-linux-gnueabi/systemd/254.4/systemd-254.4 /home/runsisi/working/bmc/openbmc/build/romulus/workspace/sources/systemd
--- stdout ---
../../../../../../workspace/sources/systemd

生成的 RELATIVE_SOURCE_PATH 宏定义如下:

// systemd-254.4/config.h

#define RELATIVE_SOURCE_PATH "../../../../../../workspace/sources/systemd"

打开 compile_commands.json 可以看到:

"command": "arm-openbmc-linux-gnueabi-gcc -c ../../../../../../workspace/sources/systemd/src/udev/cdrom_id/cdrom_id.c",

显然,如下的断言不可能发生:

assert_cc(STRLEN(__FILE__) > STRLEN(RELATIVE_SOURCE_PATH) + 1);
#define PROJECT_FILE (&__FILE__[STRLEN(RELATIVE_SOURCE_PATH) + 1])

复现

gcc 命令行:

$ export PATH=/home/runsisi/working/bmc/openbmc/build/romulus/tmp/sysroots-components/x86_64/gcc-cross-arm/usr/bin/arm-openbmc-linux-gnueabi:$PATH

$ arm-openbmc-linux-gnueabi-gcc -c ../../../../../../workspace/sources/systemd/src/udev/cdrom_id/cdrom_id.c

-fcanon-prefix-map 
-fmacro-prefix-map=/home/runsisi/working/bmc/openbmc/build/romulus/workspace/sources/systemd=/usr/src/debug/systemd/254.4 
-fdebug-prefix-map=/home/runsisi/working/bmc/openbmc/build/romulus/workspace/sources/systemd=/usr/src/debug/systemd/254.4 
-fmacro-prefix-map=/home/runsisi/working/bmc/openbmc/build/romulus/tmp/work/arm1176jzs-openbmc-linux-gnueabi/systemd/254.4/systemd-254.4=/usr/src/debug/systemd/254.4 
-fdebug-prefix-map=/home/runsisi/working/bmc/openbmc/build/romulus/tmp/work/arm1176jzs-openbmc-linux-gnueabi/systemd/254.4/systemd-254.4=/usr/src/debug/systemd/254.4 
-fdebug-prefix-map=/home/runsisi/working/bmc/openbmc/build/romulus/tmp/work/arm1176jzs-openbmc-linux-gnueabi/systemd/254.4/recipe-sysroot= 
-fmacro-prefix-map=/home/runsisi/working/bmc/openbmc/build/romulus/tmp/work/arm1176jzs-openbmc-linux-gnueabi/systemd/254.4/recipe-sysroot= 
-fdebug-prefix-map=/home/runsisi/working/bmc/openbmc/build/romulus/tmp/work/arm1176jzs-openbmc-linux-gnueabi/systemd/254.4/recipe-sysroot-native= 

gcc 预处理的结果如下:

# 83 "../../../../../../workspace/sources/systemd/src/basic/log.h" 3 4
_Static_assert
# 83 "../../../../../../workspace/sources/systemd/src/basic/log.h"
((sizeof("""/usr/src/debug/systemd/254.4/src/basic/log.h""") - sizeof(typeof("/usr/src/debug/systemd/254.4/src/basic/log.h"[0]))) > (sizeof("""../../../../../../workspace/sources/systemd""") - sizeof(typeof("../../../../../../workspace/sources/systemd"[0]))) + 1, "STRLEN(__FILE__) > STRLEN(RELATIVE_SOURCE_PATH) + 1");

此时 44 > 43 + 1 显然会触发断言。

去掉第一个 -fmacro-prefix-map 之后的 gcc 预处理结果如下:

# 83 "../../../../../../workspace/sources/systemd/src/basic/log.h" 3 4
_Static_assert
# 83 "../../../../../../workspace/sources/systemd/src/basic/log.h"
((sizeof("""../../../../../../workspace/sources/systemd/src/basic/log.h""") - sizeof(typeof("../../../../../../workspace/sources/systemd/src/basic/log.h"[0]))) > (sizeof("""../../../../../../workspace/sources/systemd""") - sizeof(typeof("../../../../../../workspace/sources/systemd"[0]))) + 1, "STRLEN(__FILE__) > STRLEN(RELATIVE_SOURCE_PATH) + 1");

一切符合预期。

规避

-fmacro-prefix-map 在 meta/conf/bitbake.conf 中定义:

TARGET_DBGSRC_DIR ?= "/usr/src/debug/${PN}/${PV}"
# Beware: applied last to first
DEBUG_PREFIX_MAP ?= "-fcanon-prefix-map \
 -fmacro-prefix-map=${S}=${TARGET_DBGSRC_DIR} \
 -fdebug-prefix-map=${S}=${TARGET_DBGSRC_DIR} \
 -fmacro-prefix-map=${B}=${TARGET_DBGSRC_DIR} \
 -fdebug-prefix-map=${B}=${TARGET_DBGSRC_DIR} \
 -fdebug-prefix-map=${STAGING_DIR_HOST}= \
 -fmacro-prefix-map=${STAGING_DIR_HOST}= \
 -fdebug-prefix-map=${STAGING_DIR_NATIVE}= \
"
DEBUG_FLAGS ?= "-g -feliminate-unused-debug-types ${DEBUG_PREFIX_MAP}"

第一个 -fmacro-prefix-map 选项是在如下 commit 中添加的:

https://github.com/openbmc/openbmc/commit/92b42cb35d755f8cfe6c17d403711a536e0f0721#diff-ff7d75b9352b9b287b7eb454608ec6de8531759530bc3de816822ad51443b0de

显然 systemd 使用 __FILE__ 的方式与 bitbake 的预期不符,如果不修改 systemd 代码,并没有好的解决办法,但是,可以在 bbapend 文件中显式移除该选项进行规避:

DEBUG_PREFIX_MAP:remove = "-fmacro-prefix-map=${S}=${TARGET_DBGSRC_DIR}"

之所以在稍早一些的版本上构建 systemd 没有问题,是因为如下这个 commit 的改动:

https://github.com/openbmc/openbmc/commit/ac13d5f36a6bd845f1709b7f41c02bd3b412ad15#diff-ff7d75b9352b9b287b7eb454608ec6de8531759530bc3de816822ad51443b0de

-fmacro-prefix-map=${S}=/usr/src/debug/${PN}/${EXTENDPE}${PV}-${PR} \

被修改为:

TARGET_DBGSRC_DIR ?= "/usr/src/debug/${PN}/${PV}"
-fmacro-prefix-map=${S}=${TARGET_DBGSRC_DIR} \

从而 /usr/src/debug/${PN}/${EXTENDPE}${PV}-${PR}

/usr/src/debug/systemd/1_254.4-r0/src/basic/log.h

变成 /usr/src/debug/${PN}/${PV}

/usr/src/debug/systemd/254.4/src/basic/log.h

显然,由于老版本的字符串长度长一些,因此断言不会触发。

appendix

DEBUG_PREFIX_MAP
https://docs.yoctoproject.org/ref-manual/variables.html#term-DEBUG_PREFIX_MAP

Syntax and Operators
https://docs.yoctoproject.org/bitbake/2.6/bitbake-user-manual/bitbake-user-manual-metadata.html

At least for gcc, the value of __FILE__ is the file path as specified on the compiler’s command line.

__FILE__ macro shows full path
https://stackoverflow.com/questions/8487986/file-macro-shows-full-path

  • -fdebug-prefix-map=OLD=NEW can strip directory prefixes from debug info. (available in all GCC versions, Clang 3.8)
  • -fmacro-prefix-map=OLD=NEW is similar to -fdebug-prefix-map, but addresses unreproducibility due to the use of __FILE__ macros in assert calls for example. (available since GCC 8 and Clang 10)
  • -ffile-prefix-map=OLD=NEW is an alias for both -fdebug-prefix-map and -fmacro-prefix-map. (available since GCC 8 and Clang 10)

Build path
https://reproducible-builds.org/docs/build-path/

bitbake.conf: Add -fcanon-prefix-map to DEBUG_PREFIX_MAP
https://patchwork.yoctoproject.org/project/oe-core/patch/20230428032030.2047920-1-raj.khem@gmail.com/


最后修改于 2024-01-03