Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

use gcc compile a project that shows "undefined reference to `abort'"

I wrote a printf myselef that use va_list/va_arg/va_start/va_end/va_arg.

typedef char *va_list;
#define  _AUPBND                (sizeof (acpi_native_int) - 1)
#define  _ADNBND                (sizeof (acpi_native_int) - 1)
#define _bnd(X, bnd)            (((sizeof (X)) + (bnd)) & (~(bnd)))
#define va_arg(ap, T)           (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
#define va_end(ap)              (void) 0
#define va_start(ap, A)         (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))

At first,I copy these macros from linux kernel and the printf can print 32-bit integer correct but cannot print 64-bit integer and print double/float may fail or collapse.Then I check the code and I guess the va_* may have errors,so I use __builtin_va_* instead of kernel's va_*.

typedef __builtin_va_list va_list;
#define va_start(v,l)   __builtin_va_start(v,l)
#define va_end(v)       __builtin_va_end(v)
#define va_arg(v,l)     __builtin_va_arg(v,l)

But gcc prompt "undefined reference to `abort'",so I write a empty abort() and myprintf works corretly. My questions are:

  1. Why linux kernel's va_list/va_arg/va_start/va_end/va_arg can not used for printf 64-bit integer and double/float?
  2. When I used __builtin_va_start/__builtin_va_arg/__builtin_va_end/__builtin_va_list, why gcc prompt "undefined reference to abort'"? But I can not find the definition of__builtin_va_*`, where're their definition?
like image 986
Ezio Avatar asked Jun 19 '13 11:06

Ezio


2 Answers

Don't cut and paste things from the Linux headers. Instead, put this at the top of your source file:

#include <stdarg.h>

This will give you everything you need in order to use va_list and va_arg. However, it won't pull in printf or any of the standard I/O stuff (which lives in <stdio.h>).

like image 114
Quuxplusone Avatar answered Nov 07 '22 18:11

Quuxplusone


gcc's __builtin_va_arg() apparently will call abort() (at least on some platforms or situations) if it's invoked with a type argument cannot have been passed in the ... part of the argument list for a function call.

For example, due to promotions a char or float passed as such an argument will have been promoted to int or double. Accessing those arguments as va_arg(ap,char) or va_arg(ap,float) is undefined behavior and gcc may call abort() in that situation - or it may do something else (my MinGW compiler will execute an invalid instruction to cause a crash).

You might see something like this when compiled:

In file included from D:\temp\test.c:2:0:
D:\temp\test.c: In function 'foo':
D:\temp\test.c:12:16: warning: 'char' is promoted to 'int' when passed through '...' [enabled by default]
  c = va_arg(ap,char);
                ^
D:\temp\test.c:12:16: note: (so you should pass 'int' not 'char' to 'va_arg')
D:\temp\test.c:12:16: note: if this code is reached, the program will abort

The 'definition' of __builtin_va_* is compiled into the compiler (that's why 'builtin' is part of the name).

As far as the Linux macros for varargs access: while the definitions you took from a linux kernel header do exist in include/acpi/platform/acenv.h, if you look carefully at the conditional compilation in effect, you'll see that those macros aren't used when building the linux kernel. I'm not exactly sure when those macros are in effect, but they won't work with x64/x86-64/amd64 builds because the ABI on that platform isn't entirely stack-based. See section 3.5.6 of the "System V Application Binary Interface - AMD64 Architecture Processor Supplement" for details.

like image 45
Michael Burr Avatar answered Nov 07 '22 17:11

Michael Burr