提问人:SRobertJames 提问时间:9/11/2023 最后编辑:SRobertJames 更新时间:9/12/2023 访问量:82
如何在没有 printf 需要的系统调用的情况下包含 snprintf?
How to include snprintf without syscalls needed for printf?
问:
在嵌入式平台上,所需的系统调用通常不可用。标准解决方案是编写简单的存根,例如 for .printf
_write
我不需要也不想要printf。我需要内部字符串格式。有没有办法在不删除系统调用的情况下获得它,无论如何都不会调用系统调用?snprintf
必须为 syscalls 编写存根只是为了获取内部字符串格式化例程,这似乎是倒退的。
更新
如果我是一个恒定的 char buf,它链接良好。这大概是因为编译器取出并只使用 .snprintf
snprintf
puts
但是,如果我有一个格式字符串,我就会收到链接器错误。"%d"
编译命令(本文换行):
$ make flash -B
arm-none-eabi-gcc src/main.c -W -Wall -Wextra -Werror
-Wundef -Wshadow -Wdouble-promotion -Wformat-truncation
-Wconversion -Wno-attributes -DSTM32F446RE -DSTM32F446XX
-DSTM32F4 -g3 -Os -fno-common -ffunction-sections
-fdata-sections -fanalyzer -I. -I./libopencm3/include
-MMD -mcpu=cortex-m4 -mthumb -mfloat-abi=hard
-mfpu=fpv4-sp-d16 -Tconfig/link.ld
--static -nostartfiles -L./libopencm3/lib -lopencm3_stm32f4
-Wl,--fatal-warnings -Wl,--gc-sections -Wl,--cref
-Wl,-Map=firmware.map
-o firmware.elf
错误:
/usr/lib/gcc/arm-none-eabi/10.3.1/../../../arm-none-eabi/bin/ld: /usr/lib/gcc/arm-none-eabi/10.3.1/../../../arm-none-eabi/lib/thumb/v7e-m+fp/hard/libg.a(lib_a-sbrkr.o): in function `_sbrk_r':
/build/newlib-pB30de/newlib-3.3.0/build/arm-none-eabi/thumb/v7e-m+fp/hard/newlib/libc/reent/../../../../../../../../newlib/libc/reent/sbrkr.c:51: undefined reference to `_sbrk'
/usr/lib/gcc/arm-none-eabi/10.3.1/../../../arm-none-eabi/bin/ld: /usr/lib/gcc/arm-none-eabi/10.3.1/../../../arm-none-eabi/lib/thumb/v7e-m+fp/hard/libg.a(lib_a-abort.o): in function `abort':
/build/newlib-pB30de/newlib-3.3.0/build/arm-none-eabi/thumb/v7e-m+fp/hard/newlib/libc/stdlib/../../../../../../../../newlib/libc/stdlib/abort.c:59: undefined reference to `_exit'
/usr/lib/gcc/arm-none-eabi/10.3.1/../../../arm-none-eabi/bin/ld: /usr/lib/gcc/arm-none-eabi/10.3.1/../../../
...
/usr/lib/gcc/arm-none-eabi/10.3.1/../../../arm-none-eabi/bin/ld: /usr/lib/gcc/arm-none-eabi/10.3.1/../../../arm-none-eabi/lib/thumb/v7e-m+fp/hard/libg.a(lib_a-readr.o): in function `_read_r':
/build/newlib-pB30de/newlib-3.3.0/build/arm-none-eabi/thumb/v7e-m+fp/hard/newlib/libc/reent/../../../../../../../../newlib/libc/reent/readr.c:49: undefined reference to `_read'
collect2: error: ld returned 1 exit status
答:
(部分)问题是某些东西想要中止,而中止调用 printf(不仅仅是 snprintf)。
您可以直接存根中止以防止这种情况发生。
看起来也像有什么东西想要 malloc。如果您有嵌入式版本,您可能还想存根 malloc,或者使用标准的 c 库 malloc 但存根 _sbrk 来告诉它从哪里获取内存。
我会使用一个独立版本,它进行最少或零的 sys 调用,并使用堆栈,这样您就不必担心并发性。
我过去见过一个&朋友实现,它根本不会调用文件系统。sprintf
我正在使用 Arm mbed 项目中的 printf。sprintf 本身不需要 IO,但因为包含了 printf,所以你只需要提供一个小的存根:for 和fpuctc
FILE
stdout
我的存根是:
struct io_stub_file
{
int chanId;
int (*putc_func)(int chan, char c);
uint32_t flags; // you might not need this
void (* pre_put_hook)(void); // you might not need this
typedef struct io_stub_file FILE;
};
extern struct io_stub_file * io_stub_ftab[];
#define stdout io_stub_ftab[1]
这样做的一个问题是,它会与 <stdio.h> 发生冲突,但您可能希望避免该标头。它不可避免地会拖入诸如此类的东西,似乎您构建不支持。
初始化条目,使其驱动您的 uart,或您喜欢考虑的任何内容。reent.h
stdout
putc_func
stdout
我建议不要像 和 这样的东西存根,因为迟早你会不小心调用 newlib 中需要这些东西才能正常工作的东西,然后你会想知道为什么你的标准库不能正常工作。如果这些东西不起作用,那么请远离 newlib 中需要它们的部分 - 如果你不小心调用它们,链接器错误很快就会告诉你,而不是随意失败。write
sbrk
评论
_write
snprintf()
-ffunction-sections -fdata-sections
--gc-sections
snprintf()
strtol()