Issue
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
Solution
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.)
Answered By - Chan Kim