0

Edited: ODR violation fixed.

Edited: I found that reordering the link directories (-L) fixed the problem but I don't know why.

The original order in the g++ command that was causing the problem was this:

-L/usr/local/lib
-L/usr/local/lib64
-L/usr/lib
-L/usr/lib/64
-L../../bin/Release

Moving -L../../bin/Release which contains the VampEngine.so fixed the problem, but why? Edited Ends Here

I have two projects. VampEngine (A shared lib) and Application (the client). When I compile both of them on Windows (visual c++), VampEngine links just fine with the Application but on Linux I get undefined errors (g++).

I checked if the correct flags and paths are passed in the compiler's arguments and it seem right. Also I'm pretty positive that I use extern "C" in both the dll implementation and in the client's decelerations.

Here is analytically the g++ makefile execution:


-------------- Clean: Debug|x64 in VampEngine (compiler: GNU GCC Compiler)---------------

Cleaned "VampEngine - Debug|x64"

-------------- Clean: Debug|x64 in Application (compiler: GNU GCC Compiler)---------------

Cleaned "Application - Debug|x64"

-------------- Build: Debug|x64 in VampEngine (compiler: GNU GCC Compiler)---------------

g++ -m64 -fPIC -I../../Depedencies/Cross-Plat/glm-0.9.9.5 -I/usr/include -I/usr/local/include -c /home/babaliaris/Deve/cpp/VampEngine/VampEngine/src/core.cpp -o ../../bin-int/Debug/VampEngine/x64/Debug/VampEngine/VampEngine/src/core.o
g++ -m64 -fPIC -I../../Depedencies/Cross-Plat/glm-0.9.9.5 -I/usr/include -I/usr/local/include -c /home/babaliaris/Deve/cpp/VampEngine/VampEngine/src/stb_image/stb_image.cpp -o ../../bin-int/Debug/VampEngine/x64/Debug/VampEngine/VampEngine/src/stb_image/stb_image.o
g++ -m64 -fPIC -I../../Depedencies/Cross-Plat/glm-0.9.9.5 -I/usr/include -I/usr/local/include -c /home/babaliaris/Deve/cpp/VampEngine/VampEngine/src/window.cpp -o ../../bin-int/Debug/VampEngine/x64/Debug/VampEngine/VampEngine/src/window.o
g++ -shared -L/usr/lib -L/usr/lib64 -L/usr/local/lib -L/usr/local/lib64 ../../bin-int/Debug/VampEngine/x64/Debug/VampEngine/VampEngine/src/core.o ../../bin-int/Debug/VampEngine/x64/Debug/VampEngine/VampEngine/src/stb_image/stb_image.o ../../bin-int/Debug/VampEngine/x64/Debug/VampEngine/VampEngine/src/window.o  -o ../../bin/Debug/libVampEngine.so -s -shared -m64 -L/usr/lib64  -lGL -lGLEW -lglfw
Output file is ../../bin/Debug/libVampEngine.so with size 138.57 KB

-------------- Build: Debug|x64 in Application (compiler: GNU GCC Compiler)---------------

