【问题标题】:extern "C" extern variable in C++ program sought as a class variable.. how to declare it?extern "C" C++ 程序中的外部变量作为类变量寻求.. 如何声明它?
【发布时间】:2021-08-25 07:47:48
【问题描述】:

我在下面有一个代码。它是一个动态共享库的一部分,由 qemu 程序(一个 C 程序)使用 dlopen 加载。

extern "C" {
extern uint64_t host_virt_offset;
}

int Driver::CallSetBareMetalMode( uint64_t * args_ptr)
{
    dbg_enter();
    
    (... some codes ..)

    baremetal_axpu_es   = (struct es_t      *)(args_ptr[0] + host_virt_offset);   // line a
    baremetal_axpu_regs = (struct reg_csr_t *)(args_ptr[1] + host_virt_offset);   // line b

    dbg_leave();
    return 0;
}

因为变量host_virt_offset是在qemu(一个C程序)中定义的,所以我认为它可以在这个共享库中使用没有问题。但是当我运行它时,会发生错误,当我使用调试器检查它时,我发现代码正在寻找Driver::host_virt_offset,而不是全局(和外部)变量host_virt_offset。我从下面那条线附近知道这一点。

(gdb) print host_virt_offset
Missing ELF symbol "AXPU::host_virt_offset".

我尝试在 a、b 行中使用 ::host_virt_offset,但它无法编译。应该如何声明和使用变量host_virt_offset?

添加:(我有一段时间无法连接到 stackoverflow)我制作了一个可重现的程序。在这里使用我使用的方法可以正常工作。所以还有别的东西。 '

>

#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

uint64_t var_from_qemu = 0x12345678;

int main(void)
{
    void * dlh = dlopen("./libbar.so", RTLD_NOW);
    if (!dlh) {
        fprintf(stderr, "%s\n", dlerror());
        exit(EXIT_FAILURE); 
    }
    void (*bar)(void) = dlsym(dlh,"bar");
    if (!bar) {
        fprintf(stderr, "%s\n", dlerror());
        exit(EXIT_FAILURE); 
    }
    bar();
    return 0;
}

>

#include <stdint.h>
#include <stdio.h>

extern "C" {
extern uint64_t var_from_qemu;
}

class BC;

class BC {
public:
    void bar(void);
    BC();
    ~BC();
};

BC::BC()
{
}

BC::~BC()
{
}

void BC::bar(void)
{
    printf("class function : var_from_qemu = %lx\n", var_from_qemu);
}

extern "C" {
void bar(void)
{
printf("global function : var_from_qemu = %lx\n", var_from_qemu);
BC tmp;
tmp.bar();
}
}

>

$ g++ bar.cpp -Wall -fpic -shared -g -o libbar.so
$ gcc main.c -o main -g -ldl -rdynamic
$ main
global function : var_from_qemu = 12345678
class function : var_from_qemu = 12345678

【问题讨论】:

  • 请发布完整且未经编辑的编译器错误消息。
  • 问题一定与您的可重现示例和真实代码之间的差异有关。请尝试使您的可重现示例逐渐更像真实代码,直到您遇到同样的失败。我建议您首先仔细查看Driver 类的定义——编译器将host_virt_offset 解析为Driver::host_virt_offset 一定是有原因的,这就是原因所在。
  • 您可以提供的其他有用信息:运行nm --dynamic --no-demangle your_real_plugin.so | grep host_virt_offset 并发布完整且未经编辑的输出。
  • 继续努力。问题的根本原因在于您认为不相关的代码。这是调试时常见的认知陷阱,所以不要难过,但要重新审视你的假设。

标签: c++ c gcc linker shared-libraries


【解决方案1】:

extern "C" 在声明变量时没有区别,它只对函数很重要。 原因是C++中的函数可以重叠,但变量不能。

例如:

#include <iostream>
using namespace std;
extern "C" {
    int n1 = 1;
    
}
int n2 =2;
int main()
{
    cout<<n1<<endl<<n2<<endl;
    return 0;
}

在上述代码中,n1n2 的访问方式相同。

【讨论】:

  • 对。我想我明天会尝试你的答案,尽管我现在至少可以访问该变量。谢谢。
【解决方案2】:

我就是这样解决的。
我在Driver 类中添加了变量host_virt_offset,这与qemu 设置的变量host_virt_offset 不同。我将外部host_virt_offset 复制到Driver 类的变量host_virt_offset 中,如下所示。

extern "C" int some_function_during_the_init(...)
{
... some codes ..
AXPU::Driver *axpu_driver = AXPU::Driver::getInstance();
axpu_driver->host_virt_offset = host_virt_offset;   // copy to the class variable
... some codes .. 
}

原来的代码应该改成下面这样。

extern "C" {
extern uint64_t host_virt_offset;
}

int Driver::CallSetBareMetalMode( uint64_t * args_ptr)
{
    dbg_enter();
    
    (... some codes ..)

    baremetal_axpu_es   = (struct es_t      *)((args_ptr + host_virt_offset/8)[0] + host_virt_offset);     // line a
    baremetal_axpu_regs = (struct reg_csr_t *)((args_ptr + host_virt_offset/8)[1] + host_virt_offset);     // line b


    dbg_leave();
    return 0;
}

现在类成员函数中的 host_virt_offset 值是正确的,并且 gdb 命令中的 print host_virt_offset 不会抱怨并显示正确的值(我认为 gdb 在此代码点显示类变量)。
此代码向 qemu 客户物理地址添加一些偏移量,以将其转换为主机虚拟地址(即 qemu 程序域地址)。 args_ptr 本身就是来宾物理加法器,所以我也必须将偏移量赋予该值。 (没有它,seg-fault 会像以前一样出现。)

【讨论】:

    猜你喜欢
    • 2010-12-22
    • 1970-01-01
    • 1970-01-01
    • 2018-05-22
    • 2021-07-30
    • 2011-05-24
    • 2022-06-17
    • 1970-01-01
    • 2011-09-15
    相关资源
    最近更新 更多