How to wrap variadic functions using LD_PRELOAD? - c

I have to perform dynamic linking on a variadic function of following format:
int foo(char *args, const char *f, ...)
Here the number of arguments are variable.
What I want to achieve is that I want to pass the obtained arguments to the original function which I am resolving using dlsym.
If I do not pass all the arguments I keep getting segmentation fault.
Thanks in advance.

I think your problem is not related to LD_PRELOAD or dlopen/dlsym: you simply want to call a variadic function from a variadic function, eg:
int printf (const char *fmt, ...) {
return fprintf (stdout, fmt, my_variadic_parameters);
}
As far as I know, it is not possible, but in carefully designed libraries (which is obviously a minority of them), every variadic function has a counterpart that uses va_list parameter, like printf and vprintf, fprintf and vfprintf:
int printf (const char *fmt, ...) {
va_list ap;
int retval;
va_start (ap, fmt);
retval= vfprintf (stdout, fmt, ap);
va_end (ap);
return retval;
}
int vmyprintf (const char *fmt, va_list pap) {
va_list ap;
int retval;
va_copy (ap, pap);
retval= vfprintf (stdout, fmt, ap);
va_end (ap);
return retval;
}
So you should ask the creator of 'foo' to create a 'vfoo':
int vfoo (char *args, const char *f, va_list v)

Have a look at man va_start, there is a nicely concise example in that man page.
You can treat the dlsym symbol address just you would any other function pointer.
The basic concept behind va_args, is using a control variable in a loop, usually a printf style format string that can be parsed to decide when to exit the call frame building loop. In your case ankit, you need to have some form "is there another argument to place in the call frame" as you build up a va_list. Basically va_start va_arg... va_end.
Again, best explained in code, and the man page has a nice example.

Given
int foo(char *args, const char *f, ...)
the information provided by either args or f ought to tell you how many parameters of which type had been passed by the caller as variadic arguments.
How this info is provided depends on foo's implementation, so see foo() documentation.
A nice example to this is printf(const char * format, ...); All info of what is received as variadic arguments shall be parseable from the 1st parameter, the non-variadic format-"string".
If you fail to gain insight in this, you are lost. In C there is no generic, "built-in" way to gather the number and types of variadic arguments passed.

Related

va_list in C: Creating a function that doesn't need a argument count like 'printf'

Using the <stdarg.h> header, one can make a function that has a variable number of arguments, but:
To start using a va_list, you need to use a va_start macro that needs to know how many arguments there, but the printf & ... that are using va_list don't need the argument count. How can I create a function that doesn't need the argument count like printf?
Let's say I want to create a function that takes a va_list and instead of using it, passes it to another function that requires a va_list? (so in pseudocode, it would be like void printfRipOff(const char* format, ...) {printf(format, ...);})
Let's say I want to create a function that takes a va_list and instead of using it, passes it to another function that requires a va_list?
Look at function vprintf( const char * format, va_list arg ); for one example of a function that takes a va_list as an input parameter.
It is basically used this way:
#include <stdio.h>
#include <stdarg.h>
void CountingPrintf(const char* format, ...)
{
static int counter = 1;
printf("Line # %d: ", counter++); // Print a Counter up front
va_list args;
va_start(args, format); // Get the args
vprintf(format, args); // Pass the "args" through to another printf function.
va_end(args);
}
int main(void) {
CountingPrintf("%s %s\n", "Hello", "World");
CountingPrintf("%d + %d == %d\n", 2, 3, 2+3);
return 0;
}
Output:
Line # 1: Hello World
Line # 2: 2 + 3 == 5
Functions like printf() and scanf() have the format string argument that tells them the number and types of the arguments that must have been provided by the function call.
If you're writing your own function, you must have some way of knowing how many arguments were provided and their types. It may be that they are all the same type. It may be that they're all pointers and you can use a null pointer to indicate the end of the arguments. Otherwise, you probably have to include a count.
You can do that as long as provided the called function expects a va_list. There are caveats (see C11 §7.16 Variable arguments) but they're manageable with a little effort.
A very common idiom is that you have a function int sometask(const char *fmt, ...) and a second function int vsometask(const char *fmt, va_list args), and you implement the first as a simple call to the second:
int sometask(const char *fmt, ...)
{
va_list args;
va_start(fmt, args);
int rc = vsometask(fmt, args);
va_end(args);
return rc;
}
The second function does the real work, of course, or calls on other functions to do the real work.
In the question, you say:
… va_start macro that needs to know how many arguments …
No; the va_start macro only needs to know which argument is the one before the ellipsis — the argument name before the , ... in the function definition.
In a comment, you say:
This is what I'm trying to write, but it didn't work.
string format(const char* text, ...)
{
// temporary string used for formatting
string formattedString;
initializeString(&formattedString, text);
// start the va_list
va_list args;
va_start(text, args);
// format
sprintf(formattedString.array, text, args);
// end the va_list
va_end(args);
return formattedString;
}
As noted by abelenky in a comment, you need to use vsprintf():
string format(const char* text, ...)
{
string formattedString;
initializeString(&formattedString, text);
va_list args;
va_start(text, args);
vsprintf(formattedString.array, text, args);
va_end(args);
return formattedString;
}
That assumes that the formattedString has enough space in the array for the formatted result. It isn't immediately obvious how that's organized, but presumably you know how it works and it works safely. Consider using vsnprintf() if you can determine the space available in the formattedString.
The printf and scanf family of functions don't need to be explicitly passed the length of the va_list because they are able to infer the number of arguments by the format string.
For instance, in a call like:
printf("%d %c %s\n", num, c, "Hello");
printf is able to examine the first parameter, the format string, and see that it contains a %d, a %c, and a %s in that order, so it can assume that there are three additional arguments, the first of which is a signed int, the second is a char and the third is a char* that it may assume is a pointer to a null-terminated string.
To write a similar function that does not need to be explicitly told how many arguments are being passed, you must find a way to subtly give it some information allowing it to infer the number and types of the arguments in va_list.

