C语言 如何在不需要系统调用的情况下包含snprintf?

z9ju0rcb  于 2023-10-16  发布在  其他
关注(0)|答案(2)|浏览(158)

在嵌入式平台上,printf所需的系统调用通常不可用。标准的解决方案是编写简单的存根,例如。对于_write
我不需要也不想要printf。我需要snprintf的内部字符串格式。有没有什么方法可以得到它,而不必根除系统调用,这将永远不会被调用?
为了获得内部字符串格式化例程而不得不为系统调用编写存根,这似乎是倒退。

更新

如果我snprintf一个常量字符buf,它链接良好。这可能是因为编译器去掉了snprintf,只使用了puts
但是如果我有一个"%d"的字符串,我会得到链接器错误。
编译命令(这篇文章的word wrapped):

$ 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
44u64gxh

44u64gxh1#

我会使用一个独立的版本,使最小或零系统调用,并使用堆栈,所以你不必担心并发。
我以前见过一个sprintf & friends实现,它根本不调用文件系统。
我使用的是Arm mbed项目中的printf。sprintf本身不需要IO,但是因为包含了printf,所以你只需要提供一个小存根:适用于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>但你可能想避免该标题无论如何冲突。它将不可避免地从reent.h和诸如此类的东西中拖进来,看起来你的构建不支持。初始化stdout条目,使其putc_func驱动您的uart,或任何您更喜欢认为是stdout的东西。
我建议不要删除像writesbrk这样的东西,因为迟早,你会不小心在newlib中调用一些东西,* 需要这些东西才能正常工作 *,然后你会想为什么你的标准库不能正常工作。如果这些东西不起作用,那么远离newlib中需要它们的部分-如果你不小心调用它们,链接器错误很快就会告诉你,而不是它偶然失败。

9wbgstp7

9wbgstp72#

(Part问题是有些东西想要中止,中止调用printf(而不仅仅是snprintf)。
你可以直接取消abort来防止这种情况。
它看起来也像是有东西想要malloc。如果你有一个嵌入式版本的话,你可能还想去掉malloc,或者使用标准的c库malloc,但是stub out _sbrk告诉它从哪里获取内存。

相关问题