Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Segfault when calling function from a shared object library #151

Closed
mesbahamin opened this issue May 31, 2018 · 3 comments
Closed

Segfault when calling function from a shared object library #151

mesbahamin opened this issue May 31, 2018 · 3 comments
Labels

Comments

@mesbahamin
Copy link

I'm using glad in a file that get's compiled as a shared object library. When
I dynamically load a function from that library and call it, I get a segfault.

This segfault occurs only when the dynamically loaded function contains calls to
OpenGL functions that have been loaded by glad. If I use Glew, rather than glad,
in the library, the program works as expected.

Example

I have tried to make a reasonably minimal example that reproduces the problem.
A glfw-provided window is opened, a function is dynamically loaded from
lib.so with dlopen() and dlsym(), and that function is called. The
function just clears the screen to blue.

When glad is used, a segfault is thrown during the call to glClearColor().

Steps to Reproduce

  1. $ chmod +x build.sh && ./build.sh && ./main
  2. See segfault
  3. Uncomment the commented out lines in build.sh
  4. $ .build.sh && ./main
  5. See a nice blue screen, as intended

Structure

.
├── build.sh
├── include
│   ├── glad
│   │   └── glad.h
│   └── KHR
│       └── khrplatform.h
└── src
    ├── glad.c
    ├── lib.c
    └── main.c

4 directories, 6 files

build.sh

#/usr/bin/sh

gcc -std=c99 -Iinclude -Wall -Wextra -Wshadow \
    src/main.c src/glad.c -o main \
    -ldl -lglfw -lGL


gcc -std=c99 -Iinclude -Wall -Wextra -Wshadow -fpic -shared \
    src/lib.c src/glad.c -o lib.so \
    -ldl -lglfw -lGL

# building the library with GLEW fixes the problem
#gcc -std=c99 -Wall -Wextra -Wshadow -fpic -shared \
#    -DUSE_GLEW \
#    src/lib.c -o lib.so \
#    -ldl -lglfw -lGL -lGLEW

src/glad.c

/*

    OpenGL loader generated by glad 0.1.24a0 on Thu May 31 07:06:52 2018.

    Language/Generator: C/C++
    Specification: gl
    APIs: gl=3.3
    Profile: compatibility
    Extensions:
        
    Loader: True
    Local files: False
    Omit khrplatform: False

    Commandline:
        --profile="compatibility" --api="gl=3.3" --generator="c" --spec="gl" --extensions=""
    Online:
        http://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D3.3
*/

// ...

src/lib.c

#ifdef USE_GLEW
#include <GL/glew.h>
#else
#include <glad/glad.h>
#endif

void draw_blue_screen()
{
    glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
}

src/main.c

#include <stdio.h>
#include <stddef.h>
#include <dlfcn.h>

#include <glad/glad.h>
#include <GLFW/glfw3.h>

typedef void (draw_blue_screen_func)(void);

int main(void)
{
    if (!glfwInit())
    {
        fprintf(stderr, "GLFW initialization failed\n");
        return -1;
    }

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    GLFWwindow* window = glfwCreateWindow(800, 600, "example", NULL, NULL);
    if (!window)
    {
        fprintf(stderr, "GLFW window creation failed\n");
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);

    if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress))
    {
        fprintf(stderr, "glad initialization failed\n");
        glfwDestroyWindow(window);
        glfwTerminate();
        return -1;
    }

    // Dynamically load a function from lib.so
    void *lib_code = dlopen("./lib.so", RTLD_LAZY);
    draw_blue_screen_func *draw_blue_screen = (draw_blue_screen_func *) dlsym(lib_code, "draw_blue_screen");

    uint64_t frame_number = 0;
    while (!glfwWindowShouldClose(window))
    {
        printf("Frame %" PRIu64 "\n", frame_number);

        draw_blue_screen();

        glfwSwapBuffers(window);
        glfwPollEvents();
        frame_number++;
    }

    glfwDestroyWindow(window);
    glfwTerminate();
    return 0;
}
@mesbahamin
Copy link
Author

Also, thanks for making glad! It's been quite nice to use.

@Dav1dde
Copy link
Owner

Dav1dde commented Jun 4, 2018

Hey,

