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 ?

Flav
  • 73
  • 3
  • 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. – Martin York Mar 04 '21 at 17:54

1 Answers1

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)
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • 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. – Flav Mar 05 '21 at 07:47
  • @Flav 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. – Martin York Mar 05 '21 at 16:36
  • 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. – Martin York Mar 05 '21 at 16:40
  • 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. – Martin York Mar 05 '21 at 16:41