实验环境 glibc2.23
下载对应版本Ubuntu glibc源码 https://launchpad.net/ubuntu/+source/glibc/
或者
sudo apt-get install glibc-source sudo apt-get install libc6-dbg sudo apt-get install libc6-dbg:i386 到/usr/src/glibc
在.gdbinit里写入dir path进行源码级调试,也可以直接在gdb里dir path
fopen FILE *fopen (const char *filename, const char *mode)
/include/stdio.h
找到了fopen的macro
# define fopen(fname, mode) _IO_new_fopen (fname, mode)
/libio/iofopen.c
_IO_new_fopen直接调用__fopen_internal
_IO_new_fopen
_IO_FILE * _IO_new_fopen (const char *filename, const char *mode) { return __fopen_internal (filename, mode, 1 ); }
__fopen_internal
_IO_FILE * __fopen_internal (const char *filename, const char *mode, int is32) { struct locked_FILE { struct _IO_FILE_plus fp ; #ifdef _IO_MTSAFE_IO _IO_lock_t lock; #endif struct _IO_wide_data wd ; } *new_f = (struct locked_FILE *) malloc (sizeof (struct locked_FILE)); if (new_f == NULL ) return NULL ; #ifdef _IO_MTSAFE_IO new_f->fp.file._lock = &new_f->lock; #endif #if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T _IO_no_init (&new_f->fp.file, 0 , 0 , &new_f->wd, &_IO_wfile_jumps); #else _IO_no_init (&new_f->fp.file, 1 , 0 , NULL , NULL ); #endif _IO_JUMPS (&new_f->fp) = &_IO_file_jumps; _IO_file_init (&new_f->fp); #if !_IO_UNIFIED_JUMPTABLES new_f->fp.vtable = NULL ; #endif if (_IO_file_fopen ((_IO_FILE *) new_f, filename, mode, is32) != NULL ) return __fopen_maybe_mmap (&new_f->fp.file); _IO_un_link (&new_f->fp); free (new_f); return NULL ; }
先用malloc分配了locked_FILE类型的结构体new_f
_IO_no_init
进行初始化 _IO_FILE_plus fp
_IO_file_init
将结构体链接进_IO_list_all
链表
_IO_file_fopen
执行系统调用打开文件
_IO_no_init
进行初始化 _IO_FILE_plus fp_IO_no_init
void _IO_no_init (_IO_FILE *fp, int flags, int orientation, struct _IO_wide_data *wd, const struct _IO_jump_t *jmp) { _IO_old_init (fp, flags); fp->_mode = orientation; #if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T if (orientation >= 0 ) { fp->_wide_data = wd; fp->_wide_data->_IO_buf_base = NULL ; fp->_wide_data->_IO_buf_end = NULL ; fp->_wide_data->_IO_read_base = NULL ; fp->_wide_data->_IO_read_ptr = NULL ; fp->_wide_data->_IO_read_end = NULL ; fp->_wide_data->_IO_write_base = NULL ; fp->_wide_data->_IO_write_ptr = NULL ; fp->_wide_data->_IO_write_end = NULL ; fp->_wide_data->_IO_save_base = NULL ; fp->_wide_data->_IO_backup_base = NULL ; fp->_wide_data->_IO_save_end = NULL ; fp->_wide_data->_wide_vtable = jmp; } else fp->_wide_data = (struct _IO_wide_data *) -1L ; #endif fp->_freeres_list = NULL ; }
先调用_IO_old_init 设置flags,其余NULL初始化fp
_IO_old_init
void _IO_old_init (_IO_FILE *fp, int flags) { fp->_flags = _IO_MAGIC|flags; fp->_flags2 = 0 ; fp->_IO_buf_base = NULL ; fp->_IO_buf_end = NULL ; fp->_IO_read_base = NULL ; fp->_IO_read_ptr = NULL ; fp->_IO_read_end = NULL ; fp->_IO_write_base = NULL ; fp->_IO_write_ptr = NULL ; fp->_IO_write_end = NULL ; fp->_chain = NULL ; fp->_IO_save_base = NULL ; fp->_IO_backup_base = NULL ; fp->_IO_save_end = NULL ; fp->_markers = NULL ; fp->_cur_column = 0 ; #if _IO_JUMPS_OFFSET fp->_vtable_offset = 0 ; #endif #ifdef _IO_MTSAFE_IO if (fp->_lock != NULL ) _IO_lock_init (*fp->_lock); #endif }
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
再把_wide_data
字段赋值并初始化fp->_wide_data->_wide_vtabl为_IO_wfile_jumps
_IO_file_init
将结构体链接进_IO_list_all
链表回到__fopen_internal
先执行_IO_JUMPS (&new_f->fp) = &_IO_file_jumps;
#define _IO_JUMPS(*THIS*) (THIS)->vtable
*THIS*
是一个指针参数,它用来表示传入宏定义的结构体指针。
在宏定义展开时,参数 THIS
会被替换为传入的结构体指针。而 *THIS*
中的星号 *
没有实际的作用,只是为了标识 THIS
是一个指针类型的参数。这样做的目的是为了方便理解宏定义的作用,明确参数类型,增加代码的可读性。
把fp的vtable设置为_IO_file_jumps地址
/libio/fileops.c
_IO_new_file_init
# define _IO_new_file_init _IO_file_init
void _IO_new_file_init (struct _IO_FILE_plus *fp) { fp->file._offset = _IO_pos_BAD; fp->file._IO_file_flags |= CLOSED_FILEBUF_FLAGS; _IO_link_in (fp); fp->file._fileno = -1 ; }
/libio/genops.c
_IO_link_in
void _IO_link_in (struct _IO_FILE_plus *fp) { if ((fp->file._flags & _IO_LINKED) == 0 ) { fp->file._flags |= _IO_LINKED; #ifdef _IO_MTSAFE_IO _IO_cleanup_region_start_noarg (flush_cleanup); _IO_lock_lock (list_all_lock); run_fp = (_IO_FILE *) fp; _IO_flockfile ((_IO_FILE *) fp); #endif fp->file._chain = (_IO_FILE *) _IO_list_all; _IO_list_all = fp; ++_IO_list_all_stamp; #ifdef _IO_MTSAFE_IO _IO_funlockfile ((_IO_FILE *) fp); run_fp = NULL ; _IO_lock_unlock (list_all_lock); _IO_cleanup_region_end (0 ); #endif } }
_IO_LINKED标志位用来指示一个streambuf对象是否已经被添加到了_list_all链表中。
如果一个streambuf对象链接到了_IO_list_all链表,则_IO_LINKED标志位会被设置为1,否则为0
fp->file._flags & _IO_LINKED检测到为0则设置为1并把_chain设置为当前_IO_list_all,把_IO_list_all设置为当前_IO_FILE_plus
_IO_file_fopen
执行系统调用打开文件回到__fopen_internal,调用_IO_file_fopen
libio/fileops.c
_IO_file_fopen
# define _IO_new_file_fopen _IO_file_fopen
_IO_FILE * _IO_new_file_fopen (_IO_FILE *fp, const char *filename, const char *mode, int is32not64) { int oflags = 0 , omode; int read_write; int oprot = 0666 ; int i; _IO_FILE *result; #ifdef _LIBC const char *cs; const char *last_recognized; #endif if (_IO_file_is_open (fp)) return 0 ; switch (*mode) { case 'r' : omode = O_RDONLY; read_write = _IO_NO_WRITES; break ; case 'w' : omode = O_WRONLY; oflags = O_CREAT|O_TRUNC; read_write = _IO_NO_READS; break ; case 'a' : omode = O_WRONLY; oflags = O_CREAT|O_APPEND; read_write = _IO_NO_READS|_IO_IS_APPENDING; break ; default : __set_errno (EINVAL); return NULL ; } #ifdef _LIBC last_recognized = mode; #endif for (i = 1 ; i < 7 ; ++i) { switch (*++mode) { case '\0' : break ; case '+' : omode = O_RDWR; read_write &= _IO_IS_APPENDING; #ifdef _LIBC last_recognized = mode; #endif continue ; case 'x' : oflags |= O_EXCL; #ifdef _LIBC last_recognized = mode; #endif continue ; case 'b' : #ifdef _LIBC last_recognized = mode; #endif continue ; case 'm' : fp->_flags2 |= _IO_FLAGS2_MMAP; continue ; case 'c' : fp->_flags2 |= _IO_FLAGS2_NOTCANCEL; continue ; case 'e' : #ifdef O_CLOEXEC oflags |= O_CLOEXEC; #endif fp->_flags2 |= _IO_FLAGS2_CLOEXEC; continue ; default : continue ; } break ; } result = _IO_file_open (fp, filename, omode|oflags, oprot, read_write, is32not64); if (result != NULL ) { ..................... ..................... ..................... } return result; }
调用
_IO_file_open
_IO_FILE * _IO_file_open (_IO_FILE *fp, const char *filename, int posix_mode, int prot, int read_write, int is32not64) { int fdesc; #ifdef _LIBC if (__glibc_unlikely (fp->_flags2 & _IO_FLAGS2_NOTCANCEL)) fdesc = open_not_cancel (filename, posix_mode | (is32not64 ? 0 : O_LARGEFILE), prot); else fdesc = open (filename, posix_mode | (is32not64 ? 0 : O_LARGEFILE), prot); #else fdesc = open (filename, posix_mode, prot); #endif if (fdesc < 0 ) return NULL ; fp->_fileno = fdesc; _IO_mask_flags (fp, read_write,_IO_NO_READS+_IO_NO_WRITES+_IO_IS_APPENDING); if ((read_write & (_IO_IS_APPENDING | _IO_NO_READS)) == (_IO_IS_APPENDING | _IO_NO_READS)) { _IO_off64_t new_pos = _IO_SYSSEEK (fp, 0 , _IO_seek_end); if (new_pos == _IO_pos_BAD && errno != ESPIPE) { close_not_cancel (fdesc); return NULL ; } } _IO_link_in ((struct _IO_FILE_plus *) fp); return fp; }
调用系统函数open打开文件
,把返回值给到_fileno字段,再次_IO_link_in
函数加入_IO_list_all
链表