微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

目的是在MUSL的x86_64 __syscall_cp_asm包装器中进行系统调用之前将传入的pthread地址保存在堆栈上?

如何解决目的是在MUSL的x86_64 __syscall_cp_asm包装器中进行系统调用之前将传入的pthread地址保存在堆栈上?

这是from musl's source代码

 1  __syscall_cp_asm:
 2  __cp_begin:
 3      mov (%rdi),%eax
 4      test %eax,%eax
 5      jnz __cp_cancel

 6      mov %rdi,%r11

 7      mov %rsi,%rax
 8      mov %rdx,%rdi
 9      mov %rcx,%rsi
10      mov %r8,%rdx
11      mov %r9,%r10
12      mov 8(%rsp),%r8
13      mov 16(%rsp),%r9

14      mov %r11,8(%rsp)

15      syscall
16  __cp_end:
17      ret
18  __cp_cancel:
19      jmp __cancel

我很好奇第6行和第14行的目的是什么(从链接的源重新编号)。

据我了解,代码的开头测试了作为第一个参数传递的指针的目标(第3–5行),然后第6行将指针移至 r11 ,然后第14行移动它传递到用于传递第7个参数的堆栈上的位置。

这似乎没有用。这些举动有什么作用吗?

解决方法

这是为了支持pthread取消点;信号处理程序可以稍后查看堆栈。

commit log for the commit that introduced this code解释说,在系统调用之前将指针存储在堆栈上的已知位置可以使“取消信号处理程序”确定“中断的代码是否处于可取消状态”。 (该代码的初始版本还保存了syscall指令的地址,但后来提交更改了该地址。)

第一个arg(asm函数存储在堆栈中)来自its C caller__syscall_cp_c,它传递了__syscall_cp_asm(&self->cancel,nr,u,v,w,x,y,z);,其中self来自{{1} }。


您是正确的,在x86-64 System V ABI之后,C调用者无法用另一个传入的arg覆盖调用者的堆栈arg。 (被调用方拥有其堆栈args;调用方必须假定它们已被覆盖,因此编译器生成的代码将永远不会读取该内存位置作为输出)。因此,我们需要寻找其他解释。


使用总共2条mov指令将输入的RDI复制到__pthread_self()之后 ,以读取内存位置,我认为这是必要的。我们不能将8(%rsp)延迟到加载之后,因为我们需要释放RDX来容纳R8,释放R8来容纳负载。您可以避免在使用R10加载另一个arg之前通过使用R10来触摸“额外”寄存器,但是它仍然至少需要2条指令。

或者可以优化arg顺序以在以后的arg中传递该指针,也许在最后一次传递调用号,并在最后一个寄存器arg中传递pthread指针(最小改组,但避免对该测试/分支进行双重取消引用)或第一个堆栈arg(无论如何都需要)。或者匹配mov %rdx,%rdi包装器的arg顺序,该包装器首先使用__syscall而没有pthread指针。

,

第7至14行中的代码按参数顺序将参数加载到syscall中。由于RDI在第8行加载,其值保存在R11中,因此可以在第14行将其写入参数8(在堆栈上)。

在手写的汇编代码中,通过将事情保持这样的组织状态,可以更容易理解和维护,而这超过了额外的移动指令的成本。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。