socket()を追う

  • socket()はどこで実装されているか?

  • とりあえずプログラムを書いてみる
    #include <stdio.h>
    #include <unistd.h>
    #include <arpa/inet.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <string.h>
      
    int main(int argc, char **argv) {
        struct sockaddr_in dst;
        int sock;
        char dat[] = "hogehoge";
      
        memset(&dst, 0, sizeof(dst));
        dst.sin_port = htons(8080);
        dst.sin_family = AF_INET;
        dst.sin_addr.s_addr = inet_addr("127.0.0.1");
      
        sock = socket(AF_INET, SOCK_STREAM, 0);
        connect(sock, (struct sockaddr *) &dst, sizeof(dst));
      
        printf("%d\n",sock);
        send(sock, dat, strlen(dat), 0);
      
        close(sock);
      
        return 0;
    }
    
  • gdbでsocket()で止めてどういうアセンブラが動いているか見てみる
  - gdb-peda$ disas
        - 0x00007ffff7ed71a0 <+0>:	endbr64
  - => 0x00007ffff7ed71a4 <+4>:	mov    eax,0x29
        - 0x00007ffff7ed71a9 <+9>:	syscall
        - 0x00007ffff7ed71ab <+11>:	cmp    rax,0xfffffffffffff001
        - 0x00007ffff7ed71b1 <+17>:	jae    0x7ffff7ed71b4 <socket+20>
        - 0x00007ffff7ed71b3 <+19>:	ret
        - 0x00007ffff7ed71b4 <+20>:	mov    rcx,QWORD PTR rip+0xc0c85        # 0x7ffff7f97e40
        - 0x00007ffff7ed71bb <+27>:	neg    eax
        - 0x00007ffff7ed71bd <+29>:	mov    DWORD PTR fs:rcx,eax
        - 0x00007ffff7ed71c0 <+32>:	or     rax,0xffffffffffffffff
        - 0x00007ffff7ed71c4 <+36>:	ret
  • システムコールを呼んでいるだけっぽい
  • eaxに入れられている0x29はsyscall_64.tblのsocketの番号に一致する

  • $ rg 'int socket'で宣言を探す
     /* Create a new socket of type TYPE in domain DOMAIN, using
      protocol PROTOCOL.  If PROTOCOL is zero, one is chosen automatically.
      Returns a file descriptor for the new socket, or -1 for errors.  */
     extern int socket (int __domain, int __type, int __protocol) __THROW;
    
/* Create a new socket of type TYPE in domain DOMAIN, using
      - protocol PROTOCOL.  If PROTOCOL is zero, one is chosen automatically.
      - Returns a file descriptor for the new socket, or -1 for errors.  */
extern int __socket (int __domain, int __type,
              - int __protocol);
libc_hidden_proto (__socket)

libc_hidden_protoはlibc内部からその関数を呼べるようにするもの(?)

  • 実装を探す
    • 内部でsocketシステムコールを呼んでいるのでOS依存→sysdeps/以下
       int
       __socket (int fd, int type, int domain)
       {
       #ifdef __ASSUME_SOCKET_SYSCALL
       return INLINE_SYSCALL (socket, 3, fd, type, domain);
       #else
       return SOCKETCALL (socket, fd, type, domain);
       #endif
       }
       libc_hidden_def (__socket)
       weak_alias (__socket, socket)
      
  • INLINE_SYSCALLからはINTERNAL_SYSCALLとかたどって最終的に
    • i386ではint 0x80されていた
    • nrは引数の数
 #undef INTERNAL_SYSCALL
 #define INTERNAL_SYSCALL(name, err, nr, args...)			\
 	internal_syscall##nr (SYS_ify (name), err, args)
 
 ...
 
 #undef internal_syscall0
 #define internal_syscall0(number, err, dummy...)			\
 ({									\
     unsigned long int resultvar;					\
     asm volatile (							\
     "syscall\n\t"							\
     : "=a" (resultvar)							\
     : "0" (number)							\
     : "memory", REGISTERS_CLOBBERED_BY_SYSCALL);			\
     (long int) resultvar;						\
 })
 
 ...
  
  • send(), sendto(), sendmsg()の実装も読む
  • 同じように読んだらええやろと思って開いたら実装がない
    • stubになってる
 /* Send N bytes of BUF to socket FD.  Returns the number sent or -1.  */
 ssize_t
 __send (int fd, const void *buf, size_t n, int flags)
 {
   __set_errno (ENOSYS);
   return -1;
 }
 libc_hidden_def (__send)
 weak_alias (__send, send)
 
  stub_warning (send)
  • いろいろパターンを変えて探す $ rg 'sendmsg \('
    • __libc_sendmsgからシステムコールを呼んでいる
    • 調べてみるとこの3つの関数はCancellation pointらしい
      • アセンブラで見てみるとなんかマルチスレッドに関係してそうな関数を呼び出している
	 ssize_t
  __libc_sendmsg (int fd, const struct msghdr *msg, int flags)
  {
  # ifdef __ASSUME_SENDMSG_SYSCALL
    return SYSCALL_CANCEL (sendmsg, fd, msg, flags);
  # else
    return SOCKETCALL_CANCEL (sendmsg, fd, msg, flags);
  # endif
  }
  weak_alias (__libc_sendmsg, sendmsg)
  weak_alias (__libc_sendmsg, __sendmsg)

Backlinks