g++ -m64 -I../../Depedencies/Cross-Plat/glm-0.9.9.5 -I../../VampEngine/src -I/usr/include -I/usr/local/include -c /home/babaliaris/Deve/cpp/VampEngine/Application/src/main.cpp -o ../../bin-int/Debug/VampEngine/x64/Debug/Application/Application/src/main.o
g++ -L/usr/lib -L/usr/lib64 -L/usr/local/lib -L/usr/local/lib64 -L../../bin/Debug -o ../../bin/Debug/Application ../../bin-int/Debug/VampEngine/x64/Debug/Application/Application/src/main.o  -s -m64 -L/usr/lib64  -lVampEngine
/usr/bin/ld: ../../bin-int/Debug/VampEngine/x64/Debug/Application/Application/src/main.o: in function `VampEngine::Core::Core(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, unsigned int, unsigned int)':
main.cpp:(.text._ZN10VampEngine4CoreC2ENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEjj[_ZN10VampEngine4CoreC5ENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEjj]+0x31): undefined reference to `Vamp_Core_Constructor'
/usr/bin/ld: ../../bin-int/Debug/VampEngine/x64/Debug/Application/Application/src/main.o: in function `VampEngine::Core::~Core()':
main.cpp:(.text._ZN10VampEngine4CoreD2Ev[_ZN10VampEngine4CoreD5Ev]+0x17): undefined reference to `Vamp_Core_Deconstructor'
/usr/bin/ld: ../../bin-int/Debug/VampEngine/x64/Debug/Application/Application/src/main.o: in function `VampEngine::Core::MainLoop()':
main.cpp:(.text._ZN10VampEngine4Core8MainLoopEv[_ZN10VampEngine4Core8MainLoopEv]+0x17): undefined reference to `Vamp_Core_MainLoop'
collect2: error: ld returned 1 exit status
Process terminated with status 1 (0 minute(s), 1 second(s))
4 error(s), 0 warning(s) (0 minute(s), 1 second(s))

Here is the core.cpp file which contains the implementation of the extern functions in the shared library:

#include "core.h"
#include "window.h"
#include <GLFW/glfw3.h>
#include "Engine/API.h"

namespace VampEngine
{

    CoreImpl::CoreImpl(std::string title, unsigned int width, unsigned int height)
        : window( new WindowImpl(title, width, height) )
    {
    }


    CoreImpl::~CoreImpl()
    {
        delete window;
    }

    void CoreImpl::MainLoop()
    {
        /* Loop until the user closes the window */
        while (!glfwWindowShouldClose(window->m_window))
        {
            /* Render here */
            glClear(GL_COLOR_BUFFER_BIT);

            /* Swap front and back buffers */
            glfwSwapBuffers(window->m_window);

            /* Poll for and process events */
            glfwPollEvents();
        }
    }
}


extern "C" VAMP_API void* Vamp_Core_Constructor(const char* title, unsigned int width, unsigned int height)
{
    return new VampEngine::CoreImpl(title, width, height);
}




extern "C" VAMP_API void Vamp_Core_Deconstructor(void* obj)
{
    VampEngine::CoreImpl *object = (VampEngine::CoreImpl*)obj;
    delete object;
}




extern "C" VAMP_API void Vamp_Core_MainLoop(void* obj)
{
    VampEngine::CoreImpl* object = (VampEngine::CoreImpl*)obj;
    object->MainLoop();
}

And this is the core.hpp which the client includes and compiles in his main.cpp file:

#ifndef VAMP_ENGINE_CORE_HPP
#define VAMP_ENGINE_CORE_HPP
#include <Engine/API.h>
#include <iostream>

extern "C"
{
    VAMP_API void* Vamp_Core_Constructor(const char* title, unsigned int width, unsigned int height);
    VAMP_API void Vamp_Core_Deconstructor(void* obj);
    VAMP_API void Vamp_Core_MainLoop(void* obj);
}

namespace VampEngine
{
    class Core
    {

    private:
        void* m_core;

    public:
        Core(std::string title, unsigned int width, unsigned int height)
            : m_core(Vamp_Core_Constructor(title.c_str(), width, height))
        {
        }

        ~Core()
        {
            Vamp_Core_Deconstructor(m_core);
        }

        void MainLoop()
        {
            Vamp_Core_MainLoop(m_core);
        }
    };
}

#endif

The VAMP_API macro is empty when compiled on Linux.

#ifndef VAMP_ENGINE_API_H
#define VAMP_ENGINE_API_H

//On Windows Platforms.
#ifdef VAMP_PLATFORM_WINDOWS
    #ifdef VAMP_BUILD_DLL
        #define VAMP_API _declspec(dllexport)
    #else
        #define VAMP_API _declspec(dllimport)
    #endif

//Unix-Based Systems (GCC).
#else
    #define VAMP_API
#endif

