【问题标题】:Replacing MSVC CRT function with private implementation用私有实现替换 MSVC CRT 函数
【发布时间】:2019-03-22 19:57:46
【问题描述】:

我有一个嵌入式 IoT 项目,我喜欢首先使用 VisualStudio 等 PC 工具进行部分开发。我的嵌入式项目只有一个用于文件系统的闪存,我想将fopenfread 等重定向到我自己在 Windows 上的私有实现。但我遇到的是无法让我的私有 CRT 库优先于内置 CRT(例如,由/MD 编译器开关驱动的内置行为)。

我有一个简单的三项目解决方案。

项目 1 是一个测试可执行文件。它有一条主线:

int main()
{
    test();
}

项目 2 和 3 是静态库。项目 2:

#include <string.h>
#include <stdio.h>

void test()
{
    printf("%s\n", strchr("x", 'x'));
}

项目 3 有:

char * strchr(const char * s, int c)  // exact signature of MSVC
{
    return "overridden";
}

我希望输出是 overridden 但实际上是

x

但如果我将这个添加到项目 1:

printf("%s\n", strchr("y", 'y'));

输出将是

overridden
overridden

第一个来自库中的test(),第二个来自可执行文件main()

有什么建议吗?

【问题讨论】:

  • 你试过什么不起作用?知道这将使任何人免于建议您已经尝试过的事情,或者使他们能够看到如果您的方法几乎是正确的,您可能会出错的地方。
  • 我不确定“文件系统只有闪存”如何排除在测试系统上使用标准文件系统 I/O 的可能性——也就是说stdio 抽象的目的。更常见的要求是使用测试环境的文件系统模拟嵌入式非易失性持久存储 API。当然,除非您正在测试的是文件系统本身。
  • 同意,它在嵌入式设备上使用特殊的 CRT,并且是使用 fopen 等的重点。但是嵌入式设备的开发工具低于标准。我喜欢拥有丰富的 PC 工具,例如很好的调试、代码覆盖率等,而这些都是嵌入式设备所不具备的。所以我需要做的是在 Windows 上模拟或模拟嵌入式 CRT,但是,它却落入了实际的 Windows CRT。
  • 如果您在进行跨平台编译并想在 PC 上测试嵌入式程序,您肯定需要一些条件预处理器魔法。您基本上是在编写自己的 HAL,并且直接在文件中包含 &lt;stdio.h> 意味着您正在跳过抽象层并直接进行实现。因此,首先创建您自己的用于进行必要接线的接头,并避免使用直接访问硬件的标准接头(如&lt;stdio.h&gt;)。很可能您也希望避免使用 &lt;stdlib.h&gt; 以及与 malloc 相关的任何内容。
  • @Groo 我能够重定向第三方库的 CRT,它是专有的并且不附带源代码。没有必要创建一个完整的抽象层。 (我会给你这个 - 链接解决方案并不漂亮。)

标签: windows visual-studio embedded msvcrt


【解决方案1】:

链接器在第一个匹配的基础上解析符号 - 优先于单独链接的 .obj 文件,然后是 .lib 文件,按照提供给命令行链接器的顺序。所以你通常可以覆盖一个库符号,只是链接你自己的替换。我从来没有用 MSVC 尝试过,这是一种有点野蛮的方法。

不依赖于特定链接器行为的替代解决方案是使用预处理器将标准符号替换为您自己的替代方案。例如:

#if defined _WIN32
    #define fopen test_fopen
    FILE* test_fopen( const char * filename, const char * mode ) ;
#endif

例如在名为 testlib.h 的头文件中添加您需要的其他宏和声明,然后使用“强制包含”(MSVC 中的/FI testlib.h)无形地包含测试接口无处不在。然后在 Windows 上构建时,所有 fopen 调用将被替换为 test_fopen 调用您的替换函数。

【讨论】:

  • MSVCRT 不作为输入传递。它是从编译器 /MD 或 /MT 开关的 OBJ 元数据中提取的。
  • @jws :在这种情况下,将替换链接为 .obj 代码而不是 .lib 文件。从您的编辑来看,这似乎可行。也就是说,既然您已经证明它可以工作,那么直接在项目中包含替换源有什么问题?它可能使调试和测试仪器更容易。
  • 我真正想要的是将 CRT 库放在命令行的最后。但据我所知,没有办法说“没有 CRT - 我会手动提供”。我试过了,静态或动态 CRT 没有区别。编译时解决方案对我不起作用,因为某些代码仅来自第三方的 lib。
  • 您能否改用 MinGW GCC 来获得更多控制权?也就是说,我不相信它不能在 MSVC 中完成 - 也许链接器选项 /NODEFAULTLIB 是您需要的。
  • MinGW with GCC 是可以尝试的。对我来说是大规模的工具链重组,我可能会接受我新发现的丑陋解决方法。
【解决方案2】:

如果问题仅由静态库中的 CRT 使用引起,并且链接器在找到私有 CRT 静态库之前找到 MSVCRT DLL 导出库,解决方法可能是强制引用可执行源文件中的私有 CRT .

上面的示例匹配代码,作为全局放置在 main 中:

static char * (*p_strchr)(const char *, int) = strchr;

它不太理想,但它以可靠的方式改变了链接器搜索顺序。

更多发现

链接器可能会发出 LNK1169“一个或多个多重定义符号”(多个定义)链接器错误的组合(我不完全理解)。例如,它可能会说fopen 被定义了多次,如果你在命令行中颠倒私有库和MS 库的顺序,那么它可能会说fread 被定义了多次。可能是由于 MSVC 库具有 DLL 导出签名,而私有库是导入符号。这种行为很难确定,因为它不会在所有被覆盖的函数上出错。

如果可以将所有私有库切换到/MT编译器开关(使用静态CRT),那么LNK1169问题就解决了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-25
    • 1970-01-01
    • 2013-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多