zoukankan      html  css  js  c++  java
  • 【转】一个从32位机器移植到64位机器时的c问题

    原文网址:http://www.jiancool.com/article/96402954887/

    最近工作中遇到了一个讨厌的问题,在32位机器上运行的好好的,但是在64位机器上,出现了诡异的 Segmental fault。

    于是调试分析,一切似乎都很正常。开始怀疑是否由于使用了变参。因为proc不支持...形式的变参,所以,不得已自己写了一个类似printf这样的变参,和proc程序分开。这个函数如下:

    1. const char * get_fmt_str(const char * fmt, ...)  
    2. {  
    3. #define SHARE_BUF_STR_LEN 4096  
    4.   static char str_buf[SHARE_BUF_STR_LEN];  
    5.   
    6.   va_list arg_ptr;  
    7.   va_start(arg_ptr, fmt);  
    8.   vsnprintf(str_buf, SHARE_BUF_STR_LEN, fmt, arg_ptr);  
    9.   va_end(arg_ptr);  
    10.   return str_buf;  
    11. }  

     调用的语句为

    get_fmt_str("%s", get_last_error());

      使用GDB调试,打印函数get_last_error(),返回正确的错误信息。
    可是接着运行,Segmentation fault 出来了。
    由于怀疑变参,于是找到一些关于x64的机器上变参的一些资料。在x64上的 参数入栈,一般是先入寄存器,大概有6个寄存器,顺序为:rdi,rsi,rdx,rcx,r8,r9。如果参数大于6个,那么就使用栈保存。这时的 va_list 不再是一个简单的char *指针,而是一个结构体:
    1. <span style="font-weight: normal;">typedef struct{  
    2. unsigned int gp_offset;  
    3. unsigned int fp_offset;  
    4. char *overflow_arg_area;  
    5. char *reg_save_area;  
    6. }va_list;</span>  
     
      由于gp_offset不到6*8=48,所以,变参就通过 gp_offset + reg_save_area 获取。
    根据这些信息,又调试,发现,gp_offset + reg_save_area  根本不对。
    于是继续调试。也许这时,大家想知道get_last_error()返回什么了。
    1. char *get_last_error()  
    2. {  
    3.    return _error_;  
    4. }  
      其实 _error_就是一个全局变量。一切都很正常。
    实在是没有什么眉目了,于是只好根据x64位参数的分配方式,查看寄存器了。
    使用 info register
    1. rax            0x2ba3320526c0   47979918862016  
    2. rbx            0x2ba331e6f5e8   47979916883432  
    3. rcx            0x0      0  
    4. rdx            0x0      0  
    5. rsi             0x320526c0       839198400  
    6. rdi             0x2ba33201c036   47979918639158  
     
    这是在get_last_error()返回以后的寄存器情况。细心的人可能已经发现问题了。
    get_last_error()返回值作为参数,首先放在了rax,然后被放到rsi作为参数,这是gcc x64上的变参调用时参数保存方式。而我们在rsi里看到什么了?
    这个值正好是rax里的低32位,这就是说,返回get_last_error()时,返回的char * ,在x64机器上应该为64位的char *,居然变成了32位的值。为什么?为什么?
    我们也可以看到,get_last_error()的返回值,确实是char * 啊。
    这种疑惑,我又做了无数的猜疑,但是突然想到一点,c语言中,如果没有函数原型的声明,那么,返回值会被默认为int型,而int在x64的机器上是32位的!
    肯定就是这个原因,于是打开get_last_error()这个函数的c文件,果然没有包含一个头文件,而头文件的作用,也就是声明函数原型。当然,调用语句所在的c文件,也没有用到声明get_last_error()的头文件。
    于是加上原型函数声明,再试,好了。纠结了近两天的问题圆满解决!
  • 相关阅读:
    poj 1321 棋盘问题 (DFS深度优先搜索)
    HDOJ1312 Red and black(DFS深度优先搜索)
    《数据结构》例1.3
    hdoj1905 Pseudoprime numbers (基础数论)
    百练4003 十六进制转十进制(水题)
    第二天——2.23
    第一天——2.22
    返校——2.21
    被盗号了——2.19
    继续咸鱼——2.18
  • 原文地址:https://www.cnblogs.com/wi100sh/p/4570574.html
Copyright © 2011-2022 走看看