linux 下va_start,va_end,va_arg,va_list这些宏到底是什么?

/* author:         hjjdebug
 * date:         2023年 07月 27日 星期四 10:50:21 CST
 * descriptor:  linux 下va_start,va_end,va_arg,va_list这些宏到底是什么?
 */
#include <stdarg.h>
#include <stdio.h>
void test_va(int num,...)
{
    va_list args; // typedef __builtin_va_list va_list;
    va_start(args,num); //#define va_start(v,l) __builtin_va_start(v,l)
    for(int i=0; i < num; i++)
        printf("%d\n", va_arg(args, int)); //#define va_arg(v,l) __builtin_va_arg(v,l)
    va_end(args); // #define va_end(v) __builtin_va_end(v)
}

// 上面的注释是通过宏展开后获得的, 我用的是ubuntu20 环境
// 所以 va_start,va_arg,va_end,及 va_list 都是__builtin_ 内置变量
// 都依赖于编译器的实现, 成了黑箱操作了.
//
// 在简易内核linux0.11 上 , va_*操作并不是黑箱操作,而是显示定义的.如下:
// va_list args: va_list被定义成char *, 即args 为一个char *
// va_start(args,para)初始化 args 为第一个参数地址+1. 下一个参数地址
// va_arg(args,type), 循环调用该宏,可依次得到传来的参数
// va_end(args), 没有操作
//
// 对于linux gcc下的黑箱操作,我们还想进一步了解一下其实现,
// 方法有2
// 1. 用gdb 跟踪看其参数(本博客进行了这一步)
// 2. 反编译为汇编代码或用gdb 汇编跟踪

int main()
{
    test_va(3,1,2,3);
    return 0;
}

/*
args 被定义成包含一个元素的结构数组.
吐槽一下,这不就是一个结构吗, 非要搞成数组形式?! 服了,是避开俗套吗?
(gdb) ptype args
  type = struct typedef __va_list_tag __va_list_tag {
      unsigned int gp_offset;
      unsigned int fp_offset;
      void *overflow_arg_area;
      void *reg_save_area;
  } [1]
(gdb) ptype va_list
  type = struct typedef __va_list_tag __va_list_tag {
      unsigned int gp_offset;
      unsigned int fp_offset;
      void *overflow_arg_area;
      void *reg_save_area;
  } [1]

我们看到, args 就是va_list类型, va_list类型就是一个元素的结构数组.

未初始化的args 是这样的.
(gdb) display args
  1: args = {{
      gp_offset = 2496,
      fp_offset = 2496,
      overflow_arg_area = 0x9c0000009c0,
      reg_save_area = 0x9c0000009c0
    }}


初始化args指向第一个参数后面, 是这样的.
(gdb) next
  1: args = {{
      gp_offset = 8,
      fp_offset = 48,
      overflow_arg_area = 0x7fffffffdcc0,
      reg_save_area = 0x7fffffffdc00
    }}


我们看看num 的地址(第一个固定参数的地址)及其附近堆栈中的数据
(gdb) p &num
  $1 = (int *) 0x7fffffffdbcc
(gdb) x/32bx 0x7fffffffdbcc
  0x7fffffffdbcc:    0x03    0x00    0x00    0x00    0xc0    0x09    0x00    0x00
  0x7fffffffdbd4:    0xc0    0x09    0x00    0x00    0xc0    0x09    0x00    0x00
  0x7fffffffdbdc:    0xc0    0x09    0x00    0x00    0x08    0x00    0x00    0x00
  0x7fffffffdbe4:    0x30    0x00    0x00    0x00    0xc0    0xdc    0xff    0xff
意外的发现,num 是对的,但其附近并没有发现推入的变参参数, 那看一下args 所指的地址: reg_save_area
(gdb) x/32bx 0x7fffffffdc00
  0x7fffffffdc00:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
  0x7fffffffdc08:    0x01    0x00    0x00    0x00    0x00    0x00    0x00    0x00
  0x7fffffffdc10:    0x02    0x00    0x00    0x00    0x00    0x00    0x00    0x00
  0x7fffffffdc18:    0x03    0x00    0x00    0x00    0x00    0x00    0x00    0x00
 我们看到第一项没有使用, 后面依次存放了1,2,3 参数, 间隔是8bytes, 于时知道了堆栈是从右向左推入参数的,
 间隔是8btes
 同时推断出 reg_save_area+gp_offset = 0x7fffffffdc00+8 = 0x7fffffffdc08 第一个参数的地址
----------------------------------------------------------------------------------------------------
我们访问va_arg(args,int), 会使args 内的gp_offset值增加8,从而指向了下一个参数
(gdb) next
  8            printf("%d\n", va_arg(args, int)); //#define va_arg(v,l) __builtin_va_arg(v,l)
  1: args = {{
      gp_offset = 16,
      fp_offset = 48,
      overflow_arg_area = 0x7fffffffdcc0,
      reg_save_area = 0x7fffffffdc00
    }}

 推断出 reg_save_area+gp_offset = 0x7fffffffdc00+16 = 0x7fffffffdc10 第二个参数的地址

----------------------------------------------------------------------------------------------------
  8            printf("%d\n", va_arg(args, int)); //#define va_arg(v,l) __builtin_va_arg(v,l)
  1: args = {{
      gp_offset = 24,
      fp_offset = 48,
      overflow_arg_area = 0x7fffffffdcc0,
      reg_save_area = 0x7fffffffdc00
    }}

 推断出 reg_save_area+gp_offset = 0x7fffffffdc00+24 = 0x7fffffffdc18 第三个参数的地址
----------------------------------------------------------------------------------------------------
  8            printf("%d\n", va_arg(args, int)); //#define va_arg(v,l) __builtin_va_arg(v,l)
  我们只有3个参数,更大的地址就不用关心了, fp_offset 我估计是最大分配了这么多,而overflow_arg_area则是超过此区域就
  溢出了,不安全了的意思吧. gcc 代码没看,这里就望文生义,不影响理解就足够了.! 可见gcc具体实现在安全性上还考虑了不少.
  1: args = {{
      gp_offset = 32,
      fp_offset = 48,
      overflow_arg_area = 0x7fffffffdcc0,
      reg_save_area = 0x7fffffffdc00
    }}
----------------------------------------------------------------------------------------------------

*/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/11614.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mac卸载与安装指定版本node

