热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

如何在AssemblyWorks中调用带有大量args的函数

如何解决《如何在AssemblyWorks中调用带有大量args的函数》经验,为你挑选了1个好方法。

这是一组示例函数,第一个是20个args,第二个是2:

int a(int n1, int n2, int n3, int n4, int n5, int n6, int n7, int n8, int n9, int n10, int n11, int n12, int n13, int n14, int n15, int n16, int n17, int n18, int n19, int n20) {
    return n1 * n2 * n3 * n4 * n5 * n6 * n7 * n8 * n9 * n10 * n11 * n12 * n13 * n14 * n15 * n16 * n17 * n18 * n19 * n20;
}

int b(int n1, int n2) {
    return a(n1, n2, n1, n2, n1, n1, n2, n1, n2, n1, n1, n2, n1, n2, n1, n1, n2, n1, n2, n1)
      + a(n1, n2, n1, n2, n1, n1, n2, n1, n2, n1, n1, n2, n1, n2, n1, n1, n2, n1, n2, n1)
      + a(n1, n2, n1, n2, n1, n1, n2, n1, n2, n1, n1, n2, n1, n2, n1, n1, n2, n1, n2, n1);
}

它被编译到这个程序集:

a(int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int):
  push    rbp
  mov     rbp, rsp
  mov     DWORD PTR [rbp-4], edi
  mov     DWORD PTR [rbp-8], esi
  mov     DWORD PTR [rbp-12], edx
  mov     DWORD PTR [rbp-16], ecx
  mov     DWORD PTR [rbp-20], r8d
  mov     DWORD PTR [rbp-24], r9d
  mov     eax, DWORD PTR [rbp-4]
  imul    eax, DWORD PTR [rbp-8]
  imul    eax, DWORD PTR [rbp-12]
  imul    eax, DWORD PTR [rbp-16]
  imul    eax, DWORD PTR [rbp-20]
  imul    eax, DWORD PTR [rbp-24]
  imul    eax, DWORD PTR [rbp+16]
  imul    eax, DWORD PTR [rbp+24]
  imul    eax, DWORD PTR [rbp+32]
  imul    eax, DWORD PTR [rbp+40]
  imul    eax, DWORD PTR [rbp+48]
  imul    eax, DWORD PTR [rbp+56]
  imul    eax, DWORD PTR [rbp+64]
  imul    eax, DWORD PTR [rbp+72]
  imul    eax, DWORD PTR [rbp+80]
  imul    eax, DWORD PTR [rbp+88]
  imul    eax, DWORD PTR [rbp+96]
  imul    eax, DWORD PTR [rbp+104]
  imul    eax, DWORD PTR [rbp+112]
  imul    eax, DWORD PTR [rbp+120]
  pop     rbp
  ret
b(int, int):
  push    rbp
  mov     rbp, rsp
  push    rbx
  sub     rsp, 8
  mov     DWORD PTR [rbp-12], edi
  mov     DWORD PTR [rbp-16], esi
  mov     r9d, DWORD PTR [rbp-12]
  mov     r8d, DWORD PTR [rbp-12]
  mov     ecx, DWORD PTR [rbp-16]
  mov     edx, DWORD PTR [rbp-12]
  mov     esi, DWORD PTR [rbp-16]
  mov     eax, DWORD PTR [rbp-12]
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, eax
  call    a(int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int)
  add     rsp, 112
  mov     ebx, eax
  mov     r9d, DWORD PTR [rbp-12]
  mov     r8d, DWORD PTR [rbp-12]
  mov     ecx, DWORD PTR [rbp-16]
  mov     edx, DWORD PTR [rbp-12]
  mov     esi, DWORD PTR [rbp-16]
  mov     eax, DWORD PTR [rbp-12]
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, eax
  call    a(int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int)
  add     rsp, 112
  add     ebx, eax
  mov     r9d, DWORD PTR [rbp-12]
  mov     r8d, DWORD PTR [rbp-12]
  mov     ecx, DWORD PTR [rbp-16]
  mov     edx, DWORD PTR [rbp-12]
  mov     esi, DWORD PTR [rbp-16]
  mov     eax, DWORD PTR [rbp-12]
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, eax
  call    a(int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int)
  add     rsp, 112
  add     eax, ebx
  mov     rbx, QWORD PTR [rbp-8]
  leave
  ret

