作者: Qingan Li 邮箱:qingan@whu.edu.cn

getchar -> syscall

1. getchar函数 -> read(@libc)调用路径

$ less glibc-2.31/libio/getchar.c

int
getchar (void)
{
  int result;
  if (!_IO_need_lock (stdin))
    return **_IO_getc_unlocked** (stdin);
  _IO_acquire_lock (stdin);
  result = _IO_getc_unlocked (stdin);
  _IO_release_lock (stdin);
  return result;
}


-> alias _IO_getc_unlocked __getc_unlocked_body -> __uflow
@glibc-2.31/libio/bits/types/struct_FILE.h

-> _IO_UFLOW(fp) @ /glibc-2.31/libio/genops.c

-> _IO_JUMPS_FUNC(fp)->__uflow) (fp) @ glibc-2.31/libio/libioP.h

-> __GI__IO_default_uflow (fp=0x7fb2cce42a40 <_IO_2_1_stdin_>) @ genops.c:362

-> _IO_new_file_underflow (fp=0x7fb2cce42a40 <_IO_2_1_stdin_>) at fileops.c:517
517	  count = _IO_SYSREAD (fp, fp->_IO_buf_base,
518			       fp->_IO_buf_end - fp->_IO_buf_base);
   _IO_SYSREAD == fp->__read  (间接调用) -> __GI___libc_read 

-> __GI___libc_read (fd=0, buf=0x5555565206b0, nbytes=1024) at /sysdeps/unix/sysv/linux/read.c:26

2. read@libc -> syscall 调用路径

  __libc_read @ read.c

     23 ssize_t
     24 __libc_read (int fd, void *buf, size_t nbytes)
     25 {
     26   return SYSCALL_CANCEL (read, fd, buf, nbytes);
     27 }

__libc_read对应的反汇编代码:

0x00007f51d7204c7e in __GI___libc_read (fd=0, buf=0x555555e262a0, nbytes=1024) at ../sysdeps/unix/sysv/linux/read.c:26
26	  return SYSCALL_CANCEL (read, fd, buf, nbytes);

==>

#define SYSCALL_CANCEL(...) \
  ({                                                                         \
    long int sc_ret;                                                         \
    if (SINGLE_THREAD_P)                                                     \
      sc_ret = INLINE_SYSCALL_CALL (__VA_ARGS__);                            \
    else                                                                     \
      {                                                                      \
        int sc_cancel_oldtype = LIBC_CANCEL_ASYNC ();                        \
        sc_ret = INLINE_SYSCALL_CALL (__VA_ARGS__);                          \
        LIBC_CANCEL_RESET (sc_cancel_oldtype);                               \
      }                                                                      \
    sc_ret;                                                                  \
  })

==> -> inline assembly code template, like:


#define internal_syscall1(number, err, arg1)                            \
({                                                                      \
    unsigned long int resultvar;                                        \
    TYPEFY (arg1, __arg1) = ARGIFY (arg1);                              \
    register TYPEFY (arg1, _a1) asm ("rdi") = __arg1;                   \
    asm volatile (                                                      \
    "syscall\n\t"                                                       \
    : "=a" (resultvar)                                                  \
    : "0" (number), "r" (_a1)                                           \
    : "memory", REGISTERS_CLOBBERED_BY_SYSCALL);                        \
    (long int) resultvar;                                               \
})

==> final code:

(gdb) disass
Dump of assembler code for function __GI___libc_read:
   0x00007f51d7204c70 <+0>:	mov    %fs:0x18,%eax
   0x00007f51d7204c78 <+8>:	test   %eax,%eax
   0x00007f51d7204c7a <+10>:	jne    0x7f51d7204c90 <__GI___libc_read+32>
   0x00007f51d7204c7c <+12>:	syscall 
=> 0x00007f51d7204c7e <+14>:	cmp    $0xfffffffffffff000,%rax
   0x00007f51d7204c84 <+20>:	ja     0x7f51d7204ce0 <__GI___libc_read+112>
   0x00007f51d7204c86 <+22>:	repz retq 
   0x00007f51d7204c88 <+24>:	nopl   0x0(%rax,%rax,1)
   0x00007f51d7204c90 <+32>:	push   %r12
   0x00007f51d7204c92 <+34>:	push   %rbp

注意:上图中,syscall的number,来自%eax = 0 (%fs:0x18 -> %eax)。对这样syscall number的跟踪,从程序分析的角度很难;但是可以从convention的角度解决。