美文网首页
Linux C 可变参数在x86和x64下的区别与实现原理

Linux C 可变参数在x86和x64下的区别与实现原理

作者: ultraji | 来源:发表于2019-09-29 18:42 被阅读0次

关于64位和32位环境下可变参数问题的一点浅显分析。通过看对应的汇编代码实现和Intel ABI手册,可以得出:

  • 在x86平台下,va_list可变传参是通过栈来进行;
  • 在x64平台下,va_list可变传参是默认的调用约定(calling convention);

一、从汇编代码分析

#include <stdio.h>
#include <stdarg.h>

long add(long num, ...)
{
    long sum = 0, i, tmp;
    va_list va;

    va_start(va, num);
    for (i = 0; i < num; i++) {
        tmp = va_arg(va, int);
        sum += tmp;
        printf("%ld ", tmp);
    }
    printf("\n");
    va_end(va);
    
    return sum;
}

int main()
{
    long sum = add(7, 2, 3, 4, 5, 6, 7, 8);
    printf("sum is %ld\n", sum);
    return 0;
}

结果: 在64位和32位下输出结果一样

首先看一下32位编译情况,

gcc -m32 -c test.c
objdump -d test.o

结果如下:

va_list_32.png

然后看一下64位编译情况,

gcc -c test.c
objdump -d test.o

结果如下:

va_list_64.png

二、官方说明

在32位上,va_list的定义为:

//注意,由于中间宏过多,这里省去了中间如_VA_LIST宏,直接给出实际定义。
typedef va_list char**;

在64位下,va_list的定义为:

 typedef struct {
         unsigned int gp_offset;
         unsigned int fp_offset;
         void *overflow_arg_area;
         void *reg_save_area;
} va_list[1];

然后再看一下Inter官方的Linux ABI文档(64位)怎么说,问题在abi中已经解释的很清楚了。

The va_start Macro

The va_start macro initializes the structure as follows:

  • reg_save_area The element points to the start of the register save area.

  • overflow_arg_area This pointer is used to fetch arguments passed on the stack. It is initialized with the address of the first argument passed on the stack, if any, and then always updated to point to the start of the next argument on the stack.

  • gp_offset The element holds the offset in bytes from reg_save_area to the place where the next available general purpose >argument register is saved. In case all argument registers have been exhausted, it is set to the value 48 (6 ∗ 8).

  • fp_offset The element holds the offset in bytes from reg_save_area to the place where the next available floating point >argument register is saved. In case all argument registers have been exhausted, it is set to the value 304 (6 ∗ 8 + 16 ∗ 16).

三、再看一下什么情况下可能出错

#include <stdio.h>
#include <stdarg.h>

long add(long num, ...)
{
    long sum = 0, i, tmp;
    long *arg = &num + 1;

    for (i = 0; i < num; i++)
    {
        tmp = arg[i];
        sum += tmp;
        printf("%ld ", tmp);
    }
    printf("\n");
    return sum;
}

int main()
{
    long sum = add(7, 2, 3, 4, 5, 6, 7, 8);
    printf("sum is %ld\n", sum);
    return 0;
}

64位下结果为随机

0 1 140733246434352 140733246434352 0 2 3
sum is 281466492868710

32位下结果正确

2 3 4 5 6 7 8
sum is 35

相关文章

  • Linux C 可变参数在x86和x64下的区别与实现原理

    关于64位和32位环境下可变参数问题的一点浅显分析。通过看对应的汇编代码实现和Intel ABI手册,可以得出: ...

  • x86系统和X64的区别

    windows x64和x86最主要的区别:windows x64是指64位的操作系统,windows x86是指...

  • x64 x86_64和 x82

    x64 x86有啥区别”这个问题和“32位和64位有啥区别”是一样的;因为x64其实就是64位,x86其实就是32...

  • 虚拟化的实现

    主要实现在x86的机器上面,其他的一些机器的设计没有考虑到后面要做虚拟化技术。 x86与x64区别:http://...

  • ubuntu

    介绍 Ubuntu 是基于Debian GNU/Linux,支持x86、amd64(即x64)和ppc架构,与De...

  • CMake教程——零碎笔记

    多系统区分 windows下x64和x86区分 将pthread作为默认线程库 由于pthread不是linux的...

  • linux 2.6 互斥锁的实现-源码分析

    Linux 2.6 kernel的源码,下面结合代码来分析一下在X86体系结构下,互斥锁的实现原理。 代码分析: ...

  • Lua可变参数

    Lua 函数可以接受可变数目的参数,和 C 语言类似,在函数参数列表中使用...表示函数有可变的参数。 把可变参数...

  • windows10内核调试环境搭建

    "C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\win...

  • iOS可变参数实现及原理剖析

    iOS可变参数实现及原理剖析 标签(空格分隔): iOS可变参数 iOS方法后面为什么有nil参数 你一定会时常见...

网友评论

      本文标题:Linux C 可变参数在x86和x64下的区别与实现原理

      本文链接:https://www.haomeiwen.com/subject/qwkqpctx.html