我有几个问题.首先,我注意到它似乎随着数量的增加而切换它如何处理args:

push    rbp
mov     rbp, rsp
mov     DWORD PTR [rbp-4], edi
...
imul    eax, DWORD PTR [rbp-8]
...

想知道那里发生了什么,为什么会这样做.它似乎对待第一个arg push,然后接下来的8个左右mov,然后剩下的imul仅与eax寄存器有关.想知道你可以拥有多少个args是否有限制.

我想知道的第二件事是以下内容.比如说a(),该函数b()调用了一些"系统函数"或其他一些外部库调用.想知道它是如何解开args的.嗯,我想,没关系,我假设C编译器将编译成汇编/机器代码所有链接的外部库.所以,我猜是的.

最后一件事是,如果系统调用都有一定数量的参数,类似于x86中最大操作数为3的方式.或者系统调用可能有任意数量的参数.看来他们想限制它的性能,所以他们只使用那些早期指示像pushmov的,而不是imul.

感谢您的帮助,只需查看在汇编时调用函数时如何处理参数的说明,尤其是在存在大量参数时.



1> 小智..:

您需要了解有关堆栈帧和应用程序二进制接口(ABI或调用约定)的更多信息.ABI定义调用者将参数传递给被调用者的方式,寄存器是易失性的,以及如何清理堆栈.

存在许多ABI,因为只要呼叫者和被呼叫者同意,任何人都可以设计他们自己的ABI.但是,只有少数ABI被广泛使用.在Windows上,大多数32位程序使用stdcall,cdecl,Microsoft的fastcall或Borland的fastcall,而64位程序大多使用Microsoft x64调用约定.在Unix 64位程序上总是使用System V AMD64 ABI,它也是编译器使用的.

让我们看一下您的代码,并附上评论:

push    rbp                     ; save the old stack frame
mov     rbp, rsp                ; establish new stack frame
mov     DWORD PTR [rbp-4], edi  ; save the first six arguments
mov     DWORD PTR [rbp-8], esi
mov     DWORD PTR [rbp-12], edx
mov     DWORD PTR [rbp-16], ecx
mov     DWORD PTR [rbp-20], r8d
mov     DWORD PTR [rbp-24], r9d
mov     eax, DWORD PTR [rbp-4]  ; load n1
imul    eax, DWORD PTR [rbp-8]  ; eax = eax * n2
imul    eax, DWORD PTR [rbp-12] ; eax = eax * n3
imul    eax, DWORD PTR [rbp-16]
imul    eax, DWORD PTR [rbp-20]
imul    eax, DWORD PTR [rbp-24]
imul    eax, DWORD PTR [rbp+16] ; eax = eax * n7
imul    eax, DWORD PTR [rbp+24] ; eax = eax * n8
imul    eax, DWORD PTR [rbp+32]
imul    eax, DWORD PTR [rbp+40]
imul    eax, DWORD PTR [rbp+48]
imul    eax, DWORD PTR [rbp+56]
imul    eax, DWORD PTR [rbp+64]
imul    eax, DWORD PTR [rbp+72]
imul    eax, DWORD PTR [rbp+80]
imul    eax, DWORD PTR [rbp+88]
imul    eax, DWORD PTR [rbp+96]
imul    eax, DWORD PTR [rbp+104]
imul    eax, DWORD PTR [rbp+112]
imul    eax, DWORD PTR [rbp+120]
pop     rbp                      ; restore old stack frame
ret                              ; exit