一、查看当前node.js版本 node -v 二、卸载当前node.js # 这里是卸载npm的 sudo npm uninstall npm -g# 这里是用来删除node创建的各种文件夹 sudo rm -rf /usr/local/lib/node sudo rm -rf /usr/local/lib/node_modules sudo rm -rf /var/db/receipts/org.nodejs.* sudo rm…

ESP32 官方AT固件编译(从零开始环境搭建到编译完成全过程)

1、下载VMware免费版 https://download3.vmware.com/software/WKST-PLAYER-1702/VMware-player-full-17.0.2-21581411.exe 2、下载Ubuntu &#xff08;ubuntu-22.04.2-desktop-amd64.iso&#xff09;** https://releases.ubuntu.com/jammy/ubuntu-22.04.2-desktop-amd64.iso…

IDE/mingw下动态库(.dll和.a文件)的生成和部署使用(对比MSVC下.dll和.lib)

文章目录 概述问题的产生基于mingw的DLL动态库基于mingw的EXE可执行程序Makefile文件中使用Qt库的\*.a文件mingw下的*.a 文件 和 *.dll 到底谁起作用小插曲 mingw 生成的 \*.a文件到底是什么为啥mingw的dll可用以编译链接过程转换为lib引导文件 概述 本文介绍了 QtCreator mi…

AI编程常用工具 Jupyter Notebook

点击上方蓝色字体&#xff0c;选择“设为星标” 回复”云原生“获取基础架构实践 深度学习编程常用工具 我们先来看 4 个常用的编程工具&#xff1a;Sublime Text、Vim、Jupyter。虽然我介绍的是 Jupyter&#xff0c;但并不是要求你必须使用它&#xff0c;你也可以根据自己的喜…

PostgreSQL PG16 逻辑复制在STANDBY 上工作 (译)

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到2群&#xff08;共…

常用测试工具汇总

目录 1.Web页面检查器 2.客户端-代理抓包 3.自动化测试工具 3.1接口自动化测试 3.2webUI自动化测试 3.3客户端UI自动化测试 4.手机模拟器测试工具 5.阿里云测试工具 1.Web页面检查器 F12查看html页面&#xff0c;查看页面大小和加载时间 2.客户端-代理抓包 Charles&a…

Mybatis-plus从入门到精通

1、什么是MyBatis-Plus MyBatis-Plus&#xff08;简称MP&#xff09;是一个基于MyBatis的增强工具&#xff0c;在MyBatis的基础上对其进行扩展&#xff0c;用于简化MyBatis操作&#xff0c;提高开发效率。它继承了MyBatis原生的所有特性&#xff0c;并且添加了一些额外的功能&…

前端随笔:HTML/CSS/JavaScript和Vue

前端随笔 1&#xff1a;HTML、JavaScript和Vue 最近因为工作需要&#xff0c;需要接触一些前端的东西。之前虽然大体上了解过HTML、CSS和JavaScript&#xff0c;也知道HTML定义了内容、CSS定义了样式、JavaScript定义了行为&#xff0c;但是却没有详细的学习过前端三件套的细节…

微服务入门---SpringCloud(一)

