测试当前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. ...
随机推荐
- VC++读写文件
目录 第1章读写文件 1 1.1 API 1 1.2 低级IO 1 1.2.1 文件序号 1 1.2.2 文本文件与二进制文件 1 1.3 流IO 2 1.4 Un ...
- function.py
#文档字符串 def square(x): 'calculates the square of the number x' return x*x square.__doc__ help(square) ...
- 【NOIP2012】 国王游戏
[题目链接] 点击打开链接 [算法] 按ai * bi升序排序,贪心即可 [代码] #include<bits/stdc++.h> using namespace std; #define ...
- NIO知识摘录
在 JDK 1. 4 中 新 加入 了 NIO( New Input/ Output) 类, 引入了一种基于通道和缓冲区的 I/O 方式,它可以使用 Native 函数库直接分配堆外内存,然后通过一个 ...
- python 查找IP地址归属地
#!/usr/bin/env python # -*- coding: utf-8 -*- #查找IP地址归属地 #writer by keery_log #Create time:2013-10-3 ...
- Linux多台服务器间SSH免密码登录配置
SSH实现各个服务器间的文件相互备份,如运行scp命令,可以实现免密码登录,从而可以使用SHELL脚本实现一些自动化的处理. 假如A机要免密码登录B机,具体方法如下: 1.在A机运行:"ss ...
- 斯坦福CS231n—深度学习与计算机视觉----学习笔记 课时11
课时11 神经网络训练细节part1(下) 2010年,Glorot等人写的论文,我们称之为Xavier初始化,他们关注了神经元的方差表达式.他们推荐一种初始化方式,那就是对每个神经元的输入进行开根号 ...
- Javascript中的回调函数和匿名函数的回调示例介绍
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- (水题)洛谷 - P1598 - 垂直柱状图
https://www.luogu.org/problemnew/show/P1598 忘记读取后清空数组,也不知道准确的长度. #include<bits/stdc++.h> using ...
- 洛谷 P2578 [ZJOI2005]九数码游戏【bfs+康托展开】
只有9!=362880个状态,用康托展开hash一下直接bfs即可 #include<iostream> #include<cstdio> #include<cstrin ...