Can I use `va_list` twice as following:

Can I use va_list as following:
void myself_printf(char* key, char* value, ...) {
char real_key[1024];
char real_value[1024];
va_list args;
va_start(args, value);
// use args twice format key and value
vsnprintf(real_key, 1024000, key, args);
vsnprintf(real_value, 1024000, value, args);
va_end(args);
}
using in demo
myself_printf("%d-%s", "%s-%d", 12, "key", "value", 24);
expect: real_key is "12-key", and real_value is "value-24"
Strictly speaking, you must use va_copy, since vsnprintf can invalidate args.
void myself_printf(char* key, char* value, ...) {
char real_key[1024000];
char real_value[1024000];
va_list args, args2;
va_start(args, value);
va_copy(args2, args);
// use args twice format key and value
vsnprintf(real_key, 1024000, key, args);
vsnprintf(real_value, 1024000, value, args2);
va_end(args);
va_end(args2);
}
This is what va_copy is designed for.
As noted, this is a large amount of stack space, although it is within typical stack sizes. Consider using vasprintf, if it is available.
Citations
n1548 §7.16
The object ap may be passed as an argument to another function; if that function invokes the va_arg macro with parameter ap, the value of ap in the calling function is indeterminate…
In other words, you cannot portably use args after you pass it to vsnprintf.
This is clarified in footnote 281:
As the functions vfprintf, vfscanf, vprintf, vscanf, vsnprintf, vsprintf, and vsscanf invoke the va_arg macro, the value of arg after the return is indeterminate.
While it seems that va_list is passed by value, that does not mean that it is actually passed by value, or that the va_list itself encapsulates all of its state. A common trick with typedefs in C is to declare them as a 1-element array:
typedef int my_type[1];
Since my_type decays to pointer type when it is passed by function, it only appears to be passed by value.
Demo
#include <stdio.h>
#include <stdarg.h>
void func(const char *msg, ...) {
va_list ap;
va_start(ap, msg);
vfprintf(stdout, msg, ap);
vfprintf(stdout, msg, ap);
va_end(ap);
}
int main(int argc, char **argv) {
func("%d + %d = %d\n", 2, 3, 5);
return 0;
}
On my computer, the output is:
2 + 3 = 5
590862432 + -1635853408 = 1586038440
As an alternative to using va_copy() — as suggested by Dietrich Epp in an answer — you can simply use va_start() and va_end() twice.
void myself_printf(char *key_fmt, char *value_fmt, ...)
{
char real_key[1024];
char real_value[1024];
va_list args;
va_start(args, value);
vsnprintf(real_key, sizeof(real_key), key_fmt, args);
va_end(args);
vs_start(args, value);
vsnprintf(real_value, sizeof(real_key), value_fmt, args);
va_end(args);
…do something useful with real_key and real_value…
}
The original version of the question used char real_key[1024000]; and similarly for real_value. Allocating almost 2 MiB of data on the stack won't work reliably on Windows (the limit there is usually 1 MiB of stack), and uses an awful lot of space on Unix systems (where the stack size is usually 8 MiB). Be cautious!
You would need to use va_copy() if the va_list was passed as an argument to your function. For example:
void myself_printf(char* key, char* value, ...)
{
va_list args;
va_start(args, value);
myself_vprintf(key, value, args);
va_end(args);
}
void myself_vprintf(char *key_fmt, char *value_fmt, va_list args1)
{
char real_key[1024];
char real_value[1024];
va_list args2;
va_copy(args2, args1);
vsnprintf(real_key, sizeof(real_key), key_fmt, args1);
vsnprintf(real_value, sizeof(real_value), value_fmt, args2);
va_end(args2);
…do something useful with real_key and real_value…
}
The specification of va_end() says:
The va_end macro facilitates a normal return from the function whose variable argument list was referred to by the expansion of the va_start macro, or the function containing the expansion of the va_copy macro, that initialized the va_list ap. The va_end macro may modify ap so that it is no longer usable (without being reinitialized by the va_start or va_copy macro). If there is no corresponding invocation of the va_start or va_copy macro, or if the va_end macro is not invoked before the return, the behavior is undefined.
Note that the specification of the vfprintf() function includes footnote 288 which says:
As the functions vfprintf, vfscanf, vprintf, vscanf, vsnprintf, vsprintf, and vsscanf invoke the va_arg macro, the value of arg after the return is indeterminate.
Granted, footnotes are not normative, but this is a strong indication that the functions are expected to use va_arg and hence the double use of arg as shown in the question leads to undefined behaviour unless there is an intervening call to va_end and another va_start or a use of va_copy (and its matching va_end).

