IAR EWARM中的条件链接

时间:2019-03-04 20:20:36

标签: c linker arm embedded iar

我正在使用IAR链接程序使用IAR EWARM 8.10.1。

我有两个编译单元使用的公共头文件。它包括具有外部链接的功能原型,并构成一个API。根据构建的配置方式,我希望将模块A或B与应用程序的其余部分链接。

[ Common_Header.h ]
    |         |
    |         +----- [Module_A.c] ---> [Module_A.o]
    |
    +--------------- [Module_B.c] ---> [Module_B.o]

我想以某种方式将参数传递给ilinkarm.exe以包含Module_A.o。

我过去使用的其他IAR工具链使用了XLINK链接器。 XLINK具有-A选项,我想这与我所需要的类似。

我本质上想要的是在Module_A处于活动状态时将Module_B中的函数定义视为__weak,反之亦然。

如果可能,我想避免在代码中加入#pragma weak。我需要能够使用一些不同的工具链来编译此代码。因此,我需要用#ifdef __ICCARM__之类的东西包装任何这样的婴儿车。此外,我需要定义一些额外的预处理器符号,以有条件地使一个模块在另一个模块处于活动状态时处于弱状态。这就是我想避免的所有复杂性。

此外,当module_A处于活动状态时,我不想从构建中排除module_B。我希望两个模块都能始终编译。如果有人对接口和module_A进行了更改,但未能更新module_B,我希望他们得到编译器错误。随着接口的发展,这将防止module_B陷入孤立和破裂的状态,我们的注意力将集中在module_A上。

我已经查看了EWARM_DevelopmentGuide.ENU.pdf,但找不到似乎可以满足我要求的命令行选项。我想知道是否存在这样的选择,而我错过了,还是有另一种方式来实现我所追求的目标。

3 个答案:

答案 0 :(得分:1)

这并不是一个完整的答案,因为我没有像您这样的新版本的编译器,而是更多可行的解决方法。

Module_A.c

#if MODULE_A_SELECTED
    #define MY_WEAK
#else
    #define MY_WEAK __weak
#endif

MY_WEAK void foo(void) { ... }
 ...

Module_B.c

#if MODULE_B_SELECTED
    #define MY_WEAK
#else
    #define MY_WEAK __weak
#endif

MY_WEAK void foo(void) { ... }
 ...

然后您将根据需要在配置中定义MODULE_*_SELECTED

答案 1 :(得分:1)

无需依赖链接程序特定的支持或IDE特定的构建管理。一种完全可移植的解决方案是用不同的符号名称定义A和B实现,然后使用有条件定义的宏来选择所需的实现。

示例:

#if defined USE_IMPLEMENTATION_A
    #define doSomething implementationA_doSomething

#elif defined USE_IMPLEMENTATION_B
    #define doSomething implementationB_doSomething

#else
    #error API implementation not defined
#endif

int implementationA_doSomething( void ) ;
int implementationB_doSomething( void ) ;

通过这种方式,实现A和B都将始终被编译,但是通过使用宏doSomething而不是实现特定的函数名称,将仅使用选定的API。

我不知道ILINK有多聪明,但是通过将实现放在单独的翻译单元(即.c文件)中,链接器应该能够从链接中消除未使用的功能。如果不是这样的话,将目标代码放在静态链接库(.lib或.a)中肯定会。


要解决维护除命名空间前缀之外两个相同的实现文件的问题,您可以使用以下原型创建一个虚拟的头文件:

int NAMESPACE_doSomething( void ) ;

然后使用诸如sed之类的工具进行预构建步骤,以通过以下方式生成实现原型标头:

sed -i 's/NAMESPACE/api_a/g' api_dummy.h > api_a.h    
sed -i 's/NAMESPACE/api_b/g' api_dummy.h > api_b.h

然后您有一个文件api.h,其中包含(片段):

#if defined USE_IMPLEMENTATION_A
    #define doSomething api_a_doSomething

#elif defined USE_IMPLEMENTATION_B
    #define doSomething api_b_doSomething

#else
    #error API implementation not defined
#endif

#include api_a.h
#include api_b.h

您可以进一步编写代码生成器,以从函数名称列表中生成api.h。在您首选的脚本语言甚至C语言中,这都不太困难。您可以编写这样的生成器以接受命令行参数:

generate_api <input> <output> <namespace1> <namespace2> ... <namespaceN>

然后称呼它:

generate_api functions.txt api.h api_a api_b

您甚至可以在虚拟头文件中使用NAMESPACE_文本来生成<input>的函数名称列表,以便可以从单个虚拟头文件生成整个API头文件集。

答案 2 :(得分:1)

我最终使用了类似于user694733建议的弱链接。但是我的方法有点不同。

我在模块A和B的顶部都添加了一个这样的块。

#if (defined __ICCARM__)
    #if(defined USE_MODULE_A) && (1 == USE_MODULE_A)
        // do nothing, make definitions in this file strong
    #elif(defined USE_MODULE_B) && (1 == USE_MODULE_B)
        #pragma weak foo_fn
        #pragma weak bar_fn
        #pragma weak baz_fn
        #pragma weak qux_fn
    #else
        #error USE_MODULE_A or USE_MODULE_B must be defined.
    #endif
#endif

这种方法不需要我用MY_WEAK装饰每个函数原型。因此,非标准的东西都归为一类。

我不喜欢使用__weak / #pragma弱的几件事:

我不喜欢的第一件事是它增加了两个模块之间的耦合。如果未定义任何符号,则两个模块的定义都将很弱。到那时,您如何知道将使用哪个?因此,每个模块必须存在另一个模块,或者至少有一个以上的选项。我本可以使用一个定义,而只是更改值,但是我选择以这种方式进行操作,以使名称具有描述性。

我不喜欢的第二件事是,我用某种代码来弄乱了代码,这些东西是项目构建方式的产物。我想在实际的时候将这种逻辑引入到构建系统中。

第三点是它不是完全可移植的,必须用#if (defined __ICCARM__)来封闭。

但这将是我所使用的,除非我找到一种方法来完成对我来说更好的工作。如果发生这种情况,我会发布/接受其他答案。