libcのputcharについて

649 views

Published on

NetBSD-6.0.1のソースコードリーディング。

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
649
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
5
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

libcのputcharについて

  1. 1. libcのputcharについて         スタート低レイヤー#3 #start_printf                     @kusabanachi
  2. 2. putchar src/lib/libc/stdio/putchar.c 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 /*  * A subroutine version of the macro putchar  */ int putchar(c)     int c; {     FILE *fp = stdout;         int r;       FLOCKFILE(fp);     r = __sputc(c, fp);     FUNLOCKFILE(fp);     return r; } 引数の文字cを、stdout(標準出力)に __sputcで書き込みしているぽい
  3. 3. stdout src/lib/libc/stdio/putchar.c 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 /*  * A subroutine version of the macro putchar  */ int putchar(c)     int c; {     FILE *fp = stdout;         int r;       FLOCKFILE(fp);     r = __sputc(c, fp);     FUNLOCKFILE(fp);     return r; }
  4. 4. src/include/stdio.h 215 #define    stdout    (&__sF[1]) src/lib/libc/stdio/findfp.c 81 82 83 84 85 FILE __sF[3] = {     std(__SRD, STDIN_FILENO),        /* stdin */     std(__SWR, STDOUT_FILENO),        /* stdout */     std(__SWR|__SNBF, STDERR_FILENO)    /* stderr */ }; src/include/stdio.h 152 153 154 155 #define    __SNBF    0x0002        /* unbuffered */ #define    __SRD    0x0004        /* OK to read */ #define    __SWR    0x0008        /* OK to write */     /* RD and WR are never simultaneously asserted */ src/include/unistd.h 80 81 82 #define    STDIN_FILENO    0    /* standard input file descriptor */ #define    STDOUT_FILENO    1    /* standard output file descriptor */ #define    STDERR_FILENO    2    /* standard error file descriptor */ stdin, stdout, stderrが __SF[3] に格納されている。 stdoutは std( WRフラグ, ファイル識別子番号(1) )
  5. 5. src/lib/libc/stdio/findfp.c 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 #define    std(flags, file) {      ._p = NULL,      ._r = 0,      ._w = 0,      ._flags = (flags),      ._file = (file),       ._bf = { ._base = NULL, ._size = 0 },      ._lbfsize = 0,       ._cookie = __sF + (file),      ._close = __sclose,      ._read = __sread,      ._seek = __sseek,      ._write = __swrite,      ._ext = { ._base = (void *)(__sFext + (file)), ._size = 0 },      ._up = NULL,      ._ur = 0,      ._ubuf = { [0] = '0', [1] = '0', [2] = '0' },      ._nbuf = { [0] = '0' },      ._flush = NULL,      ._lb_unused = { '0' },      ._blksize = 0,      ._offset = (off_t)0,  } stdはFILE構造体のメンバを初期化するマクロ close, read, seek, write関数が割り当てられている
  6. 6. FLOCKFILE, FUNLOCKFILE src/lib/libc/stdio/putchar.c 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 /*  * A subroutine version of the macro putchar  */ int putchar(c)     int c; {     FILE *fp = stdout;         int r;       FLOCKFILE(fp);     r = __sputc(c, fp);     FUNLOCKFILE(fp);     return r; } __sputc の処理をはさんでロックしている
  7. 7. src/lib/libc/include/reentrant.h #ifdef _REENTRANT ... #define    FLOCKFILE(fp)        __flockfile_internal(fp, 1) #define    FUNLOCKFILE(fp)        __funlockfile_internal(fp, 1)   #else /* _REENTRANT */ ... #define    FLOCKFILE(fp)         #define    FUNLOCKFILE(fp)           #endif /* _REENTRANT */ _REENTRANT が定義されている時は処理を行っている 再入可能なプログラムで、ファイルを扱うためのロックのようだ   src/lib/libc/Makefile.inc 27 CPPFLAGS+=    ­D_LIBC ­DLIBC_SCCS ­DSYSLIBC_SCCS ­D_REENTRANT libcのMakefileで_REENTRANTの定義が指定されている
  8. 8. __sputc src/lib/libc/stdio/putchar.c 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 /*  * A subroutine version of the macro putchar  */ int putchar(c)     int c; {     FILE *fp = stdout;         int r;       FLOCKFILE(fp);     r = __sputc(c, fp);     FUNLOCKFILE(fp);     return r; }
  9. 9. src/include/stdio.h 447 448 449 450 451 452 453 454 455 456 457 458 459 460 /*  * The __sfoo macros are here so that we can   * define function versions in the C library.  */ #define    __sgetc(p) (­­(p)­>_r < 0 ? __srget(p) : (int)(*(p)­>_p++)) #if defined(__GNUC__) && defined(__STDC__) static __inline int __sputc(int _c, FILE *_p) {     if (­­_p­>_w >= 0 || (_p­>_w >= _p­>_lbfsize && (char)_c != 'n'         return (*_p­>_p++ = _c);     else         return (__swbuf(_c, _p)); } #else ... /* Cライブラリ内で関数のバージョン群を定義するため、    __sfooマクロ群はここにある */  →いくつかの異なる処理を定義(分岐)したかった?   __GNUC__ はGNUコンパイラのメジャー番号 __STDC__ はコンパイラがISO標準Cに準拠しているか  => 普通のコンパイルではこちらのコードが使われるだろう
  10. 10. 次に続く、__GNUC__ または __STDC__ が定義されていない場合 の__sputcは 459 460 461 462 463 464 465 466 467 468 469 470 471 #else /*  * This has been tuned to generate reasonable code on the vax using pcc.  */ #define    __sputc(c, p)      (­­(p)­>_w < 0 ?          (p)­>_w >= (p)­>_lbfsize ?              (*(p)­>_p = (c)), *(p)­>_p != 'n' ?                  (int)*(p)­>_p++ :                  __swbuf('n', p) :              __swbuf((int)(c), p) :          (*(p)­>_p = (c), (int)*(p)­>_p++)) #endif /* これは、vax上でpccを使う場合に合理的なコードを出力する版 */
  11. 11. src/include/stdio.h ファイル構造体から typedef    struct __sFILE {     unsigned char *_p;    /* バッファ内の現在位置   current position in (some) buff     int    _w;        /* 残っているwriteスペース   write space left for putc() */     int    _lbfsize;    /* 0、またはラインバッファ時はバッファのサイズの負の値   0 or ­_b   バッファ無しでなければ、バッファに貯まってから下層のwriteが行 われる。 _wが_lbfsizeより値が小さくなるか、ラインバッファの改行で 下層のwriteに移る。
  12. 12. src/include/stdio.h ファイル構造体から抜粋 typedef    struct __sFILE {     unsigned char *_p;    /* バッファ内の現在位置   current position in (some) buff     int    _w;        /* 残っているwriteスペース   write space left for putc() */     int    _lbfsize;    /* 0、またはラインバッファ時はバッファのサイズの負の値   0 or ­_b   * (__GNUC__) && (__STDC__) 版の __putc をもう一度見ます #if defined(__GNUC__) && defined(__STDC__) static __inline int __sputc(int _c, FILE *_p) {   // ファイルの_wをデクリメント   if (­­_p­>_w >= 0 || (_p­>_w >= _p­>_lbfsize && (char)_c != 'n'))     // _wが0より大きい ||                           <通常のバッファ、貯め中>     // (_wが_lbfsizeより大きく && 文字が改行でない)   <ラインバッファ、貯め中>     return (*_p­>_p++ = _c);   // ファイルのバッファに書いて終わり   else     // そうでない場合     return (__swbuf(_c, _p));  // __swbufに処理を委せる } stdoutの初期値は_wやバッファサイズが0なので、 1回目は__swbufへ進む
  13. 13. /****************************************************************** putchar             ... stdoutに1文字書き込み => __sputc             ... バッファに書き込み、貯まったら__swbufへ ******************************************************************/
  14. 14. __swbuf src/lib/libc/stdio/wbuf.c 1/3 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 /*  * 引数の文字cを、引数fpの(たぶん満杯な)バッファに書く。  * バッファがすでに満杯だったり、満杯になる場合、または  * c=='n'(ラインバッファ時)なら、フラッシュする。  */ int __swbuf(c, fp)     int c;     FILE *fp; {     int n;       _DIAGASSERT(fp != NULL);       _SET_ORIENTATION(fp, ­1);  
  15. 15. src/lib/libc/stdio/wbuf.c 1/3 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 /*  * 引数の文字cを、引数fpの(たぶん満杯な)バッファに書く。  * バッファがすでに満杯だったり、満杯になる場合、または  * c=='n'(ラインバッファ時)なら、フラッシュする。  */ int __swbuf(c, fp)     int c;     FILE *fp; {     int n;       _DIAGASSERT(fp != NULL);       _SET_ORIENTATION(fp, ­1);   _SET_ORIENTATION()は、FILEの入出力単位を ワイド文字かバイトか設定する。 引数-1なのでバイト単位に設定。
  16. 16. src/lib/libc/stdio/wbuf.c 2/3 66 67 68 69 70 71 72 73 74 75 76 77 78 79 /*  * writeできないか、早期にlongjmpで追い出される場合は、  * またここに呼ばれるために、_wが 0(バッファが満杯かバッファ無し)  * または­_bf._sizeと等しい(ラインバッファ時)ようにすること。  * それをしないと、十分な数のputc()が、  * _wを負数から正数にしてしまうかもしれない。  */ fp­>_w = fp­>_lbfsize; if (cantwrite(fp)) {     errno = EBADF;     return (EOF); } c = (unsigned char)c; コメントに書いてあることの対処として、 _wの値を_lvfsize(__swbufを呼ぶ条件を満たすよう)にする。
  17. 17. src/lib/libc/stdio/wbuf.c 2/3 66 67 68 69 70 71 72 73 74 75 76 77 78 79 /*  * writeできないか、早期にlongjmpで追い出される場合は、  * _wが 0(満杯かバッファ無し)または ­_bf._sizeと等しい  * (ラインバッファの場合)事を、またここに呼ばれるために確認すること。  * それをしないと、十分な数のputc()が、  * _wを負数から正数にしてしまうかもしれない。  */ fp­>_w = fp­>_lbfsize; if (cantwrite(fp)) {     errno = EBADF;     return (EOF); } c = (unsigned char)c; canwriteはfpの書き込みフラグとバッファの有無をチェックする。 どちらかが無い場合は、フラグとバッファをセットアップする。 stdoutのバッファはここで初めて割り当てられる
  18. 18. src/lib/libc/stdio/wbuf.c 3/3 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101     /*      * すでに満杯ならフラッシュする。その後、どちらにしろcをバッファに      * 詰めこむ。それでバッファが満杯になったり、      * cが'n'でラインバッファなら、フラッシュする(たぶん2回目のフラッシュで)。      * 2回目のフラッシュは`_bf._size==1`のバッファ無しならいつも起こる。      * fflush()は、putc()がいつも_wを0にして      * wbuf()をコールすることを保証するから、      * 私達は他に何かする必要はない。      */     n = fp­>_p ­ fp­>_bf._base;     if (n >= fp­>_bf._size) {         if (fflush(fp))             return (EOF);         n = 0;     }     fp­>_w­­;     *fp­>_p++ = c;     if (++n == fp­>_bf._size || (fp­>_flags & __SLBF && c == 'n'))         if (fflush(fp))             return (EOF);     return (c); } nはバッファ内に書き込んであるサイズ。 nがバッファのサイズ以上なら、"すでに満杯"なのでフラッシュす る。
  19. 19. src/lib/libc/stdio/wbuf.c 3/3 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101     /*      * すでに満杯ならフラッシュする。その後、どちらにしろcをバッファに      * 詰めこむ。それでバッファが満杯になったり、      * cが'n'でラインバッファなら、フラッシュする(たぶん2回目のフラッシュで)。      * 2回目のフラッシュは`_bf._size==1`のバッファ無しならいつも起こる。      * fflush()は、putc()がいつも_wを0にして      * wbuf()をコールすることを保証するから、      * 私達は他に何かする必要はない。      */     n = fp­>_p ­ fp­>_bf._base;     if (n >= fp­>_bf._size) {         if (fflush(fp))             return (EOF);         n = 0;     }     fp­>_w­­;     *fp­>_p++ = c;     if (++n == fp­>_bf._size || (fp­>_flags & __SLBF && c == 'n'))         if (fflush(fp))             return (EOF);     return (c); } fpのバッファにcを書き込む。 nをインクリメントして、バッファの サイズと等しければ"満杯になった"。 または、ラインバッファで'n'の書き込みならフラッシュする
  20. 20. src/lib/libc/stdio/wbuf.c 3/3 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101     /*      * すでに満杯ならフラッシュする。その後、どちらにしろcをバッファに      * 詰めこむ。それでバッファが満杯になったり、      * cが'n'でラインバッファなら、フラッシュする(たぶん2回目のフラッシュで)。      * 2回目のフラッシュは`_bf._size==1`のバッファ無しならいつも起こる。      * fflush()は、putc()がいつも_wを0にして      * wbuf()をコールすることを保証するから、      * 私達は他に何かする必要はない。      */     n = fp­>_p ­ fp­>_bf._base;     if (n >= fp­>_bf._size) {         if (fflush(fp))             return (EOF);         n = 0;     }     fp­>_w­­;     *fp­>_p++ = c;     if (++n == fp­>_bf._size || (fp­>_flags & __SLBF && c == 'n'))         if (fflush(fp))             return (EOF);     return (c); } fflushは0の戻り値が成功で、その時100行目に到達して __swbufは書き込んだ文字cを返す。 失敗時は__swbufはEOFを返して、それがputharの戻り値になる。
  21. 21. /****************************************************************** putchar             ... stdoutに1文字書き込み => __sputc             ... バッファに書き込み、貯まったら__swbufへ  => __swbuf             ... バッファの初期化、バッファ貯まってたらfflushへ ******************************************************************/
  22. 22. fflush src/lib/libc/stdio/fflush.c 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 /* 1つのファイルか, (fpが NULLの場合) 全てのファイルをフラッシュする.  */ int fflush(fp)     FILE *fp; {     int r;       if (fp == NULL) {         rwlock_rdlock(&__sfp_lock);         r = _fwalk(__sflush);         rwlock_unlock(&__sfp_lock);         return r;     }       FLOCKFILE(fp);     if ((fp­>_flags & (__SWR | __SRW)) == 0) {         errno = EBADF;         r = EOF;     } else {         r = __sflush(fp);     }     FUNLOCKFILE(fp);     return r; }
  23. 23. src/lib/libc/stdio/fflush.c 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 /* 1つのファイルか, (fpが NULLの場合) 全てのファイルをフラッシュする.  */ int fflush(fp)     FILE *fp; {     int r;       if (fp == NULL) {         rwlock_rdlock(&__sfp_lock);         r = _fwalk(__sflush);         rwlock_unlock(&__sfp_lock);         return r;     }       FLOCKFILE(fp);     if ((fp­>_flags & (__SWR | __SRW)) == 0) {         errno = EBADF;         r = EOF;     } else {         r = __sflush(fp);     }     FUNLOCKFILE(fp);     return r; } fp == NULLの場合、_fwalkで全てのファイルを辿って 各ファイルに __sflushを適用している
  24. 24. src/lib/libc/stdio/fflush.c 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 /* 1つのファイルか, (fpが NULLの場合) 全てのファイルをフラッシュする.  */ int fflush(fp)     FILE *fp; {     int r;       if (fp == NULL) {         rwlock_rdlock(&__sfp_lock);         r = _fwalk(__sflush);         rwlock_unlock(&__sfp_lock);         return r;     }       FLOCKFILE(fp);     if ((fp­>_flags & (__SWR | __SRW)) == 0) {         errno = EBADF;         r = EOF;     } else {         r = __sflush(fp);     }     FUNLOCKFILE(fp);     return r; } writeとread&write、両方無ければエラー。 そうでなければ、__sflush(fp)を呼ぶ。
  25. 25. /****************************************************************** putchar             ... stdoutに1文字書き込み => __sputc             ... バッファに書き込み、貯まったら__swbufへ  => __swbuf             ... バッファの初期化、バッファ貯まってたらfflushへ   => fflush             ... 引数NULLなら全ファイル、もしくは1ファイルを__sflush ******************************************************************/
  26. 26. __sflush src/lib/libc/stdio/fflush.c 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 int __sflush(fp)     FILE *fp; {     unsigned char *p;     int n, t;       _DIAGASSERT(fp != NULL);       t = fp­>_flags;     if ((t & __SWR) == 0)         return (0);       if ((p = fp­>_bf._base) == NULL)         return (0);       n = fp­>_p ­ p;        /* write this much */  
  27. 27. src/lib/libc/stdio/fflush.c 1/2 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 int __sflush(fp)     FILE *fp; {     unsigned char *p;     int n, t;       _DIAGASSERT(fp != NULL);       t = fp­>_flags;     if ((t & __SWR) == 0)         return (0);       if ((p = fp­>_bf._base) == NULL)         return (0);       n = fp­>_p ­ p;        /* write this much */   writeフラグがなければ戻る
  28. 28. src/lib/libc/stdio/fflush.c 1/2 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 int __sflush(fp)     FILE *fp; {     unsigned char *p;     int n, t;       _DIAGASSERT(fp != NULL);       t = fp­>_flags;     if ((t & __SWR) == 0)         return (0);       if ((p = fp­>_bf._base) == NULL)         return (0);       n = fp­>_p ­ p;        /* write this much */   pにバッファの先頭位置を入れる バッファが無ければ戻る
  29. 29. src/lib/libc/stdio/fflush.c 1/2 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 int __sflush(fp)     FILE *fp; {     unsigned char *p;     int n, t;       _DIAGASSERT(fp != NULL);       t = fp­>_flags;     if ((t & __SWR) == 0)         return (0);       if ((p = fp­>_bf._base) == NULL)         return (0);       n = fp­>_p ­ p;        /* write this much */   n はバッファの 現在位置 - 先頭。 なので、書き込みサイズ
  30. 30. src/lib/libc/stdio/fflush.c 2/2 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112     /*      * longjmpの問題の回避と、ユーザーのwrite関数での      * バッファの交換(setvbuf経由)の許可のため、即座にこれらをセットする。      */     fp­>_p = p;     fp­>_w = t & (__SLBF|__SNBF) ? 0 : fp­>_bf._size;       for (; n > 0; n ­= t, p += t) {         t = (*fp­>_write)(fp­>_cookie, (char *)p, n);         if (t <= 0) {             fp­>_flags |= __SERR;             return (EOF);         }     }     return (0); } バッファの現在位置_pを、バッファの先頭に変える。 ラインバッファ か バッファ無しなら、_wは0。 どちらでもなければ、_wはバッファのサイズにする。   これは、_pと_wの値を初期化している(理由は上のコメント)
  31. 31. src/lib/libc/stdio/fflush.c 2/2 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112     /*      * longjmpの問題の回避と、ユーザーのwrite関数での      * バッファの交換(setvbuf経由)の許可のため、即座にこれらをセットする。      */     fp­>_p = p;     fp­>_w = t & (__SLBF|__SNBF) ? 0 : fp­>_bf._size;       for (; n > 0; n ­= t, p += t) {         t = (*fp­>_write)(fp­>_cookie, (char *)p, n);         if (t <= 0) {             fp­>_flags |= __SERR;             return (EOF);         }     }     return (0); } ファイル構造体の_write関数をコールする。   fp->_writeの戻り値t は書き込んだサイズ 書き込みサイズのnになるまで書き込む
  32. 32. src/lib/libc/stdio/fflush.c 2/2 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112     /*      * longjmpの問題の回避と、ユーザーのwrite関数での      * バッファの交換(setvbuf経由)の許可のため、即座にこれらをセットする。      */     fp­>_p = p;     fp­>_w = t & (__SLBF|__SNBF) ? 0 : fp­>_bf._size;       for (; n > 0; n ­= t, p += t) {         t = (*fp­>_write)(fp­>_cookie, (char *)p, n);         if (t <= 0) {             fp­>_flags |= __SERR;             return (EOF);         }     }     return (0); } 負の値が返ってきたら、エラーを設定して、EOFで戻る。
  33. 33. stdoutの定義を確認 src/lib/libc/stdio/findfp.c 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 #define    std(flags, file) {      ._p = NULL,      ._r = 0,      ._w = 0,      ._flags = (flags),      ._file = (file),       ._bf = { ._base = NULL, ._size = 0 },      ._lbfsize = 0,       ._cookie = __sF + (file),      ._close = __sclose,      ._read = __sread,      ._seek = __sseek,      ._write = __swrite,      ._ext = { ._base = (void *)(__sFext + (file)), ._size = 0 },      ._up = NULL,      ._ur = 0,      ._ubuf = { [0] = '0', [1] = '0', [2] = '0' },      ._nbuf = { [0] = '0' },      ._flush = NULL,      ._lb_unused = { '0' },      ._blksize = 0,      ._offset = (off_t)0,  } stdoutの_write関数は__write
  34. 34. /****************************************************************** putchar             ... stdoutに1文字書き込み => __sputc             ... バッファに書き込み、貯まったら__swbufへ  => __swbuf             ... バッファの初期化、バッファ貯まってたらfflushへ   => fflush             ... 引数NULLなら全ファイル、もしくは1ファイルを__sflush    => __sflush             ... バッファに貯まったサイズを全てfile­>_writeで書く ******************************************************************/
  35. 35. __swrite src/lib/libc/stdio/stdio.c 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 int __swrite(cookie, buf, n)     void *cookie;     char const *buf;     int n; {     FILE *fp = cookie;       _DIAGASSERT(cookie != NULL);     _DIAGASSERT(buf != NULL);       if (fp­>_flags & __SAPP)         (void) lseek(__sfileno(fp), (off_t)0, SEEK_END);     fp­>_flags &= ~__SOFF;    /* in case FAPPEND mode is set */     return write(__sfileno(fp), buf, (size_t)n); }
  36. 36. src/lib/libc/stdio/stdio.c 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 int __swrite(cookie, buf, n)     void *cookie;     char const *buf;     int n; {     FILE *fp = cookie;       _DIAGASSERT(cookie != NULL);     _DIAGASSERT(buf != NULL);       if (fp­>_flags & __SAPP)         (void) lseek(__sfileno(fp), (off_t)0, SEEK_END);     fp­>_flags &= ~__SOFF;    /* in case FAPPEND mode is set */     return write(__sfileno(fp), buf, (size_t)n); }   #define    __SAPP    0x0100        /* fdopen()ed in append mode */   追記フラグが立っていたら、lseekでファイル末尾にオフセットを移 動する
  37. 37. src/lib/libc/stdio/stdio.c 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 int __swrite(cookie, buf, n)     void *cookie;     char const *buf;     int n; {     FILE *fp = cookie;       _DIAGASSERT(cookie != NULL);     _DIAGASSERT(buf != NULL);       if (fp­>_flags & __SAPP)         (void) lseek(__sfileno(fp), (off_t)0, SEEK_END);     fp­>_flags &= ~__SOFF;    /* in case FAPPEND mode is set */     return write(__sfileno(fp), buf, (size_t)n); }   #define    __SOFF  0x1000   /* set iff _offset is in fact correct */                       /* _offsetの値が正しい時、その時に限ってセットされる */ FAPPENDモードの処理で、 _offsetの値が正しくないかもしれないので __SOFFフラグを消す
  38. 38. src/lib/libc/stdio/stdio.c 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 int __swrite(cookie, buf, n)     void *cookie;     char const *buf;     int n; {     FILE *fp = cookie;       _DIAGASSERT(cookie != NULL);     _DIAGASSERT(buf != NULL);       if (fp­>_flags & __SAPP)         (void) lseek(__sfileno(fp), (off_t)0, SEEK_END);     fp­>_flags &= ~__SOFF;    /* in case FAPPEND mode is set */     return write(__sfileno(fp), buf, (size_t)n); } stdoutの __sfileno(fp) は1 write( 識別子番号, バッファのポインタ, 書き込みサイズ );   writeはどこか?
  39. 39. /****************************************************************** putchar             ... stdoutに1文字書き込み => __sputc             ... バッファに書き込み、貯まったら__swbufへ  => __swbuf             ... バッファの初期化、バッファ貯まってたらfflushへ   => fflush             ... 引数NULLなら全ファイル、もしくは1ファイルを__sflush    => __sflush             ... バッファに貯まったサイズを全てfile­>_writeで書く     => __swrite :: (file­>_write)             ... 追記フラグをチェックしてwriteを呼ぶ ******************************************************************/
  40. 40. write     writeのcソースファイルは見つからない
  41. 41. gdb ステップ実行画面 0xbbbbfe1e in __swrite () from /usr/lib/libc.so.12 (gdb)                                         __swrite関数からgdbでステップ実行をしていきます
  42. 42. gdb ステップ実行画面 0xbbbbfe1e in __swrite () from /usr/lib/libc.so.12 (gdb) si 0xbbaf5f9c in write@plt () from /usr/lib/libc.so.12 (gdb)                                   ステップ実行を続けると、write@pltがコールされる。 (初回は動的リンクが行われる)
  43. 43. gdb ステップ実行画面 0xbbbbfe1e in __swrite () from /usr/lib/libc.so.12 (gdb) si 0xbbaf5f9c in write@plt () from /usr/lib/libc.so.12 (gdb) si 0xbbaf9080 in write () from /usr/lib/libc.so.12 (gdb)                               リンクの完了後だと、write関数にたどり着く。 write関数はlibc内にあった
  44. 44. gdb ステップ実行画面 0xbbbbfe1e in __swrite () from /usr/lib/libc.so.12 (gdb) si 0xbbaf5f9c in write@plt () from /usr/lib/libc.so.12 (gdb) si 0xbbaf9080 in write () from /usr/lib/libc.so.12 (gdb) disassemble disassemble Dump of assembler code for function write: => 0xbbaf9080 <+0>:    mov    $0x4,%eax    0xbbaf9085 <+5>:    int    $0x80    0xbbaf9087 <+7>:    jb     0xbbaf908a < write+10 >    0xbbaf9089 <+9>:    ret        0xbbaf908a <+10>:    push   %ebx    0xbbaf908b <+11>:    call   0xbbaf9090 < write+16 >    0xbbaf9090 <+16>:    pop    %ebx    0xbbaf9091 <+17>:    add    $0xd752c,%ebx    0xbbaf9097 <+23>:    mov    ­0x200(%ebx),%ecx    0xbbaf909d <+29>:    pop    %ebx    0xbbaf909e <+30>:    jmp    *%ecx End of assembler dump. (gdb)   writeのアセンブリコードを表示します
  45. 45. gdb ステップ実行画面 0xbbbbfe1e in __swrite () from /usr/lib/libc.so.12 (gdb) si 0xbbaf5f9c in write@plt () from /usr/lib/libc.so.12 (gdb) si 0xbbaf9080 in write () from /usr/lib/libc.so.12 (gdb) disassemble disassemble Dump of assembler code for function write: => 0xbbaf9080 <+0>:    mov    $0x4,%eax    0xbbaf9085 <+5>:    int    $0x80    0xbbaf9087 <+7>:    jb     0xbbaf908a < write+10 >    0xbbaf9089 <+9>:    ret        0xbbaf908a <+10>:    push   %ebx    0xbbaf908b <+11>:    call   0xbbaf9090 < write+16 >    0xbbaf9090 <+16>:    pop    %ebx    0xbbaf9091 <+17>:    add    $0xd752c,%ebx    0xbbaf9097 <+23>:    mov    ­0x200(%ebx),%ecx    0xbbaf909d <+29>:    pop    %ebx    0xbbaf909e <+30>:    jmp    *%ecx End of assembler dump. (gdb) src/sys/sys/syscall.h 28 #define    SYS_write    4
  46. 46. gdb ステップ実行画面 0xbbbbfe1e in __swrite () from /usr/lib/libc.so.12 (gdb) si 0xbbaf5f9c in write@plt () from /usr/lib/libc.so.12 (gdb) si 0xbbaf9080 in write () from /usr/lib/libc.so.12 (gdb) disassemble disassemble Dump of assembler code for function write: => 0xbbaf9080 <+0>:    mov    $0x4,%eax    0xbbaf9085 <+5>:    int    $0x80    0xbbaf9087 <+7>:    jb     0xbbaf908a < write+10 >    0xbbaf9089 <+9>:    ret        0xbbaf908a <+10>:    push   %ebx    0xbbaf908b <+11>:    call   0xbbaf9090 < write+16 >    0xbbaf9090 <+16>:    pop    %ebx    0xbbaf9091 <+17>:    add    $0xd752c,%ebx    0xbbaf9097 <+23>:    mov    ­0x200(%ebx),%ecx    0xbbaf909d <+29>:    pop    %ebx    0xbbaf909e <+30>:    jmp    *%ecx End of assembler dump. (gdb)   オペコード   命令        説明 CD ib      INT imm8    割り込みベクタ番号の即値バイトによる指定。
  47. 47. write(の始めの2行)まとめ => 0xbbaf9080 <+0>:    mov    $0x4,%eax    0xbbaf9085 <+5>:    int    $0x80 write関数はwriteシステムコールの番号をEAXレジスタに書いて、 int 0x80を実行する intは割り込みハンドラをコールする命令。 (IA-32 マニュアルより) 0x80はおそらく、システムコールに対応する 割り込みベクタ番号のはず (だけど、それを示すコードは見つけられなかった...)   (int以降のアセンブリも読めていません...)
  48. 48. /****************************************************************** putchar             ... stdoutに1文字書き込み => __sputc             ... バッファに書き込み、貯まったら__swbufへ  => __swbuf             ... バッファの初期化、バッファ貯まってたらfflushへ   => fflush             ... 引数NULLなら全ファイル、もしくは1ファイルを__sflush    => __sflush             ... バッファに貯まったサイズを全てfile­>_writeで書く     => __swrite :: (file­>_write)             ... 追記フラグをチェックしてwriteを呼ぶ      => write             ... EAXにwriteシステムコールの番号を書いて、int 0x80       => int 0x80 (EAX 4)             ... ? ******************************************************************/
  49. 49. 終わり

×