Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I work around the "unknown conversion type character `z' in format" compiler-specific warning?

I'm working on code that is cross-compiled to several target architectures.

I looked at the handful of hits from searching Stack Overflow for "printf size_t unknown conversion type character" warning, however those posts all seem to be related to minGW, so those answers, essentially ifdefing against _WIN32, do not apply to my instance of essentially the same problem, i.e. printf not recognizing "%zu" as the format-specifier for size_t, but with a mips cross compiler.

Is there an existing compiler flag (for the noted cross-compiler) that enables libc to recognize "%zu" as the format-specifier for size_t?

$ cat ./main.c
// main.c

#include <stdio.h>

int main( int argc, char* argv[] )
{
  size_t i = 42;
  printf( "%zu\n", i );
  return 0;
}

$ /path/to/mips_fp_le-gcc --version
2.95.3
$ 
$ file /path/to/libc.so.6
/path/to/libc.so.6: ELF 32-bit LSB pie executable, MIPS, MIPS-I version 1 (SYSV), dynamically linked, interpreter /lib/ld.so.1, for GNU/Linux 2.2.15, not stripped, too many notes (256)
$ 
$ /path/to/mips_fp_le-gcc -mips2 -O2 -EL -DEL -pipe -Wall -Wa,-non_shared -DCPU=SPARC -DLINUX -D_REENTRANT -DPROCESS_AUID -DTAGGING -fPIC -I. -I../../../root/include -I../include -I../../../common/include -I../../..
/root/include  -DDISABLE_CSL_BITE -DDISABLE_DNS_LOOKUP     -DOS=UNIX -DLINUX -DPOSIX_THREADS -D__USE_GNU -D_FORTIFY_SOURCE=2 -DHANDLE_CSL_DUPLICATES  -DOS=UNIX -DLINUX -DPOSIX_THREADS -D__USE_GNU -D_FORTIFY_SOURCE=2 -DHANDLE_CSL_DUPLICATES  -DOS=UNIX -DLINUX -DPOSIX_THREADS -D__USE_GNU -D_FORTIFY_SOURCE=2 -DHANDLE_C
SL_DUPLICATES  -DOS=UNIX -DLINUX -DPOSIX_THREADS -D__USE_GNU -D_FORTIFY_SOURCE=2 -DHANDLE_CSL_DUPLICATES -o ./main.o -c main.c 
main.c: In function `main':
main.c:6: warning: unknown conversion type character `z' in format
main.c:6: warning: too many arguments for format

If the direct answer to the bolded question is "no", what are other possible solutions? Possibilities that come to mind are...

  1. register_printf_function()
  2. Wrap the format-specifier in a target-specific macro (similar to this minGW-specific post)

...any other ideas? I'd have a strong preference for solutions not involving target-specific preprocessor code, for which reason the above two are not ideal.

I think (but am not sure) that the cross-compiler version is old; are newer versions of the noted toolchain known/guaranteed to have a libc that recognize "%zu" as the format-specifier to size_t?


Update: This cross-compiler seems to not recognize -std=c99; adding it to the compiler flags generates the error "cc1: unknown C standard 'c99'"

like image 286
StoneThrow Avatar asked Dec 02 '19 22:12

StoneThrow


2 Answers

I work with a big codebase that's compiled under several different compilers, some of which are old and don't understand %z, so we just do things like

printf("size = %d", (int)size);

That's the easy way for small sizes, of course. If the size might be large, other alternatives are

printf("size = %u", (unsigned)size);

or

printf("size = %lu", (unsigned long)size);

(and there are other obvious possibilities as well).

like image 150
Steve Summit Avatar answered Oct 22 '22 12:10

Steve Summit


Your gcc does not support z as a length modifier. It's nothing to do with MIPS, which makes no difference at all, but rather that version 2.95.3 lacks support.

Support for a Z length modifier was added on Feb 9th 1998, commit by Andreas Schwab "c-common.c (format_char_info): Add new field zlen.". There was a gcc extension of Z as a conversion type specifier (rather than length modifier) for size_t before that. This code is in gcc 2.95.3, so it should recognize Z, but not z.

Support for z was added on July 17 2000 by Joseph Myers, "c-common.c (scan_char_table): Allow "z" length modifiers on diouxXn formats". Despite predating gcc 2.95.3 in time, this was in a gcc 3 branch and wasn't released until until gcc 3.0. So your ancient compiler simply hasn't got it.

So you could change your code to use Z, which is still supported. You could also define a macro based on compiler version:

#if __GNUC__ < 3
#define PZ "Z"
#else
#define PZ "z"
#endif  

Then use this as in printf("The size is %"PZ"u\n", sizeof(int)); You'll still have to modify your code. But it wouldn't be any different in the end, as the format string, after the preprocessor, would still be %zu on newer compilers and %Zu on old ones. The idea of casting the size_t arguments to something else will actually change the result of the code, as they will be cast to larger/smaller types in some cases, depending on what size_t is and what you cast to.

Alternatively, if you can build your toolchain, you could patch gcc to know about z. I think a one line change in the case statement that uses zlen in "c-common.c" would do it.

register_printf_function() is part of glibc, which is where the printf() code lives. It would allow you to extend printf with new formats at run time. There's nothing you could do at compile time with it that will change the compiler. And I don't believe gcc will be able to know that a new format has been added when it does printf type checking when register_printf_function() is used.

like image 35
TrentP Avatar answered Oct 22 '22 12:10

TrentP