测试当前C环境的栈帧增长方向以及传递参数时的压栈顺序
前文链接:上次由于一个很常见的printf-bug(下文有提及)引发了我对栈的思考,并写下了一点总结。这次就尝试对不同的C环境进行实践,检测其传递参数的一些性质。
这是今天写的检查C环境的一段程序、能够判断环境的大小端、栈帧增长方向、传递参数时的压栈顺序、以及参数的求值顺序。
代码如下:
#include <stdio.h>
#include <assert.h>
#include <inttypes.h>
typedef const char *string_literal;
string_literal Endian() {
union {
uint16_t u16;
uint8_t u8; /* if FF small endian */
} u = {.u16 = 0x00FF};
return u.u8 ? "Small Endian" : "Big Endian";
}
enum {H2L, L2H} SD;
string_literal StackFrameDirection()
{
static string_literal *addr;
string_literal rtn;
return !addr ? addr = &rtn, rtn = StackFrameDirection(), addr = NULL, rtn
: &rtn < addr ? SD = H2L , "High -> Low" : (SD = L2H, "Low -> High");
}
enum {R2L, L2R} APO;
string_literal ArgumentsPushOrder(int a, int b)
{
(void)StackFrameDirection();
return (APO = !!SD ^ (&a > &b) ? L2R : R2L) ? "Left -> Right" : "Right -> Left";
}
string_literal ArgumentsEvaluationOrder(int a, int b)
{
return a < b ? "Left -> Right" : "Right -> Left";
}
int a_arg() {
static int cnt;
return ++cnt;
}
int main()
{
printf("In this C implementation:\n");
printf("\tEndian: %s\n", Endian());
printf("\tStackFrameDirection: %s\n", StackFrameDirection());
printf("\tArgumentsPushOrder: %s\n", ArgumentsPushOrder(a_arg(), a_arg()));
/* Evaluation Order below is determined by Complier and maybe not always same */
printf("\tArgumentsEvaluationOrder: %s\n", ArgumentsEvaluationOrder(a_arg(), a_arg()));
return 0;
}
我在macOS(intel)上以及树莓派OS(ARM Cortex-A)上都是这个结果:
In this C implementation:
Endian: Small Endian
StackFrameDirection: High -> Low
ArgumentsPushOrder: Left -> Right
ArgumentsEvaluationOrder: Left -> Right
在某咸鱼的 win10(intel) mingw 上的结果:
In this C implementation:
Endian: Small Endian
StackFrameDirection: High -> Low
ArgumentsPushOrder: Right -> Left
ArgumentsEvaluationOrder: Right -> Left
!!只有压栈顺序不一样。
Win下的压栈顺序和 WIN32 缓冲区溢出的知识相互照应了。
树莓派的压栈顺序又和学 ARM 的 ATPCS 相互照应了。
所以上次在树莓派(ILP32)上的异常结果的具体原因可以尝试分析一下了:
int64_t i = 1;
printf("%ld\n", i); // "%" PRId32
$ ./a.out
0
$
因为树莓派上的 GCC 的数据模型为 ILP32,
所以 printf("%ld\n", i); 可以简化成 F(P32, LL64);;
假设 P32 为 0xFFFFFCD0 , LL64 为 1 即 0x0000000000000001;
因为参数从左边开始压入栈中,且为小端模式,树莓派的栈是从高地址端向低地址端增长,
所以传递参数的时候字节的压栈顺序是 FF FF FC D0 00 00 00 00 00 00 00 01;
按照 C 传递参数以及可变参数 stdarg.h 的原理,printf 会根据 P32 的内容,把更低地址的四个字节00 00 00 00理解成 long 并输出,所以最后输出了0。
思考:前文检测的规律是有标准的吗?那又是谁制定的呢?
嗯,ATPCS 是否会在 Linux 上起作用,这点真不好说。
假如编译器有自己的传参标准的话,那系统调用怎么处理?
编译器肯定要遵循某种操作系统决定的标准。
可能编译器为了优化,会选择在程序内部的调用使用自己的标准?
测试当前C环境的栈帧增长方向以及传递参数时的压栈顺序的更多相关文章
- 大端小端系统_union_栈的增长方向
一道题引发的思考: 1.看一下之前写的union的特点,理解一下共享内存的概念 2.栈的增长方向是从高地址向低地址增长(数组比较特别,a[0]在低地址,a[n-1]在高地址)(堆由低地址到高地址存储) ...
- Windows x64 栈帧结构
0x01 前言 Windows 64位下函数调用约定变为了快速调用约定,前4个参数采用rcx.rdx.r8.r9传递,多余的参数从右向左依次使用堆栈传递.本次文章是对于Windows 64位下函数调用 ...
- c函数调用过程原理及函数栈帧分析
转载自地址:http://blog.csdn.net/zsy2020314/article/details/9429707 今天突然想分析一下函数在相互调用过程中栈帧的变化,还是想尽量以比 ...
- JAVA栈帧
简介 Java栈是一块线程私有的内存空间.java堆和程序数据相关,java栈就是和线程执行密切相关的,线程的执行的基本行为是函数调用,每次函数调用的数据都是通过java栈来传递的. Java栈与数据 ...
- 汇编3栈帧,参数传递,串操作,混合汇编,x64,asm文件
基础知识2 选择结构 通过判断 + 条件跳转指令来实现 循环结构 通过判断 + 条件跳转指令来实现(会有一个向上跳转的语句) 函数调用约定 C调用约定: 由外部平衡栈 标准调用约定 : 由函数内部平衡 ...
- 图解JVM字节码执行引擎之栈帧结构
一.执行引擎 “虚拟机”的概念是相对于“物理机”而言的,这两种“机器”都有执行代码的能力.物理机的执行引擎是直接建立在硬件处理器.物理寄存器.指令集和操作系统层面的:而“虚拟机”的执行引擎是 ...
- 栈帧%ebp,%esp详解
首先应该明白,栈是从高地址向低地址延伸的.每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息.寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部( ...
- Java虚拟机运行时栈帧结构--《深入理解Java虚拟机》学习笔记及个人理解(二)
Java虚拟机运行时栈帧结构(周志明书上P237页) 栈帧是什么? 栈帧是一种数据结构,用于虚拟机进行方法的调用和执行. 栈帧是虚拟机栈的栈元素,也就是入栈和出栈的一个单元. 2018.1.2更新(在 ...
- 栈帧 2.6. Frames 虚拟机内存模型
https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-2.html#jvms-2.6 小结: 1. https://docs.oracle. ...
随机推荐
- iOS NSInteger/NSUInteger与int/unsigned int、long/unsigned long之间的区别!
在iOS开发中经常使用NSInteger和NSUInteger,而在其他的类似于C++的语言中,我们经常使用的是int.unsigned int.我们知道iOS也可以使用g++编译器,那么它们之间是否 ...
- CoreGpaphics
CoreGpaphics基本应用 CGAffineTransformMake开头的函数 是基于最初始的位置来变化的 带有CGAffineTransform参数是基于CGAffineTransform的 ...
- pymssql读取varchar字段中文显示乱码的问题分析
问题 用python的pymssql模块读取旧业务系统后台SQL Server 2000数据库展示数据为乱码 开发环境 操作系统:windows 8 数据库 MS SQL Server 2000,默认 ...
- DLL分配的内存如何在EXE里面释放
总结下面几个要点: 1. 保证内存分配和清除的统一性:如果一个DLL提供一个能够分配内存的函数,那么这个DLL同时应该提供一个函数释放这些内存.数据的创建和清除应该在同一个层次上. 曾经遇到过这样的例 ...
- bzoj 3872: [Poi2014]Ant colony【树形dp+二分】
啊我把分子分母混了WA了好几次-- 就是从食蚁兽在的边段成两棵树,然后dp下去可取的蚂蚁数量区间,也就是每次转移是l[e[i].to]=l[u](d[u]-1),r[e[i].to]=(r[u]+1) ...
- PAT团体程序设计天梯赛 - 模拟赛
由于本人愚笨,最后一题实在无力AC,于是只有前14题的题解Orz 总的来说,这次模拟赛的题目不算难,前14题基本上一眼就有思路,但是某些题写起来确实不太容易,编码复杂度有点高~ L1-1 N个数求和 ...
- 洛谷P3537 [POI2012]SZA-Cloakroom(背包)
传送门 蠢了……还以为背包只能用来维护方案数呢……没想到背包这么神奇…… 我们用$dp[i]$表示当$c$的和为$i$时,所有的方案中使得最小的$b$最大时最小的$b$是多少 然后把所有的点按照$a$ ...
- 在IDEA中使用JSP中的out内置对象,out.println()——println红色解决方法
今天在学习JSP的时候,在jsp中使用out内置对象,开发工具用的是IDEA,结果如下图所示 郁闷了半天找度娘,可能关键字输的不准确,乱七八糟的方法一大堆,什么加依赖啊啥的,反正都不管用,最后找到一篇 ...
- “XXX.Index”不扩展类“System.Web.UI.Page”,因此此处不允许的问题
“XXX.Index”不扩展类“System.Web.UI.Page”,因此此处不允许的问题 原因:设计页面继承的路径和后台.cs页面类的路径不一致造成的 看下图 这个是设计页面的样式 这个是后台cs ...
- MYSQL性能调优与架构设计之select count(*)的思考
select count(*)的思考 原文:MYSQL性能调优与架构设计 举例: 这里我们就拿一个看上去很简单的功能来分析一下. 需求:一个论坛帖子总量的统计 附加要求:实时更新 在很多人看来,这 ...