• 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)
    
    • 1
    • 2
    • 3

    使用到了下列内置函数来实现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);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    代码

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    汇编

       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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    调用

    int main()
    {
    printf( "abc=%d\n", add(1,3,4,5,8,9,10);
    return 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在 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 
     
    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    实例

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

    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    宏里的使用

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

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

    gcc

    s->n__VA_ARGS__ = cpp_lookup (pfile, DSC(“VA_ARGS”));

  • 相关阅读:
    达梦数据库常用SQL
    ThreadLocal源码解析 2.ThreadLocalMap内核
    不是所有数据增强都可以提升精度
    文档在线预览(三)将word、txt、ppt、excel、图片转成pdf来实现在线预览
    c# 中的类
    postgresSQL 数据库本地创建表空间读取本地备份SQL文件
    Cesium中的DataSource和Entity关系
    重生奇迹MU游戏的特别之处
    Mesa新版来袭
    使用jmeter+ant+jenkins+git搭建自动化测试平台
  • 原文地址:https://blog.csdn.net/qq_36428903/article/details/121479199