我正在一个具有自己的库(例如 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用z
和lib
包围库并搜索多个目录。
我如何称呼链接器(.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
答案 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功能,并在需要的地方使用它。