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
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
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
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
Hello moderators,
First of all, I’m sorry for moderators that I did flag mistakenly when I reply to Flank’s answer to my answer. Always I’m confused how to reply to answer…
Hello Frank,
By the way, in my memory, assert() is a keypoint to understand why such functions are necessary in case of newlib.
As you know, other than MCU world, assert() will kill the process of the program if assert fails. Therefore newlib’s assert() needs functions such as kill(), getpid(), etc. Unfortunately if assert() is used unexpectedly in newlib, even if MCU world, such functions are required.
That is my understanding as of today. And now, as you wonder, I wonder why -O0 doesn’t need such functions. I will try to check newlib source code which are provided in GNURX installer package.
Best regards,
NoMaY
Dear Frank Kjul Larsen,
We have investigated the issue and we see similar behavior for -O0 and -O3. Please let us know if we missed anything. We used the latest e2 studio and the same RX toolchain version (2020.04).
Without implementing the functions write, read, lseek, isatty, fstat, and close we get warnings (as expected) for each of them, both using -O3 and -O0:
However, if we add our own write function, we do not get a warning for the write function anymore (which is correct), both when using -O3 and -O0.
int write(int file, char* ptr, int len) {
(void)file; return 0;
}
int main(void) {
printf("OK"); return 0;
}
Please note that the write function we used does not have a leading underscore, which is where we believe lies the root cause of your problem.
Let us know if we can be of any more help, or if you are still experiencing a different behavior for -O0 and -O3.
__
Best regards,
The GNU Tools Team