Zephyr:undefined reference to `__device_dts_ord_xx‘

Posted 17岁boy想当攻城狮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Zephyr:undefined reference to `__device_dts_ord_xx‘相关的知识,希望对你有一定的参考价值。

我最近为开发板开发了一个wm89xx编码器驱动程序,因为Zephyr对多媒体支持较少,所以我打算自己为Zephyr开发Driver,我的注册方式是drivertree,注册代码如下:

#define WM89XX_CONFIG_I2C(inst) \\
                               \\
                .bus.i2c = I2C_DT_SPEC_INST_GET(inst), \\
                .bus_io = &wm89xx_bus_io_i2c,           \\
        
#define WM89XX_DEFINE(inst)\\
        static struct WM89XX_Data wm89xx_data_##inst; \\
        static const struct WM89XX_Config wm89xx_config_##inst = \\
                                                WM89XX_CONFIG_I2C(inst); \\
        DEVICE_DT_INST_DEFINE(inst,     \\
                              wm89xx_init,\\
                              NULL,\\
                              &wm89xx_data_##inst,\\
                              &wm89xx_config_##inst,\\
                              POST_KERNEL,\\
                              MY_INIT_PRIO,\\
                              &wm89xx_api);

DT_INST_FOREACH_STATUS_OKAY(WM89XX_DEFINE)

但我在编译时遇到了一个问题:

:/home/zhihao/zephyrproject/zephyr/drivers/wm89xx/src/wm89xx.c:39: undefined reference to `__device_dts_ord_92'
collect2: error: ld returned 1 exit status

提醒我未定义的引用“设备dts ord 92”,于是我仔细去研究了一下我的注册代码,最终定位发现问题在于这个注册宏:

DEVICE_DT_INST_DEFINE(inst,     \\
                              wm89xx_init,\\
                              NULL,\\
                              &wm89xx_data_##inst,\\
                              &wm89xx_config_##inst,\\
                              POST_KERNEL,\\
                              MY_INIT_PRIO,\\
                              &wm89xx_api);

于是我就去查看Zephyr的源代码,去查看这个宏的实现(为了更好地让大家理解我将代码中的注释翻译成了中文在发出来的):

/**
*@def DEVICE_DT_INST_DEFINE
*
*@brief Like DEVICE_DT_为DT_DRV_COMPAT兼容的实例定义
*
*@param inst实例号。这被替换为
*<tt>DT\\u DRV\\u COMPAT(inst)</tt>在对设备的调用中定义。
*
*@param。。。设备预期的其他参数定义。
*/
#define DEVICE_DT_INST_DEFINE(inst, ...) \\
        DEVICE_DT_DEFINE(DT_DRV_INST(inst), __VA_ARGS__)

通过上面的定义和描述,我明白了device_ dts_ ord_92,其中92是树中设备的ID,所以我认为这个函数是一个自动生成的函数,所以理论上应该由zephyr构造工具自动生成,但我不知道为什么这个函数不是为我的驱动程序项目生成的,所以我检查了生成的zephyr.map文件(在build/zephyr目录下)

zephyr.map:2282:                0x0000000008003b60                __device_dts_ord_18
zephyr.map:2286:                0x0000000008003b78                __device_dts_ord_274
zephyr.map:2287:                0x0000000008003b90                __device_dts_ord_273
zephyr.map:2288:                0x0000000008003ba8                __device_dts_ord_19
zephyr.map:2289:                0x0000000008003bc0                __device_dts_ord_272
zephyr.map:2290:                0x0000000008003bd8                __device_dts_ord_271
zephyr.map:2291:                0x0000000008003bf0                __device_dts_ord_270
zephyr.map:2292:                0x0000000008003c08                __device_dts_ord_269
zephyr.map:2293:                0x0000000008003c20                __device_dts_ord_268
zephyr.map:2294:                0x0000000008003c38                __device_dts_ord_49
zephyr.map:2295:                0x0000000008003c50                __device_dts_ord_267
zephyr.map:2296:                0x0000000008003c68                __device_dts_ord_69
zephyr.map:2299:                0x0000000008003c80                __device_dts_ord_45
zephyr.map:2302:                0x0000000008003c98                __device_dts_ord_68
zephyr.map:2303:                0x0000000008003cb0                __device_dts_ord_65
zephyr.map:2313:                0x0000000008003ce0                __device_dts_ord_94
zephyr.map:2318:                0x0000000008003d10                __device_dts_ord_42

我的函数没有导入到映射中,所以我确信构建器没有为我生成这个函数

我再次检查了DTS文件中的定义。因为这个编码器被用作I2C的从设备,所以我在I2C下声明了它

&i2c3 
        pinctrl-0 = <&i2c3_scl_ph7 &i2c3_sda_ph8>;
        status = "okay";
        clock-frequency = <I2C_BITRATE_FAST>;
         wm89xx@1a
                compatible = "micro,wm89xx";
                reg = <0x1a>;
                label = "wm89xx";
                status = "okay";
        ;
;

我认为上述DTS定义应该没有问题

所以我回到了zephyr DEVICE_DT_INST_DEFINE,在定义源代码的阶段,我发现它叫做DEVICE_ DT_ DEFINE,所以我再次检查了它的源代码

最后,我找到了这个宏函数,它是最终注册驱动的宏

/* 与DEVICE_DEFINE类似,但采用节点id和开发人员名称,并带有尾随
 * 来自外部devicetree的依赖句柄
 */
#define Z_DEVICE_DEFINE(node_id, dev_name, drv_name, init_fn, pm_action_cb, \\
                        data_ptr, cfg_ptr, level, prio, api_ptr, ...)   \\
        Z_DEVICE_DEFINE_PRE(node_id, dev_name, pm_action_cb, __VA_ARGS__) \\
        COND_CODE_1(DT_NODE_EXISTS(node_id), (), (static))              \\
                const Z_DECL_ALIGN(struct device)                       \\
                DEVICE_NAME_GET(dev_name) __used                        \\
        __attribute__((__section__(".z_device_" #level STRINGIFY(prio)"_"))) =  \\
                .name = drv_name,                                       \\
                .config = (cfg_ptr),                                    \\
                .api = (api_ptr),                                       \\
                .state = &Z_DEVICE_STATE_NAME(dev_name),                \\
                .data = (data_ptr),                                     \\
                Z_DEVICE_DEFINE_INIT(node_id, dev_name)                 \\
        ;                                                              \\
        BUILD_ASSERT(sizeof(Z_STRINGIFY(drv_name)) <= Z_DEVICE_MAX_NAME_LEN, \\
                     Z_STRINGIFY(DEVICE_NAME_GET(drv_name)) " too long"); \\
        Z_INIT_ENTRY_DEFINE(DEVICE_NAME_GET(dev_name), init_fn,         \\
                (&DEVICE_NAME_GET(dev_name)), level, prio)

通过对源代码的分析,我知道'Z_DEVICE_ DEFINE_ P'注册诸如电源管理之类的驱动程序函数,并使用'COND_ CODE_ 1'确定驱动程序是否已传入电源函数指针,'Z_DECL_ALIGN'用于做段对其。

真正引起我注意的是这个宏:

Z_DEVICE_DEFINE_INIT(node_id, dev_name) 

展开并进入,这个宏是这样描述的:

/*初始生成提供与设备对象关联的记录
*使用其devicetree序号,并提供依赖项序号。
*这些是作为弱定义提供的(以防止引用
*在编译原始对象文件时被捕获),以及
*在不同的pass1节中(将替换为
*后处理)。
*
*(实验上)还需要提供明确的对齐
*在每个对象上。否则x86-64版本将引入填充
*单个对象中同一输入区域中的对象之间
*文件,这些文件将保留在后续链接中
*空间,并导致相对于pass2的聚合大小更改
*当所有对象都位于同一输入部分时。
*
*如果设备句柄更改大小,则生成断言将失败,这将导致
*指链接器脚本和中的对齐指令
*“gen_handles.py`必须更新。
*/

