【转】一个从32位机器移植到64位机器时的c问题
2024-08-27 22:58:13
原文网址:http://www.jiancool.com/article/96402954887/
最近工作中遇到了一个讨厌的问题,在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 *指针,而是一个结构体:
- <span style="font-weight: normal;">typedef struct{
- unsigned int gp_offset;
- unsigned int fp_offset;
- char *overflow_arg_area;
- char *reg_save_area;
- }va_list;</span>
由于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()的头文件。
于是加上原型函数声明,再试,好了。纠结了近两天的问题圆满解决!
最新文章
- java 多线程(ThreadPoolExecutor (补充))
- 最小生成树之Kruskal算法
- 【BZOJ】1069: [SCOI2007]最大土地面积(凸包+旋转卡壳)
- MySQL存储引擎之InnoDB
- 如何用腾讯云打造一款微视频APP
- 二模 (9) day2
- jQuery无缝滚动插件
- 【JavaScript】JavaScript函数的参数
- Android 操作系统的内存回收机制[转]
- TCPDUMP详解(续)
- dev控件chart简单实现多图例,双曲线,双柱图,曲线与柱图
- bzoj 2186 [Sdoi2008]沙拉公主的困惑 欧拉函数
- 修改AD中PCB各层的透明度
- lenovo 笔记本ideapad 320c-15改装win7问题
- 012-mac下shell,zsh,oh-my-zsh,以及插件
- CROSSUI桌面工具 分布加载模块(Distributed UI Module) 与 主模块Module 之间数据传输!
- Nginx:论高并发,在座各位都是渣渣
- AI---训练集(train set) 验证集(validation set) 测试集(test set)
- 并发编程之 LinkedBolckingQueue 源码剖析
- java向上转型和向下转型
热门文章
- Java基础 -- 冒泡排序算法(带详细注释)
- ffmpeg + sdl -03 简单音频播放器实现
- JavaScript运算符有哪些
- MySQL server version for the right syntax to use near &;#39;type=InnoDB&;#39; at line 1
- React 入门最好的实例-TodoList
- IP转发和子网路由
- LINUX进程上锁查看方法
- RMAN常用备份恢复命令汇总
- 【转】Difference between Point-To-Point and Publish/Subscribe JMS Messaging Models
- Linux基础知识笔记