Segmentation fault when importing a C++ shared object in Python
Asked Answered
E

1

7

I have the following shared object:

MyLib.cpp

#include <iostream>

class MyClass
{
 public:
 MyClass(){}

 void function()
 {
  std::cout << "hello" << std::endl;
  //var = 10;
 }

 private:

 int var;

};



extern "C" {
 MyClass* create()
 {
  return new MyClass();
 }


 void func(MyClass* myclass)
 {
  myclass->function();
 }

}

That I compile with: g++ -fPIC -shared -o MyLib.so MyLib.cpp

I then use it with the following Python script:

script.py

import ctypes

lib = ctypes.cdll.LoadLibrary("./MyLib.so")

MyClass = lib.create()
lib.func(MyClass)

Like this, it works perfectly, but if I uncomment the line //var = 10;, Python makes a segmentation fault (Python 3.8). This happens every time the object MyClass makes a change to one of its local variable (except inside the constructor, where it works). It looks like the address of the variable var is wrong and when accessing it, there is a segmentation fault. I tried using the keyword "virtual" for function without any change, and I tried to import the shared object in another C++ program using dlfcn, which worked fine. Any idea what is wrong ?

Etana answered 4/3, 2021 at 17:43 Comment(1)
Side not that does not answer your question: Since the users of create() and func() don't really know or care about the type of the object they use. The interface should probably return void* as an indicator of this. The func() function can the cast it back to the correct type before calling the method.Cardenas
C
5

The pointers are not the same:

extern "C" {
    MyClass* create()
    {
        MyClass* myclass = new MyClass();
        std::cerr << "Returning: " << myclass << "\n";
        return myclass;
    }


    void func(MyClass* myclass)
    {
        std::cerr << "Calling:   " << myclass << "\n";
        myclass->function();
    }

}

Running I get:

Returning: 0x7faab9c06580
Calling:   0xffffffffb9c06580

Looks like somewhere there was a sign extension issue.

You need to tell the python the type of the objects being passed around otherwise it thinks they are int and nasty things happen:

import ctypes

lib = ctypes.cdll.LoadLibrary("./MyLib.so")
lib.create.restype = ctypes.c_void_p;
lib.func.argtypes = [ ctypes.c_void_p ];


MyClass = lib.create();
lib.func(MyClass)
Cardenas answered 4/3, 2021 at 18:0 Comment(4)
Very good catch, thank you! Indeed this solves the problem, however I don't really understand what exact nasty thing happens, and why the function with the wrong pointer works when just printing a text.Etana
@Etana it works when just printing because the pointer is not used. You called a function (passing the "this" parameter) but you never use "this" so the code has no issues. When you try and use "this" to accesses a member variable "var " it is an invalid and points to some random place in memory which causes a seg fault when you try and write to it.Cardenas
The nasty thing looks like sign extension. Put a negative number in an 8 bit variable. Now copy that 8 bit variable to a 16 bit variable. You can not just copy the 8 bits as that would make the result a positive 16 bit value. You need to sign extend the value to store it in a 16 bit value (Notice above where are all the top bits are turned to f that's sign extension). It happens when you move signed values between variables of different sizes.Cardenas
Note: Integers are signed. But pointers are not signed values. So sign extending them is not doing you anything useful you are changing the value of the pointer. Effectively making it point to a completely different page.Cardenas

© 2022 - 2024 — McMap. All rights reserved.