我注意到这句话:“使用其devicetree序号,并提供依赖项序号”和Z_DEVICE_DEFINE中的描述:“但采用节点id和开发人员名称,以及来自设备树外部的后续依赖句柄”

因此,我认为它需要一些联想。我的设备在I2C DTS下声明,包括我的驱动程序中使用的一些I2C设备代码。因此,我认为有必要设置一些选项来启用I2C支持,但DTS中的“status”已被写为“ok”,因此我在示例中引用了一些编译后的示例

我使用的是'sensor/bme280'的示例。我发现在这个例子中有一个Kconf

```config
config LOG
        default y

config LOG_PRINTK
        default y

config SENSOR_LOG_LEVEL
        default 4

#默认情况下启用SPI和I2C支持,以便示例可以与
#设备以任意方式连接。如果需要,可以覆盖这些默认值
#需要。
config SPI
        default y

config I2C
        default y

source "Kconfig.zephyr"
```

我开始尝试。我发现当我将'config I2C'设置为N时,会出现与我相同的问题,这让我非常惊喜,因为大致已经找到了问题,然后我在我的驱动程序kconfig中将I2C设置为y时,我的驱动项目被编译通过了!

所以我猜原因是'Z_DEVICE_DEFINE'宏DEFINE获取依赖设备节点的属性,它使用'COND_CODE_1'这个宏执行条件编译,这个条件编译中就会判断附加依赖设备节点的Driver是否被编译进来了,如果没有被编译则不会为子依赖设备生成device_dts_ord函数,而驱动需要device_dts_ord函数来注册到Zephyr内核里去,所以在最终link时找不到这个函数。

为了进一步验证我的猜测,我将app项目中的'prj.conf'里添加到`CONF_I2C=y`,发现效果与`Kconfig`中的效果相同,所以我的结论基本确定:当你的驱动设备是使drivertree方式从dts文件中获取注册信息时,如果这个设备依赖某个父节点设备那么需要在编译时将父节点的驱动一并编译进来,否则Z_DEVICE_DEFINE中的COND_CODE_1条件宏判定依赖父节点的驱动没有被编译那么不会生成device_dts_ord函数。

这个问题我已经提交到了Zephyr官方的Issue中,大家可以在这里找到我提交的issue:undefined reference to `__device_dts_ord_xx' · Issue #41677 · zephyrproject-rtos/zephyr · GitHub

以上是关于Zephyr:undefined reference to `__device_dts_ord_xx‘的主要内容,如果未能解决你的问题,请参考以下文章

Undefined symbols for architecture i386: "_deflate", referenced from:

MAC编译出错:Undefined symbols for architecture x86_64: “_CFRelease“, referenced from:

微信SDK导入报错 Undefined symbols for architecture i386:"operator delete[](void*)", referenced f

lib1funcs.S(用于解决裸板实现 printf 中的问题: undefined reference to `__aeabi_uidivmod' 和 undefined referenc

Undefined symbols for architecture i386:和"_OBJC_CLASS_$_xx", referenced from:问题解决方法

Undefined symbols for architecture i386:和"_OBJC_CLASS_$_xx", referenced from:问题解决方法