extern "C" extern variable in C++ program sought as a class variable.. how to declare it?

Viewed 201

I have a code below. It is part of a dynamic shared library which is loaded by qemu program (a C program) using 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;
}

Because the variable host_virt_offset is defined in qemu (a C program), I thought it can be used inside this shared library with no problem. But when I run it, error occurs, and when I examined it using debugger, I found the code is looking for Driver::host_virt_offset instead of the global (and extern) variable host_virt_offset. I know this through below near that line.

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

I tried using ::host_virt_offset in lines a,b instead but it doesn't compile. How should I declare and use the variable host_virt_offset?

ADD : (I couldn't connect to stackoverflow for a while) I made a reproducible program. Here it works ok using method I used. So there is something else. '

<< main.c >>

#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;
}

<<bar.cpp>> <-- shared library

#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();
}
}

<< result >>

$ 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
2 Answers

The extern "C" makes no difference when declaring variables, it matters only for functions. The reason is that functions in C++ can be overlapped but variables can't.

for example:

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

In the above code, n1 and n2 are accessed the same.

This is how I solved it.
I added the variable host_virt_offset in class Driver which is different from the variable host_virt_offset which is set by qemu. And I copied the extern host_virt_offset to the class Driver's variable host_virt_offset like below.

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 .. 
}

And the original code should be changed like below.

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;
}

Now the host_virt_offset value is correct inside the class member function and print host_virt_offset in gdb command doesn't complain and shows the correct value(I think the gdb is showing the class variable at this code point).
This code is adding some offset to the qemu guest physical address to convert it to host virtual address (i.e., qemu program domain adderess). The args_ptr itself was guest physical adderss so I had to give the offset to the value too. (without it, seg-fault will arise as before.)

Related