编译选项引发的代码异常

1.问题

最近在调用darknet动态库的时候,遇到了一个奇怪的问题,库里面的内容无法传出,但是在库里打印出的结果是正常的,经过仔细排查,发现是由于我们在调用这个libdarknet.a文件编译时候没有添加-DGPU选项,导致地址出现偏移所致。

2.举例

为了将表述得更清楚,写一个简单的代码来还原问题。

fun.h,内容如下:

#include <iostream>
void get_res(struct RESULT *res_);
struct RESULT
{
	float *res;
#ifdef GPU
	float *res_gpu;
#endif
	int n;
	int c;
	int h;
	int w;
};

fun.cpp,主要功能就是给结构体内的成员赋值:

#include "fun.h"
void get_res(RESULT *res_)
{
    res_->res = (float*)malloc(10 * sizeof(float));
    for (int i = 0; i < 10; i++)
    {
        rs_->res[i] = i;
    }
    res_->n = 1;
    res_->c = 2;
    res_->h = 3;
    res_->w = 4;
}

编译动态库
g++ -fPIC -shared -g -DGPU -o libfun.so fun.cpp

编写调用动态库代码:

#include "fun.h"

int main(int argc, char **argv)
{
    struct RESULT *result = (struct RESULT*)malloc(sizeof(struct RESULT));
    get_res(result);
    std::cout << "n:" << result->n << std::endl;
    std::cout << "c:" << result->c << std::endl;
    std::cout << "h:" << result->h << std::endl;
    std::cout << "w:" << result->w << std::endl;
    return 0;
}

首先使用-DGPU选项编译测试代码:g++ main.cpp -g -Ilib -Llib -lfun -Wl,-rpath lib -DGPU -o main
看下运行结果:

./main
n:1
c:2
h:3
w:4

输出结果和预期一致。

接下来去掉编译选项-DGPU:g++ main.cpp -g -Ilib -Llib -lfun -Wl,-rpath lib -o main

./main
n:0
c:0
h:1
w:2

很明显是错的,仔细看貌似h的结果原本是属于n,而w的结果原本是属于c的,根本原因就是-DGPU引起的地址偏移造成的。下面调试一下该代码来验证我们的猜想。

gdb ./main
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./main...done.
(gdb) b 6
Breakpoint 1 at 0x400a14: file main.cpp, line 6.
(gdb) r
Starting program: /home/gcs/work/study/diy_cnn/code/compile_opt/main

Breakpoint 1, main (argc=1, argv=0x7fffffffe398) at main.cpp:6
6	    get_res(result);
(gdb) p &result->n
$1 = (int *) 0x613c28
(gdb) p &result->c
$2 = (int *) 0x613c2c
(gdb) p &result->h
$3 = (int *) 0x613c30
(gdb) p &result->w
$4 = (int *) 0x613c34
(gdb) s
get_res (res_=0x613c20) at fun.cpp:5
5	    res_->res = (float*)malloc(10 * sizeof(float));
(gdb) p &res_->n
$5 = (int *) 0x613c30
(gdb) p &res_->c
$6 = (int *) 0x613c34
(gdb) p &res_->h
$7 = (int *) 0x613c38
(gdb) p &res_->w
$8 = (int *) 0x613c3c
(gdb)

从我们打印出的地址可以发现在进入.so之前,n,c,h,w对应的地址分别是

n:0x613c28
c:0x613c2c
h:0x613c30
w:0x613c34

而进入到.so之后,n,c,h,w的地址变成了:

n:0x613c30
c:0x613c34
h:0x613c38
w:0x613c3c

打印出更多的地址信息后,与结构体对应起来

进入库函数之前的result变量地址如下:

result------------------0x613c20
struct RESULT
{
	float *res;---------0x613c20
#ifdef GPU
	float *res_gpu;-----由于编译选项里没有定义,所以该变量无地址
#endif
	int n;--------------0x613c28
	int c;--------------0x613c2c
	int h;--------------0x613c30
	int w;--------------0x613c34
};

进入库函数之后的res_变量地址如下:

res_--------------------0x613c20
struct RESULT
{
	float *res;---------0x613c20
#ifdef GPU
	float *res_gpu;-----0x613c28
#endif
	int n;--------------0x613c30
	int c;--------------0x613c34
	int h;--------------0x613c38
	int w;--------------0x613c3c
};

通过对比地址后,已经很明显了,我们试图获取n的值时,也就是使用0x613c28地址所在值,发现在动态库根本就没有对该地址赋值,所以自然获取到的结果和预期不一样,c也是同理,再看h,当我们在动态库外边获取h的值,也就是0x613c30地址时候,可以看出在动态库里面对应的是n的值,与上述结果吻合。

3.总结
  • 我们在调用库函数时,发现结果异常时,要考虑一下是否是由编译选项引起的。
  • 我们在写库的时候,件编译选项尽量不要放在结构体中,在实现文件中使用条件编译。
  • 调用库函数使用的头文件要和编译库的头文件版本保持一直,避免库升级导致头文件结构发生变化,产生地址偏移。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值