glibc with vtable
vtable을 glibc에서 찾다가 짜증나서 정리하는 글이다.
vtable 관련코드를 따라가다 보면,
C
return _IO_UFLOW (fp);다음과 같은 코드가 있고
C
#define _IO_UFLOW(FP) JUMP0 (__uflow, FP)여기서 다음과 같은 매크로가 설정되어 있다.
C
#define JUMP0(FUNC, THIS) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS)
#define JUMP1(FUNC, THIS, X1) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS, X1)
#define JUMP2(FUNC, THIS, X1, X2) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS, X1, X2)
#define JUMP3(FUNC, THIS, X1,X2,X3) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS, X1,X2, X3)근데 이런 jump시리즈는 모두 _IO_JUMPS_FUNC 라는 매크로에 되어 있고
C
# define _IO_JUMPS_FUNC(THIS) \
(*(struct _IO_jump_t **) ((void *) &_IO_JUMPS_FILE_plus (THIS) \
+ (THIS)->_vtable_offset))이는 _IO_jump_t 구조체에서 특정 함수 offset으로 점프를 하는 코드이다.
근데 여기서부터는 단순히 코드로만 따라갈 수 없다. _IO_jump_t 구조체가 메모리에 올라와있고 거기서 있는 주소들의 테이블에 대해서 점프를 하는 것이기 때문이다. 따라서 여기서 바로 따라갈 수 없어서 짜증이 난다.
여기서 그래서 한눈에 볼 수 있게 정리해놓겠다.
C
struct _IO_jump_t
{
JUMP_FIELD(size_t, __dummy);
JUMP_FIELD(size_t, __dummy2);
JUMP_FIELD(_IO_finish_t, __finish);
JUMP_FIELD(_IO_overflow_t, __overflow);
JUMP_FIELD(_IO_underflow_t, __underflow);
JUMP_FIELD(_IO_underflow_t, __uflow);
JUMP_FIELD(_IO_pbackfail_t, __pbackfail);
/* showmany */
JUMP_FIELD(_IO_xsputn_t, __xsputn);
JUMP_FIELD(_IO_xsgetn_t, __xsgetn);
JUMP_FIELD(_IO_seekoff_t, __seekoff);
JUMP_FIELD(_IO_seekpos_t, __seekpos);
JUMP_FIELD(_IO_setbuf_t, __setbuf);
JUMP_FIELD(_IO_sync_t, __sync);
JUMP_FIELD(_IO_doallocate_t, __doallocate);
JUMP_FIELD(_IO_read_t, __read);
JUMP_FIELD(_IO_write_t, __write);
JUMP_FIELD(_IO_seek_t, __seek);
JUMP_FIELD(_IO_close_t, __close);
JUMP_FIELD(_IO_stat_t, __stat);
JUMP_FIELD(_IO_showmanyc_t, __showmanyc);
JUMP_FIELD(_IO_imbue_t, __imbue);
#if 0
get_column;
set_column;
#endif
};이렇게 있으며,
https://elixir.bootlin.com/glibc/glibc-2.23/source/libio/iopopen.c#L349
C
static const struct _IO_jump_t _IO_proc_jumps = {
JUMP_INIT_DUMMY,
JUMP_INIT(finish, _IO_new_file_finish),
JUMP_INIT(overflow, _IO_new_file_overflow),
JUMP_INIT(underflow, _IO_new_file_underflow),
JUMP_INIT(uflow, _IO_default_uflow),
JUMP_INIT(pbackfail, _IO_default_pbackfail),
JUMP_INIT(xsputn, _IO_new_file_xsputn),
JUMP_INIT(xsgetn, _IO_default_xsgetn),
JUMP_INIT(seekoff, _IO_new_file_seekoff),
JUMP_INIT(seekpos, _IO_default_seekpos),
JUMP_INIT(setbuf, _IO_new_file_setbuf),
JUMP_INIT(sync, _IO_new_file_sync),
JUMP_INIT(doallocate, _IO_file_doallocate),
JUMP_INIT(read, _IO_file_read),
JUMP_INIT(write, _IO_new_file_write),
JUMP_INIT(seek, _IO_file_seek),
JUMP_INIT(close, _IO_new_proc_close),
JUMP_INIT(stat, _IO_file_stat),
JUMP_INIT(showmanyc, _IO_default_showmanyc),
JUMP_INIT(imbue, _IO_default_imbue)
};다음과 같은 함수의 위치로 적용이 된다.
즉, 맨처음에 보았던
C
#define _IO_UFLOW(FP) JUMP0 (__uflow, FP)이럴때는 __uflow 이므로
JUMP_INIT(uflow, _IO_default_uflow), 이고
_IO_default_uflow 함수를 찾아서 보면되는 것이다.