跳过正文
  1. Posts/

x86 calling conventions

·3 分钟

x86的调用约定主要说的是这几件事:

  • The order in which atomic (scalar) parameters, or individual parts of a complex parameter, are allocated
  • How parameters are passed (pushed on the stack, placed in registers, or a mix of both)
  • Which registers the called function must preserve for the caller (also known as: callee-saved registers or non-volatile registers)
  • How the task of preparing the stack for, and restoring after, a function call is divided between the caller and the callee

调用约定实际上并不唯一

我们比较关注gcc编译器下的cdecl(C declaration)

对于如下这段代码:

1    
2    int callee(int, int, int);
3    
4    int caller(void)
5    {
6    	return callee(1, 2, 3) + 5;
7    }

调用过程如下:

 1caller:
 2	; make new call frame
 3	; (some compilers may produce an 'enter' instruction instead)
 4	push    ebp       ; save old call frame
 5	mov     ebp, esp  ; initialize new call frame
 6	; push call arguments, in reverse
 7	; (some compilers may subtract the required space from the stack pointer,
 8	; then write each argument directly, see below.
 9	; The 'enter' instruction can also do something similar)
10	; sub esp, 12      : 'enter' instruction could do this for us
11	; mov [ebp-4], 3   : or mov [esp+8], 3
12	; mov [ebp-8], 2   : or mov [esp+4], 2
13	; mov [ebp-12], 1  : or mov [esp], 1
14	push    3
15	push    2
16	push    1
17	call    callee    ; call subroutine 'callee'
18	add     eax, 5    ; modify subroutine result
19	                  ; (eax is the return value of our callee,
20	                  ; so we don't have to move it into a local variable)
21	; restore old call frame
22	; (some compilers may produce a 'leave' instruction instead)
23	; add   esp, 12   ; remove arguments from frame, ebp - esp = 12.
24	                  ; compilers will usually produce the following instead,
25	                  ; which is just as fast, and, unlike the add instruction,
26	                  ; also works for variable length arguments
27	                  ; and variable length arrays allocated on the stack.
28	mov     esp, ebp  ; most calling conventions dictate ebp be callee-saved,
29	                  ; i.e. it's preserved after calling the callee.
30	                  ; it therefore still points to the start of our stack frame.
31	                  ; we do need to make sure
32	                  ; callee doesn't modify (or restores) ebp, though,
33	                  ; so we need to make sure
34	                  ; it uses a calling convention which does this
35	pop     ebp       ; restore old call frame
36	ret               ; return

在c语言中,函数的参数被以从右向左的顺序压入栈,也就是最后一个参数最先入栈。

这里是栈指的是调用栈(Call_stack)

调用栈的结构如下(注意此图的栈是从下往上增长的,这与通常情况并不相符,不过不影响此处的说明),这是在调用的DrawSquare函数中调用DrawLine函数时的情景

stack frame通常按照入栈顺序(写在前面的先入栈)由三部分组成(可能某部分为空):

  * 函数的参数值(以从右向左的顺序入栈)
  * caller的地址值,为的是调用函数之后能继续执行caller其余的代码。
  * 函数的局部变量

接下来我们看一下调用过程对寄存器的影响。这里暂且不提eax寄存器通常用来保存结果之类,主要想谈谈调用过程对sp和bp两个寄存器的影响。

sp是stack pointer,保存的是当前栈顶地址

bp是base pointer(就是stack frame中的frame pointer), 值为函数刚刚被调用时的栈顶位置。

bp这个寄存器的作用主要是比较方便,因为如果只有stack pointer,那么在函数里面,stack pointer也是可能变的,显然不如使用base pointer方便。

具体来说,在使用base pointer的情况下,函数的返回地址永远为ebp + 4,第一个参数的地址为ebp+8,第一个局部变量的地址为ebp-4

而且使用bp的情况下,回溯调用栈会变得非常方便。

At ebp is a pointer to ebp for the previous frame (this is why push ebp; mov ebp, esp is such a common way to start a function).  This effectively creates a linked list of base pointers.  This linked list makes it very easy to trace backwards up the stack.  For example if foo() calls bar() and bar() calls baz() and you’re debugging baz() you can easily find the parameters and local variables for foo() and bar().

为什么ebp指向的内容是上一个 stack frame中的ebp?我们看push ebp; mov ebp esp这两条指令。push ebp相当于先esp-=4,然后将ebp放到esp所指向的位置。接着mov ebp esp,相当于把当前的esp,也就是上一个ebp所在的位置,赋值给新的ebp.  所以。。这其实是个链表啊

参考资料:

x86 calling conventions

Stack_register

Call_stack#STACK-FRAME

What is exactly the base pointer and stack pointer? To what do they point?

All About EBP

相关文章

【施工完成】MIT 6.828 lab 1: C, Assembly, Tools and Bootstrapping

·40 分钟
花费了30+小时,终于搞定了orz # Part 1: PC Bootstrap # The PC’s Physical Address Space # 8086/8088时代 # 1+------------------+ <- 0x00100000 (1MB) 2| BIOS ROM | 3+------------------+ <- 0x000F0000 (960KB) 4| 16-bit devices, | 5| expansion ROMs | 6+------------------+ <- 0x000C0000 (768KB) 7| VGA Display | 8+------------------+ <- 0x000A0000 (640KB) 9| | 10| Low Memory | 11| | 12+------------------+ <- 0x00000000 由于8086/8088只有20跟地址线,因此物理内存空间就是2^20=1MB.地址空间从0x00000到0xFFFFF.其中从0x00000开始的640k空间被称为"low memory",是PC真正能使用的RAM。从 0xA0000 到 0xFFFFF 的384k的non-volatile memory被硬件保留,用作video display buffers和BIOS等。

边界的链码,归一化链码,差分玛,形状数

·4 分钟
复习一下数字图像处理。 # # # # 按照我自己的理解简单来说: # 原链码:按照任意起点走边界一周,方向按照上图对应的表示,得到的数字序列就是原链码。 # 归一化链码:为了解决原链码中起点不唯一而产生的序列不唯一的问题,规定,对于所有起点得到的原链码中,字典序最小的即为归一化链码(由于序列都是自然数,因此字典序最小也可以理解成,把该序列看成有前导0的自然数之后的数值之后的数值最小。 # 差分码:为了解决图形旋转之后,原链码和归一化链码都会发生变化,引入差分码。n 位原链码(或归一化链码,由于归一化链码只是一种特殊的原链码,之后不再单独强调)可以得到 n-1 位差分码。具体来说,对于原链码 a[i](i 属于 1..n),可以得到差分码 b[i],b[i] = ((a[i+1] - a[i]) + mod) % mod(i 属于 1..n-1),mod 根据实际有几个方向决定,通常为 4 或者 8。 # 形状数:需要强调的是,形状数也是一个序列,而不是一个数。其实形状数就是把差分码按照字典序排序之后,最小的序列。形状数的阶数是该序列的长度。 # Freeman链码(弗雷曼链码)是指用曲线起始点的坐标和边界点方向代码来描述曲线或边界的方法,常被用来在图像处理、计算机图形学、模式识别等领域中表示曲线和区域边界。它是一种边界的编码表示法,用边界方向作为编码依据,为简化边界的描述,一般描述的是边界点集。

软件体系结构复习笔记

·7 分钟
Cha1 1软件架构概念: 2 是系统的一个或多个结构,它们由软件组件,组件的外部可见属性以及组件之间的关系组成。 3 组件的外部可见属性是指其他组件对该组件所做的假设。 4软件架构的多个结构: 5 静态的角度: 6 模块结构 7 分析类结构 8 类结构 9 动态的角度: 10 进程结构 11 数据流 12 控制流 13 使用结构 14 调用结构 15 层次结构 16 部署的角度: 17 物理结构 18 19架构不止是功能需求的结果 20 21Ch2: 22需求包含三要素:功能,质量,限制条件 23质量属性:系统在其生命周期过程中所表现出来的各种特征 24质量属性的关系: 25 一个质量属性的获取对其他质量属性可能产生正面或者负面的影响。 26 任何质量属性都不可能在不考虑其他属性情况下单独获取。 27质量属性举例: 28 运行时可见属性:性能,可用性,安全性 29 维护时可见属性:可修改,可扩展,可移植 30 易用性: 31 可学习性 32 可记忆性 33 错误避免 34 错误处理 35 满意度 36 37质量场景创建的参与人员: 38 最终用户 39 系统管理员 40 维护人员 41 客户 42 开发组织 43构架本身的质量属性: 44 一致性 45 正确性和完整性 46 可构建性 47生成质量属性场景的目的和意义: 48 帮助构架师生成有意义的质量属性需求 49 使质量属性需求的描述规范化 50 某一场景是一类场景的代表,系统将以完全相同的方式做出反应。 51构架的商业属性(限制): 52 上市时间 53 成本和收益 54 预期系统生命周期长短 55 目标市场 56 推出计划 57 与老系统的集成 58 59第三章: 60软件架构样式的种类: 61 以数据为中心 62 数据流 63 虚拟机 64 调用-返回 65 独立组件 66 C/S 67构架的异质性: 68 局部异质 69 层次异质 70 并行异质 71 72ISO/OSI七层参考模型: 73 应用层 74 表示层 75 会话层 76 传输层 77 网络层 78 数据链路层 79 物理层 80 81软件框架: 82 提取特定领域软件的共性部分形成的体系结构。 83框架和架构的关系: 84 框架不是构架。 85 构架确定了系统整体结构、层次划分、不同部分之间的协作等设计老驴。 86 框架比构架更具体,更偏重于技术。 87 一个框架对应一个架构,一个架构可以有多个框架。 88 89第四章: 90架构战术:影响质量属性的设计决策。 91架构策略:架构中所采用的战术的集合。 92可用性的战术: 93 错误检测的战术: 94 回声 95 心跳 96 异常 97 错误恢复的战术: 98 表决 99 主动冗余 100 被动冗余 101 备件 102 状态再同步 103 检查点/回滚 104 错误预防的战术: 105 进程监视器 106 从服务中删除 107 事物 108可修改性的战术: 109 局部化修改的战术: 110 维持语义一致性 111 预期期望的变更 112 泛化模块 113 限制可能的选择 114 防止连锁反应的战术: 115 信息隐藏 116 维持现有的接口 117 添加结构 118 添加适配器 119 提供一个占位程序 120 推迟绑定时间的战术: 121 运行时注册 122 配置文件 123 多态 124 组件更换 125 遵守已定义的协议 126实施性能的战术: 127 影响响应时间的两个基本因素: 128 资源消耗 129 阻塞时间: 130 资源争用 131 资源的可用性 132 对其他计算的依赖性 133 控制对资源需求的战术: 134 减少处理一个事件所需要的资源: 135 提高计算效率 136 减少计算开销 137 减少需要同时处理: 138 管理事件率 139 控制采样频率 140 控制系统的使用: 141 限制执行时间 142 限制队列的大小 143 资源管理的战术: 144 引入并发 145 维持数据或计算的多个副本 146 增加可用资源 147 资源仲裁常见的调度策略: 148 先进/先出 149 固定优先级:语义重要性;时限时间单调;速率单调 150 动态优先级调度:轮转;时限时间最早优先 151 静态调度 152实施安全性的战术: 153 用于抵抗攻击的战术: 154 对用户进行身份验证 155 对用户进行授权 156 维护数据的机密性 157 维护完整性 158 限制暴露的信息 159 限制访问 160 检测攻击的战术: 161 从攻击中恢复的战术: 162 回复状态 163 识别攻击者 164易用性的战术: 165 运行时战术: 166 维持任务的一个模型 167 维护用户的一个模型 168 维护系统的一个模型 169 设计时战术: 170软件架构样式与战术的关系: 171 软件架构样式是从战略层面解决质量问题,战术是从具体部署上给猪解决质量问题的局部策略。 172 173 174第五章:设计构架 175基于构架的开发步骤: 176 为软件系统创建一个商业案例 177 弄清系统需求 178 构建构架 179 正确表述此构架,并与有关各方进行交流 180 对此构架进行分析和评价 181 实现基于构架的系统并保证与构架相一致 182 系统维护时,构架文档应同步维护 183构架驱动的因素: 184 功能 185 质量 186 部分限制条件(限制条件的某个子集) 187 188良好架构的评判原则(判断题常考): 189 设计构架过程的建议: 190 架的设计应该由一门设计师来完成 191 设计师应该全面掌握对系统的技术需求,以及对各项定性指标的优先级清单。 192 构架的文档完备,并蚕蛹所有人员认可的文档形式。 193 构架设计文档应让各风险承担者积极评估。 194 通过对构架分析,得出明确的定性与定量指标。 195 构架设计应该有助于具体实现。 196 允许构架带来一定的资源争用,并给出可行的解决方案。 197 关于构架的结构的建议: 198 构架由定义良好的模块组成,各个模块的功能划分应该基于信息隐藏。 199 模块的划分应体现出相互独立的原则。 200 把计算机基础结构的特性封装在一定的模块 201 构架尽量不依赖某个特定版本的商品产品或工具。 202 产生数据的功能和使用数据的功能应分属于不同的模块。 203 对并发系统,构架应充分考虑进程与模块结构的不对应。 204 进程编写要考虑到与特定处理器的关系,并容易改变关系。 205 构架应尽量采用一些已知的设计模式。 206 207ADD构架设计的步骤: 208 样本输入 209 选择要分解的模块 210 根据下列5个步骤对模块进行求精(重点): 211 从具体的质量场景和功能需求集合中选择构架驱动因素。 212 选择满足构架驱动因素的构架模式。 213 实例化模块并根据用例分配功能,使用多个视图进行表示 214 定义子模块的接口 215 验证用例和质量场景并对其进行求精,使它们称为子模块的限制。 216 对需求进一步分解的每个模块重复上述步骤。 217创建骨架系统: 218 思想:提供一种基本能力,以一种对项目有利的顺序实现系统的功能。 219 好处: 220 提高开发效率,鼓舞士气。 221 能更早发现复杂的依赖关系。 222 使开发人员更多关注最难实现的部分。 223 能够缩短系统集成时间,降低其成本,并使集成成本更明确。 224 便于评审和测试。 225 步骤: 226 实现处理构架组件交互的软件部分 227 选择组件逐步添加到系统中。 228 逐步进行测试。 229架构师的职责: 230 了解所在组织的业务目标,使架构更好地支持业务目标。 231 规划产品的开发与严禁 232 规划和建设架构级的重用etc 233 234 235分析软件构架的原因(重要): 236 它是风险承担者之间的交流平台,是早期设计决策的体现,是可传递的模型。 237 软件质量不可能在软件开发的最后阶段追加上去,必须在设计之初就考虑到。 238 239第七章: 240构架评审: 241 成本: 242 人员时间成本 243 构架评审部门的组织开销 244 构架评审部分要求高级设计人员参与的代价(不就是人员时间成本吗。。。 245 收益: 246 及早发现构架中存在的问题 247 构架的改进 248 财务收益 249 强制位评审做准备 250 捕获构架设计的基本思想 251 验证需求的有效性 252评审实施: 253 按问题的重要性进行分类 254 强调那些与偶家相符或相悖的重要问题 255 必须记载评审中所提的每个问题 256构架评审的主要指导原则: 257 把由独立部门实施的正规的构架评审作为项目开发周期规划的一部分。 258 选择评审的最佳时间,尽早预审一次。 259 选择恰当的评审技巧 260 签署评审合同 261 限制所要品神的质量属性的个数 262 要保证评审小组中有构架方面的专家,领域专家,资料员,后勤员。 263 一定要有系统设计师。 264 收集各种场景数据,并在此基础上形成评审清单。 265 266第八章: 267架构权衡分析法(ATAM): 268 特点:不仅可以揭示出构架满足特定质量目标的情况,而且可以让我们更清楚地认识质量目标之间的联系。 269 输入:用场景集合捕获的质量要求。 270 输出: 271 简介的框架表述 272 表述清楚的业务目标 273 构架决策到质量需求的映射 274 所确定的敏感点和权衡点集合 275 有风险决策和无风险决策 276 风险主题的集合 277 阶段: 278 评估小组和项目决策者共同决定评估细节 279 评估小组收集信息和分析 280 风险承担着参与评估 281 评估小组自我检查和改进,提交书面报告 282 步骤(重点): 283 ATAM方法的表述 284 商业动机的表述 285 构架的表述 286 对构架方法进行分类 287 生成质量属性效用树 288 分析构架方法 289 集体讨论并确定场景优先级 290 再次分析构架方法 291 结果的表述 292 293第九章: 294 文档: 295 目的与作用:让不同的风险承担者都能快速找到和理解他们需要的信息。 296 基本原则:从读者的角度出发。