注意:前两行与参数无关; 它们是创建一个堆栈框架,以便您可以轻松访问局部变量和参数.如果没有堆栈帧你仍然可以用[RSP +*]访问它们,但偏移根据需要任意调整PUSHPOP您使用.

接下来是将参数存储到局部变量的指令.寄存器经常被更改,并且需要存储寄存器中传递的参数,以防您以后需要使用它们.但是,在这种情况下,没有必要.所以优化的代码可以

push    rbp                     ; save the old stack frame
mov     rbp, rsp                ; establish new stack frame
mov     eax, edi                ; eax = n1
imul    eax, esi                ; eax = eax * n2
imul    eax, edx                ; eax = eax * n3
imul    eax, ecx                ; eax = eax * n4
imul    eax, e8d                ; eax = eax * n5
imul    eax, e9d                ; eax = eax * n6
imul    eax, DWORD PTR [rbp+16] ; eax = eax * n7
imul    eax, DWORD PTR [rbp+24] ; eax = eax * n8
imul    eax, DWORD PTR [rbp+32] ; eax = eax * n9
imul    eax, DWORD PTR [rbp+40]
imul    eax, DWORD PTR [rbp+48]
imul    eax, DWORD PTR [rbp+56]
imul    eax, DWORD PTR [rbp+64]
imul    eax, DWORD PTR [rbp+72]
imul    eax, DWORD PTR [rbp+80]
imul    eax, DWORD PTR [rbp+88]
imul    eax, DWORD PTR [rbp+96]
imul    eax, DWORD PTR [rbp+104]
imul    eax, DWORD PTR [rbp+112]
imul    eax, DWORD PTR [rbp+120]
pop     rbp                      ; restore old stack frame
ret                              ; exit

从上面的例子,你应该能够猜出的第一个参数是在传递edi(或rdi,di,dil取决于大小),第二个是在esi,然后edx,ecx,r8dr9d(整数只,浮标在矢量寄存器中传递) .当你有超过6个参数时,另一个被推到堆栈并且可以使用[rbp + 16],[rbp + 24],...来访问([rbp + 8]是旧的rbp; [rbp]正在返回地址).

对于来电者

mov     r9d, DWORD PTR [rbp-12]  ; r9d = n6
mov     r8d, DWORD PTR [rbp-12]  ; r8d = n5
mov     ecx, DWORD PTR [rbp-16]  ; ecx = n4
mov     edx, DWORD PTR [rbp-12]  ; edx = n3
mov     esi, DWORD PTR [rbp-16]  ; esi = n2
mov     eax, DWORD PTR [rbp-12]  ; eax = n1  ; will assign to edi
mov     edi, DWORD PTR [rbp-12]  ; push n20
push    rdi
mov     edi, DWORD PTR [rbp-16]  ; push n19
push    rdi
mov     edi, DWORD PTR [rbp-12]
push    rdi
mov     edi, DWORD PTR [rbp-16]
push    rdi
mov     edi, DWORD PTR [rbp-12]
push    rdi
mov     edi, DWORD PTR [rbp-12]
push    rdi
mov     edi, DWORD PTR [rbp-16]
push    rdi
mov     edi, DWORD PTR [rbp-12]
push    rdi
mov     edi, DWORD PTR [rbp-16]
push    rdi
mov     edi, DWORD PTR [rbp-12]
push    rdi
mov     edi, DWORD PTR [rbp-12]
push    rdi
mov     edi, DWORD PTR [rbp-16]
push    rdi
mov     edi, DWORD PTR [rbp-12]
push    rdi
mov     edi, DWORD PTR [rbp-16]  ; push n7
push    rdi
mov     edi, eax                 ; edi = n1
call    a()                      ; call the function
add     rsp, 112                 ; clean up the stack, 14 * 8 = 112 bytes
mov     ebx, eax                 ; result is in eax

一个更直接的版本是

