1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

cpu/avr8_common/flash_utils: use C and linker for aliases

`flash_<funcname>()` is implemented by `<funcname>_P()` provided by
the AVR libc on AVR targets. Previously, the preprocessor was used
to do the aliasing, but this causes issues with LLVM: The signatures of
e.g. `printf_P()` expects `const char *`, whereas flash utils expects
`FLASH_ATTR const char *`. For GCC this will just implicitly drop the
`FLASH_ATTR`, while it requires an explicit cast for LLVM.

To implement the explicit cast, `static inline` function wrappers
where used instead where possible. But for the variadic functions
(e.g. `printf(fmt, ...)`) the linker is used to provide the aliases,
as there is no way to pass the variadic functions throw in C. The
alternative would be to implement `flash_printf()` by calling
`vprintf_P()`, but that increased ROM size quite a bit.

Finally, a work around for a bug in Ubuntu's toolchain has been added:
An unused function that calls to `printf_P()`, `fprintf_P()` and
`snprintf_P()`. Since this function is garbage collected anyway, it
has no impact on the generated ELF file.
This commit is contained in:
Marian Buschsieweke 2023-11-10 11:13:22 +01:00
parent d6797867fb
commit cee7cccfd0
No known key found for this signature in database
GPG Key ID: 77AA882EC78084E6
3 changed files with 82 additions and 15 deletions

View File

@ -7,4 +7,8 @@ ifneq (,$(filter printf_float,$(USEMODULE)))
LINKFLAGS += -Wl,-u,vfprintf -lprintf_flt -lm
endif
# Add aliases for flash_printf, flash_fprintf, flash_snprintf:
LINKFLAGS += -Wl,--defsym=flash_printf=printf_P
LINKFLAGS += -Wl,--defsym=flash_fprintf=fprintf_P
LINKFLAGS += -Wl,--defsym=flash_snprintf=snprintf_P
include $(RIOTMAKE)/arch/avr8.inc.mk

View File

@ -21,7 +21,7 @@
#define FLASH_UTILS_ARCH_H
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <avr/pgmspace.h>
#ifdef __cplusplus
@ -35,20 +35,68 @@ extern "C" {
#define FLASH_ATTR __flash
#define PRIsflash "S"
#define TO_FLASH(x) __extension__({static FLASH_ATTR const char __c[] = (x); &__c[0];})
#define flash_strcmp strcmp_P
#define flash_strncmp strncmp_P
#define flash_strlen strlen_P
#define flash_strcpy strcpy_P
#define flash_strncpy strncpy_P
#define flash_printf printf_P
/* avrlibc seemingly forgot to provide vprintf_P(), but vfprintf_P() is there */
#define flash_vprintf(fmt, arglist) vfprintf_P(stdout, fmt, arglist)
#define flash_fprintf fprintf_P
#define flash_vfprintf vfprintf_P
#define flash_snprintf snprintf_P
#define flash_vsnprintf vsnprintf_P
#define flash_puts puts_P
#define flash_memcpy memcpy_P
static inline int flash_strcmp(const char *ram, FLASH_ATTR const char *flash)
{
return strcmp_P(ram, (const char *)flash);
}
static inline int flash_strncmp(const char *ram, FLASH_ATTR const char *flash, size_t n)
{
return strncmp_P(ram, (const char *)flash, n);
}
static inline size_t flash_strlen(FLASH_ATTR const char *flash)
{
return strlen_P((const char *)flash);
}
static inline char * flash_strcpy(char *ram, FLASH_ATTR const char *flash)
{
return strcpy_P(ram, (const char *)flash);
}
static inline char * flash_strncpy(char *ram, FLASH_ATTR const char *flash, size_t n)
{
return strncpy_P(ram, (const char *)flash, n);
}
static inline int flash_vprintf(FLASH_ATTR const char *flash, va_list args)
{
/* vprintf_P() is not provided by avr-libc. But vfprintf_P() with
* stdout as stream can be used to implement it */
return vfprintf_P(stdout, (const char *)flash, args);
}
static inline int flash_vfprintf(FILE *stream, FLASH_ATTR const char *flash,
va_list args)
{
return vfprintf_P(stream, (const char *)flash, args);
}
static inline int flash_vsnprintf(char *buf, size_t buf_len,
FLASH_ATTR const char *flash, va_list args)
{
return vsnprintf_P(buf, buf_len, (const char *)flash, args);
}
static inline void flash_puts(FLASH_ATTR const char *flash)
{
puts_P((const char *)flash);
}
static inline void * flash_memcpy(void *dest, FLASH_ATTR const void *src,
size_t n)
{
return memcpy_P(dest, (const void *)src, n);
}
/* aliases need to be provided by the linker, as passing through va-args is
* not possible */
int flash_printf(FLASH_ATTR const char *flash, ...);
int flash_fprintf(FILE *stream, FLASH_ATTR const char *flash, ...);
int flash_snprintf(char *buf, size_t buf_len, FLASH_ATTR const char *flash, ...);
#endif /* Doxygen */

View File

@ -0,0 +1,15 @@
#include <avr/pgmspace.h>
#include <stdio.h>
/* The outdated linker from Ubuntu's toolchain contains a bug in which it will
* garbage collect symbols referenced only by --defsym= command line options,
* and subsequently complain that the symbols are not defined. Adding other
* references to those symbols from an unused function makes that buggy linker
* happy. Since this function is never used, it will be garbage collected and
* not impact the ROM size. */
void work_around_for_shitty_ubuntu_toolchain_and_not_expected_to_be_called(void)
{
printf_P(NULL);
fprintf_P(stdout, NULL);
snprintf_P(NULL, 0, NULL);
}