I started to wonder how the printf function is declared, it always receive a string as first parameter (well, const char*) and then the rest of the parameters can be a varierty of types, a variable number of them and given in different order.
Does this mean the printf function is declared and overridden for each of the possibilities? This does not make much sense to me, so does it really work like this or it's way different?
Also, how is the function implemented? If it's too complicated I'd just like to know how it works internally in general.
how the printf function is declared
printf is a variadic function and it declared since C99 as follows:
int printf( const char *restrict format, ... );
^^^
The ... or ellipses indicate that there are a variable number of argument and we would use the va_start, va_arg, va_end macros and va_list type to access the arguments.
how is the function implemented?
and example of a very simple printf is given in the document linked above is as follows and modified to work in C:
#include <stdio.h>
#include <stdarg.h>
void simple_printf(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
while (*fmt != '\0') {
if (*fmt == 'd') {
int i = va_arg(args, int);
printf( "%d\n", i ) ;
} else if (*fmt == 'c') {
int c = va_arg(args, int);
printf( "%c\n", (char)c ) ;
} else if (*fmt == 'f') {
double d = va_arg(args, double);
printf( "%f\n", d ) ;
}
++fmt;
}
va_end(args);
}
int main()
{
simple_printf("dcff", 3, 'a', 1.999, 42.5);
}
The prototype of printf is:
int printf(const char *restrict format, ...);
This feature (the argument ...) is called variable arguments functions. You can do it as well with the help of stdarg.h.
Here's a start: C FAQ: Variable-Length Argument Lists
Each standard library has corresponding header containing the function prototype for all the functions in that library and definitions of various data types and constants that needed by the functions. The header for printf is <stdio.h> which includes its prototype
int printf( const char *restrict format, ... );
Here's a cheesy little program that shows that the prototype for printf is:
int printf ( const char * format, ... );
(and that it doesn't require the restrict keyword as shown by others).
Notice that printf here works withOUT including the otherwise-required stdio.h header file. This is because simply declaring a prototype for the printf function keeps the compiler happy by telling it that this function prototype does indeed exist, and since the object code for the definition (implementation) of printf also happens to exist elsewhere, the linker is happy at the time of linking, after compilation.
Notice the extern "C" {} thing required for C++, however, to prevent the C++ compiler from name-mangling the function name. See the comments above the code for all compile commands I used and tested. Also note that the printing of the "true" stuff is just for kicks, as I was testing some stuff out.
hello_world.c:
/*
hello_world
Gabriel Staples
www.ElectricRCAircraftGuy.com
27 Mar. 2019
Examples of how to compile & run:
- NB: c90 requires C-style comments (slash star, star slash) and does NOT work with modern
C++-style // comments!
C:
gcc -Wall -o hello_world hello_world.c && ./hello_world
gcc -Wall -std=c90 -o hello_world hello_world.c && ./hello_world
gcc -Wall -std=c99 -o hello_world hello_world.c && ./hello_world
gcc -Wall -std=c11 -o hello_world hello_world.c && ./hello_world
C++:
g++ -Wall -o hello_world hello_world.c && ./hello_world
g++ -Wall -std=c++98 -o hello_world hello_world.c && ./hello_world
g++ -Wall -std=c++03 -o hello_world hello_world.c && ./hello_world
g++ -Wall -std=c++11 -o hello_world hello_world.c && ./hello_world
*/
// #include <stdio.h> // for printf
#include <stdbool.h> // for `true` and `false`
#ifdef __cplusplus
extern "C" {
#endif
int printf ( const char * format, ... ); // a hacky alternative to including stdio.h above!
#ifdef __cplusplus
}
#endif
int main(void)
{
printf("Hello World\n");
printf("`true == 1`? = %i, `true == 1`? = %s\n", true, (true == 1 ? "true" : "false"));
return 0;
}
Sample output:
$ gcc -Wall -o hello_world hello_world.c && ./hello_world
Hello World
`true == 1`? = 1, `true == 1`? = true
Related
I am creating a light test framework. For my local debugging in linux with gcc and clang, I do not get any complaints for mocking a function that has arguments, but mocking it with no arguments. eg.
add.c
int add(int a, int b) {
return a + b;
}
foo.c
#include "add.h"
int add_2(int a) {
return add(a, 2);
}
Now, in order to mock add. I simply created these macros.
testframework.h
#define DECLARE_MOCK(type, name) \
type __var_##name[255]; \
size_t __var_##name##_inc = 0; \
size_t __var_##name##_actual = 0; \
type name() { return (type)__var_##name[__var_##name##_inc++]; }
#define MOCK(name, value) __var_##name[__var_##name##_actual++] = value;
This works well on my linux machine. add(x,y) requires two arguments, but gcc or clang doesn't complain that the mock will essentially have no arguments passed to it, and works perfectly as a stand in. it's this line here type name() ...
Here is the usage. Notice I am mocking add.c capability in this test file.
#include "foo.h"
#include "testframework.h"
DECLARE_MOCK(int, add);
int main() {
DESCRIBE("things");
MOCK(add, 2);
SHOULDB("add", {
ASSERT(add(0, 2) == 2);
});
}
The issue comes in on gcc mac, which complains.
[INFO] CMD: gcc -Wall -Werror -std=c11 -O3 -o target/things tests/things.c obj/things/lib.o
tests/things.c:29:57: error: too many arguments in call to 'add' [-Werror]
SHOULDB("add", { ASSERT(add(0, 2) == 2); });
I would like to keep -Wall and -Werror, I was hoping there was an attribute I could add to the macro, which is the opposite of sentinel.
I am trying to use sscanf() from preload.so which is generated from preload.c.
To check my sscanf() from preload.so is called, I added extra print statement: printf("test\n");
Is there something I am missing?
Files content mentioned below:
//preload.c
#include <stdarg.h>
#include <stdio.h>
__attribute__((force_align_arg_pointer)) int sscanf(const char *str, const char *format, ...)
{
int ret;
va_list ap;
va_start(ap, format);
printf("test\n");
ret = vsscanf(str, format, ap);
va_end(ap);
return ret;
}
//foo.c
#include <stdio.h>
int main(void)
{
int i;
sscanf("42", "%d", &i);
printf("%d\n", i);
return 0;
}
I am doing below steps:
# gcc -fPIC -shared preload.c -o preload.so -ldl -D_GNU_SOURCE=1
# export LD_PRELOAD=$PWD/preload.so
# gcc foo.c -o foo
test
test
test
test
test
test
test
test
test
test
test
O/p I am getting:
# echo $LD_PRELOAD
/AMIT/sscanf_override/preload.so
# ./foo
42
# LD_PRELOAD=$PWD/preload.so ./foo
42
The expected output is:
$ gcc foo.c -o foo
$ LD_PRELOAD=$PWD/preload.so ./foo
test
42
Even ldd output is pointing to preload.so as below, still while execution its giving preference to sscanf() of system and not the one from preload.so
root#***sscanf_override]# ldd foo
linux-vdso.so.1 (0x00007fff4a5e0000)
/AMIT/sscanf_override/preload.so (0x00007f1cf270a000)
libc.so.6 => /lib64/libc.so.6 (0x00007f1cf2345000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f1cf2141000)
/lib64/ld-linux-x86-64.so.2 (0x00007f1cf290c000)
[root#***sscanf_override]#
Tweak here is, if I remove -D_GNU_SOURCE=1,it works, gcc -fPIC -shared preload.c -o preload.so -ldl But I can not proceed ahead without defining GNU_SOURCE as its must for future use .
As suggested by #Rachid K, it worked when I redefined my preload.c as below:
#include <stdarg.h>
#include <stdio.h>
__attribute__((force_align_arg_pointer)) int sscanf(const char *str, const char *format, ...)
{
int ret;
va_list ap;
va_start(ap, format);
printf("test\n");
ret = vsscanf(str, format, ap);
va_end(ap);
return ret;
}
__attribute__((force_align_arg_pointer)) int __isoc99_sscanf(const char *str, const char *format, ...)
{
int ret;
va_list ap;
va_start(ap, format);
printf("test\n");
ret = vsscanf(str, format, ap);
va_end(ap);
return ret;
}
Have a look at readelf -s foo.
I think it is likely that there is no call to sscanf in your executable. Assuming you are using GLIBC as libc, I suspect a call to __isoc99_sscanf. This is a redirection which the library makes, apparently because its original sscanf variant uses extensions conflicting with the standard, see this question.
If you look then at readelf -s preload.so as well, it will probably show a definition for sscanf.
The redirection happens via macro in stdio.h which you include in both, but I suspect _GNU_SOURCE=1 disables the redirection, so that even though stdio.h is included in preload.c, it is not replacing sscanf with __isoc99_sscanf there.
In the compilation of foo, you probably do not use -D_GNU_SOURCE=1 and therefore you are getting a mismatch between the symbol names.
Symbol interposition with LD_PRELOAD is always a bit tricky. In addition to issues such as above, there are also many situations in which compilers will optimize out or transform standard library calls. For example printf -> puts if the format string doesn't use any formatting.
sscanf() may be a macro referencing an internal function. Have a look at <stdio.h>. For example, on my system, I have:
extern int __isoc99_sscanf (const char *__restrict __s,
const char *__restrict __format, ...) __THROW;
# define fscanf __isoc99_fscanf
# define scanf __isoc99_scanf
# define sscanf __isoc99_sscanf
Hence, sscanf() is actually a macro referencing __isoc99_sscanf(). So, if you redefine the latter, you get what you expect.
#include <stdarg.h>
#include <stdio.h>
//__attribute__((force_align_arg_pointer)) int sscanf(const char *str, const char *format, ...)
int __isoc99_sscanf(const char *str, const char *format, ...)
{
int ret;
va_list ap;
va_start(ap, format);
printf("test\n");
ret = vsscanf(str, format, ap);
va_end(ap);
return ret;
}
After rebuild, you get:
$ gcc -fPIC -shared preload.c -o preload.so -ldl -D_GNU_SOURCE=1
$ LD_PRELOAD=`pwd`/preload.so ./foo
test
42
For example, if I have the following function
void printText(char text [100]){
printf("%d", text);
}
could I then do this in the command line
printText(Hello World)
and then get my expected output as
Hello World
It depends on your shell. Some shells do support functions. In bash, the POSIX shell, and probably others, the following is the correct syntax:
printText() {
printf '%s\n' "$1"
}
printText 'Hello World'
If you meant your question literally, then no, it's not possible to call a function without even mentioning the file in which it's located. The language used to write the function is irrelevant.
But it is possible to compile a C function and have it called from the shell somehow? Yes. If you created a shared library (shared object on unixy systems or DLL on Windows) from the function, you could. It would require a tool to do so, but such a tool could exit. (Windows also supports COM objects and a number of derived techs. Some of these might even make the task easier.)
(I can't tell if such tools actually do exist or what they are because software recommendations are off-topic on StackOverflow. I will say that such a tool could be built around a library such as libffi.)
One solution would be to rely on dlopen()/LoadLibrary() and
dlsym()/GetProcAddress() but you cannot ensure the function
prototype conforms to your expectation.
A more robust solution consists in providing a lookup table filled
with functions that you know are compliant with the intended usage.
/**
gcc -std=c99 -o prog_c prog_c.c \
-pedantic -Wall -Wextra -Wconversion \
-Wc++-compat -Wwrite-strings -Wold-style-definition -Wvla \
-g -O0 -UNDEBUG -fsanitize=address,undefined
$ ./prog_c printText "Hello world"
printText --> <Hello world>
$ ./prog_c textLen "Hello world"
textLen --> 11
$ ./prog_c what "Hello world"
cannot find function 'what'
**/
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
void
printText(const char *text)
{
printf("printText --> <%s>\n", text);
}
void
textLen(const char *text)
{
printf("textLen --> %d\n", (int)strlen(text));
}
typedef struct
{
const char *name;
void (*fnct)(const char *);
} TextFunction;
bool // success
call_text_function(const char *name,
const char *arg)
{
static TextFunction table[]={ {"printText", printText},
{"textLen", textLen},
{NULL, NULL} };
for(int i=0; table[i].name!=NULL; ++i)
{
if(strcmp(table[i].name, name)==0)
{
table[i].fnct(arg);
return true;
}
}
return false;
}
int
main(int argc,
char **argv)
{
if(argc!=3)
{
fprintf(stderr, "usage: %s function arg\n", argv[0]);
return 1;
}
if(!call_text_function(argv[1], argv[2]))
{
fprintf(stderr, "cannot find function '%s'\n", argv[1]);
return 1;
}
return 0;
}
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.
#include <stdio.h>
#include <string.h>
int main()
{
char arrDst[5] = {0};
char arrSrc[10] = "123456";
memcpy( arrDst, arrSrc, sizeof( arrSrc ) );
return 0;
}
Here in this program it is clear that there is a memory corruption.
Is there any option in gcc compiler by which I can recognize this problem at compile time?
Note: I used valgrind --leak-check=full, but it doesn't help.
$ gcc -Wall -O1 t.c
In file included from /usr/include/string.h:642:0,
from t.c:3:
In function ‘memcpy’,
inlined from ‘main’ at t.c:13:9:
/usr/include/bits/string3.h:52:3: warning: call to __builtin___memcpy_chk
will always overflow destination buffer [enabled by default]
GCC can recognize some of these. That generally requires turning on optimizations (at least -01) and warnings (-Wall, add -Wextra too).
It may not scale to the large program you are really interested in, but you can find this error with Frama-C:
$ frama-c -cpp-command "gcc -C -E -I`frama-c -print-share-path`/libc/ -nostdinc" mem.c `frama-c -print-share-path`/libc/fc_runtime.c -val
...
[value] computing for function memcpy <- main.
Called from mem.c:13.
.../libc/string.h:54:[value] Function memcpy: precondition got status invalid.
This message means that you are calling memcpy() with arguments that do not satisfy its contract. In this case the pre-condition that fails is the first in the list, about the validity of the destination for writing:
/*# requires \valid(((char*)dest)+(0..n - 1));
# requires \valid_read(((char*)src)+(0..n - 1));
# requires \separated(((char *)dest)+(0..n-1),((char *)src)+(0..n-1));
# assigns ((char*)dest)[0..n - 1] \from ((char*)src)[0..n-1];
# assigns \result \from dest;
# ensures memcmp((char*)dest,(char*)src,n) == 0;
# ensures \result == dest;
#*/
extern void *memcpy(void *restrict dest,
const void *restrict src, size_t n);