Jump to content
  • Advertisement
Sign in to follow this  
Dospro

Shared objects in Linux

This topic is 3452 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi everyone. I have aproblem, i don't know how to solve. I have an abstract class, yea pure virtual, which serves as an interface. Then in a shared object i made an implementation of the base class. With another class i made all the loading things. Everything compiles fine. But when i run it, it says something like:
Quote:
ERROR: Couldn't load DPI_Render/DPI_render_OGL/libdpiogl.so DPI_Render/DPI_render_OGL/libdpiogl.so: undefined symbol: _ZN11cDPI_RenderD2Ev
where cDPI_Render is the name of my abstract class. Here are the relevant portions of my code.
//This the base class
#ifndef DPI_RENDER_DEVICE
#define DPI_RENDER_DEVICE


class cDPI_Render {
	public:
		cDPI_Render(){};
		virtual ~cDPI_Render(){};
		
		virtual bool Init(int width, int height)=0;
		virtual void ClearScreen(float r, float g, float b)=0;
		virtual void Update(void)=0;
		virtual void Release(void)=0;
};

extern "C" {
	bool createRenderDevice(cDPI_Render *dpird_ptr);
	void releaseRenderDevice(cDPI_Render *dpird_ptr);
};

#endif

I used extern "C" so the names don't get screwed up int the shared object so i could get them easily with dlsym(...) This is the code that fails:
bool cSOLoader::create(int flag, char *shName)
{
	bool (*render_ptr)(cDPI_Render *dpir_ptr);
	bool (*input_ptr)(cDPI_Input *dpii_ptr);
	bool hres;
	
	switch(flag)
	{
		case DPI_RENDER:
			sh_module[DPI_RENDER]=dlopen(shName, RTLD_NOW|RTLD_GLOBAL);/*Here it fails*/

.
.
.
.

And finally, the test program which shows how i load the library.
#include"DPI_Render/dpi_render.h"
#include<stdio.h>


int main(int argc, char *argv[])
{
	cDPI_Render *render;
	cSOLoader loader;
	printf("Iniciando\n");
	
	loader.create(DPI_RENDER, "DPI_Render/DPI_render_OGL/libdpiogl.so");
	puts("Loader creado");
	render=(cDPI_Render*)loader.getObject(DPI_RENDER);
	puts("Render creado");
	loader.release();
	puts("Adios");
	
	
	return 0;
}

Somehow the name of the abstract class gets screwed when compiling the shared object. Any ideas how to fix this?

Share this post


Link to post
Share on other sites
Advertisement
extern "C" only works for functions. Classes related symbols are always going to get mangled to encode more informations in them than just the name.

There a small command line utility that comes with binutils (and so should be available if gcc is) called c++filt that can decode a mangled c++ symbol name.

So, with that said, if you run _ZN11cDPI_RenderD2Ev through c++filt, you'll see that it is cDPI_Render::~cDPI_Render().
What probably happens is that the virtual function table for cDPI_Render in libdpiogl.so references cDPI_Render's destructor, but the actual non-inline version of the destructor hasn't been generated because it's declared inline.

A virtual function is always going to be called through the virtual function table though, so declaring them inline is pointless and makes little sense.

So my suggestion is: try to put cDPI_Render's destructor body in a cpp file instead of directly in the header file.

Share this post


Link to post
Share on other sites
Well. Testing what you said, first i commented the constructor and the destructor and it worked fine(although with possibly memory leaks). The i uncommented both, but delete the body of both.
I'm still getting the same problem.
Do i really have to create a cpp just to solve this problem? i mean, neither the constructor nor the destructor actually have any code, so, what other way, can this be solved?

EDIT:

Alright. i don't know why, but this time the code worked. I left just as i posted it above and works. ??????????
Any ideas, what's going on?

Share this post


Link to post
Share on other sites
The root of your problem is that there was no implementation of the destructor in the runtime linkloader's search path at the time it was needed.

If it didn't used to work but now works, I would suspect some procedural problem in which you are picking up older libraries at run time, and after some futzing around the newer libraries get put in place.

There is also the problem of where the class-specific code gets emitted: it's pretty arbitrary but you can force it with GCC by providing a non-inline destructor in a separate .cc file. That would eliminate the possibility that the compiler or linker is guessing (and guessing wrong) about where to emit the destructor code. Don't forget that even a default (empty) destructor may have automatically-generated code.

Share this post


Link to post
Share on other sites
Well, this time i've got some inner problems with this code.

At fisrt it seems to run fine.
But after i tried to use the implementation trhough the interface i get Segment violation.I used GDB to see what's going on.

What i've got is that in dlopen(where i open the shared library) this messages appear:

Quote:

[Thread debugging using libthread_db enabled]
[New Thread 0x7f44966db700 (LWP 5936)]
Error while reading shared library symbols:
Cannot find new threads: generic error
.
.
Program received signal SIGSEGV, Segmentation fault.



I still don't get why it can't find the correct symbols. I mean, the .so is not in a system dir, its inside the project folder hirarchy, and i load it explicitly.
Any suggestions?

Share this post


Link to post
Share on other sites
I don't really have any idea regarding that seg fault, but something occurred to me: that pure virtual class that you implement in your shared object, it's located in your executable, right?

the problem is that by default, symbols from the executable itself aren't exported and thus shared object can't import them. You have to pass the -E option to the linker when linking the executable (using -Wl,-E when invoking gcc if you link by calling the gcc front-end) for that.

There are some infos about how those things can cause issues in the gcc faq: http://gcc.gnu.org/faq.html#dso

Share this post


Link to post
Share on other sites
Quote:
Original post by Dospro
Do i really have to create a cpp just to solve this problem? i mean, neither the constructor nor the destructor actually have any code, so, what other way, can this be solved?


The problem. The ctor/dtor have not been compiled into the *.so by the compiler. Whether they do or do not have any code is entirely moot.

The solution. Somewhere in your code - at least once - you must have included that header file into a cpp file. The simple act of doing that, will allow the compiler to compile those empty ctor/dtors into empty code blocks in your so. C++ compilers do not compile header files.

As for your segfault, my guess from looking at your func prototypes is that you're doing something very dumb indeed... I'm guessing that you're are doing this in your dll:


bool createRenderDevice(cDPI_Render *dpird_ptr)
{
// the address will be lost when you exit the scope.
dpird_ptr = new dllRenderer();
return true;
}



which is pretty darned wrong if you ask me. surely it should be this... ?


bool createRenderDevice(cDPI_Render** dpird_ptr)
{
*dpird_ptr = new dllRenderer();
return true;
}



Which would explain why you seg fault since the pointer passed to createRenderDevice will still be un-initialised after the function is called.

Share this post


Link to post
Share on other sites
Quote:

The solution. Somewhere in your code - at least once - you must have included that header file into a cpp file. The simple act of doing that, will allow the compiler to compile those empty ctor/dtors into empty code blocks in your so. C++ compilers do not compile header files.


Well, actually, i include the header file inside the .so cpp.

About the wrong code, i don't understand why is the address lost?
I pass a pointer and when i use the operator new the pointer points to this noew address? Am i wrong? Why should i need double pointers?

Share this post


Link to post
Share on other sites
Quote:
Original post by Dospro
About the wrong code, i don't understand why is the address lost?
I pass a pointer and when i use the operator new the pointer points to this noew address? Am i wrong? Why should i need double pointers?


Google 'passing argument by value vs by reference in C++' and you shall have your answer. I'm guessing you skipped all of the chapters on functions and pointers.....


void func(int a)
{
a = 22;
}

int b=1;
func(b);

cout << b << endl; // 1




And that is what your code does:


void func(int* a)
{
a = new int;
}

int* b = 0;
func(b);

cout << b << endl; // prints 0




once you've understood the difference about passing args by value vs reference, you'd realise pretty quickly that you are trying to modify the value of b within func, which means you have to pass it by reference - not value as you are doing. i.e.


void func(int** a)
{
*a = new int;
}

int* b = 0;
func(&b);

cout << b << endl; // prints some random address



or even



void func(int*& a)
{
a = new int;
}

int* b = 0;
func(b);

cout << b << endl; // prints some random address


Share this post


Link to post
Share on other sites
Well. i already understand quite well, both type of argument passing.
What i didn't actually know was that pointers can actually be passed as reference.
Still, kind of confusing, but works.
Thanks, now everything works fine.
Thanks everybody.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!