I have an application that uses a custom putchar(); which until now has been working fine.
I bumped up the optimization level of the application to -O2, and now my putchar isn't used.
I already use -fno-builtin, and based on some googling I added -fno-builtin-putchar to my CFLAGS, but that didn't matter.
Is there a "correct" way to get around this or do I have to go into my code and add something like
#define putchar myputchar
to be able to use -O2 and still pull in my own putchar() function?
edit--
Since my original post of this question, I stumbled on -fno-builtin-functions=putchar, as yet another gcc commandline option. Both this and the one above are accepted by gcc, but don't seem to have any noticeable effect.
edit more--
Experimenting further I see that gcc swallows -fno-builtin-yadayada also, so apparently the options parsing at the gcc front end is just passing the text after the second dash to some lower level which ignores it.
more detail:
Three files try1.c, try2.c and makefile...
try1.c:
#include <stdio.h>
int
main(int argc, char *argv[])
{
putchar('a');
printf("hello\n");
return(0);
}
try2.c:
#include <stdio.h>
int
putchar(int c)
{
printf("PUTCHAR: %c\n",c);
return(1);
}
makefile:
OPT=
try: try1.o try2.o
gcc -o try try1.o try2.o
try1.o: try1.c
gcc -o try1.o $(OPT) -c try1.c
try2.o: try2.c
gcc -o try2.o $(OPT) -c try2.c
clean:
rm -f try1.o try2.o try
Here's the output:
Notice that without optimization it uses the putchar I provided; but with -O2 it gets it from some other "magic" place...
els:make clean
rm -f try1.o try2.o try
els:make
gcc -o try1.o -c try1.c
gcc -o try2.o -c try2.c
gcc -o try try1.o try2.o
els:./try
PUTCHAR: a
hello
els:
els:
els:
els:make clean
rm -f try1.o try2.o try
els:make OPT=-O2
gcc -o try1.o -O2 -c try1.c
gcc -o try2.o -O2 -c try2.c
gcc -o try try1.o try2.o
els:./try
ahello
els:
Ideally, you should produce an MCVE (Minimal, Complete, Verifiable Example) or
SSCCE (Short, Self-Contained, Correct Example) — two names (and links) for the same basic idea.
When I attempt to reproduce the problem, I created:
#include <stdio.h>
#undef putchar
int putchar(int c)
{
fprintf(stderr, "%s: 0x%.2X\n", __func__, (unsigned char)c);
return fputc(c, stdout);
}
int main(void)
{
int c;
while ((c = getchar()) != EOF)
putchar(c);
return 0;
}
When compiled with GCC 4.9.1 on Mac OS X 10.9.4 under both -O2 and -O3, my putchar function was called:
$ gcc -g -O2 -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Werror pc.c -o pc
$ ./pc <<< "abc"
putchar: 0x61
putchar: 0x62
putchar: 0x63
putchar: 0x0A
abc
$
The only thing in the code that might be relevant to you is the #undef putchar which removes the macro override for the function.
Why try1.c doesn't call your putchar() function
#include <stdio.h>
int
main(int argc, char *argv[])
{
putchar('a');
printf("hello\n");
return(0);
}
The function putchar() may be overridden by a macro in <stdio.h>. If you wish to be sure to call a function, you must undefine the macro.
If you don't undefine the macro, that will override anything you do. Hence, it is crucial that you write the #undef putchar (the other changes are recommended, but not actually mandatory):
#include <stdio.h>
#undef putchar
int main(void)
{
putchar('a');
printf("hello\n");
return(0);
}
Note that putchar() is a reserved symbol. Although in practice you will get away with using it as a function, you have no grounds for complaint if you manage to find an implementation where it does not work. This applies to all the symbols in the standard C library. Officially, therefore, you should use something like:
#include <stdio.h>
#undef putchar
extern int put_char(int c); // Should be in a local header
#define putchar(c) put_char(c) // Should be in the same header
int main(void)
{
putchar('a');
printf("hello\n");
return(0);
}
This allows you to leave your 'using' source code unchanged (apart from including a local header — but you probably already have one to use). You just need to change the implementation to use the correct local name. (I'm not convinced that put_char() is a good choice of name, but I dislike the my_ prefix, for all it is a common convention in answers.)
ISO/IEC 9899:2011 §7.1.4 Use of library functions
Each of the following statements applies unless explicitly stated otherwise in the detailed
descriptions that follow: …
Any function
declared in a header may be additionally implemented as a function-like macro defined in
the header, so if a library function is declared explicitly when its header is included, one
of the techniques shown below can be used to ensure the declaration is not affected by
such a macro. Any macro definition of a function can be suppressed locally by enclosing
the name of the function in parentheses, because the name is then not followed by the left
parenthesis that indicates expansion of a macro function name. For the same syntactic
reason, it is permitted to take the address of a library function even if it is also defined as
a macro.185) The use of #undef to remove any macro definition will also ensure that an
actual function is referred to. Any inv ocation of a library function that is implemented as
a macro shall expand to code that evaluates each of its arguments exactly once, fully
protected by parentheses where necessary, so it is generally safe to use arbitrary
expressions as arguments.186) Likewise, those function-like macros described in the
following subclauses may be invoked in an expression anywhere a function with a
compatible return type could be called.187)
185) This means that an implementation shall provide an actual function for each library function, even if it
also provides a macro for that function.
186) Such macros might not contain the sequence points that the corresponding function calls do.
187) Because external identifiers and some macro names beginning with an underscore are reserved,
implementations may provide special semantics for such names. For example, the identifier
_BUILTIN_abs could be used to indicate generation of in-line code for the abs function. Thus, the
appropriate header could specify
#define abs(x) _BUILTIN_abs(x)
for a compiler whose code generator will accept it.
In this manner, a user desiring to guarantee that a given library function such as abs will be a genuine
function may write
#undef abs
whether the implementation’s header provides a macro implementation of abs or a built-in
implementation. The prototype for the function, which precedes and is hidden by any macro
definition, is thereby revealed also.
Judging from what you observe, in one set of headers, putchar() is not defined as a macro (it does not have to be, but it may be). And switching compilers/libraries means that now that putchar() is defined as a macro, the missing #undef putchar means that things no longer work as before.
Related
I ran into an issue invoking gcc where if I omit a library .c file, I got no output from the binary (unexpected behavior change) but since this is a missing dependency, I kind of expected the compile to fail (or at least warn)...
Example for this issue is from Head First C page 185 (but is not errata, see my compile mis-step below):
encrypt.h:
void encrypt(char *message);
encrypt.c:
#include "encrypt.h"
void encrypt(char *message)
{
// char c; errata
while (*message) {
*message = *message ^ 31;
message++;
}
}
message_hider.c:
#include <stdio.h>
#include "encrypt.h"
int main() {
char msg[80];
while (fgets(msg, 80, stdin)) {
encrypt(msg);
printf("%s", msg);
}
}
NOW, everything works fine IF I faithfully compile as per exercise instruction:
gcc message_hider.c encrypt.c -o message_hider
... but bad fortune led me to compile only the main .c file, like so:
$ gcc message_hider.c -o message_hider
This surprisingly successfully builds, even if I added -Wall -Wextra -Wshadow -g.
Also surprisingly, it silently fails, with no output from encrypt() function:
$ ./message_hider < ./encrypt.h
$
my gcc is:
$ /usr/bin/gcc --version
Apple clang version 13.1.6 (clang-1316.0.21.2.5)
Target: x86_64-apple-darwin21.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
Mindful that even with a Makefile, I could "still" end up with a missing .c file due to a mistake in the recipe.
Q: Is it possible to force a hard error if I forget to tell gcc about a .c file?
As I noted in a (misspelled) comment:
There is probably a function encrypt() in the system library.
On a Mac, man -s 3 encrypt shows:
CRYPT(3) BSD Library Functions Manual CRYPT(3)
NAME
crypt, encrypt, setkey -- DES encryption
SYNOPSIS
#include <unistd.h>
char *
crypt(const char *key, const char *salt);
void
encrypt(char *block, int edflag);
#include <stdlib.h>
void
setkey(const char *key);
…
The encrypt() and setkey() functions are part of POSIX, so they'll be available on most POSIX-like systems. Curiously, as shown in the manual page extract, the functions are declared in separate headers — <unistd.h> for encrypt() and
<stdlib.h> for setkey(). There's probably a good (enough) historical reason for the disconnect.
You should have received a compiler warning about the function being undeclared — if you didn't, you are presumably compiling using the C90 standard. That is very old and should not still be being taught; you need to be learning C11 or C18 (almost the same).
Since C99, the C standard requires functions to be declared before use — you can define a static function without pre-declaring it, but all other functions (except main()) should be declared before they are used or defined. You can use GCC compiler warning options such as -Wmissing-prototypes -Wstrict-prototypes (along with -Wold-style-declaration and -Wold-style-definition) to trigger warnings. Of these, -Wold-style-declaration is enabled by -Wextra (and none by -Wall). Be aware: as noted in the comments, clang does not support -Wold-style-declaration though true GCC (not Apple's clang masquerading as gcc) does support it.
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...
register_printf_function()
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'"
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).
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.
I don't always use all return values, and sometimes I must handle return values. For instance:
$ make
gcc -pedantic -std=c99 -Wall -O3 -ledit -g -DVERSION=\"v0.160425-2-gc443\" -c -o main.o main.c
main.c: In function ‘int_handler’:
main.c:532:5: warning: ignoring return value of ‘write’, declared with attribute warn_unused_result [-Wunused-result]
write(fileno(stdin), s, sizeof s - 1);
^
gcc -o shell main.o errors.c util.c pipeline.c -ledit
What should I do to avoid the warning? Is there a good way to "use" a variable like writing it to /dev/null so that the compiler won't complain? My own idea is this, which deals with the problem without ignoring the error and I can commit my code without warnings and deal with this later:
void int_handler(int signum) {
if (write(fileno(stdin), s, sizeof s - 1)) {
} else {}
}
Typically to suppress this warning, you can do:
(void)write(fileno(stdin), s, sizeof s - 1);
But the reason for the warning is because write() is declared with an attribute in the header (<unistd.h>) which leads to the warning.
This (attribute) is not done for every function but selectively. For example, printf()'s return is typically ignored.
Since, you would want to check the results of IO operation, it's done for write().
So, if you really want to ignore the result, you can use the (void)func(...); to suppress this.
First of all, in case of file handling functions in production-quality code, you should almost certainly handle all return values.
Otherwise, the standard way to ignore a variable or a function result, is to cast it to (void).
It is a bad idea to not take in account the return value of a call to write, as an I/O may fail it is important to take care of such an event. If you really want to remove the warning, you should explicitly ignore the return value:
(void)write(...);
The prototype for write in the header file unistd.h has been declared as follows
extern ssize_t write (int __fd, const void *__buf, size_t __n) __wur
and __wur has been defined as (in cdefs.h)
# if __USE_FORTIFY_LEVEL > 0
# define __wur __attribute_warn_unused_result__
# endif
#else
# define __attribute_warn_unused_result__ /* empty */
So when you are compiling with fortify switch on, wur gets defined as
__attribute_warn_unused_result. This attribute essentially tells the compiler to throw out warning if return value of function is not used.
You can redirect your stdout and stderr to /dev/null
gcc -c file.c > /dev/null 2>&1
and get rid of the warning (though even error is redirected to null) or you can redirect to file as
gcc -c warn.c > xx 2>&1
Hope this helps . And like said above, always check the value of "write", the error will automatically vanish
:)
I'm trying to intercept calls to printf using ld's -wrap option. I have the two files:
main.c:
#include <stdio.h>
int main(void) {
printf("printing\n");
printf("printing");
}
printf_wrapper.c:
int __real_printf(const char *format, ...);
int __wrap_printf(const char *format, ...) {
(void)format;
return __real_printf("WRAPPED\n");
}
And I compile with the following command:
gcc -Wl,-wrap,printf *.c
When I run the resulting a.out binary, I get this output:
printing
WRAPPED
Why does the wrapping fail if there's a newline in the string? I checked my system's stdio.h and printf isn't a macro. This is with gcc 5.3.0
Use the -fno-builtin option to tell gcc not to mess around with some specified functions. So, if you added -fno-builtin-printf it should work. In general it may cause some problems that would have been caught by the compiler to be missed. See the gcc docs for details, e.g. https://gcc.gnu.org/onlinedocs/gcc-4.2.2/gcc/C-Dialect-Options.html
I have a homework assignment that requires us to open, read and write to file using system calls rather than standard libraries. To debug it, I want to use std libraries when test-compiling the project. I did this:
#ifdef HOME
//Home debug prinf function
#include <stdio.h>
#else
//Dummy prinf function
int printf(const char* ff, ...) {
return 0;
}
#endif
And I compile it like this: gcc -DHOME -m32 -static -O2 -o main.exe main.c
Problem is that I with -nostdlib argument, the standard entry point is void _start but without the argument, the entry point is int main(const char** args). You'd probably do this:
//Normal entry point
int main(const char** args) {
_start();
}
//-nostdlib entry point
void _start() {
//actual code
}
In that case, this is what you get when you compile without -nostdlib:
/tmp/ccZmQ4cB.o: In function `_start':
main.c:(.text+0x20): multiple definition of `_start'
/usr/lib/gcc/i486-linux-gnu/4.7/../../../i386-linux-gnu/crt1.o:(.text+0x0): first defined here
Therefore I need to detect whether stdlib is included and do not define _start in that case.
The low-level entry point is always _start for your system. With -nostdlib, its definition is omitted from linking so you have to provide one. Without -nostdlib, you must not attempt to define it; even if this didn't get a link error from duplicate definition, it would horribly break the startup of the standard library runtime.
Instead, try doing it the other way around:
int main() {
/* your code here */
}
#ifdef NOSTDLIB_BUILD /* you need to define this with -D */
void _start() {
main();
}
#endif
You could optionally add fake arguments to main. It's impossible to get the real ones from a _start written in C though. You'd need to write _start in asm for that.
Note that -nostdlib is a linker option, not compile-time, so there's no way to automatically determine at compile-time that that -nostdlib is going to be used. Instead just make your own macro and pass it on the command line as -DNOSTDLIB_BUILD or similar.