finally got time to look into this. The problem here is, lib also doesn't work with glew, it seems to work because it links against libGL and glew uses the symbols from libGL.so, compare:

/tmp/issue151 » ldd lib.so   # built with USE_GLEW    
       linux-vdso.so.1 (0x00007fff6434d000)
        libdl.so.2 => /lib/libdl.so.2 (0x00007f3a8a97e000)
        libGLEW.so.2.1 => /lib/libGLEW.so.2.1 (0x00007f3a8a6d6000)
        libc.so.6 => /lib/libc.so.6 (0x00007f3a8a31a000)
        /usr/lib64/ld-linux-x86-64.so.2 (0x00007f3a8ad84000)
        libGL.so.1 => /lib/libGL.so.1 (0x00007f3a8a08f000)
        libX11.so.6 => /lib/libX11.so.6 (0x00007f3a89d50000)
        libGLX.so.0 => /lib/libGLX.so.0 (0x00007f3a89b1f000)
        libXext.so.6 => /lib/libXext.so.6 (0x00007f3a8990d000)
        libGLdispatch.so.0 => /lib/libGLdispatch.so.0 (0x00007f3a89657000)
        libpthread.so.0 => /lib/libpthread.so.0 (0x00007f3a89439000)
        libxcb.so.1 => /lib/libxcb.so.1 (0x00007f3a89210000)
        libXau.so.6 => /lib/libXau.so.6 (0x00007f3a8900c000)
        libXdmcp.so.6 => /lib/libXdmcp.so.6 (0x00007f3a88e06000)

and

/tmp/issue151 » ldd lib.so  # built without USE_GLEW (glad)
        linux-vdso.so.1 (0x00007ffeab9c2000)
        libdl.so.2 => /lib/libdl.so.2 (0x00007efc18716000)
        libc.so.6 => /lib/libc.so.6 (0x00007efc1835a000)
        /usr/lib64/ld-linux-x86-64.so.2 (0x00007efc18b36000)

I added some additional printf's in your source files:

    printf("[lib] glClearColor: %p@%p\n", glClearColor, &glClearColor);
    printf("[lib] glClear: %p@%p\n", glClear, &glClear);
    printf("[lib] glFramebufferTexture: %p@%p\n", glFramebufferTexture, &glFramebufferTexture);
#ifdef USE_GLEW
    printf("[glew] glClearColor: %p@%p\n", __glewClearColorx, &__glewClearColorx);
#endif
[glad] glClearColor: 0x7fb491c2d720@0x55de31f07428
[glad] glClear: 0x7fb491c2d600@0x55de31f06af0
[dlsym] glClearColor: 0x7fb4939fb720@0x7fff96d92858
[dlsym] glClear: 0x7fb4939fb600@0x7fff96d92850
Frame 0
[lib] glClearColor: 0x7fb4939fb720@0x7fb4939fb720
[lib] glClear: 0x7fb4939fb600@0x7fb4939fb600
[lib] glFramebufferTexture: (nil)@0x7fb48db18050
[glew] glClearColor: (nil)@0x7fb48db13db8

As you can see glFramebufferTexture is nil as well as the internal glew symbol for glClearColor. lib.c would break the moment you use anything but OpenGL 1.0.

You can work around this by initializing OpenGL (glad or glew) in your library:

void lib_init() 
{
#ifndef USE_GLEW
    gladLoadGL();
#endif
}
    // Dynamically load a function from lib.so
    void *lib_code = dlopen("./lib.so", RTLD_LAZY);
    draw_blue_screen_func *draw_blue_screen = (draw_blue_screen_func *) dlsym(lib_code, "draw_blue_screen");
    ((void(*)()) dlsym(lib_code, "lib_init"))();

I don't know if you can do some tricks and force the library to use the already loaded symbols of your application, it may work if you dynamically link against glad/glew as a dynamic library in both files (main.c and lib.c) but honestly I am not familiar enough with POSIX nor dynamic linking.

I hope that answers your question.

@mesbahamin
Copy link
Author

Thanks for taking a look at this. After seeing working solution to my example, I ended up investigating and learning a lot more about just what glad is doing when I call gladLoadGL(). It makes sense now why the symbols weren't accessible to the shared object library.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants