组装中的“对齐堆栈”是什么意思?

如何解决组装中的“对齐堆栈”是什么意思?

ASMx64中的堆栈对齐方式如何工作?什么时候需要在调用函数之前对齐堆栈,以及需要减去多少?

我不明白它的目的是什么。我知道还有其他相关帖子,但对我来说还不够清楚。例如:

extern foo
global bar

section .text
bar:
  ;some code...
  sub  rsp,8     ; Why 8 (I saw this on some posts) ? Can it be another value ? Why do we need to substract?
  call foo        ; Do we need to align stack everytime we call a function?
  add  rsp,8
  ;some code...
  ret

解决方法

寻址通常是基于字节的。唯一地址指向一个字节(可以是一个字或双字等的第一个字节,但引用该地址)。

在任何编号系统中,最低有效位的值均以0为底数(数字1)。幂1的下一个最小基数,幂2的下一个最小基数。十进制是1列,十列和百列。用二进制1、2、4表示...对齐方式意味着可以被整除,这也意味着最低有效位是零。

您总是在字节边界上“对齐”,但二进制的16位边界表示最低有效位为零,32位对齐为两个零,依此类推。

0x1234在16位和32位边界上对齐,但不在64位上对齐
0x1235未对齐(字节对齐确实不是问题)
在16位边界上对齐的0x1236
0x1230四个零,所以16、32、64、128位不是字节。 2,4,8,16字节。

为什么出于性能原因,所有存储器和数据总线都具有固定的宽度,一旦实现,您就无法在逻辑中神奇地添加或移除导线,存在物理限制,您可以选择不使用全部它们是设计的一部分,但您不能添加任何内容。

因此,虽然x86总线更宽,但可以说您拥有32位宽的数据总线和32位宽的内存(请考虑使用高速缓存,但也可以使用dram,但一般来说我们不直接访问dram)。

如果我想将16位0xAABB保存到小端机中的地址0x1001,则0x1001将获得0xBB,0x1002将获得0xAA。如果我在其远端有一个32位数据总线和一个32位内存,那么如果我为此设计了总线,则可以通过将0xXXAABBXX写入地址0x1000并使用字节通道掩码0b0110告诉内存来移动这16位控制器使用与基于BYTE的地址0x1000关联的32位内存,并且总线上的字节通道掩码告诉控制器仅保存中间两个字节,外部两个无关紧要。

通常,内存是固定宽度的,因此所有事务都必须为全宽度,它将读取32位,用0xAABB修改中间的16位,然后将32位写回。这当然是低效的。更糟糕的是将0xAABB写入0x1003,这将是两次总线事务,一次总线传输用于地址0x1000的0xBBXXXXXX,另一次总线传输用于地址0x1004的0xXXXXXXAA。在总线上以及在内存上的读-修改-写操作上,这都是很多额外的周期。

现在,堆栈对齐规则不会阻止写入时进行读取-修改-写入。对于发生较大传输的情况,可能会有性能提高的机会,例如,如果总线为32位并且内存,并且您对地址0x1000进行了64位传输,那么根据总线设计,这看起来像是一次传输,但长度为两个。发生总线握手,然后两个背靠背的时钟移动数据,而不是握手和一个较小宽度的数据总线。因此,如果内存为32位宽,则可以在其中获得收益,而这是两次写入,而无需对缓存中的sram进行读-修改-写操作。很干净,要避免读取-修改-写入。

现在,随着事情的发展以及硬件和工具要求堆栈对齐,现在要这样做一段时间。

根据指令集,显然您在这里询问的是x86,但是作为程序员,您有时可以选择说将一个字节压入堆栈,然后对其进行调整以使其对齐。或者,如果您要为局部变量腾出空间,则取决于指令集(如果堆栈指针具有足够的通用性,可以对它进行数学运算),则可以简单地减去,因此sub sp,#8与推入两个相同将32位项目添加到堆栈中只是为了为两个32位项目腾出空间。

如果规则是说32位对齐并且您压入一个字节,则需要将堆栈指针调整3,以使堆栈指针中的总更改为4字节(32位)的倍数。

您怎么知道您只需简单地计算一下。如果它是16字节对齐并且按4,则需要再按12或将堆栈指针再调整12。

这里的关键是,如果每个人都同意保持堆栈对齐,那么您实际上不必查看堆栈指针的低位,您只需在调用其他内容之前就跟踪要推送和弹出的内容即可。