微服务入门---SpringCloud&#xff08;一&#xff09; 1.认识微服务1.0.学习目标1.1.单体架构1.2.分布式架构1.3.微服务1.4.SpringCloud1.5.总结 2.服务拆分和远程调用2.1.服务拆分原则2.2.服务拆分示例2.2.1.导入Sql语句2.2.2.导入demo工程 2.3.实现远程调用案例2.3.1.案例需求…

72. 编辑距离

题目介绍 给你两个单词 word1 和 word2&#xff0c; 请返回将 word1 转换成 word2 所使用的最少操作数 。 你可以对一个单词进行如下三种操作&#xff1a; 插入一个字符删除一个字符替换一个字符 示例 1&#xff1a; 输入&#xff1a;word1 "horse", word2 &q…

CoTracker跟踪器 - CoTracker: It is Better to Track Together

论文地址&#xff1a;https://arxiv.org/pdf/2307.07635v1.pdf 官方地址&#xff1a;https://co-tracker.github.io/ github地址&#xff1a;https://github.com/facebookresearch/co-tracker/tree/main 引言 在计算机视觉领域&#xff0c;光流估计是历史最久远的问题之一&a…

利用VBA制作一个转盘游戏之五:最终的游戏过程

【分享成果&#xff0c;随喜正能量】真正厉害的人&#xff0c;从来不说难听的话&#xff0c;因为人心不需要听真话&#xff0c;只需要听好听的话&#xff0c;所以学着做一个有温度且睿智的人。不相为谋&#xff0c;但我照样能心平气和&#xff0c;冷眼相待&#xff0c;我依旧可…

BGP的介绍

目录 BGP基础 BGP的发展历史 BGP在企业中的应用 距离矢量型协议和路径矢量型协议的区别 BGP的特征 1.可控性 2.可靠性 3.AS-BY-AS BGP的对等关系 BGP的数据包 1.open报文 2.Keepalive报文 3.Update报文 4.Notification 报文 5.Route-refresh报文 BGP的状态机 …

【暑期每日一练】 day7

目录 选择题 &#xff08;1&#xff09; 解析&#xff1a; &#xff08;2&#xff09; 解析&#xff1a; &#xff08;3&#xff09; 解析&#xff1a; &#xff08;4&#xff09; 解析&#xff1a; &#xff08;5&#xff09; 解析&#xff1a; 编程题 题一…

【深度学习中常见的优化器总结】SGD+Adagrad+RMSprop+Adam优化算法总结及代码实现

文章目录 一、SGD&#xff0c;随机梯度下降1.1、算法详解1&#xff09;MBSGD&#xff08;Mini-batch Stochastic Gradient Descent&#xff09;2&#xff09;动量法&#xff1a;momentum3&#xff09;NAG(Nesterov accelerated gradient)4&#xff09;权重衰减项&#xff08;we…

使用 ChatGPT 进行研究的先进技术

在这篇文章中&#xff0c;您将探索改进您研究的先进技术。尤其&#xff0c; 分析和解释研究数据进行文献综述并找出研究差距废话不多说直接开始吧&#xff01;&#xff01;&#xff01; 分析和解释研究数据 一家小企业主希望分析客户满意度数据以改善客户服务。他们使用包含 10…

【Lua学习笔记】Lua入门

文章目录 Lua变量数据类型变量声明其他表示 Lua语法判断逻辑判断&#xff08;Lua很特殊&#xff0c;这个比较重要&#xff09;短路判断 ifif else 循环whileforrepeat 迭代器泛型for迭代器无状态迭代器多状态的迭代器 Lua函数select方法 数组字符索引_G &#xff08;不是教程&a…

opencv对相机进行畸变矫正,及矫正前后的坐标对应

文章目录 1.背景2.需求分析3.解决方案3.1.镜头畸变矫正3.2.知道矫正后的画面坐标(x, y)&#xff0c;求其在原画面的坐标(x, y)3.2.知道原画面坐标(x1, y1)&#xff0c;求其在矫正后的画面坐标(x2, y2) 4.效果5.代码 1.背景 目前有个项目&#xff0c;需要用到热成像相机。但是这…

k8s deployment(k8s经典版)|PetaExpress

Deployment是什么&#xff1f; Deployment是指在软件开发中将应用程序或系统部署到目标环境中的过程。它包括将代码编译、配置、打包并安装到目标服务器或设备上的步骤。k8s deployment是&#xff08;k8s经典版&#xff09;中用来管理发布的控制器&#xff0c;在开发的过程中使…

selenium等待的三种方式(详细)

1.强制等待 time.sleep(3) 这种方式会是操作强行等待3s才会进行下一步操作&#xff0c;但是这种放法&#xff0c;可能会延长测试的时间&#xff0c;如果元素在1s中出现&#xff0c;就会浪费2s的时间&#xff0c;并且这种放法单次有效&#xff0c;每次需要等待元素的操作都需要…