printf() usage in RX toolchain 20.04 change when optimisation change
printf() usage in RX toolchain 20.04 change when optimisation change
Hello GNU Tools – Thanks for maintaining a wonderful and efficient toolchain!
I recently discovered a strange behaviour, when using printf from the RX toolchain (Windows version 20.04).
Until now (many versions of GNU toolchains for RX and H8), I have implemented the following 2 functions (overwrite of weak declared library functions) in order to have printf output on a UART.
int _fstat(int filedes, struct stat *buf)
{
(void)filedes; // Tell compiler not to warn of unused arguments
(void)buf;
return 0;
}
int _write(int file, char* ptr, int len)
{
(void)file;
// Direct output from printf to service port
UARTWrite(SERVICE_PORT, (unsigned char*)ptr, len);
return len;
}
That still works fine with no optimisation (-O0)
When I raise optimisation to -O3, linking fails. The following list of functions is not found:
ssize_t read(int fd, void *buf, size_t count);
off_t lseek(int fd, off_t offset, int whence);
int kill(pid_t pid, int sig);
int close(int fd);
int isatty(int fd);
pid_t getpid(void);
int fstat(int filedes, struct stat *buf);
int write(int file, char* ptr, int len);
When I implement those mentioned functions – only write needs to have real functionality – printf works and the program compiles without warnings and errors.
Why does no weak references exist when optimisation is -O3?
Best regards Frank Kjul Larsen
Hello Frank,
Is this the first time that you use -O3(or maybe -O2, etc)? In my memory, not only 202004 but also earlier version of GNURX needs something like stab functions (in other words, empty functions you said) to link newlib’s printf() (and moreover, sprintf()/sscanf(), in my memory) successfully.
Of course, I know that this reply isn’t an answer about weak reference you said, but I understood for a long time that I had to add such functions to use newlib’s printf(). I thought that it is the newlib’s way.
Best regards,
NoMaY
This is an additional research for my interest. (but might be useful for other people too.)
Yesterday moderator said that OPTLIB doesn’t cause linker warnings. Today I notice that the difference between two projects generated by e2 studio, one for OPTLIB and the other for NEWLIB, is only two options ‘-loptm -loptc’ or ‘-lm -lc’ as follows:
In case of a project generated by e2 studio for OPTLIB:
’rx-elf-gcc -O0 -ffunction-sections -fdata-sections -fdiagnostics-parseable-fixits -g2 -Wstack-usage=100 -mcpu=rx71m -misa=v2 -mlittle-endian-data -o “TestGUNRXprintf.elf” ./src/TestGUNRXprintf.o ./generate/hwinit.o ./generate/inthandler.o ./generate/start.o ./generate/vects.o -T “C:/Renesas/GitHubDesktop/workspaces/workspace_e2v780/TestGUNRXprintf/generate/linker_script.ld” -Wl,–start-group -loptm -loptc -lgcc -Wl,–end-group -nostartfiles -Wl,-e_PowerON_Reset -Wl,-M=TestGUNRXprintf.map’
In case of a project generated by e2 studio for NEWLIB:
‘rx-elf-gcc -O0 -ffunction-sections -fdata-sections -fdiagnostics-parseable-fixits -g2 -Wstack-usage=100 -mcpu=rx71m -misa=v2 -mlittle-endian-data -o “TestGUNRXprintf.elf” ./src/TestGUNRXprintf.o ./generate/hwinit.o ./generate/inthandler.o ./generate/start.o ./generate/vects.o -T “C:/Renesas/GitHubDesktop/workspaces/workspace_e2v780/TestGUNRXprintf/generate/linker_script.ld” -Wl,–start-group -lm -lc -lgcc -Wl,–end-group -nostartfiles -Wl,-e_PowerON_Reset -Wl,-M=TestGUNRXprintf.map’
According to yesterday’s Frank’s post and my research, though ‘-loptm -loptc’ is used but warnings are displayed. Why?
In case of my yesterday project based on Frank’s option:
SET OPTIONS=-mcpu=rx71m -mlittle-endian-data -D__RX_LITTLE_ENDIAN__=1 -std=c11 -nostartfiles -Wall -Wextra -Wl,-warn-common -Wl,-gc-sections -ffunction-sections -fdata-sections -DROMSTART -loptc -loptm -DCPPAPP -O0
rx-elf-gcc %OPTIONS% -o “HardwareDebug/TestGUNRXprintf.elf” HardwareDebug/TestGUNRXprintf.o HardwareDebug/hwinit.o HardwareDebug/inthandler.o HardwareDebug/start.o HardwareDebug/vects.o -T “generate/linker_script.ld”
I notice that the order of linker option causes this difference as follows:
——– TestGUNRXprintf_20210127_1.zip
Linked without linker warnings: (probably OPTLIB is correctly used.)
SET OPTIONS=-mcpu=rx71m -mlittle-endian-data -D__RX_LITTLE_ENDIAN__=1 -std=c11 -nostartfiles -Wall -Wextra -Wl,-warn-common -Wl,-gc-sections -ffunction-sections -fdata-sections -DROMSTART -loptc -loptm -O0
rx-elf-gcc -o “HardwareDebug/TestGUNRXprintf.elf” HardwareDebug/TestGUNRXprintf.o HardwareDebug/hwinit.o HardwareDebug/inthandler.o HardwareDebug/start.o HardwareDebug/vects.o %OPTIONS% -T “generate/linker_script.ld”
(no linker warnings)
——– TestGUNRXprintf_20210127_2.zip
Linked but with linker warnings: (perhaps NEWLIB is used incorrectly though OPTLIB is intended to be used.)
SET OPTIONS=-mcpu=rx71m -mlittle-endian-data -D__RX_LITTLE_ENDIAN__=1 -std=c11 -nostartfiles -Wall -Wextra -Wl,-warn-common -Wl,-gc-sections -ffunction-sections -fdata-sections -DROMSTART -loptc -loptm -O0
rx-elf-gcc %OPTIONS% -o “HardwareDebug/TestGUNRXprintf.elf” HardwareDebug/TestGUNRXprintf.o HardwareDebug/hwinit.o HardwareDebug/inthandler.o HardwareDebug/start.o HardwareDebug/vects.o -T “generate/linker_script.ld”
c:/renesas/gcc/redhat/gnurx-elf/8.3.0.202004/rx-elf/rx-elf/bin/../lib/gcc/rx-elf/8.3.0.202004-GNURX/../../../../rx-elf/lib\libc.a(lib_a-closer.o): In function `_close_r’:
(.text._close_r+0x10): warning: _close is not implemented and will always fail
c:/renesas/gcc/redhat/gnurx-elf/8.3.0.202004/rx-elf/rx-elf/bin/../lib/gcc/rx-elf/8.3.0.202004-GNURX/../../../../rx-elf/lib\libc.a(lib_a-fstatr.o): In function `_fstat_r’:
(.text._fstat_r+0x12): warning: _fstat is not implemented and will always fail
c:/renesas/gcc/redhat/gnurx-elf/8.3.0.202004/rx-elf/rx-elf/bin/../lib/gcc/rx-elf/8.3.0.202004-GNURX/../../../../rx-elf/lib\libc.a(lib_a-isattyr.o): In function `_isatty_r’:
(.text._isatty_r+0x10): warning: _isatty is not implemented and will always fail
c:/renesas/gcc/redhat/gnurx-elf/8.3.0.202004/rx-elf/rx-elf/bin/../lib/gcc/rx-elf/8.3.0.202004-GNURX/../../../../rx-elf/lib\libc.a(lib_a-lseekr.o): In function `_lseek_r’:
(.text._lseek_r+0x14): warning: _lseek is not implemented and will always fail
c:/renesas/gcc/redhat/gnurx-elf/8.3.0.202004/rx-elf/rx-elf/bin/../lib/gcc/rx-elf/8.3.0.202004-GNURX/../../../../rx-elf/lib\libc.a(lib_a-readr.o): In function `_read_r’:
(.text._read_r+0x14): warning: _read is not implemented and will always fail
c:/renesas/gcc/redhat/gnurx-elf/8.3.0.202004/rx-elf/rx-elf/bin/../lib/gcc/rx-elf/8.3.0.202004-GNURX/../../../../rx-elf/lib\libc.a(lib_a-writer.o): In function `_write_r’:
(.text._write_r+0x14): warning: _write is not implemented and will always fail
——–
These zip files are here:
TestGUNRXprintf_20210127_1.zip
TestGUNRXprintf_20210127_2.zip
Best regards,
NoMaY
This is a correction of my memory. I checked my disk and found the following source code of few years ago. I remember that I was confused by the following issue at the time.
* Though fprintf()/printf() aren’t used in Amazon FreeRTOS source code (or are replaced by my dummy function), linker warnings such as in this thread were somehow displayed.
And I found the following reason. (Please note that these might be different from now.)
* Using assert() causes such linker warnings.
* Using sscanf() causes such linker warnings.
So, my yesterday’s post regarding assert()/sprintf() was wrong. I’m sorry for this mistake.
——– assert.c
#if 0 /* This file will be removed later because assert() macro in the tinycbor
was replaced by modifying assert_p.h and cbor.h to call vAssertCalled() when
GNURX’s __RX__ is defined. */
#include <assert.h>
#include “FreeRTOS.h”
/* Messages by __assert_func() and __assert() are output to stderr
* but this functionality is not implemented. */
void __assert_func(const char *file, int line, const char *func, const char *failedexpr)
{
(void)file;
(void)line;
(void)func;
(void)failedexpr;
vAssertCalled();
for(;;){}
}
void __assert(const char *file, int line, const char *failedexpr)
{
(void)file;
(void)line;
(void)failedexpr;
vAssertCalled();
for(;;){}
}
#endif
——– read.c
#include <sys/unistd.h>
/* Both sscanf() and fscanf() use common std() of newlib/libc/stdio/findfp.c internally
* and the std() requires the address of __sread() which calls _read_r() –> read().
* Because of this, read() is necessary for not only fscanf() but also sscanf(). */
int read(int fd, _PTR buf, size_t cnt)
{
(void)fd;
(void)buf;
(void)cnt;
return -1;
}
——– write.c
#include <sys/unistd.h>
/* Both sscanf() and fscanf() use common std() of newlib/libc/stdio/findfp.c internally
* and the std() requires the address of __swrite() which calls _write_r() –> write().
* Because of this, write() is necessary for not only fscanf() but also sscanf(). */
int write(int fd, _CONST _PTR buf, size_t cnt)
{
(void)fd;
(void)buf;
(void)cnt;
return -1;
}
——– lseek.c
#include <sys/unistd.h>
/* Both sscanf() and fscanf() use common std() of newlib/libc/stdio/findfp.c internally
* and the std() requires the address of __sseek() which calls _lseek_r() –> lseek().
* Because of this, lseek() is necessary for not only fscanf() but also sscanf(). */
_off_t lseek(int fd, _off_t pos, int whence)
{
(void)fd;
(void)pos;
(void)whence;
return (_off_t) -1;
}
——– close.c
#include <sys/unistd.h>
/* Both sscanf() and fscanf() use common std() of newlib/libc/stdio/findfp.c internally
* and the std() requires the address of __sclose() which calls _close_r() –> close().
* Because of this, close() is necessary for not only fscanf() but also sscanf(). */
int close(int fd)
{
(void)fd;
return -1;
}
——–
Best regards,
NoMaY
Dear Frank,
After looking at the options (OPTIONS += -loptc OPTIONS += -loptm) we have deduced that you are linking using optlib. Optlib will not give you warnings for -O0 or -O3, so there may be a chance this is related to the behavior you had described.
However please note that optlib is deprecated and we encourage you to use newlib or newlib-nano. Please refer to https://llvm-gcc-renesas.com/release-notes/rx/latest/8.3.0.202004/release_notes.pdf, the “Known Issues” section 5 (about the Optlib library).
In your case, if you are using optlib to get a smaller code size, you should choose newlib-nano. Newlib and newlib-nano are better optimized than optlib, however the size of the libraries is bigger because optlib cuts some corners by not implementing all features.
In order to do this, please select Newlib as the library inside the Library Generator Settings, and tick Use newlib-nano checkbox inside the settings at Linker-> Miscellaneous.
Please note that you can also send us the linking command from the console inside e2 studio, in order for us to assess further options.
__
Best regards,
The GNU Tools Team
Hello Frank and moderators,
I notice one more thing. Doesn’t the following linker option mean that Frank use KPIT’s optlib? In case of KPIT’s optlib, is it necessary to handle _write() etc to use printf() with UART, etc? In my memory, handling simple functions such as charget() or charput() is enough for it.
-loptc -loptm
Best regards,
NoMaY