在阅读和学习开源操作系统时,我偶然发现了一种在汇编中调用“方法”的极其复杂的方式。它使用 'ret' 指令调用库方法来执行此操作:
push rbp ; rsp[1] = rbp
mov rbp, .continue ; save return label to rbp
xchg rbp, QWORD [rsp] ; restore rbp and set rsp[1] to return label
push rbp ; rsp[0] = rbp
mov rbp, 0x0000700000000000 + LIB_PTR_TABLE.funcOffset ; rbp = pointer to func pointer
mov rbp, QWORD [rbp] ; rbp = func pointer
xchg rbp, QWORD [rsp] ; restore rbp and set rsp[0] to func pointer
; "call" library by "returning" to the address we just planted
ret
.continue:
我添加评论是为了自己理解它,似乎我是对的或足够接近,因为我所做的所有实验都成功了。但后来我尝试这样做,这也很有效:
mov rax, 0x0000700000000000 + LIB_PTR_TABLE.funcOffset ; rax = ptr to func ptr
mov rax, QWORD [rax] ; rax = func ptr
call rax ; actually call the library function in a normal fashion
查看指令的数量以及 CPU 在这两种情况下实际必须做的事情,人们会假设,如果一种更快,那将是“调用”变体。但是由于使用了“ret”变体,并且首先需要大量知识,所以第一个变体有什么优势?(或者是吗?)