How should I set my_printf, so it would do what printf("%p") does + without using printf.
void my_printf(char * format, ...)
{
va_list ap;
va_start(ap, format);
if(!strcmp(format,"%p"))
{
void *address= va_arg (ap, void*);
char *arr=malloc(sizeof(address));
arr=address;
arr[strlen(arr)]='\0';
write(1,arr,strlen(arr));
}
va_end (ap);
//it has to print an address in hexadecimal format.
}
In :
char *arr=malloc(sizeof(address));
arr=address;
the allocated block is lost, this is a memory leak
To do :
arr[strlen(arr)]='\0';
has no sense, if you are able to use strlen that means the null character is already present.
But it is not sure at all you have a string so you cannot use strlen on that pointer.
You do not know if you can modify it for a lot of reasons, and in fact it is useless to do that.
In
write(1,arr,strlen(arr));
again supposes you have a string, which can be wrong, and your goal is not to write the contain of the pointed value but its address.
A way to do is :
#include <stdint.h>
#include <stdio.h>
#include <limits.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h> /* for malloc use in main */
void my_printf(char * format, ...)
{
va_list ap;
va_start(ap, format);
if (!strcmp(format,"%p"))
{
void * address= va_arg(ap, void*);
uintptr_t u = (uintptr_t) address;
if (u == 0)
fputs("(nil)", stdout);
else {
char s[2 * ((sizeof(u) * CHAR_BIT + 7) / 8) + 3];
int i = sizeof(s) - 1;
s[i] = 0;
do {
s[--i] = ((u & 0xf) < 10) ? ('0' + (u & 0xf)) : ('a' + (u & 0xf) - 10);
u >>= 4;
} while(u);
s[--i] = 'x'; /* can also putchar('0') */
s[--i] = '0'; /* can also putchar('x') */
fputs(s+i, stdout);
}
/* check */
printf("\n%p\n", address);
}
va_end (ap);
}
int main()
{
void * p = malloc(1);
my_printf("%p", 0);
my_printf("%p", &main);
my_printf("%p", &p);
my_printf("%p", p);
free(p);
return 0;
}
Compilation and execution :
pi#raspberrypi:/tmp $ gcc -Wall p.c
pi#raspberrypi:/tmp $ ./a.out
(nil)
(nil)
0x106b8
0x106b8
0xbec2d26c
0xbec2d26c
0x10b1558
0x10b1558
pi#raspberrypi:/tmp $
Note your printf is very limited and only manage the simple format %p
Related
I have gone through similar questions and seen answers given, where given and none of those points me to what I am doing wrong. I keep getting undefined reference to 'concatf' while the function is properly defined in the header file.
concatf.c
#include "concatf.h"
/*
* vscprintf:
* MSVC implements this as _vscprintf, thus we just 'symlink' it here
* GNU-C-compatible compilers do not implement this, thus we implement it here
*/
#ifdef _MSC_VER
#define vscprintf _vscprintf
#endif
#ifdef __GNUC__
int vscprintf(const char *format, va_list ap)
{
va_list ap_copy;
va_copy(ap_copy, ap);
int retval = vsnprintf(NULL, 0, format, ap_copy);
va_end(ap_copy);
return retval;
}
#endif
int vasprintf(char **strp, const char *format, va_list ap)
{
int len = vscprintf(format, ap);
if (len == -1)
return -1;
char *str = (char*)malloc((size_t) len + 1);
if (!str)
return -1;
int retval = vsnprintf(str, len + 1, format, ap);
if (retval == -1) {
free(str);
return -1;
}
*strp = str;
return retval;
}
int concatf(char **strp, const char *format, ...)
{
va_list ap;
va_start(ap, format);
int retval = vasprintf(strp, format, ap);
va_end(ap);
return retval;
}
concatf.h
#ifndef CONCATF_H
#define CONCATF_H
#if defined(__GNUC__) && ! defined(_GNU_SOURCE)
#define _GNU_SOURCE /* needed for (v)asprintf, affects '#include <stdio.h>' */
#endif
#include <stdio.h> /* needed for vsnprintf */
#include <stdlib.h> /* needed for malloc, free */
#include <stdarg.h> /* needed for va_* */
int concatf(char **strp, const char *format, ...);
#endif // CONCATF_H
build.c
#include "concatf.h"
#include <stdio.h>
#include <stdlib.h>
extern char _binary_script_php_start;
extern char _binary_script_php_end;
int main(int argc, char *argv[]) {
// EXTRACT OUR RESOURCE OBJECT INTO /tmp/test.php
char *p = &_binary_script_php_start;
FILE *fp = fopen("/tmp/test.php","wb");
while ( p != &_binary_script_php_end ) {
fputc(*p++,fp);
}
fclose(fp);
// NOW READ IN OUR STANDARD ARGUMENTS AND LAUNCH OUR COMMAND
int i = 1;
char *cmd = "php /tmp/test.php";
char *s = NULL;
// asprintf(&s, "%s",cmd);
// for(i = 1; i < argc; i++) {
// asprintf(&s, "%s \"%s\"",s,argv[i]);
// }
// concatf("%s",cmd);
// for(i = 1; i < argc; i++) {
// concatf("%s \"%s\"",s,argv[i]);
// }
concatf(&s, "%s",cmd);
for(i = 1; i < argc; i++) {
concatf(&s, "%s \"%s\"",s,argv[i]);
}
system(s);
free(s);
unlink("/tmp/test.php"); // comment me out for debugging if you want
}
These codes run on my environment but fail to run on the second windows environment and those of my colleagues, I can't seem to point why that is the case.
To run it you will need a php file named script.php run ld -r -b binary script.php data.o to complie your php file and gcc build.c data.o -o runme to link the php file with the build function. You can find the sample project here https://github.com/Sammiiie/C_php_http.
This function should return a string that contains string representations of the second and subsequent arguments, each to two decimal places and separated by commas. The first argument is a count of the number of arguments that follow. I did some research and found out about va_list and tried to work with it. Unfortunately the program is crashing and I have no idea why. I'm posting this hoping that someone could maybe spot an obvious mistake or something like that.
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
char *to_string(int count,...)
{
int i,x,k=0;
float tmp;
va_list valist;
va_start(valist, count+1);
char comma=',';
char buffer[count*6];
for(i=0;i<count;i++)
{
tmp=va_arg(valist, double)*100;
x=tmp/100.0;
k+=4;
snprintf(buffer + k, "%C",comma);
k++;
snprintf(buffer + 1, "%.2f", x);
}
va_end(valist);
printf("%s", buffer);
return buffer;
}
int main()
{
to_string(2,3.14876,6.123243);
}
As noted in the comments, you need to read the manual pages for va_start() and snprintf() in particular, and also pay attention to memory management (you cannot safely return a pointer to a local (non-static) variable).
Here's a revamped piece of code that cleans up the issues identified and that doesn't crash (for me on my Mac running macOS Sierra 10.12.1 and GCC 6.2.0).
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
static char *to_string(char *buffer, size_t buflen, int count, ...)
{
va_list valist;
va_start(valist, count);
char *data = buffer;
for (int i = 0; i < count; i++)
{
int x = va_arg(valist, double) * 100;
double tmp = x / 100.0 + 0.005;
int n = snprintf(data, buflen, ",%.2f", tmp);
if (n > (int)(buflen - 1))
{
fprintf(stderr, "%s: buffer not big enough\n", __func__);
break;
}
data += n;
buflen -= n;
}
va_end(valist);
printf("%s: [%s]\n", __func__, buffer);
return buffer;
}
int main(void)
{
char buffer[80];
printf("main: [%s]\n", to_string(buffer, sizeof(buffer), 2, 3.14876, 6.123243));
return 0;
}
Example output:
to_string: [,3.15,6.12]
main: [,3.15,6.12]
Note that there's the leading comma which you probably don't want. That can be fixed like this:
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
static char *to_string(char *buffer, size_t buflen, int count, ...)
{
va_list valist;
va_start(valist, count);
char *data = buffer;
const char *pad = "";
for (int i = 0; i < count; i++)
{
int x = va_arg(valist, double) * 100;
double tmp = x / 100.0 + 0.005;
int n = snprintf(data, buflen, "%s%.2f", pad, tmp);
if (n > (int)(buflen - 1))
{
fprintf(stderr, "%s: buffer not big enough\n", __func__);
break;
}
pad = ",";
data += n;
buflen -= n;
}
va_end(valist);
printf("%s: [%s]\n", __func__, buffer);
return buffer;
}
int main(void)
{
char buffer[80];
printf("main: [%s]\n", to_string(buffer, sizeof(buffer), 2, 3.14876, 6.123243));
printf("main: [%s]\n", to_string(buffer, sizeof(buffer), 20,
6.67, 0.04, 8.81, 8.49, 3.50, 1.20, 4.28, 0.67, 1.93, 5.63,
5.30, 8.43, 1.99, 4.62, 5.54, 7.21, 9.43, 2.02, 4.77, 0.29));
return 0;
}
Example output:
to_string: buffer not big enough
to_string: [3.15,6.12]
main: [3.15,6.12]
to_string: [6.67,0.04,8.82,8.50,3.50,1.20,4.29,0.68,1.93,5.63,5.30,8.44,1.99,4.62,5.54,7.21]
main: [6.67,0.04,8.82,8.50,3.50,1.20,4.29,0.68,1.93,5.63,5.30,8.44,1.99,4.62,5.54,7.21]
I am trying to create a shared library in Linux. How can I pass an argument to function my_load() when library is loaded? In my C application, I make a call to test_func() then it automatically executes my_load() first before the called function then lastly it executes my_unload()
#include <stdio.h>
void __attribute__ ((constructor)) my_load(int argc, char *argv[]);
void __attribute__ ((destructor)) my_unload(void);
void test_func(void);
void my_load(int argc, char *argv[]) {
printf("my_load: %d\n", argc);
}
void my_unload(void) {
printf("my_unload\n");
}
void test_func(void) {
printf("test_func()\n");
}
Your dynamic library can always read /proc/self/cmdline to see what the command-line parameters used to execute the current executable are. example.c:
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
static char **get_argv(int *const argcptr)
{
char **argv;
char *data = NULL;
size_t size = 0; /* Allocated to data */
size_t used = 0;
size_t argc, i;
ssize_t bytes;
int fd;
if (argcptr)
*argcptr = 0;
do {
fd = open("/proc/self/cmdline", O_RDONLY | O_NOCTTY);
} while (fd == -1 && errno == EINTR);
if (fd == -1)
return NULL;
while (1) {
if (used >= size) {
char *old_data = data;
size = (used | 4095) + 4096;
data = realloc(data, size + 1);
if (data == NULL) {
free(old_data);
close(fd);
errno = ENOMEM;
return NULL;
}
}
do {
bytes = read(fd, data + used, size - used);
} while (bytes == (ssize_t)-1 && errno == EINTR);
if (bytes < (ssize_t)0) {
free(data);
close(fd);
errno = EIO;
return NULL;
} else
if (bytes == (ssize_t)0)
break;
else
used += bytes;
}
if (close(fd)) {
free(data);
errno = EIO;
return NULL;
}
/* Let's be safe and overallocate one pointer here. */
argc = 1;
for (i = 0; i < used; i++)
if (data[i] == '\0')
argc++;
/* Reallocate to accommodate both pointers and data. */
argv = realloc(data, (argc + 1) * sizeof (char *) + used + 1);
if (argv == NULL) {
free(data);
errno = ENOMEM;
return NULL;
}
data = (char *)(argv + argc + 1);
memmove(data, argv, used);
/* In case the input lacked a trailing NUL byte. */
data[used] = '\0';
/* Assign the pointers. */
argv[0] = data;
argc = 0;
for (i = 0; i < used; i++)
if (data[i] == '\0')
argv[++argc] = data + i + 1;
/* Final pointer points to past data. Make it end the array. */
argv[argc] = NULL;
if (argcptr)
*argcptr = (int)argc;
return argv;
}
/* Example standard error functions, that avoid the use of stdio.h.
*/
static void wrerr(const char *p)
{
if (p != NULL) {
const char *const q = p + strlen(p);
ssize_t n;
while (p < q) {
n = write(STDERR_FILENO, p, (size_t)(q - p));
if (n > (ssize_t)0)
p += n;
else
if (n != (ssize_t)-1)
return;
else
if (errno != EINTR)
return;
}
}
}
static void wrerrint(const int i)
{
char buffer[32];
char *p = buffer + sizeof buffer;
unsigned int u;
if (i < 0)
u = (unsigned int)(-i);
else
u = (unsigned int)i;
*(--p) = '\0';
do {
*(--p) = '0' + (u % 10U);
u /= 10U;
} while (u > 0U);
if (i < 0)
*(--p) = '-';
wrerr(p);
}
static void init(void) __attribute__((constructor));
static void init(void)
{
int argc, i, saved_errno;
char **argv;
saved_errno = errno;
argv = get_argv(&argc);
if (argv == NULL) {
const char *const errmsg = strerror(errno);
wrerr("libexample.so: get_argv() failed: ");
wrerr(errmsg);
wrerr(".\n");
errno = saved_errno;
return;
}
for (i = 0; i < argc; i++) {
wrerr("libexample.so: argv[");
wrerrint((int)i);
wrerr("] = '");
wrerr(argv[i]);
wrerr("'\n");
}
free(argv);
errno = saved_errno;
return;
}
Compile using e.g.
gcc -Wall -fPIC -shared example.c -ldl -Wl,-soname,libexample.so -o libexample.so
and test using e.g.
LD_PRELOAD=./libexample.so /bin/echo foo bar baz baaz
(Note that plain echo is a shell built-in, and you need to execute another binary like /bin/echo to load the preload library.)
However, most dynamic libraries take arguments in environment variables instead; for example, YOURLIB_MEM for some memory size hint, or YOURLIB_DEBUG for enabling verbose debugging output during runtime.
(My example code does not use stdio.h output, because not all binaries use it, especially if written in some other language. Instead, the wrerr() and wrerrint() are small stupid helper functions that use low-level unistd.h I/O to write directly to standard error; this always works, and causes minimal side effects at run time.)
Questions?
You can't.
__attribute__((constructor)) simply doesn't support this.
There doesn't seem to be any reason you can't just call my_load(argc, argv) at the very beginning of main().
You can use atexit to register a function to be called when your program exits normally, or returns from main.
int main(int argc, char **argv)
{
my_load(argc, argv);
atexit(my_unload);
// ...
}
AFAIK, there is no way to pass arguments to gcc constructor and destructor functions. The best you can do is to use global variables.
In you example, you could try :
In main :
int Argc;
char *Argv[];
int main(int argc, char *argv[]) {
Argc = argc;
Argv = argv;
...
}
In shared library :
extern int Argc;
...
void __attribute__ ((constructor)) my_load();
...
void my_load() {
printf("my_load: %d\n", Argc);
}
But anyway, it can only work if you explicitely load the shared library through dlopen. It it is directly referenced at link time, the constructor function will be called before first instruction in main and you will always find the original value or 0 in Argc.
Sorry to resurrect an oldie here but I just tested this on both Linux and Mac OS:
$ gcc -x c -o test_prog -
#include <stdio.h>
void __attribute__ ((constructor)) my_load(int argc, char *argv[]);
void __attribute__ ((destructor)) my_unload(void);
void test_func(void);
void my_load(int argc, char *argv[]) {
printf("my_load: %d\n", argc);
}
void my_unload(void) {
printf("my_unload\n");
}
void test_func(void) {
printf("test_func()\n");
}
int main() { return 0; }
And it prints this result on both systems:
$ ./test_prog foo bar baz
my_load: 4
my_unload
In order for it to work as a shared library I did have to add the linker option -Wl,--no-gc-sections because it otherwise aggressively removed the constructor & destructor. But otherwise yeah this works already.
This doesn’t use the __attribute__ ((constructor)) syntax, but if you specify a custom _init function, you can do so:
// foo.c
#include <stdio.h>
void my_constructor(int argc, char**argv) {
printf("my_constructor init: %s\n", argv[1]);
}
To do so you need to pass ld -init my_constructor or gcc -Wl,-init,my_constructor, e.g.
gcc foo.c -shared -o libfoo.so -Wl,-init,my_constructor
how to create a function with variable pointers parameters that
take any number of pointer and print it such as :
print("hello ","world ");
print("i'm ","adil"," blah "," blah");
result like that :
$ ./myprogramme
hello word im adil blah blah
I found stdarg.h but i don't know how to make it ?
In C, you can create a variadic function using stdarg.h. Example from Wikipedia link,
#include <stdarg.h>
double average(int count, ...)
{
va_list ap;
int j;
double sum = 0;
va_start(ap, count); /* Requires the last fixed parameter
(to get the address) */
for (j = 0; j < count; j++) {
sum += va_arg(ap, double); /* Increments ap to the next argument. */
}
va_end(ap);
return sum / count;
}
This is almost what you wanted, but it needed a NULL terminator
#include <stdio.h>
#include <stdarg.h>
void print (char *first, ...)
{
va_list argptr;
char *next;
va_start (argptr, first);
next = first;
while (next) {
printf ("%s", next);
next = va_arg(argptr, char*);
}
va_end (argptr);
}
int main()
{
print("hello ","world ", NULL);
print("i'm ","adil"," blah "," blah", NULL);
return 0;
}
But by adding a different argument, there's an easy answer anyway
printf("%s%s", "hello ","world ");
printf("%s%s%s%s", "i'm ","adil"," blah "," blah");
I've discovered weird behavior of vsprintf on OSX.
This is my code:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#if defined(WIN32)
#include <windows.h>
#define VSNPRINTF _vsnprintf
#elif defined(LINUX) || defined (DARWIN)
#define VSNPRINTF vsnprintf
#include <sys/types.h>
#include <unistd.h>
#endif
char *f(const char *fmt, ...)
{
char *out = NULL;
const int step = 32;
int n = -1, lout = step;
va_list arg;
if(fmt!=NULL)
{
va_start(arg, fmt);
do
{
if(!out)
{
free(out);
out = NULL;
}
out = (char*)malloc(lout + 1);
if(!out) break;
memset(out, 0, lout + 1);
n = VSNPRINTF(out, lout, fmt, arg);
if(n == -1 || n + 1 > lout)
{
lout += step;
n = -1;
}
}while(n == -1);
va_end(arg);
}
return out;
}
int main()
{
char *msg = NULL;
unsigned long x = 0xDEADBEEF;
msg = f("%X%X%X%X", x, x, x, x);
if(!msg) return -1;
puts(msg);
return 0;
}
The function shall return allocated string (char*) containing formatted text. It works correctly on Linux and Windows. It returns badly formatted text on OSX and sometimes it leads to Segmentation fault (EXC_BAD_ACCESS). Btw, I know that I can use vasprintf.
What can be the problem?
Your problem is most likely that you're calling vsnprintf with the same va_list multiple times. This doesn't work in some ABIs.
Look up the man page of va_copy. The short version is to do something like this:
va_list c;
va_copy(c, arg);
n = VSNPRINTF(out, lout, fmt, c);
va_end(c);