如果堆栈与中断处理程序共享(实际上不是在当前运行操作系统的x86中共享,但是在通用处理器的许多其他用例中仍然可行并且可能),我没有看到此规则适用于您将会看到编译器执行小于对齐大小的推入或弹出操作,然后用其他推入或弹出操作或减法或加法进行调整。如果两者之间发生中断,则处理程序将看到未对齐的堆栈。

某些架构将在未对齐的访问时发生故障,这是保持堆栈对齐的另一个原因。

如果您的代码没有弄乱堆栈,那么您就不需要弄乱堆栈(指针)。仅当您通过在堆栈上分配空间(堆栈指针上的推数或数学运算)在代码中使用堆栈时,才需要关心,并且需要知道与该代码链接并遵循的编译器惯例那。如果这全部是汇编语言,而没有编译器,那么您可以自己决定约定,基本上可以在处理器本身的限制范围内做任何您想做的事情。

从您的标题问题开始,它根本与汇编无关,也与机器代码无关。它与您的代码及其作用有关。汇编语言只是一种语言,您可以在其中表达要调整堆栈指针的数量,而指令并不关心或不知道任何此类事情,它会使用提供的常量并将其用于寄存器。汇编是少数几个(即使不是唯一一种)允许您在堆栈指针寄存器上进行数学运算的程序,因此存在这种连接。但是对齐方式和组装方式无关。

,

什么时候需要在函数调用和....?之前对齐堆栈?

当调用的函数需要对齐的堆栈时,需要对齐堆栈。

以其他语言(例如C)编写的函数以及以汇编形式编写但设计为从其他语言调用的函数将遵守某种调用约定(其中不仅包括堆栈对齐,还包括-如何传递参数,参数在哪里,诸如“红色区域”之类的东西);对于64位80x86,2种通用调用约定期望堆栈对齐到16字节边界。

在“纯程序集”项目中,您正在调用为程序集调用程序在程序集中编写的函数;程序员可以自由地做自己喜欢的事情(例如,对性能最有利的事情),而不必担心其他会降低性能的语言的限制/约束(调用约定)。在这种情况下,您可能根本不需要对齐堆栈(但是,如果您要处理的是AVX-512,则函数可能希望将堆栈对齐为64字节,而如果您要处理的是AVX2,则函数可能希望将堆栈对齐。到32个字节,以及..)。

...以及您需要减去多少?

如果您不知道堆栈是否足够对齐;然后通常使用AND来对齐堆栈(例如,将and rsp,0xFFFFFFFFFFFFFFF0对齐到16字节边界)。这也意味着您需要将旧的堆栈指针存储在某个地方,以便可以还原它。这通常意味着多出4条指令(对齐前先push rbpmov rbp,rsp,然后再进行mov rsp,rbppop rbp的恢复)。

但是;如果您知道调用方为您对齐了堆栈(并且您调用的函数需要相同或更少的对齐方式),则可以通过跟踪已压入堆栈的数量来计算要减去的额外费用。例如,如果调用者将堆栈对齐为32字节,并且在堆栈上压入了四个64位(8字节)值,则call指令将压入另一个64位值(返回地址);那么总共是5 * 8 = 40字节;因此,如果您要与16个字节对齐,则需要再减去8个字节,以使总数达到48个字节;如果要与32个字节对​​齐,则应减去另外24个字节,以使总数达到64个字节。这也避免了保存原始堆栈指针的需要(您可以添加以后减去的任何值),从而可以保存4条指令。

当然(对于“纯汇编”),您将查看所调用的所有函数的要求,并选择最坏的情况,并将堆栈与该函数对齐一次(并避免将堆栈不同地对齐,每次对齐一次)您调用的函数);并且您可能会说“我的函数需要将堆栈对齐到我调用的函数的最坏情况”,以确保您可以计算出要减去的量(并避免使用更昂贵的“ AND with ...”方法) 。但是(对于“纯粹的组装”)这会给您的呼叫者带来负担(谁可能会给他们的呼叫者造成负担,谁可能会..),这样会使性能变差(呼叫链中的所有祖先都必须这样做)额外的工作,这样您就可以减少工作量)。换一种说法;用于“纯组装”;要达到最高的效率/性能需要花费很多工作(确定是否/何时应该对齐堆栈多少,并在必要时最小化确保堆栈对齐的开销)。

这也是为什么编译器将对齐方式放在其调用约定中的部分原因-必需的“大多数情况下不太可能是最优的”标准对齐方式使编译器更容易。

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

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)> insert overwrite table dwd_trade_cart_add_inc > select data.id, > data.user_id, > data.course_id, > date_format(
错误1 hive (edu)> insert into huanhuan values(1,'haoge'); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive> show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 <configuration> <property> <name>yarn.nodemanager.res