8.2 printf-InnenlebenWie funktioniert der printf wirklich im Kern hier beim COBRA-Linux? Das ist die Frage! andreas@gericom:~/Development/COBRA_rot/uClinux/uClinux-dist-20040218-cobra-20040324 \ /linux-2.4.x> find . -name "*" | xargs -n3 grep 'e printf(' ./fs/bfs/bfs_defs.h:#define printf(format, args...) \ ./arch/i386/kernel/microcode.c:#define printf(x...) printk(##x) ./arch/i386/kernel/microcode.c:#define printf(x...) ./drivers/net/skfp/h/osdef1st.h:#define printf(s,args...) printk(KERN_INFO s, ## args) ./Documentation/cdrom/sbpcd: else printf("this CD is not an XA disk.\n"); andreas@gericom:~/Development/COBRA_rot/uClinux/uClinux-dist-20040218-cobra-20040324/linux-2.4.x> Also ist printf ein #define. Zumindest auf Kernelebene. Bei der Suche in der libc: andreas@gericom:~/Development/COBRA_rot/uClinux/uClinux-dist-20040218-cobra-20040324 \ /uClibc/libc> find . -name "*" | xargs -n3 grep ' printf(' ./inet/rpc/ruserpass.c: printf(_("out of memory")); ./inet/rpc/ruserpass.c: printf(_("out of memory")); ./misc/regex/regex.c: printf("/%lx", (long int) *p++); ./misc/regex/regex.c: printf("[:%lx:]", (long int) *p++); ./misc/regex/regex.c: printf("%C", *p++); ./stdio/stdio.c:int printf(const char * __restrict format, ...); ./stdio/printf.c:int printf(const char * __restrict format, ...) ./stdio/old_vfprintf.c: * 1) printf("%c",0) returned 0 instead of 1. andreas@gericom:~/Development/COBRA_rot/uClinux/uClinux-dist-20040218-cobra-20040324/uClibc/libc> Im User-Space dürfte sehr oft die Fassung aus der libc gerufen werden Hier der Blick auf ./stdio/printf.c:int printf(const char * __restrict format, ...) /**********************************************************************/ #ifdef L_printf int printf(const char * __restrict format, ...) { va_list arg; int rv; va_start(arg, format); rv = vfprintf(stdout, format, arg); va_end(arg); return rv; } #endif /**********************************************************************/ printf ruft also vfprintf. Diese Funktion findet sich hier: andreas@gericom:~/Development/COBRA_rot/uClinux/uClinux-dist-20040218-cobra-20040324 \ /uClibc/libc/stdio> find . -name "*" | xargs -n3 grep ' vfprintf(' ./stdio.c:int vfprintf(FILE * __restrict stream, const char * __restrict format, ./printf.c: rv = vfprintf(&f, format, arg); ./printf.c: rv = vfprintf((FILE *) &f, format, arg); ./printf.c: rv = vfprintf(&f, format, arg); ./printf.c: rv = vfprintf(&f, format, arg); ./printf.c: rv = vfprintf(f, format, arg); ./printf.c: return vfprintf(stdout, format, arg); ./printf.c: rv = vfprintf(stream, format, arg); ./printf.c: rv = vfprintf(stdout, format, arg); ./old_vfprintf.c:int vfprintf(FILE * __restrict op, register const char * __restrict fmt, andreas@gericom:~/Development/COBRA_rot/uClinux/uClinux-dist-20040218-cobra-20040324 \ /uClibc/libc/stdio> Konkret ist bei ./stdio.c:int vfprintf(FILE * __restrict stream, const char * __restrict format, der gefundene String auskommentiert und auch nur eine Deklaration. Wirklich definiert wird es immer noch in der Datei printf.c mittels: #ifdef L_vfprintf #define VFPRINTF vfprintf #define FMT_TYPE char #define OUTNSTR _outnstr #define STRLEN strlen #define _PPFS_init _ppfs_init #define OUTPUT(F,S) fputs(S,F) #define _outnstr(stream, string, len) _stdio_fwrite(string, len, stream) #define FP_OUT _fp_out_narrow #ifdef __STDIO_PRINTF_FLOAT ... #endif /* __STDIO_PRINTF_FLOAT */ #else /* L_vfprintf */ #define VFPRINTF vfwprintf #define FMT_TYPE wchar_t #define OUTNSTR _outnwcs #define STRLEN wcslen #define _PPFS_init _ppwfs_init #define OUTPUT(F,S) fputws(S,F) #define _outnwcs(stream, wstring, len) _wstdio_fwrite(wstring, len, stream) #define FP_OUT _fp_out_wide static void _outnstr(FILE *stream, const char *s, size_t wclen) { ... } #ifdef __STDIO_PRINTF_FLOAT ... #endif /* __STDIO_PRINTF_FLOAT */ static int _ppwfs_init(register ppfs_t *ppfs, const wchar_t *fmt0) { ... return 0; } #endif /* L_vfprintf */ static void _charpad(FILE * __restrict stream, int padchar, size_t numpad) { ... } /* TODO -- Dynamically allocate work space to accomodate stack-poor archs? */ static int _do_one_spec(FILE * __restrict stream, register ppfs_t *ppfs, int *count) { ... #ifdef L_vfprintf #ifdef __UCLIBC_HAS_WCHAR__ ... #else /* __UCLIBC_HAS_WCHAR__ */ _outnstr(stream, s, slen); #endif /* __UCLIBC_HAS_WCHAR__ */ #else /* L_vfprintf */ ... #endif /* L_vfprintf */ _charpad(stream, ' ', numpad); } return 0; } int VFPRINTF (FILE * __restrict stream, register const FMT_TYPE * __restrict format, va_list arg) { ppfs_t ppfs; int count, r; register const FMT_TYPE *s; __STDIO_THREADLOCK(stream); count = 0; s = format; #if defined(L_vfprintf) && defined(__UCLIBC_HAS_WCHAR__) ... #endif if (_PPFS_init(&ppfs, format) < 0) { /* Bad format string. */ OUTNSTR(stream, (const FMT_TYPE *) ppfs.fmtpos, STRLEN((const FMT_TYPE *)(ppfs.fmtpos))); #if defined(L_vfprintf) && !defined(NDEBUG) fprintf(stderr,"\nIMbS: \"%s\"\n\n", format); #endif count = -1; } else { _ppfs_prepargs(&ppfs, arg); /* This did a va_copy!!! */ do { while (*format && (*format != '%')) { ++format; } if (format-s) { /* output any literal text in format string */ if ( (r = OUTNSTR(stream, s, format-s)) < 0) { count = -1; break; } count += r; } if (!*format) { /* we're done */ break; } if (format[1] != '%') { /* if we get here, *format == '%' */ /* TODO: _do_one_spec needs to know what the output funcs are!!! */ ppfs.fmtpos = (const char *)(++format); /* TODO: check -- should only fail on stream error */ if ( (r = _do_one_spec(stream, &ppfs, &count)) < 0) { count = -1; break; } s = format = (const FMT_TYPE *) ppfs.fmtpos; } else { /* %% means literal %, so start new string */ s = ++format; ++format; } } while (1); va_end(ppfs.arg); /* Need to clean up after va_copy! */ } #if defined(L_vfprintf) && defined(__UCLIBC_HAS_WCHAR__) DONE: #endif __STDIO_THREADUNLOCK(stream); return count; } #endif /**********************************************************************/ Wie man hier sieht, wird eigentlich die Ausgabe eines zusammenhängenden Strings sehr wohl mittels den zwei Befehlen: int VFPRINTF (FILE * __restrict stream, register const FMT_TYPE * __restrict format, va_list arg) { ... __STDIO_THREADLOCK(stream); ... if (format-s) { /* output any literal text in format string */ if ( (r = OUTNSTR(stream, s, format-s)) < 0) { count = -1; break; } count += r; } ... __STDIO_THREADUNLOCK(stream); return count; } also __STDIO_THREADLOCK und __STDIO_THREADUNLOCK eingesperrt, damit nicht ein zweiter Prozess oder Thread Zeichen dazwischen schummeln kann und so Buchstabensalat entsteht. Um mir Klarheit zu verschaffen, werde ich deshalb eine Debugger-Session fahren, in welcher auch die libc unter die Lupe genommen wird. Copyright © Andreas Birkert Letzte Aktualisierung am 20. Dezember 2013 |