#endif

As you can see I use exactly the same names.

So what else can cause an undefined error?

I use arch linux x64, with gcc version:

$gcc --version
gcc (GCC) 8.3.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
babaliaris
  • 663
  • 8
  • 16
  • 2
    This is an ODR violation. You have two implementations of `VampEngine::Core::MainLoop` and your program's behavior is therefore undefined. – Miles Budnek May 18 '19 at 00:56
  • 1
    The linker gets to choose between two implementations of this function. It is much easier to get lucky on Windows, the one you want has a different name thanks to the dllimport attribute. No such luck on *nix, same name. Not a feature that was designed to increase luck: https://stackoverflow.com/a/4490536/17034 – Hans Passant May 18 '19 at 00:59
  • @MilesBudnek Thanks for mentioning ODR, I'm going to check it out, though from your comment I probably understood what it means. – babaliaris May 18 '19 at 01:24
  • @HansPassant Thank you! You made me understand why it works on Windows. I will probably have to try finding another way of solving my problem. – babaliaris May 18 '19 at 01:26
  • 1
    What does `nm -CD libVampEngine.so | grep Vamp_Core_Constructor` show? – n. m. could be an AI May 19 '19 at 05:06
  • @n.m. ```nm:libVampEngine.so: no symbols``` (Even without the filtering of grep) – babaliaris May 19 '19 at 13:20
  • 1
    Have you used `nm -CD`? My first comment version was incorrect. Have you provided the correct path to the library? – n. m. could be an AI May 19 '19 at 13:54
  • @n.m. You where right I did something wrong. This is the output: ```00000000000035bb T Vamp_Core_Constructor``` – babaliaris May 19 '19 at 14:25
  • So your library does have the nesessary symbol. Try passing `--verbose` to `ld`. – n. m. could be an AI May 19 '19 at 14:35
  • You mean when I link with g++? ```g++ -shared --verbose ... ``` – babaliaris May 19 '19 at 14:37
  • or just running on the terminal ```ld --verbose``` – babaliaris May 19 '19 at 14:40
  • It looks like you are using `-s` linker option. This might be your problem. It makes no sense whatsoever in a debug build, and it will screw up your linking. Remove it. In a release build, prefer a standalone `strip` program *at the install step* (don't do it before every target is compiled and linked). – n. m. could be an AI May 19 '19 at 14:50
  • I removed it but still getting the same errors... – babaliaris May 19 '19 at 15:09
  • @n.m. Can you read my question again? I edited it because I found a clue!!! – babaliaris May 19 '19 at 17:25
  • I still see -s everywhere. Not sure what has changed in this regard. As for the link directory order, I'm pretty sure you have an old version of your library somewhere in there. This is exactly where --verbose option was supposed to help. – n. m. could be an AI May 19 '19 at 17:29
  • I found the problem! Should I write an answer? (I'm new to stack overflow) This might help a lot of people in the future. – babaliaris May 19 '19 at 17:39
  • Accidentally I made a copy in /usr/lib of the libVampEngine.so of a previous version, so the linker was using that outdated version because /usr/lib was first getting searched by the linker before the ../../bin/Release directory that actually had the new version. – babaliaris May 19 '19 at 17:41

1 Answers1

0

Problem solved.

My code was just fine, the culprit was actually another libVampEngine.so library that was copied in /usr/lib by me. The linker was searching in the /usr/lib directory first and then the bin/ directory of my project, so instead of using the right .so file to link against, it was using the outdated one.

If anyone else is experiencing a problem like this and is almost certain that his code is fine, follow these steps:

1) Ensure that the linker is actually linking the correct shared library.
2) Check the library directories (-L) that the linker is searching for binaries.
    -Make sure these directories do not contain a binary with the exact same name as 
     your own library.
3) Be carefull of outdated binary files. If the linker is linking against 
   one but your code has been updated, even if the program links correctly you will get
   undefined behavior on runtime. 
babaliaris
  • 663
  • 8
  • 16