32位应用程序转64位笔记
更新记录
正文
最近做一些32位应用程序编译到64位应用的工作,环境是centos 7.9,程序都是用c语言编写,使用gcc编译。
查看相关信息
- 查看gcc版本
1 |
|
- 查看系统位数
1 |
|
x86_64代表系统是64位的;通过查看WORD_BIT和LONG_BIT也可以判断,32位系统两者均为32,64位系统其值分别为32和64。
查看文件的位数
方法1:file filename
方法2:readelf -h filename
编译
64位的gcc,默认编译64位程序,如果需要编译32位,那么添加-m32
即可
修改原则
修改参考以下内容,后续从中选择补充到本文
实际修订的内容总结
本节内容根据实际遇到的问题编写
va_list
定义不同
va_list
在c里面用于可变参数,在32位应用场景下编译是一个指针,而在64位应用中的实现则是一个结构体。
因此,在32位程序中可以直接赋值,也可以重复使用va_list变量;但是如果进行64位编译,那么连编译都无法通过。
va_list
位于stdarg.h
查看stdarg.h
文件:
1 |
|
发现定义还是比较简单,__builtin_va_list看起来和平台有关,继续找实现没有找到很具体的。参考别人的博客linux c va_list 32位和64位的差异和自己用gdb看到的数据结构
1 |
|
基本可以确定在32位系统下,va_list
的定义如下:
1 |
|
在64位系统中,va_list是一个结构体数组,通过数组记录偏移量
1 |
|
用法修改
直接赋值
stdarg.h
提供了va_copy()
接口用于复制va_list
错误用法(指32位允许,64位不允许)
1
va_list bak = p_list;
正确的用法
1
2
3
4
5va_list bak;
va_copy(bak, p_list);
//use bak do somethine
va_end(bak);
重复使用
类似
vsnprintf()
一类的接口会修改va_list的偏移量。由于在32位系统下va_list
是一个指针,因此传入接口的值传递不影响调用方重复使用;但是在64位应用程序当中,传入va_list结构体会使得内部偏移量变化,在重复使用时就会指到错误的内存地址错误用法
1
2
3
4
5
6
7va_list p_list;
va_start(p_list, fmt);
//重复调用
vsnprintf(..., p_list);
vsnprintf(..., p_list);
va_end(p_list);正确用法
1
2
3
4
5
6
7
8
9
10va_list p_list;
va_list bak;
va_start(p_list, fmt);
//重复调用
va_copy(bak, p_list);
vsnprintf(..., p_list);
va_end(p_list);
vsnprintf(..., bak);
va_end(bak);
32位类型和64位类型的转换
在32位应用程序当中,int,long,指针均为32位;但是在64位应用程序中,int为32位,long和指针变成了64位。由于位数的变化,部分变量间的转换会出现问题。下面举例一部分不好的地方:
问题一:用void *指针存储数值
void *指针不指定类型,在32位应用中有不少图方便直接用其存储数值的使用,在编译成64位时会引发gcc警告。
解决方案一:数值存储前先转换成intptr_t类型
解决方案二:非临时变量传给void *其地址
问题二:用int型直接存储了指针
由于32位应用int和指针长度一样,可以指针存储指针,有时也做指针用int存储的操作。编译成64位应用时,指针就会被截断导致无效。如果一定要存储用intptr_t存储
问题三:只是需要布尔值
代码片段
1 |
|
此处第3个参数在函数的实现当中只是做一个布尔值的逻辑判断,此处强转为整形在64位下会引发gcc警告。可以直接使用!!对p指针做两次逻辑运算,自然获得了合法的布尔值,如下:
1 |
|
问题四:位数变化导致隐式强转结果错误
代码片段:
1 |
|
此处intptr_t在32位应用中为有符号32位整数,当编译成64位时,变成了64位有符号整数。而ntohl的返回值是无符号32位整数。
dst[0]
中存储的值为网络字节序的 -1, 转换成主机字节序的u32数据时,其值为UINT32_MAX。这个值在强制类型转换为有符号32位整数时,自动就编码成了-1,结果正确;但是在64位应用中,低32位完全可以容纳UINT32_MAX,导致其值不为 -1,使得程序逻辑出现问题。
问题五:%ld
和%lld
使用printf
指定格式的时候,64位变量在32位应用要用%lld
,但是在64位应用只需要%ld
即可。为了解决这种不一致问题,可以使用%"PRId32
和%"PRId64
代替。