Home COBRA-Boot Widerspruch: init läuft, obwohl execve zurückkehrt printf-Hello-world Debug-Session
 

8.2 printf-Innenleben

Wie 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
Home COBRA-Boot Widerspruch: init läuft, obwohl execve zurückkehrt printf-Hello-world Debug-Session