最近工作中遇到了一个讨厌的问题,在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()的头文件。
于是加上原型函数声明,再试,好了。纠结了近两天的问题圆满解决!
分享到:
相关推荐
### C语言编程中64位和32位机器的区别 #### 一、数据类型差异 在C语言编程中,理解不同类型在不同平台上的表现是非常重要的。C99标准并未明确规定各种基本数据类型的精确大小,而是提供了最低界限的要求。在16位、...
标题“Python-OpenNMT开源神经机器翻译系统OpenNMT的Pytorch一个移植”揭示了我们讨论的主题——OpenNMT,这是一个基于Python的开源神经机器翻译框架,其移植版是用PyTorch实现的。PyTorch是Facebook人工智能研究...
本篇文章将详细讲解如何在不使用Cygwin的情况下,将一个基于Cocos2d-x的Win32项目成功地移植到Android平台。Cygwin是一个在Windows上模拟Linux环境的工具,但在某些情况下,我们可能希望避免它的使用,例如为了减少...
本文旨在提供一个关于如何将GCC移植到新平台的指南,特别是针对i386架构的移植过程。 #### 方法概述 ##### ABI(应用二进制编程接口)的规范化 ABI定义了程序与操作系统之间交互的基本规则,包括数据类型的内存分配...
OpenCV是一个强大的计算机视觉库,广泛应用于图像处理和机器学习任务,而ESP32和ESP32S3则是专为物联网应用设计的高性能SoC,它们集成了Wi-Fi和蓝牙功能,适用于各种智能设备。 首先,OpenCV库包含了丰富的C++类和C...
标题"GRBLF407.rar_G代码_grbl_stm32 grbl 1.1_stm32f407_stm32f407移植GRBL"指出,这个压缩包包含了将GRBL 1.1版本移植到STM32F407微控制器上的相关资源。STM32F407是一款高性能、低功耗的ARM Cortex-M4内核的微...
1.24 我在一个文件中定义了一个extern数组,然后在另一个文件中使用,为什么sizeof取不到数组的大小? 声明问题 1.25 函数只定义了一次,调用了一次,但编译器提示非法重声明了。 *1.26 main的正确定义是什么...
STM32F103 GRBL代码移植是一个将开源的GRBL固件适配到STM32F103微控制器上的过程。GRBL是一种基于Arduino的、用于控制CNC机器(如3D打印机、激光切割机等)的固件,它实现了G代码解释器,能够将G代码指令转化为电机...
阿里云OSS(Object Storage Service)C-SDK交叉编译移植是将阿里云提供的C语言SDK在非目标系统上进行编译,以便在特定的目标硬件或操作系统上运行OSS相关的应用,例如物联网设备、嵌入式系统等。在这个过程中,我们...
总的来说,这个项目涉及的知识点包括但不限于:GRBL固件的工作原理、G代码解析、STM32微控制器的架构与编程、嵌入式系统的开发流程、C语言编程、硬件接口设计、实时操作系统(RTOS)概念、以及嵌入式软件调试技术。...
对于s3c2440这样的嵌入式设备,你需要一个交叉编译环境,这通常意味着在宿主机(如Linux或Windows系统)上搭建一个能够生成适用于目标板(s3c2440)的二进制文件的环境。 1. **环境准备** - 安装交叉编译工具链:...
【移植Linux内核到2440】是一个涉及嵌入式Linux系统开发的过程,主要针对友善之臂2440开发板。以下是移植步骤及关键知识点的详细说明: 1. **准备阶段** - **系统环境**:Ubuntu 9.04作为开发环境,内核版本为...
【S3C6410的Linux内核移植】是一个涉及嵌入式系统开发的重要环节,主要用于在三星S3C6410处理器上运行Linux操作系统。S3C6410是一款高性能的ARM Cortex-A8核心的微处理器,常用于高端的嵌入式设备,如智能手机、平板...
这个过程需要解决三个根本问题:设计一种较好的中间语言,设计一种对目标机恰当的机器描述,在机器描述与编译主体之间设计一种统一接口。 RTL(Register Transfer Language)是一种中间语言,用于描述计算机行为。...
在本文中,我们将深入探讨如何使用C语言矩阵库在STM32微控制器上进行操作,并了解如何将此类库轻松地移植到其他应用场景。C语言是一种广泛应用于嵌入式系统编程的强大语言,尤其在处理数学计算和实时控制时,如STM32...