c:变参函数:汇编解析;va_list;marco 宏:__VA_ARGS__

这篇具有很好参考价值的文章主要介绍了c:变参函数:汇编解析;va_list;marco 宏:__VA_ARGS__。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

参考

https://git.sr.ht/~gregkh/presentation-security/blob/3547183843399d693c35b502cf4a313e256d0dd8/security-stuff.pdf
https://www.man7.org/linux/man-pages/man3/stdarg.3.html

gcc 内部的宏定义

宏定义:
使用的时builtin_va_end 宏定义。

#define va_start(v,l)	__builtin_va_start(v,l)
#define va_end(v)	__builtin_va_end(v)
#define va_arg(v,l)	__builtin_va_arg(v,l)

使用到了下列内置函数来实现va-start、end、arg相关的宏。

/* Expand EXP, a call to __builtin_va_start.  */

static rtx
expand_builtin_va_start (tree exp)
{
  rtx nextarg;
  tree valist;
  location_t loc = EXPR_LOCATION (exp);

代码

int add(int first, int second, ... )
{
        int r=first + second;
        va_list va;;;
        va_start(va, second);
        while(int v= va_arg(va,int))
        {
                r+=v;
        }
        va_end(va);
        return r;
}

汇编

   962 0000000000400c56 <add(int, int, ...)>:
   963   400c56:       55                      push   %rbp
   964   400c57:       48 89 e5                mov    %rsp,%rbp
   965   400c5a:       48 83 ec 68             sub    $0x68,%rsp  申请的栈空间是 0x68,但是使用的远远大于这个值。
   966   400c5e:       89 bd 2c ff ff ff       mov    %edi,-0xd4(%rbp)   六个寄存器参数都用上了
   967   400c64:       89 b5 28 ff ff ff       mov    %esi,-0xd8(%rbp)    而且将这六个参数放到超远的栈上。
   968   400c6a:       48 89 95 60 ff ff ff    mov    %rdx,-0xa0(%rbp)
   969   400c71:       48 89 8d 68 ff ff ff    mov    %rcx,-0x98(%rbp)
   970   400c78:       4c 89 85 70 ff ff ff    mov    %r8,-0x90(%rbp)
   971   400c7f:       4c 89 8d 78 ff ff ff    mov    %r9,-0x88(%rbp)
   972   400c86:       84 c0                   test   %al,%al    看着调用一直时0,是否用到floating 的变量?几个
   973   400c88:       74 20                   je     400caa <add(int, int, ...)+0x54> 如果又floating的数值,将floating寄存器的值放到 栈
   974   400c8a:       0f 29 45 80             movaps %xmm0,-0x80(%rbp)  floating的值放到floating 寄存器。
   975   400c8e:       0f 29 4d 90             movaps %xmm1,-0x70(%rbp)
   976   400c92:       0f 29 55 a0             movaps %xmm2,-0x60(%rbp)
   977   400c96:       0f 29 5d b0             movaps %xmm3,-0x50(%rbp)
   978   400c9a:       0f 29 65 c0             movaps %xmm4,-0x40(%rbp)
   979   400c9e:       0f 29 6d d0             movaps %xmm5,-0x30(%rbp)
   980   400ca2:       0f 29 75 e0             movaps %xmm6,-0x20(%rbp)
   981   400ca6:       0f 29 7d f0             movaps %xmm7,-0x10(%rbp)
   982   400caa:       8b 95 2c ff ff ff       mov    -0xd4(%rbp),%edx
   983   400cb0:       8b 85 28 ff ff ff       mov    -0xd8(%rbp),%eax
   984   400cb6:       01 d0                   add    %edx,%eax   第一个和第二个参数相加放到eax
   985   400cb8:       89 85 4c ff ff ff       mov    %eax,-0xb4(%rbp)  然后放到 栈
   986   400cbe:       c7 85 30 ff ff ff 10    movl   $0x10,-0xd0(%rbp)16 放到 栈
   987   400cc5:       00 00 00
   988   400cc8:       c7 85 34 ff ff ff 30    movl   $0x30,-0xcc(%rbp)0x30 放到栈
   989   400ccf:       00 00 00
   990   400cd2:       48 8d 45 10             lea    0x10(%rbp),%rax   将rbp + 16 的值放到rax,使用到了上一个函数栈。
   991   400cd6:       48 89 85 38 ff ff ff    mov    %rax,-0xc8(%rbp)  ,放到栈
   992   400cdd:       48 8d 85 50 ff ff ff    lea    -0xb0(%rbp),%rax   将rbp -0xb0的地址放到rax
   993   400ce4:       48 89 85 40 ff ff ff    mov    %rax,-0xc0(%rbp)    放到栈
   994   400ceb:       8b 85 30 ff ff ff       mov    -0xd0(%rbp),%eax    将rbp-0xd0的值放到eax
   995   400cf1:       83 f8 2f                cmp    $0x2f,%eax     对比0x2f 为什么比对2f996   400cf4:       77 23                   ja     400d19 <add(int, int, ...)+0xc3>
   997   400cf6:       48 8b 85 40 ff ff ff    mov    -0xc0(%rbp),%rax
   998   400cfd:       8b 95 30 ff ff ff       mov    -0xd0(%rbp),%edx
   999   400d03:       89 d2                   mov    %edx,%edx
  1000   400d05:       48 01 d0                add    %rdx,%rax
  1001   400d08:       8b 95 30 ff ff ff       mov    -0xd0(%rbp),%edx
  1002   400d0e:       83 c2 08                add    $0x8,%edx
  1003   400d11:       89 95 30 ff ff ff       mov    %edx,-0xd0(%rbp)
  1004   400d17:       eb 12                   jmp    400d2b <add(int, int, ...)+0xd5>
  1005   400d19:       48 8b 85 38 ff ff ff    mov    -0xc8(%rbp),%rax
  1006   400d20:       48 8d 50 08             lea    0x8(%rax),%rdx
  1007   400d24:       48 89 95 38 ff ff ff    mov    %rdx,-0xc8(%rbp)
  1008   400d2b:       8b 00                   mov    (%rax),%eax
  1009   400d2d:       89 85 48 ff ff ff       mov    %eax,-0xb8(%rbp)
  1010   400d33:       83 bd 48 ff ff ff 00    cmpl   $0x0,-0xb8(%rbp)
  1011   400d3a:       74 0e                   je     400d4a <add(int, int, ...)+0xf4>
  1012   400d3c:       8b 85 48 ff ff ff       mov    -0xb8(%rbp),%eax
  1013   400d42:       01 85 4c ff ff ff       add    %eax,-0xb4(%rbp)   将 eax 加到栈里
  1014   400d48:       eb a1                   jmp    400ceb <add(int, int, ...)+0x95>
  1015   400d4a:       8b 85 4c ff ff ff       mov    -0xb4(%rbp),%eax  最终的结果放到eax
  1016   400d50:       c9                      leaveq
  1017   400d51:       c3                      retq

调用

int main()
{
printf( "abc=%d\n", add(1,3,4,5,8,9,10);
return 1;
}
0000000000400968 <main>:
  400968:       55                      push   %rbp
  400969:       48 89 e5                mov    %rsp,%rbp
  40096c:       48 83 ec 08             sub    $0x8,%rsp  按16 字节对齐。多8个字节占栈,如果多出来一个参数
  400970:       6a 0a                   pushq  $0xa
  400972:       41 b9 09 00 00 00       mov    $0x9,%r9d
  400978:       41 b8 08 00 00 00       mov    $0x8,%r8d
  40097e:       b9 05 00 00 00          mov    $0x5,%ecx
  400983:       ba 04 00 00 00          mov    $0x4,%edx
  400988:       be 03 00 00 00          mov    $0x3,%esi
  40098d:       bf 01 00 00 00          mov    $0x1,%edi
  400992:       b8 00 00 00 00          mov    $0x0,%eax  把 eax 清空
  400997:       e8 aa fe ff ff          callq  400846 <add(int, int, ...)>
  40099c:       48 83 c4 10             add    $0x10,%rsp  释放栈
  4009a0:       89 c6                   mov    %eax,%esi
  4009a2:       bf b2 0a 40 00          mov    $0x400ab2,%edi
  4009a7:       b8 00 00 00 00          mov    $0x0,%eax
  4009ac:       e8 3f fd ff ff          callq  4006f0 <printf@plt>
  4009b1:       b8 01 00 00 00          mov    $0x1,%eax
  4009b6:       c9                      leaveq
  4009b7:       c3                      retq

在 SEI CERT C++ Coding Standard 这个标准里

提到了更安全的C++定义方式。 这种方式将编程从运行时变参,转移到了编译时,更安全。

c N157

The identifier _ VA_ARGS _ shall occur only in the replacement-list of a function-like macro that uses the ellipsis notation in the parameters.

示例

https://en.cppreference.com/w/cpp/language/parameter_pack

#include <iostream>
 
void tprintf(const char* format) // base function
{
    std::cout << format;
}
、、 这个会产生多少个函数来?
template<typename T, typename... Targs>
void tprintf(const char* format, T value, Targs... Fargs) // recursive variadic function
{
    for ( ; *format != '\0'; format++ ) {
        if ( *format == '%' ) {
           std::cout << value;
           tprintf(format+1, Fargs...); // recursive call
           return;
        }
        std::cout << *format;
    }
}
 
int main()
{
    tprintf("% world% %\n","Hello",'!',123);
}

实例

变参传递到另一个函数里:

static inline void abc(int level, const char *format, ...)
{
	char buff[UMAX_LOG_SIZE];
	int msgLen;
	va_list arglist;

	memset(buff, 0, sizeof(buff));
	 va_start(arglist, format);
    msgLen = vsnprintf(buff, UMAX_LOG_SIZE, format, arglist);
	va_end(arglist);

宏里的使用

下面这个宏,只包含有,三个点所代表的参数;不包含三个点以外有名称的参数。

__VA_ARGS__
#define _FUNC1_(tn, constness, ct, Method, ...) \
class mock_##Method { \
public:\
  RESULT_(tn, __VA_ARGS__) ct Method( \  // 这里__VA_ARGS__, 不包含  tn,constness,ct和Method

gcc

s->n__VA_ARGS__ = cpp_lookup (pfile, DSC(“VA_ARGS”));文章来源地址https://www.toymoban.com/news/detail-717454.html

到了这里,关于c:变参函数:汇编解析;va_list;marco 宏:__VA_ARGS__的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C语言——变参函数

    目录 目录 一、定义 二、声明方式 三、使用 3.1用指针的方式 3.2用宏定义的方式 四、printf的实现         一般函数的参数列表是固定的,所以在调用时传入的实参的个数和格式必须和实参匹配;在函数式中,不需要关心实参,直接调用形参即可。          变参函数,

    2023年04月11日
    浏览(14)
  • 【ARM64 常见汇编指令学习 12 -- ARM 汇编函数 的学习】

    上篇文章:ARM64 常见汇编指令学习 11 – ARM 汇编宏 .macro 的学习 下篇文章:ARM64 常见汇编指令学习 13 – ARM 汇编 ORG 伪指令学习 ARM汇编中的函数定义并不像高级语言那样有特定的语法,但通常可以通过 标签(label) 和 子程序调用指令 (如BL,BLX) 来实现类似于函数的功能。 例如

    2024年02月14日
    浏览(35)
  • 5. 函数调用过程汇编分析

    __cdecl 调用方式 __stdcall 调用方式 __fastcall 调用方式 不同的编译器实现不一样,上述情况只是VC++6.0的编译实现 即便是在同一个编译器,开启优化和关闭优化也不一样 即便是同一个编译器同一种模式,32位和64位下情况也会不一样 参考 GCC GNU 文档属性描述 fastcall On x86-32 target

    2024年01月22日
    浏览(22)
  • 5.5 汇编语言:函数调用约定

    函数是任何一门高级语言中必须要存在的,使用函数式编程可以让程序可读性更高,充分发挥了模块化设计思想的精髓,今天我将带大家一起来探索函数的实现机理,探索编译器到底是如何对函数这个进行实现的,并使用汇编语言模拟实现函数编程中的参数传递调用规

    2024年02月12日
    浏览(31)
  • 单片机 STM32启动文件详解(汇编语言解析)

    以前讲了固件库,从ST官网下载的固件库里面,有许多的启动文件(汇编语言写的.s文件) 启动文件 说明 startup_stm32f10x_ld.s Low Density 小容量 startup_stm32f10x_md.s Medium Density 中容量 startup_stm32f10x_hd.s High Density 高容量 startup_stm32f10x_xl.s Extra Large Density 超大容量 startup_stm32f10x_cl.s Con

    2023年04月25日
    浏览(42)
  • 在C语言中调用汇编语言的函数

    在C语言中调用汇编文件中的函数,要做的主要工作有两个: 一是在C语言中声明函数原型,并加extern; 二是在汇编中用EXPORT导出函数名,并用该函数名作为汇编代码段的标识,最后用mov pc, lr返回。然后,就可以在C语言中使用该函数了。 从C语言的角度,并不知道该函

    2024年02月14日
    浏览(31)
  • X86_64函数调用汇编程序分析

    %rdi, %rsi, %rdx, %rcx, %r8, %r9分别用于函数调用过程中的前6个参数,对于6的参数存放在栈中传递 %rsp用做栈指针寄存器,指向栈顶 %rbp用作栈框寄存器,指向栈底 %rax用做函数返回值的第一个寄存器 2.1.1 main的C代码实现 2.1.2 main函数对应汇编及其分析 这段汇编代码实现了一个简单的

    2024年02月09日
    浏览(30)
  • 【裸机开发】中断系统(二)—— Reset 中断服务函数(汇编实现)

    目录 一、Reset 中断服务函数的实现步骤 二、汇编实现 Reset 中断服务函数 1、禁止/打开全局中断 2、设置SP指针 3、清除 .bss 段 4、完整 Reset 中断服务函数 实现 Reset 中断服务函数的基本步骤如下: 设置各个模式下的SP指针 。当中断发生后,会进入到对应的工作模式下,每个工

    2024年02月10日
    浏览(29)
  • VA01/VA02/VA03 销售订单根据定价和步骤校验权限隐藏价格

    针对用户使用销售订单时,根据定价和步骤顺序,判断是否有权限。此处通过有权限则隐藏价格的方式,隐藏销售订单抬头和行项目的部分价格数据 要限制的定价和步骤在spro中的位置 限制的步骤 创建带有定价和步骤的权限对象 分配权限给需要隐藏价格的用户 隐藏抬头和行

    2024年02月07日
    浏览(32)
  • uboot启动流程-涉及lowlevel_init汇编函数

    之前文章简单分析了 uboot启动流程的开始,从链接脚本文件 u-boot.lds 中,我们已经知道了入口点是 arch/arm/lib/vectors.S 文件中的 _start 函数。 _start 函数 : 调用了 reset 函数 ,reset 函数内部:最终调用了 save_boot_params_ret 函数。 save_boot_params_ret 函数: ①  将处理器设置为SVC模式

    2024年02月07日
    浏览(24)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包