测试当前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. ...
随机推荐
- [Java] String类, StringBuffer类
1. String 类 1. 创建: String s1 = new String; s1 = "abc"; String s2 = new String("abc&qu ...
- BZOJ_1812_[Ioi2005]riv_树形DP
BZOJ_1812_[Ioi2005]riv_树形DP Description 几乎整个Byteland王国都被森林和河流所覆盖.小点的河汇聚到一起,形成了稍大点的河.就这样,所有的河水都汇聚并流进了 ...
- ORACLE PL/SQL 实例精解之第三章 PL/SQL中的SQL
3.1 在PL/SQL中使用DML 在PL/SQL语块中,两种变量赋值a. := 初始化.b. select into语法,PL/SQL语块的声明部分被声明的变量,后期可以使用选择语句进行赋值. 3. ...
- (4)ASP.NET Core 中间件
1.前言 整个HTTP Request请求跟HTTP Response返回结果之间的处理流程是一个请求管道(request pipeline).而中间件(middleware)则是一种装配到请求管道以 ...
- Codeforces - 9D - How many trees? - 简单dp - 组合数学
https://codeforces.com/problemset/problem/9/D 一开始居然还想直接找公式的,想了想还是放弃了.原来这种结构是要动态规划. 状态是知道怎么设了,$t_{nh} ...
- hdoj1078【DP·记忆化搜索】
还是满水的一道题目吧...这个一看肯定要搜索了..然后又是这么DP,那就是记忆化搜索了...走K步,下一步要比他多...很好写啊/// #include<iostream> #includ ...
- python __builtins__ bytearray类 (7)
7.'bytearray', 返回一个新字节数组.这个数组里的元素是可变的,并且每个元素的值范围: 0 <= x < 256. class bytearray(object) | byte ...
- 为什么使用Stylus
CSS预处理器的出现大大的提高了前端开发的效率和逼格,它让CSS可编程化.LESS和Sass/SCSS是两种最为常见的预处理器,拥有大量的用户基数,数目庞杂的第三方库.然而,还有一种预处理器并未引起足 ...
- Python递归和迭代
递归 在函数内部,调用函数自身的编程技巧称为递归( recursion).递归函数结构清晰,很直观的理解计算过程,但也有严重缺点:相对于普通循环而言,递归运行效率较低,经过很多冗余的计算,递归会消耗大 ...
- LCA UESTC 92 Journey
题目传送门 题意:先给一棵树,然后有一条额外的边,问u走到v从现在最短的路走和原来不加边走的路节省了多少距离 分析:首先跑不加边的树的LCA,这样能求出任意两点的距离,那么现在x和y多连了一条边,如果 ...