如何在没有 printf 需要的系统调用的情况下包含 snprintf?

How to include snprintf without syscalls needed for printf?

提问人:SRobertJames 提问时间:9/11/2023 最后编辑:SRobertJames 更新时间:9/12/2023 访问量:82

问:

在嵌入式平台上,所需的系统调用通常不可用。标准解决方案是编写简单的存根,例如 for .printf_write

我不需要也不想要printf。我需要内部字符串格式。有没有办法在不删除系统调用的情况下获得它,无论如何都不会调用系统调用?snprintf

必须为 syscalls 编写存根只是为了获取内部字符串格式化例程,这似乎是倒退的。


更新

如果我是一个恒定的 char buf,它链接良好。这大概是因为编译器取出并只使用 .snprintfsnprintfputs

但是,如果我有一个格式字符串,我就会收到链接器错误。"%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
C IO 嵌入式 printf

评论

1赞 pmacfarlane 9/11/2023
我不明白为什么你需要存根才能得到.您的平台/系统是什么?它(例如,像默认的 STM32 项目一样)在链接时是否会删除未使用的符号?_writesnprintf()
2赞 0___________ 9/11/2023
对于嵌入式目标,我建议编写自己的简化实现。
0赞 pmacfarlane 9/11/2023
你用的是什么编译器?如果 gcc,您可以使用和链接进行编译,并且未使用的函数(和数据)将不包含在二进制文件中。因此,您不必存根任何不需要的东西。-ffunction-sections -fdata-sections--gc-sections
0赞 SRobertJames 9/11/2023
@pmacfarlane 使用 gcc 和那些标志;更新了帖子的详细信息。
0赞 B. Pantalone 9/11/2023
值得一提的是,我在嵌入式系统上一直有问题。您将获得更可靠的行为及其亲戚。snprintf()strtol()

答:

0赞 Tom V 9/11/2023 #1

(部分)问题是某些东西想要中止,而中止调用 printf(不仅仅是 snprintf)。

您可以直接存根中止以防止这种情况发生。

看起来也像有什么东西想要 malloc。如果您有嵌入式版本,您可能还想存根 malloc,或者使用标准的 c 库 malloc 但存根 _sbrk 来告诉它从哪里获取内存。

1赞 Rodney 9/12/2023 #2

我会使用一个独立版本,它进行最少或零的 sys 调用,并使用堆栈,这样您就不必担心并发性。

我过去见过一个&朋友实现,它根本不会调用文件系统。sprintf

我正在使用 Arm mbed 项目中的 printf。sprintf 本身不需要 IO,但因为包含了 printf,所以你只需要提供一个小的存根:for 和fpuctcFILEstdout

我的存根是:

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.hstdoutputc_funcstdout

我建议不要像 和 这样的东西存根,因为迟早你会不小心调用 newlib 中需要这些东西才能正常工作的东西,然后你会想知道为什么你的标准库不能正常工作。如果这些东西不起作用,那么请远离 newlib 中需要它们的部分 - 如果你不小心调用它们,链接器错误很快就会告诉你,而不是随意失败。writesbrk