我在学组装。
按照我学习任何一门新语言的通常步骤,我已经学会了使用汇编语言进行网络连接。
很遗憾,这并不顺利,因为我在第0步几乎失败了,这将获得一个可以开始通信的套接字。
汇编代码应大致等于以下C代码:
#include <stdio.h>
#include <sys/socket.h>
int main(){
int sock;
sock = socket(AF_INET, SOCK_STREAM, 0);
}
字符串
(Let我们忽略它现在没有关闭套接字的事实。)
这就是我到目前为止所做的:
- 已检查the manual。这意味着我需要做一个
socketcall()
,这一切都很好。问题开始于它需要一个int
来描述它应该进行哪种socketcall。调用manpage也没有多大帮助,因为它只描述了:
在一些体系结构上-例如x86-64和ARM-没有socketcall()系统调用;相反,socket(2)、accept(2)、bind(2)等实际上是作为单独系统调用实现的。
- 然而,在原始的系统调用列表中没有这样的调用--据我所知,
socket()
、accept()
、bind()
、listen()
等。是来自libnet
的调用,而不是来自内核的。这让我非常困惑,所以我决定编译上面的C
代码并用strace
检查它。结果如下:
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
型
- 虽然这并没有让我更了解
socket()
**是什么,但它确实解释了它的参数。对于女巫,我似乎没有找到适当的文件(再次)。我以为PF_INET
,SOCK_STREAM
,IPPROTO_IP
会在<sys/socket.h>
中定义,但是我的grep
-ing似乎没有找到任何用处。所以我决定使用gdb
和disass main
来查找值。这给出了以下输出:
Dump of assembler code for function main:
0x00000000004004FD <+0>: push rbp
0x00000000004004FE <+1>: mov rbp,rsp
0x0000000000400501 <+4>: sub rsp,0x10
0x0000000000400505 <+8>: mov edx,0x0
0x000000000040050A <+13>: mov esi,0x1
0x000000000040050F <+18>: mov edi,0x2
0x0000000000400514 <+23>: call 0x400400 <socket@plt>
0x0000000000400519 <+28>: mov DWORD PTR [rbp-0x4],eax
0x000000000040051C <+31>: leave
0x000000000040051D <+32>: ret
End of assembler dump.
型
- 根据我的经验,这意味着
socket()
从EDX
(PF_INET
)、ESI
(SOCK_STREAM
)和EDI
(IPPROTO_IP
)获取参数。这对于系统调用来说是奇怪的(因为Linux系统调用的约定是使用EAX
/RAX
作为调用号,其他寄存器按升序存储参数,例如。RBX
、RCX
、RDX
...)。这是CALL
-艾德而不是INT 0x80
'd的事实也意味着这实际上不是一个系统调用,而是从一个共享对象调用的东西。或者别的什么 - 但话说回来。在寄存器中传递参数对于
CALL
-ed来说是非常奇怪的。据我所知,通常情况下,被调用的东西的参数应该是PUSH
-ed到堆栈上,因为编译器不知道他们会尝试使用什么寄存器。 - 当使用
ldd
检查生成的二进制文件时,这种行为变得更加奇怪:
linux-vdso.so.1 (0x00007fff4a7fc000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f56b0c61000)
/lib64/ld-linux-x86-64.so.2 (0x00007f56b1037000)
型
- 似乎没有任何网络库的链接。
这就是我的点子用光的地方。
所以我要求如下:
- 描述
x86-64
linux内核的实际系统调用及其相关编号的文档。(最好作为C
的头文件。) - 定义
PF_INET
,SOCK_STREAM
,IPPROTO_IP
的头文件,因为它真的让我很困扰,我无法在自己的系统上找到它们。 - 也许是
x86-64
Linux上汇编中的网络教程。(对于x86-32
,很容易找到素材,但由于某种原因,我没有找到64位的素材。
3条答案
按热度按时间kh212irz1#
64位调用约定确实使用寄存器来传递参数,无论是在用户空间还是在系统调用中。如您所见,用户空间约定为
rdi
、rsi
、rdx
、rcx
、r8
、r9
。对于系统调用,使用r10
而不是rcx
,后者被syscall指令破坏。请参阅维基百科或ABI文档了解更多详情。各种常量的定义都隐藏在头文件中,但是如果您安装了必要的开发包,则可以通过文件系统搜索轻松找到这些头文件。您应该查看
/usr/include/x86_64-linux-gnu/bits/socket.h
和/usr/include/linux/in.h
。至于系统调用列表,对google one来说是微不足道的,比如this。当然,你也可以随时查看内核源代码。
f45qwnt82#
socket.asm
字符串
more docs...
b91juud33#
这是x86系统。如果要用于x86_64系统,请将x86寄存器更改为x86_64。例如将'eax'改为'rax'或将'esp'改为'rsp'。并更改eax(rax)中的syscall值,参见https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md
字符串