glibc\sysdeps\unix\sysv\linux\socket.c
此文件中定义了socket函数
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) //给__socket起了个别名 socket
首先给出一个进行socket调用的例子:
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
linux网络协议栈从BSD socket layer开始就完全是内核部分,所以linux的网络编程接口在应用层几乎什么也没有做,例如这个socket函数的调用,仅仅在glibc中提供了一个系统调用的接口,下一步跟进SOCKETCALL宏定义,来分析工作模式(当然在mach系统下,socket函数的调用和linux系统下的工作方式区别很大,但目前只专注于分析linux的网络协议栈)
\glibc\sysdeps\unix\sysv\linux\socketcall.h
#define SOCKETCALL(name, args...) \
({ \
long int sc_ret = __SOCKETCALL (SOCKOP_##name, args); \
sc_ret; \
})
发现在代码块中又调用另一个__SOCKETCALL (SOCKOP_##name, args)并返回sc_ret
继续跟进__SOCKETCALL会发现下列的一系列定义
#define __SOCKETCALL_NARGS_X(a,b,c,d,e,f,g,h,n,...) n
#define __SOCKETCALL_NARGS(...) \
__SOCKETCALL_NARGS_X (__VA_ARGS__,7,6,5,4,3,2,1,0,)
#define __SOCKETCALL_CONCAT_X(a,b) a##b
#define __SOCKETCALL_CONCAT(a,b) __SOCKETCALL_CONCAT_X (a, b)
#define __SOCKETCALL_DISP(b,...) \
__SOCKETCALL_CONCAT (b,__SOCKETCALL_NARGS(__VA_ARGS__))(__VA_ARGS__)
#define __SOCKETCALL(...) __SOCKETCALL_DISP (__SOCKETCALL, __VA_ARGS__)
#define __SOCKETCALL1(name, a1) \
INLINE_SYSCALL (socketcall, 2, name, \
((long int [1]) { (long int) (a1) }))
#define __SOCKETCALL2(name, a1, a2) \
INLINE_SYSCALL (socketcall, 2, name, \
((long int [2]) { (long int) (a1), (long int) (a2) }))
#define __SOCKETCALL3(name, a1, a2, a3) \
INLINE_SYSCALL (socketcall, 2, name, \
((long int [3]) { (long int) (a1), (long int) (a2), (long int) (a3) }))
#define __SOCKETCALL4(name, a1, a2, a3, a4) \
INLINE_SYSCALL (socketcall, 2, name, \
((long int [4]) { (long int) (a1), (long int) (a2), (long int) (a3), \
(long int) (a4) }))
#define __SOCKETCALL5(name, a1, a2, a3, a4, a5) \
INLINE_SYSCALL (socketcall, 2, name, \
((long int [5]) { (long int) (a1), (long int) (a2), (long int) (a3), \
(long int) (a4), (long int) (a5) }))
#define __SOCKETCALL6(name, a1, a2, a3, a4, a5, a6) \
INLINE_SYSCALL (socketcall, 2, name, \
((long int [6]) { (long int) (a1), (long int) (a2), (long int) (a3), \
(long int) (a4), (long int) (a5), (long int) (a6) }))
所以翻译__SOCKETCALL(SOCKOP_#name, args)可以得到:(按步骤)
1.__SOCKETCALL_DISP (__SOCKETCALL, SOCKOP__socket,args)
2.__SOCKETCALL_CONCAT (__SOCKETCALL,__SOCKETCALL_NARGS( SOCKOP__socket,fd, type, domain))( SOCKOP#name,args)
3.翻译__SOCKETCALL_NARGS
__SOCKETCALL_CONCAT (__SOCKETCALL,__SOCKETCALL_NARGS_X (SOCKOP__socket,fd, type, domain,7,6,5,4,3,2,1,0,))( SOCKOP__socket,fd, type, domain)
4.翻译__SOCKETCALL_NARGS_X
__SOCKETCALL_CONCAT (__SOCKETCALL,3)( SOCKOP__socket,fd, type, domain)
5.__SOCKETCALL_CONCAT_X (__SOCKETCALL,3)( SOCKOP__socket,fd, type, domain)
6.翻译__SOCKETCALL_CONCAT_X
__SOCKETCALL3(SOCKOP_socket,fd,type,domain)
这个时候代入我们的例子
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
其中:
#define AF_INET PF_INET
#define PF_INET 2
enum __socket_type
{
SOCK_STREAM = 1,
#define SOCK_STREAM SOCK_STREAM
...
}
enum {
IPPROTO_TCP = 6,
#define IPPROTO_TCP IPPROTO_TCP
...
}
#define SOCKOP_socket 1
这些定义都比较好找就不提供源代码位置了
最后得到
__SOCKETCALL3(1,2,1,6)
然后代入
#define __SOCKETCALL3(name, a1, a2, a3) \
INLINE_SYSCALL (socketcall, 2, name, \
((long int [3]) { (long int) (a1), (long int) (a2), (long int) (a3) }))
最后翻译得:
INLINE_SYSCALL(socketcall,2,1,((long int [3]){(long int)(2),(long int)(1),(long int)(6)}))
别忘了这一步是从SOCKETCALL 得来的,也就是说:
int
__socket (int fd, int type, int domain)
{
#ifdef __ASSUME_SOCKET_SYSCALL
return INLINE_SYSCALL (socket, 3, fd, type, domain);
#else
return INLINE_SYSCALL(socketcall,2,1,((long int [3]){(long int)(2),(long int)(1),(long int)(6)}))
#endif
}
现在应用层socket变成了这个样子
我们跟进INLINE_SYSCALL,到了这步,就离系统调用更加近了
glibc\sysdeps\unix\sysv\linux\i386\sysdep.h
#undef INLINE_SYSCALL
#if IS_IN (libc)
# define INLINE_SYSCALL(name, nr, args...) \
({ \
unsigned int resultvar = INTERNAL_SYSCALL (name, , nr, args); \
__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (resultvar, )) \
? __syscall_error (-INTERNAL_SYSCALL_ERRNO (resultvar, )) \
: (int) resultvar; })
#else
# define INLINE_SYSCALL(name, nr, args...) \
({ \
unsigned int resultvar = INTERNAL_SYSCALL (name, , nr, args); \
if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (resultvar, ))) \
{ \
__set_errno (INTERNAL_SYSCALL_ERRNO (resultvar, )); \
resultvar = 0xffffffff; \
} \
(int) resultvar; })
#endif
对于#if SI_IN (libc)
官方的解释是这样的:to detect what component is being compiled
因为两个定义并没有太大的区别,而且皆借用了INTERNAL_SYSCALL,并返回宏定义的值
所以就按照上面那个宏去分析
现在应用层socket长这样:
int
__socket (int fd, int type, int domain)
{
#ifdef __ASSUME_SOCKET_SYSCALL
return INLINE_SYSCALL (socket, 3, fd, type, domain);
#else
return INTERNAL_SYSCALL (socketcall, , 2, 1,((long int [3]){(long int)(2),(long int)(1),(long int)(6)}));
#endif
}
分析INTERNAL_SYSCALL
\glibc\sysdeps\unix\sysv\linux\i386
#define INTERNAL_SYSCALL(name, err, nr, args...) \
({ \
register unsigned int resultvar; \
INTERNAL_SYSCALL_MAIN_##nr (name, err, args); \
(int) resultvar; })
我们先保留宏的样子,先翻译内部的INTERNAL_SYSCALL_MAIN_##nr (name, err, args)
即:
INTERNAL_SYSCALL_MAIN_2(socketcall, 1,((long int [3]){(long int)(2),(long int)(1),(long int)(6)})
继续:
#define INTERNAL_SYSCALL_MAIN_2(name, err, args…)
INTERNAL_SYSCALL_MAIN_INLINE(name, err, 2, args)
得到:
INTERNAL_SYSCALL_MAIN_INLINE(socketcall, 2, 1,((long int [3]){(long int)(2),(long int)(1),(long int)(6)})
所以此时函数块中的样子为:
({ \
register unsigned int resultvar; \
INTERNAL_SYSCALL_MAIN_INLINE(socketcall,, 2, 1,((long int [3]){(long int)(2),(long int)(1),(long int)(6)}); \
(int) resultvar; })
现在再来整理一下:
现在应用层长这样:
int
__socket (int fd, int type, int domain)
{
#ifdef __ASSUME_SOCKET_SYSCALL
return INLINE_SYSCALL (socket, 3, fd, type, domain);
#else
register unsigned int resultvar;
INTERNAL_SYSCALL_MAIN_INLINE(socketcall,, 2, 1,((long int [3]){(long int)(2),(long int)(1),(long int)(6)});
return (int)resultvar;
#endif
}
继续分析INTERNAL_SYSCALL_MAIN_INLINE:
\glibc\sysdeps\unix\sysv\linux\i386\sysdep.h
#if I386_USE_SYSENTER
# ifdef OPTIMIZE_FOR_GCC_5
# ifdef PIC
# define INTERNAL_SYSCALL_MAIN_INLINE(name, err, nr, args...) \
LOADREGS_##nr(args) \
asm volatile ( \
"call *%%gs:%P2" \
: "=a" (resultvar) \
: "a" (__NR_##name), "i" (offsetof (tcbhead_t, sysinfo)) \
ASMARGS_##nr(args) : "memory", "cc")
# define INTERNAL_SYSCALL_NCS(name, err, nr, args...) \
({ \
register unsigned int resultvar; \
LOADREGS_##nr(args) \
asm volatile ( \
"call *%%gs:%P2" \
: "=a" (resultvar) \
: "a" (name), "i" (offsetof (tcbhead_t, sysinfo)) \
ASMARGS_##nr(args) : "memory", "cc"); \
(int) resultvar; })
# else
# define INTERNAL_SYSCALL_MAIN_INLINE(name, err, nr, args...) \
LOADREGS_##nr(args) \
asm volatile ( \
"call *_dl_sysinfo" \
: "=a" (resultvar) \
: "a" (__NR_##name) ASMARGS_##nr(args) : "memory", "cc")
# define INTERNAL_SYSCALL_NCS(name, err, nr, args...) \
({ \
register unsigned int resultvar; \
LOADREGS_##nr(args) \
asm volatile ( \
"call *_dl_sysinfo" \
: "=a" (resultvar) \
: "a" (name) ASMARGS_##nr(args) : "memory", "cc"); \
(int) resultvar; })
# endif
# else /* GCC 5 */
# ifdef PIC
# define INTERNAL_SYSCALL_MAIN_INLINE(name, err, nr, args...) \
EXTRAVAR_##nr \
asm volatile ( \
LOADARGS_##nr \
"movl %1, %%eax\n\t" \
"call *%%gs:%P2\n\t" \
RESTOREARGS_##nr \
: "=a" (resultvar) \
: "i" (__NR_##name), "i" (offsetof (tcbhead_t, sysinfo)) \
ASMFMT_##nr(args) : "memory", "cc")
# define INTERNAL_SYSCALL_NCS(name, err, nr, args...) \
({ \
register unsigned int resultvar; \
EXTRAVAR_##nr \
asm volatile ( \
LOADARGS_##nr \
"call *%%gs:%P2\n\t" \
RESTOREARGS_##nr \
: "=a" (resultvar) \
: "0" (name), "i" (offsetof (tcbhead_t, sysinfo)) \
ASMFMT_##nr(args) : "memory", "cc"); \
(int) resultvar; })
# else
# define INTERNAL_SYSCALL_MAIN_INLINE(name, err, nr, args...) \
EXTRAVAR_##nr \
asm volatile ( \
LOADARGS_##nr \
"movl %1, %%eax\n\t" \
"call *_dl_sysinfo\n\t" \
RESTOREARGS_##nr \
: "=a" (resultvar) \
: "i" (__NR_##name) ASMFMT_##nr(args) : "memory", "cc")
# define INTERNAL_SYSCALL_NCS(name, err, nr, args...) \
({ \
register unsigned int resultvar; \
EXTRAVAR_##nr \
asm volatile ( \
LOADARGS_##nr \
"call *_dl_sysinfo\n\t" \
RESTOREARGS_##nr \
: "=a" (resultvar) \
: "0" (name) ASMFMT_##nr(args) : "memory", "cc"); \
(int) resultvar; })
# endif
# endif /* GCC 5 */
#else
# ifdef OPTIMIZE_FOR_GCC_5
# define INTERNAL_SYSCALL_MAIN_INLINE(name, err, nr, args...) \
LOADREGS_##nr(args) \
asm volatile ( \
"int $0x80" \
: "=a" (resultvar) \
: "a" (__NR_##name) ASMARGS_##nr(args) : "memory", "cc")
# define INTERNAL_SYSCALL_NCS(name, err, nr, args...) \
({ \
register unsigned int resultvar; \
LOADREGS_##nr(args) \
asm volatile ( \
"int $0x80" \
: "=a" (resultvar) \
: "a" (name) ASMARGS_##nr(args) : "memory", "cc"); \
(int) resultvar; })
# else /* GCC 5 */
# define INTERNAL_SYSCALL_MAIN_INLINE(name, err, nr, args...) \
EXTRAVAR_##nr \
asm volatile ( \
LOADARGS_##nr \
"movl %1, %%eax\n\t" \
"int $0x80\n\t" \
RESTOREARGS_##nr \
: "=a" (resultvar) \
: "i" (__NR_##name) ASMFMT_##nr(args) : "memory", "cc")
# define INTERNAL_SYSCALL_NCS(name, err, nr, args...) \
({ \
register unsigned int resultvar; \
EXTRAVAR_##nr \
asm volatile ( \
LOADARGS_##nr \
"int $0x80\n\t" \
RESTOREARGS_##nr \
: "=a" (resultvar) \
: "0" (name) ASMFMT_##nr(args) : "memory", "cc"); \
(int) resultvar; })
# endif /* GCC 5 */
#endif
可以看到这里边有很多对此宏的定义
emmmmm我还没有搞明白应该是使用哪一个。。。
所以先写到这里
但是可以确定的是,宏定义的汇编代码无非都是进入内核的软件中断指令,有利用sysenter去进入内核的,有利用系统调用号int $0x80进入内核的,这两者的区别在于cpu使用的资源不尽相同,int $0x80是通过中断门进行内核陷入,将在下一篇博客进行解释(开新坑)
这篇博客仅仅将应用层的socket函数的宏定义翻译了一遍,终于找到了他的汇编系统调用部分
可以知道socket的应用层接口没有做任何实质性的任务,仅仅将系统调用进行了封装
to be continue