C vararg recursive function : what is the syntax?

I'm trying to make a recursive function with variable args in C, and I can't seem to pass the args when writing the recursive call.
Code:
void f(const char* s, ...) {
va_list args;
va_start(args, s);
f(s,args);
va_end(args);
}
}
Don't mind the infinite call stack. It's not the point here, so I discarded every other aspect in the code.
A va_list is an implementation-defined way to access arguments of a function you don't know at compile time -- for example (depending on the architecture) a pointer somewhere in the stack frame of the function. You can't use it in place of actual arguments.
If you need to pass on variadic arguments, the typical approach is to have your implementation in a function that takes a va_list argument. The idiomatic way to name that function is to prepend a v to its name. So in the end, you have two functions:
void vf(const char* s, va_list ap) {
// your logic ...
vf(s, ap); // recursive call
}
// just a wrapper:
void f(const char* s, ...) {
va_list args;
va_start(args, s);
vf(s, args);
va_end(args);
}
Note that this passes on a reference to the arguments your function was originally called with. If this is a problem for your logic, you can copy the whole argument list with the va_copy macro.

Forward Variadic Argument List to ncurses printw function in c

This has been asked in several different flavors. Yet I can still not get it to work. Here is my function definition.
void
ncurses_add_line(const char *fmt, ...)
{
if (ncurses_window) {
va_list args;
va_start(args, fmt);
printw(fmt, args);
printw("\n");
va_end(args);
}
}
When I call this function I get gibberish in the variadic print out of my function. If I call printw directly it all works. For example, if I call ncurses_add_line like ncurses_add_line("Hello %d", var) I get a value not store in var. However, if I call printw("Hello %d", var), I see the value of var displayed next to "Hello" as in, if var == 1 then "Hello 1" is printed with printw but this is not the case for ncurses_add_line.
What do I need to change?
My reason for wrapping this up is because I don't want to include in my header file, only in my c file.
Try vwprintw instead of printw. vwprintw takes a va_list as its argument.
The idiom you're trying to use -- passing a va_list to a function that takes a variable number of arguments -- won't work. One solution is to find a variant of the function that will work (in this case, vwprintw). An alternative is to "flatten" the va_list: in this case, you could use vsprintf to create a formatted string, and then pass that into curses.
args is not something like an array of arguments. It is an internal structure. You have to read out every single argument by passing the type. Please keep in mind, that in C there is no runtime reflection, so you have to add the types in your code.
void ncurses_add_line(const char *fmt, ...)
{
if (ncurses_window)
{
va_list args;
va_start(args, fmt);
char *arg = va_arg( args, int ); // take out one arg by giving the type (int)
printw(fmt, arg);
printw("\n");
va_end(args);
}
}

converting va_list to variable argument? [duplicate]

This question already has answers here:
Passing variable arguments to another function that accepts a variable argument list
(11 answers)
Closed 8 years ago.
I have a function log_message it takes variable arguments.
log_message(int level, char *fmt, ...)
now before calling this(log_message) function i have to add new function(_log_message), and new function will call log_message.
_log_message(int level, char *fmt, ...)
new function is also same. when _log_message will call log_message it will convert variable input to va_list. Now i have va_list, i don't wanna change the original one, is there any way to change back to variable input, so i will able to call the original one(log_message).
No, there is no way to turn a va_list back into a list of arguments.
The usual approach is to define a base function which takes a va_list as an argument. For example, the standard C library defines printf and vprintf; the first is a varargs function and the second has exactly the same functionality but takes a va_list instead. Similarly, it defines fprintf and vfprintf. It's trivial to define printf, vprintf and fprintf in terms of vfprintf:
int fprintf(FILE* stream, const char* format, ...) {
va_list ap;
va_start(ap, format);
int n = vfprintf(stream, format, ap);
va_end(ap);
return n;
}
int vprintf(const char* format, va_list ap) {
return vfprintf(stdout, format, ap);
}
int printf(const char* format, ...) {
va_list ap;
va_start(ap, format);
int n = vprintf(format, ap);
va_end(ap);
return n;
}
(Similarly for the various exec* functions, which come in both va_list and varargs varieties.)
I'd suggest you adopt a similar strategy.

Resources