mov     r9d, DWORD PTR [rbp-12]  ; r9d = n6
mov     r8d, DWORD PTR [rbp-12]  ; r8d = n5
mov     ecx, DWORD PTR [rbp-16]  ; ecx = n4
mov     edx, DWORD PTR [rbp-12]  ; edx = n3
mov     esi, DWORD PTR [rbp-16]  ; esi = n2
mov     edi, DWORD PTR [rbp-12]  ; edi = n1
push    [rbp-12]                 ; push n20
push    [rbp-16]                 ; push n19
push    [rbp-12]
push    [rbp-16]
push    [rbp-12]
push    [rbp-16]
push    [rbp-12]
push    [rbp-16]
push    [rbp-12]
push    [rbp-16]
push    [rbp-12]
push    [rbp-16]
push    [rbp-12]                 ; push n8
push    [rbp-16]                 ; push n7
call    a()                      ; call the function
add     rsp, 112                 ; clean up the stack, 14 * 8 = 112 bytes
mov     ebx, eax                 ; result is in eax

请注意,参数按相反顺序推送.

由于您可以在调用函数之前将任何数字推送到堆栈(在它溢出之前),因此对参数的数量没有限制.


推荐阅读
  • 本文介绍了PHP常量的定义和使用方法,包括常量的命名规则、大小写敏感性、全局范围和标量数据的限制。同时还提到了应尽量避免定义resource常量,并给出了使用define()函数定义常量的示例。 ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 本文介绍了[从头学数学]中第101节关于比例的相关问题的研究和修炼过程。主要内容包括[机器小伟]和[工程师阿伟]一起研究比例的相关问题,并给出了一个求比例的函数scale的实现。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • WebSocket与Socket.io的理解
    WebSocketprotocol是HTML5一种新的协议。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
  • Redis底层数据结构之压缩列表的介绍及实现原理
    本文介绍了Redis底层数据结构之压缩列表的概念、实现原理以及使用场景。压缩列表是Redis为了节约内存而开发的一种顺序数据结构,由特殊编码的连续内存块组成。文章详细解释了压缩列表的构成和各个属性的含义,以及如何通过指针来计算表尾节点的地址。压缩列表适用于列表键和哈希键中只包含少量小整数值和短字符串的情况。通过使用压缩列表,可以有效减少内存占用,提升Redis的性能。 ... [详细]
  • mac php错误日志配置方法及错误级别修改
    本文介绍了在mac环境下配置php错误日志的方法,包括修改php.ini文件和httpd.conf文件的操作步骤。同时还介绍了如何修改错误级别,以及相应的错误级别参考链接。 ... [详细]
  • Linux下安装免费杀毒软件ClamAV及使用方法
    本文介绍了在Linux系统下安装免费杀毒软件ClamAV的方法,并提供了使用该软件更新病毒库和进行病毒扫描的指令参数。同时还提供了官方安装文档和下载地址。 ... [详细]
  • 本文概述了JNI的原理以及常用方法。JNI提供了一种Java字节码调用C/C++的解决方案,但引用类型不能直接在Native层使用,需要进行类型转化。多维数组(包括二维数组)都是引用类型,需要使用jobjectArray类型来存取其值。此外,由于Java支持函数重载,根据函数名无法找到对应的JNI函数,因此介绍了JNI函数签名信息的解决方案。 ... [详细]
  • 本文介绍了使用readlink命令获取文件的完整路径的简单方法,并提供了一个示例命令来打印文件的完整路径。共有28种解决方案可供选择。 ... [详细]
  • 本文介绍了在Linux中执行.sh脚本时出现/bin/sh^M: bad interpreter: No such file or directory异常的原因分析,并提供了两种解决方法:在Windows下进行编码格式转换,或在Linux中修改文件格式和执行权限。具体操作步骤也在摘要中给出。 ... [详细]
  • 正则表达式及其范例
    为什么80%的码农都做不了架构师?一、前言部分控制台输入的字符串,编译成java字符串之后才送进内存,比如控制台打\, ... [详细]
author-avatar
翻版的袁城
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有