在函数调用期间获取变量的错误地址

时间:2016-09-07 03:21:01

标签: c

在进行函数调用时,我将丢失(全局)变量的地址(在共享对象中定义)。为了证明这一点,我故意将变量num_accounts的值设置为55,当函数get_num_accounts()开始执行时,错误地接收到该变量的指针。我的证据是这个gdb会话:

accounts_init () at accounts.c:31
31      err_code=read_accounts();
(gdb) print num_accounts
$1 = 0
(gdb) print &num_accounts
$2 = (account_idx_t *) 0x7ffff767c640 <num_accounts>
(gdb) set var num_accounts=55
(gdb) print num_accounts
$3 = 55
(gdb) s
read_accounts () at accounts.c:66
66      err_code=get_num_accounts(&num_accounts);
(gdb) s
get_num_accounts (num_accounts_ptr=0x604780 <num_accounts>) at accounts.c:119
119     *num_accounts_ptr=0;
(gdb) print num_accounts
$4 = 55
(gdb) print *num_accounts_ptr
$5 = 0
(gdb) 
(gdb) print num_accounts_ptr
$6 = (account_idx_t *) 0x604780 <num_accounts>
(gdb) 

变量num_accounts的地址是 0x7ffff767c640 ,但是当执行该函数时,我得到 0x604780 ,为什么会发生这样奇怪的事情呢?

函数 get_num_accounts()的源代码是这样的:

err_code_t get_num_accounts(account_idx_t *num_accounts_ptr) {
    err_code_t err_code;
    uint32_t file_size;
    div_t div_result;
    unsigned short number;

    *num_accounts_ptr=0;
    err_code=get_dir();
    if (err_code!=ERR_NO_ERROR) return err_code;
    err_code=get_file(ACCOUNTS_FILENAME,sizeof(ACCOUNTS_FILENAME),&file_size);
    if (err_code!=ERR_NO_ERROR) return err_code;

    div_result=div(file_size,sizeof(tbl_account_t));
    if (div_result.rem!=0) {
        return ERR_BAD_CONFIG_FILE_FORMAT;
    }
    number=div_result.quot;
    *num_accounts_ptr=number;
    return ERR_NO_ERROR;
}

键入account_idx_t定义为:

typedef         unsigned short          account_idx_t;

全局变量num_accounts在accounts.c文件的开头定义:

account_idx_t       num_accounts=0;

基本上,函数的作用是获取文件的大小并在读取之前计算文件包含的记录数。 (它是一个数据库)

这是调用代码,它调用get_num_accounts()函数:

err_code_t accounts_init(void) {
    err_code_t err_code;

    err_code=read_accounts();
    if (err_code!=ERR_NO_ERROR) return err_code;

    return ERR_NO_ERROR;
}
err_code_t read_accounts(void) {
    err_code_t err_code;
    int ret;

    err_code=get_num_accounts(&num_accounts);
    if (err_code!=ERR_NO_ERROR) return err_code;
    if (num_accounts==0) return ERR_NO_ERROR;

    int fd=open(filename_buf,O_RDONLY); // filename_buf is global, it holds filename from previous call
    if (fd==-1) {
        return ERR_SYS_ERROR;
    }
    ret=read(fd,accounts,sizeof(tbl_account_t)*num_accounts);
    if (ret==-1) {
        return ERR_SYS_ERROR;
    }
    ret=close(fd);  // TO_DO: validate return value of close(fd)
    if (ret==-1) {
        return ERR_SYS_ERROR;
    }
    return ERR_NO_ERROR;
}

我正在使用-fPIC标志编译库:

[niko@dev1 src]$ make accounts.o
gcc -g -ffunction-sections -fdata-sections -Wall -Wextra -Wunreachable-code -Wmissing-prototypes -Wmissing-declarations -Wunused -Winline -Wstrict-prototypes -Wimplicit-function-declaration -Wformat -D_GNU_SOURCE -fshort-enums -fPIC -c accounts.c

没有其他&#39; num_accounts&#39;在源代码中的任何位置,我仔细检查过:

[niko@dev1 src]$ nm *o|grep num_accounts 
0000000000000000 T get_num_accounts
0000000000000000 B num_accounts
[niko@dev1 src]$ 

有关进一步调试步骤的任何建议吗?

1 个答案:

答案 0 :(得分:1)

gdb正在查看的可执行映像中有两个名为num_accounts的不同符号。 gdb直接告诉你,当你告诉它打印具有指针类型值的东西时,gdb会对可执行文件的符号表中的该地址进行反向查找,如果找到了什么,将打印出名称&lt;&gt;中的符号所以当你执行gdb命令时:

(gdb) print &num_accounts
$2 = (account_idx_t *) 0x7ffff767c640 <num_accounts>

gdb告诉您0x7ffff767c640指向名为num_accounts的符号。类似地,

(gdb) print num_accounts_ptr
$6 = (account_idx_t *) 0x604780 <num_accounts>

告诉您0x604780指向一个也称为num_accounts的符号。

现在的问题是你如何得到两个同名的符号。大地址0x7ffff767c640将位于共享库中,因为共享库会加载到这样的地址。小地址0x604780位于基本可执行文件中。