2013-06-19 9 views
5

Napisałem printf myselef, który używa va_list/va_arg/va_start/va_end/va_arg.użyj gcc skompiluj projekt, który pokazuje "niezdefiniowane odniesienie do` abort '"

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)))) 

Początkowo skopiować te makra z jądrem Linux i printf może drukować 32-bitową liczbę całkowitą poprawne, ale nie może drukować 64-bitową liczbę całkowitą i drukować podwójnej/pływaka może zawieść lub collapse.Then sprawdzić kod i Domyślam się, że va_ * może zawierać błędy, więc używam __builtin_va_ * zamiast va_ * kernela.

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) 

Ale gcc szybka "niezdefiniowana odniesienia do` Abort '", więc piszę pusty abort() i myprintf działa corretly. Moje pytania są następujące:

  1. Dlaczego Linux Kernel va_list/va_arg/va_start/va_end/va_arg nie można stosować do printf 64-bitowej liczby całkowitej i double/pływaka?
  2. Kiedy użyłem __builtin_va_start/__builtin_va_arg/__builtin_va_end/__builtin_va_list, dlaczego gcc szybka „niezdefiniowana odniesienia do abort'"? But I can not find the definition of __builtin_va_ *`, Dokąd ich definicji?
+2

Co powiesz na ?Również '_bnd' (_bnd ma być _va_align) jest zdefiniowane inaczej:' #define __va_align (ty) ((sizeof (ty) + sizeof (int) - 1) & ~ (sizeof (int) - 1)) ' –

+0

Zgadzam się z @Armin; dlaczego nie używasz tylko standardowego nagłówka dla rzeczy va_arg? –

+0

@Oli Charlesworth, nasz projekt działa bez wsparcia dla os i mamy kontrolę nad tym, gdzie drukować, więc stdio.h nie jest zgodny z naszymi wymaganiami. – Ezio

Odpowiedz

0

Nie wycinać i wklejać rzeczy z nagłówków Linux. Zamiast stawiać to na top z pliku źródłowego:

#include <stdarg.h> 

to daje wszystko, czego potrzeba, aby wykorzystać va_list i va_arg jednak nie będzie ciągnąć w printf lub dowolnego standardu i/o stuf. f (który mieszka w <stdio.h>).

1

GCC __builtin_va_arg() najwyraźniej wezwie abort() (przynajmniej na niektórych platformach lub sytuacji), jeśli jest wywołany z argumentem typu nie zostały przekazane w ... części listy argumentów dla wywołania funkcji.

Na przykład, ze względu na promocji char lub float przekazywane jako taki argument zostały podniesione do int lub double. Uzyskanie dostępu do tych argumentów jako va_arg(ap,char) lub va_arg(ap,float) jest niezdefiniowanym zachowaniem, a gcc może wywołać w tej sytuacji abort() - lub może zrobić coś innego (mój kompilator MinGW wykona niepoprawną instrukcję powodującą awarię).

Można zobaczyć coś takiego, gdy skompilowany:

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 

do „definicja” z __builtin_va_* jest kompilowany do kompilatora (dlatego „wbudowane” jest częścią nazwy).

Jeśli chodzi o makra Linuksa do uzyskiwania dostępu do varargs: podczas gdy definicje z nagłówka jądra Linuksa istnieją w pliku include/acpi/platform/acenv.h, jeśli uważnie przyjrzymy się kompilacji warunkowej, Zobaczysz, że te makra nie są używane podczas budowania jądra Linuksa. Nie jestem do końca pewien, czy te makra działają, ale nie będą działać z wersjami x64/x86-64/amd64, ponieważ ABI na tej platformie nie jest całkowicie oparte na stosach. Aby uzyskać szczegółowe informacje, patrz sekcja 3.5.6 "Binarny interfejs aplikacji systemu V - dodatek do procesora architektury AMD64".

Powiązane problemy