如何避免多个定义?链接器忽略定义的符号,但有一些例外

时间:2019-05-09 15:01:58

标签: c arm embedded libc linaro

我正在一个具有自己的库(例如 libc_alt )的嵌入式系统上实现标准libc( fopen,fclose,fread,fseek,ftell )中的某些功能但不是全部(我需要标准 libc 中的 memset,memcpy,longjmp,setjmp等等)。当我尝试将两个库都提供给编译器时

提供libc_alt

# makefile.mk 
(SRC_C += \
    $(COMP_PATH_libc_alt)/stdio.c \
) 

提供标准库

STD_LIBS += -lc -lgcc

在链接器步骤中,编译器(arm-eabi-gcc)正确地抱怨了fclose()的多个定义。 我的问题是,是否可以指示编译器排除标准libc定义fclose()并使用以$(COMP_PATH_libc_alt)/stdio.c编写的我的定义?

或者,如何指示编译器先使用我的 stdio.c ,然后在忽略重复的函数定义的同时使用标准libc的 stdio.c

例如在 $(COMP_PATH_libc_alt)/stdio.c 中找到fopen()定义之后;它将忽略标准libc中的fopen()。这样,我可以同时使用libc_alt fopen()和标准libc memcpy(),memset()

更新:谢谢大家的回答。 @artless_noise我确实将stdio.c放在了-lc之前。关于使用--wrap symbol,如果我理解正确,对fclose()的引用将更改为__wrap_fclose(),我需要在我的 stdio中更改fclose()的名称。 c __wrap__fclose();不幸的是,修改“我的” stdio.c 毫无疑问。 实际上,最奇怪的是,因为我将 stdio.c 放在-lc之前,所以 LinaroGCC arm-eabi-gcc 可以为 { {1}}(不错)。但这会给fopen(), fseek(), ftell(), fread()带来multiple definition错误

根据https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Link-Options.html;在fclose()上; 链接器应该忽略了ld -l之后的-lc中的重复符号。而且确实如此,只有一个例外libc_alt.a ,我不知道为什么?

  

在命令中写入此选项的位置有所不同;链接器按照指定的顺序搜索和处理库和目标文件。因此,fclose()在文件foo.o之后但在bar.o之前搜索库foo.o -lz bar.o如果bar.o引用了z中的函数,则这些函数可能无法加载。   链接器在标准目录列表中搜索该库,该库实际上是一个名为liblibrary.a的文件。然后,链接程序将使用此文件,就好像它是通过名称精确指定的一样。   搜索的目录包括几个标准系统目录以及您使用-L指定的目录。   通常,以这种方式找到的文件是库文件,即归档文件,其成员是目标文件。链接器通过扫描存档文件来查找成员,这些成员定义了已但尚未定义的符号。但是,如果找到的文件是普通的目标文件,则以通常的方式链接。使用-l选项和指定文件名之间的唯一区别是-l用zlib包围库并搜索多个目录。

我如何称呼链接器(.a包含“ my” stdio.o)

libc_alt.a

使用gcc-linaro-7.1.1-2017.08-x86_64_arm-eabi/bin/arm-eabi-gcc -Xlinker --wrap=fclose -nostdlib -Xlinker --gc-sections -Xlinker --fatal-warnings -Xlinker --no-wchar-size-warning -Xlinker --no-enum-size-warning -Xlinker --build-id=none -T /home/alice/Tools/Out/trusted_application.ld -Xlinker -pie -o /home/alice/Out/Bin/taMbedTLS.axf /home/alice/Locals/Code/aes.o /home/alice/Locals/Code/sha1.o /home/alice/libraries/libc_alt.a -lc -lgcc 作为我的nm的内容

libc_alt.a

使用stdio.o: U __aeabi_uidiv 000000d0 t $d 0000004c t $d 00000030 t $d 00000010 N $d 00000001 T fclose 00000001 T fopen 00000001 T fread 00000001 T fseek 00000001 T ftell U memset U __stack_chk_fail U __stack_chk_guard U strchr U strcmp U strlen stdlib.o: ,Linaro的内容 LinaroGCC arm-eabi-gcc nm

libc.a

2 个答案:

答案 0 :(得分:1)

您可以做到。您无需以任何特殊方式指示编译器/链接器,只需链接您自己的东西即可使用。

这是一个玩具示例,展示了如何获取自己的fread()实现。

像这样创建文件fopen_test.c

#include <stdio.h>
FILE *fopen(const char *pathname, const char *mode) {
  printf("My own fopen implementation, just returning NULL.\n");
  return NULL;
}

现在可以像这样编译了:

gcc -c fopen_test.c

生成目标文件fopen_test.o

如果您在fopen_test_main.c中具有以下主程序:

#include <stdio.h>
int main() {
  printf("Calling fopen()\n");
  FILE* f = fopen("file.txt", "rb");
  printf("After fopen()\n");
  return 0;
}

然后,您可以查看该程序的行为方式,具体取决于是否与fopen_test.o对象文件链接。

首先,使用标准的fopen()实现进行尝试:

$ gcc fopen_test_main.c
$ ./a.out

给出以下输出:

Calling fopen()
After fopen()

现在,尝试相同的操作,但是与包含特殊fopen实现的目标文件链接:

$ gcc fopen_test_main.c fopen_test.o
$ ./a.out

其中包含以下内容:

Calling fopen()
My own fopen implementation, just returning NULL.
After fopen()

因此,现在使用fopen_test.c中的fopen()实现代替了标准实现。

(我不知道您为什么会抱怨“多个定义”,因此需要更多有关您为弄清这一点所做的工作的详细信息。)

答案 1 :(得分:0)

您不能用C语言做到这一点。

您能做的最好的就是用不同的名称编写函数。

例如,代替fopen在代码中实现fileopen功能,并在需要的地方使用它。