`
airu
  • 浏览: 267450 次
  • 性别: Icon_minigender_1
  • 来自: 云南
社区版块
存档分类
最新评论

一个从32位机器移植到64位机器时的c问题

 
阅读更多

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

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

 

 

const char * get_fmt_str(const char * fmt, ...)
{
#define SHARE_BUF_STR_LEN 4096
  static char str_buf[SHARE_BUF_STR_LEN];

  va_list arg_ptr;
  va_start(arg_ptr, fmt);
  vsnprintf(str_buf, SHARE_BUF_STR_LEN, fmt, arg_ptr);
  va_end(arg_ptr);
  return str_buf;
}

 

 调用的语句为

 

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 *指针,而是一个结构体:
typedef struct{
unsigned int gp_offset;
unsigned int fp_offset;
char *overflow_arg_area;
char *reg_save_area;
}va_list;
 
 由于gp_offset不到6*8=48,所以,变参就通过 gp_offset + reg_save_area 获取。
根据这些信息,又调试,发现,gp_offset + reg_save_area  根本不对。
于是继续调试。也许这时,大家想知道get_last_error()返回什么了。
char *get_last_error()
{
   return _error_;
}
 其实 _error_就是一个全局变量。一切都很正常。
实在是没有什么眉目了,于是只好根据x64位参数的分配方式,查看寄存器了。
使用 info register
rax            0x2ba3320526c0   47979918862016
rbx            0x2ba331e6f5e8   47979916883432
rcx            0x0      0
rdx            0x0      0
rsi             0x320526c0       839198400
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()的头文件。
于是加上原型函数声明,再试,好了。纠结了近两天的问题圆满解决!

 

分享到:
评论

相关推荐

    解析gcc编译器的移植方法

    设计一个新的编译器往往需要几年的时间因此我们希望能找到一种缩短开发时间的方法修改已有的编译器就是一种有效的手段本文讨论的是GNU C编译器的移植GNU C编译器自身有许多符合移植的特点如支持不同系统的配置文件...

    你必须知道的495个C语言问题

    1.24 我在一个文件中定义了一个extern数组,然后在另一个文件中使用,为什么sizeof取不到数组的大小? 声明问题 1.25 函数只定义了一次,调用了一次,但编译器提示非法重声明了。 *1.26 main的正确定义是什么...

    《你必须知道的495个C语言问题》

    1.24 我在一个文件中定义了一个extern数组,然后在另一个文件中使用,为什么sizeof取不到数组的大小? 13 声明问题 14 1.25 函数只定义了一次,调用了一次,但编译器提示非法重声明了。 14 *1.26 main的正确...

    你必须知道的495个C语言问题.pdf

    1.24 我在一个文件中定义了一个extern数组,然后在另一个文件中使用,为什么sizeof取不到数组的大小? 声明问题 1.25 函数只定义了一次,调用了一次,但编译器提示非法重声明了。 1.26 main的正确定义是什么?void ...

    C语言FAQ 常见问题列表

    o 2.2 64 位机上的 64 位类型是什么样的? o 2.3 怎样定义和声明全局变量和函数最好? o 2.4 extern 在函数声明中是什么意思? o 2.5 关键字 auto 到底有什么用途? o 2.6 我似乎不能成功定义一个链表。我试过 ...

    你必须知道的495个C语言问题(PDF)

    1.2 64 位机上的64 位类型是什么样的? . . . . . . . . . . . . . . . . 1 1.3 怎样定义和声明全局变量和函数最好? . . . . . . . . . . . . . . . 2 1.4 extern 在函数声明中是什么意思? . . . . . . . . . . . ...

    完整乐蛙rom移植教程

    ……首先,本篇中我们讲的是第三方 ROM 的移植,不包括官方 ROM 和官方 ROM 美化版,在第一章中我们 也有所介绍,关于官方 ROM 的移植,我们后续文章中将会有所介绍。 在这里我们推荐选用 CM 的 ROM 作为 base ...

    C语言详解C语言详解

    计算机程序设计语言的发展,经历了从机器语言、汇编语言到高级语言的历程。 1.1.1机器语言 用“0”和“1”组成的指令序列交由计算机执行——机器语言。运算效率是所有语言中最高的。机器语言,是第一代计算机语言。 ...

    STM32之启动文件详解

    在嵌入式应用程序开发过程里,由于使用C语言编程,基本很少涉及到机器底层寄存器的执行过程,一般都会直接在main函数里开始写代码,似乎main成为了理所当然的起点,尽管从C程序的角度来看程序都是直接从main函数开始...

    c语言编写单片机技巧

    当开发一个较复杂而又开发时间短的项目时,用C还是用汇编开发好? 答:对于复杂而开发时间紧的项目时,可以采用C语言,但前提是要求对该MCU系统的C语言和C编译器非常熟悉,特别要注意该C编译系统所能支持的数据...

    C语言程序员的可移植基础库,旨在提高性能和简化操作。-C/C++开发

    旨在帮助C开发人员(以及不喜欢使用“ mo SX:C / C ++程序和游戏的便携式基础库”的C ++程序员)@septag SX是一个最小且性能卓越的基础C库,可在不同的平台和操作系统上运行。由Sean Barret,Branimir Karadric,...

    51单片机C语言入门.pdf

    学习单片机实在不是件易事,一来要购买高价格的编程器,仿真器,二来要学习编程语 言,还有众多种类的单片机选择真是件让人头痛的事。在众多单片机中51 架构的芯片风行 很久,学习资料也相对很多,是初学的较好的...

    windows环境下32位汇编语言程序设计

    笔者从事汇编编程已经有十几年的历史了,从8086时代的DOS汇编编程开始到当前的Win32汇编编程,从一个初学者到现在能利用Win32汇编来解决大部分编程需求,中间也经过了很长时间的摸索和大量的挫折,所以笔者很清楚...

    C51单片机C语言入门教程

    51的编程语言常用的有二种,一 种是汇编语言,一种是C语言。汇编语言的机器代码生成效率很高但可读性却并不强,复 杂一点的程序就更是难读懂,而C语言在大多数情况下其机器代码生成效率和汇编语言相 当,但可读性...

    dotnet-NumSharp是NumPy的一个C移植

    它的目标是让.NET开发人员使用NumPy的语法编写机器学习代码,从而最大限度地借鉴现有大量在python代码的转译成本。 NumSharp使用最新的Span技术安全高效地访问内存,优化每个模拟API的性能,确保最底层的NDArray达到...

Global site tag (gtag.js) - Google Analytics