I've been tracing a problem that I've narrowed down to this skeleton:
#include <unistd.h>
#include <stddef.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct __log_file_details {
FILE *fp ;
char *name ;
} log_file_details_t;
typedef struct __log_files {
char is_open;
log_file_details_t lf[3];
void (*open_log)(struct __log_files *ctl);
void (*close_log)(struct __log_files *ctl);
} log_files_t ;
int write_log(const int file_nbr, log_files_t *ctl, char *log_this, ...);
void close_log(log_files_t *ctl);
void open_log(log_files_t *ctl);
// Here we go...
int main(int argc, char *argv[]) {
log_files_t log_ctl = {
0,
{ {NULL, NULL}, {NULL, NULL}, {NULL, NULL} },
&open_log,
&close_log
};
write_log(0, &log_ctl, "foo"); // That's it.
return 0;
}
void open_log(log_files_t *ctl) {}
void close_log(log_files_t *ctl) {}
int write_log(const int file_nbr, log_files_t *ctl, char *log_this, ...)
{
int rc;
/* ... */
rc = 0;
}
When I compile this code using gdb -g -o foo foo.c it works on most of my linux systems.
However, I have an ARM device (actually, a Netgear Stora running Linux) on which it fails miserably.
On that device, if I use GDB to step through this code, when write_log executes (line 58), I see:
Breakpoint 1, write(log(file_nbr=-1092220616, ctl=0x0, log_this=0xbee60ac4 "-garbage-") at foo.c:58
(where -1092220616 is a varying value, and -garbage- tends to contain a bunch of control characters.)
I don't know how to determine if this is a runtime problem (one of the libraries?), a problem with one of the standard headers, a gcc problem, or something else. What might I do in order to identify and resolve this problem?
If I remove the va_list definition of write_log, all works well, but of course that's not what I want.)
You need to properly clean up the variable args in your function:
int write_log(const int file_nbr, log_files_t *ctl, char *log_this, ...)
{
int rc;
va_list vl;
va_start(vl,log_this);
/* ... */
va_end(vl);
rc = 0;
}
Related
I'm developing a library and I would like to know some data about the caller of one of the functions I'm offering. In particular, I would need to know the file name, function name and line where my function (a redefined malloc) is being called.
EDIT: Here's a minimum working example where I can detect when a user calls malloc and "redirect" him to my own malloc function:
main.c:
#include <stdio.h>
#include <stdlib.h>
#include "myLib.h"
int main(){
printf("Inside main, asking for memory\n");
int *p = malloc(sizeof(int));
*p = 3;
free(p);
return 0;
}
myLib.c:
#include "myLib.h"
void * myAlloc (size_t size){
void * p = NULL;
fprintf(stderr, "Inside my own malloc\n");
p = (malloc)(size);
return p;
}
#undef malloc
#define malloc(size) myAlloc(size)
myLib.h:
#ifndef MYLIB_H
#define MYLIB_H
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#define malloc(size) myAlloc(size)
void * myAlloc(size_t size);
#endif
I've tried using _FILE_ _func_ and _LINE_ keywords, but I can't make it work since it's in a different module.
You could:
//mylib.h
#ifndef MYLIB_H
#define MYLIB_H
#include <stdlib.h>
// replace malloc in case it's already a macro
#ifdef malloc
#undef malloc
#endif
// I believe that from the standards point of view, this is undefined behavior
#define malloc(size) my_alloc(size, __FILE__, __LINE__, __func__)
#ifdef __GNUC__
// Allow compiler to do static checking.
__attribute__((__alloc_size__(1), __malloc__))
#endif
void *my_alloc(size_t size, const char *file, int line, const char *func);
// ^^^^^^^^ I do not like camelCase case - one snake case to rule them all.
#endif
// mylib.c
#include "mylib.h" // do not ever mix uppercase and lowercase in filenames
#undef malloc // undef malloc so we don't call ourselves recursively
#include <stdio.h>
void *my_alloc(size_t size, const char *file, int line, const char *func){
fprintf(stderr, "Och my god, you wouldn't believe it!\n"
"A function %s in file %s at line %d called malloc!\n",
func, file, line);
return malloc(size);
}
You might also see how assert does it. If you are aiming at glibc, read glibc docs replacing malloc.
Still as you discovered a user may do (malloc)(size) cicumvent macro expansion. You could do:
void *my_alloc(size_t size, const char *file, int line, const char *func);
static inline void *MY_ALLOC(size_t size) {
return my_alloc(size, NULL, 0, NULL);
}
#define MY_ALLOC(size) my_alloc(size, __FILE__, __LINE__, __func__)
// if called with `malloc()` then MY_ALLOC is expanded
// if called as `(malloc)`, then just expands to MY_ALLOC.
#define malloc MY_ALLOC
int main() {
malloc(10); // calls my_alloc(10, "main.c", 62, "main");
(malloc)(20); // calls my_alloc(20, NULL, 0, NULL);
}
GLIBC defines hidden symbols for malloc(), free()... which are called __libc_malloc(), __libc_free()...
So, you can tremendously simplify your debug macros.
In m.h, just define the following:
#if DEBUG_LEVEL > 0
extern void *__libc_malloc (size_t bytes);
extern void *myMalloc(size_t size, const char *filename, const char *funcname, int line);
#define malloc(size) myMalloc(size, __FILE__, __FUNCTION__, __LINE__)
#endif
Then you can write a program defining myMalloc() as follow (e.g. file name is m.c):
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "m.h"
#if DEBUG_LEVEL > 0
void *myMalloc(
size_t size,
const char *filename,
const char *funcname,
int line
) {
fprintf(stderr, "malloc(%zu) called from %s/%s()#%d\n", size, filename, funcname, line);
return __libc_malloc(size);
}
#endif
char *dup_str(char *string) {
char *str = malloc(strlen(string) + 1);
strcpy(str, string);
return str;
}
int main(int ac, char *av[]) {
char *str;
if (av[1]) {
str = dup_str(av[1]);
} else {
str = dup_str("NULL");
}
printf("String = '%s'\n", str);
free(str);
return 0;
}
When you compile this example program in non debug mode:
$ gcc m.c -DDEBUG_LEVEL=0
$ ./a.out azerty
String = 'azerty'
When you compile your program in debug mode:
$ gcc m.c -DDEBUG_LEVEL=1
$ ./a.out azerty
malloc(7) called from m.c/dup_str()#27
String = 'azerty'
I need to call a .so shared library in PHP, so I write C code using "dlopen" and "dlsym" to do this, and it works.
sample.h:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
typedef int (*libfunc) (char *msg, int msglen);
int
sfunc(char *msg, int msglen);
sample.c:
#include "sample.h"
int
sfunc(char *msg, int msglen)
{
void *lib;
libfunc lib_func;
int result;
...
lib = dlopen(THE_LIB_PATH, RTLD_NOW);
lib_func = (libfunc) dlsym(lib, THE_LIB_FUNC);
...
result = lib_func(msg, msglen); // return 0 if success
...
dlclose(lib);
return result;
}
It always return 0.
And then I write a PHP extension as a wrapper, it takes PHP arguments and calls the C code before.
php_sample.c:
...
#include "sample.h"
...
PHP_FUNCTION(s_func)
{
char *msg;
long msglen;
int result;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &msg, &msglen) == FAILURE) {
RETURN_NULL();
}
result = sfunc(msg, (int) msglen);
RETURN_LONG(result);
}
Then I added the extension(compiled to .so) to PHP and test the PHP function:
php -r "echo s_func("somestring");"
The call always failed, just return something not zero.
Why? Is there any difference?
Note: I tested this on one other computer and it works. So is there any problems about environments or something?
I'm implementing a verbose mode. Here's what I attempt to do : defining a global variable VERBOSE (in verbose.h) in such way that files requiring verbose only need to include that file. For example :
verbose.h:
void setVerbose(int test);
verbose.c:
#include "verbose.h"
// define VERBOSE if called
void setVerbose(int test) {
if (test) {
#ifndef VERBOSE
#define VERBOSE
#endif
}
}
point.h:
typedef struct Point Point;
struct Point {
int x, y;
};
void printPoint(Point *point);
point.c:
#include "point.h"
#include "verbose.h"
void printPoint(Point *point) {
#ifdef VERBOSE
printf("My abscissa is %d\n", point->x);
printf("My ordinate is %d\n", point->y);
#endif
printf("[x,y] = [%d, %d]\n", point->x, point->y);
}
And the main :
main.c:
#include "verbose.h"
#include "point.h"
int main(int argc, char *argv[]) {
if (argc >= 2 && !strcmp(argv[1], "-v"))
setVerbose(1);
Point *p = init_point(5,7);
printPoint(p);
return 0;
}
The executable has been produced with :
$ gcc -o test main.c point.c verbose.c
The outputs wanted are :
$ ./test
[x,y] = [5, 7]
$ ./test -v
My abscissa is 5
My ordinate is 7
[x,y] = [5, 7]
Problem is, it seems that VERBOSE is not defined in point.c when calling printPoint().
Preprocessor commands are decided at compile time, not run time, so your system won't work. What I would recommend instead is using a global bool Verbose and providing a verbose() function to do the printing (or not).
verbose.h
#include <stdbool.h>
int verbose(const char * restrict, ...);
void setVerbose(bool);
verbose.c
#include "verbose.h"
#include <stdbool.h>
#include <stdarg.h>
#include <stdio.h>
bool Verbose = false;
void setVerbose(bool setting) {
Verbose = setting;
}
int verbose(const char * restrict format, ...) {
if( !Verbose )
return 0;
va_list args;
va_start(args, format);
int ret = vprintf(format, args);
va_end(args);
return ret;
}
main.c
#include "verbose.h"
#include <stdbool.h>
int main() {
verbose("Verbose is off\n");
setVerbose(true);
verbose("Verbose is on\n");
int foo = 42;
verbose("Number: %d\n", foo);
return 0;
}
Just a small program of stdarg.h, when I organizing source file in a single source file as following:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
void
print(const char *, ...);
void
print(const char *format, ...)
{
va_list vlist;
va_start(vlist, format);
vfprintf(stdout, format, vlist);
va_end(vlist);
return;
}
int
main(int argc, char **argv)
{
const char *message = "Just a test";
print("==> %s <==\n", message);
return EXIT_SUCCESS;
}
The below code in a single file works well under gdb when debugging, it stopped where I expected.
But when I organizing the code in 3 files: print.c main.c test.c
/* print.c */
void
print(const char *, ...);
void
print(const char *format, ...)
{
va_list vlist;
va_start(vlist, format);
vfprintf(stdout, format, vlist);
va_end(vlist);
return;
}
/* main.c */
int
main(int argc, char **argv)
{
const char *message = "Just a test";
print("==> %s <==\n", message);
return EXIT_SUCCESS;
}
/* test.c */
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include "print.c"
#include "main.c"
I use command gcc -g test.c, and run it under linux. When breaking at print, it refused to break and continue to run to the end, within a warning "error removing breakpoint 0".
I suppose that maybe it related to stdarg.h, because I am always organizing code in that way, but the first time met the problem.
No matter what I do I can't get __objc_msg_forward to work on x86_64 on Linux. If I compile with -m32 it works fine. I put together this simple program to demonstrate. It should print Crasho Barfo twice.
#import <objc/Object.h>
#import <objc/objc-api.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#interface Object (Test)
-(id) doSomething:(id) anObject;
#end
typedef void *(*vafunc)(void *a1, void *a2, ...);
vafunc getvtest(void *s1);
int main(int argc, char *argv[])
{
id o1;
vafunc ptr;
int na;
ptr = getvtest(NULL);
na = 4;
(*ptr)(ptr, &na, "dog", "cat");
__objc_msg_forward = getvtest;
o1 = [[Object alloc] init];
[o1 doSomething:o1];
exit(0);
}
void *aptest(void *a1, void *a2, va_list ap)
{
fprintf(stderr, "Barfo\n");
return nil;
}
void *vtest(void *a1, void *a2, ...)
{
va_list ap;
void *ret = NULL;
fprintf(stderr, "Crasho\n");
va_start(ap, a2);
ret = aptest(a1, a2, ap);
va_end(ap);
return ret;
}
vafunc getvtest(void *s1)
{
return (vafunc) vtest;
}
What the heck am I doing wrong? When I run it this happens:
./vtest
Crasho
Barfo
Segmentation fault
If I pull it up in gdb it says Illegal Instruction.
Whose 64-bit Obj-C runtime are you using? As far as I know, Apple's only supporting the Obj-C 2.0 runtime on x86_64.