Reputation: 330
I am trying to create a Mono/.Net wrapper class around an existing C++ shared library, but I am running into issues while the unmanaged code is executing. Although I am successfully calling into the library, the unmanaged code is throwing a segmentation fault. This does not happen when I call the library function from an unmanaged application written in C++.
C++ Header code:
extern "C" void some_function();
C++ Source code:
void some_function()
{
std::vector<uint8_t> v = std::vector<uint8_t> { 0x00 };
}
C# P/Invoke code:
[DllImport("somelib.so", EntryPoint = "some_function")]
public static extern void some_function();
As you can see, there are no parameters that need marshaling, so the problem isn't with data that is being passed in. I am having this problem with several functions in this library, but I am also able to call several without any issues. Usually the segfault occurs when attempting to allocate memory (in at least one case it's for an std::vector), but not always. I have attempted to debug in gdb, and have noticed that there are 5 threads when called from C#, but only 1 when called from C++. I am also doing this with Mono on Ubuntu, if that makes a significant difference.
Is there anything I may have missed while implementing my P/Invoke calls, or is there something else going on here?
UPDATE: I have added a second, simpler example function that is having the same issue as the first.
UPDATE: I have removed the first example, and provided a simple yet full implementation of the problem. My earlier tests did not have me initializing the vector with values. I've tried several methods of initializing the vector, but none work. It seems the segfault occurs whenever memory is allocated for a new item in the vector, including during initialization.
Upvotes: 0
Views: 1414
Reputation: 11
I ran into this exact issue recently. I was unable to allocate std::vector
or std::map
without crashing to access violation when calling a function in an unmanaged DLL from a managed executable. Similarly, everything worked when calling directly from unmanaged executable.
In our case, the problem turned out to be that a wrong (and possibly broken) std DLL was being loaded at runtime when invoked from managed context, and simply removing the location of this DLL from PATH fixed the issue.
This was difficult to identify as the error message initially gave no hints what could be the cause, but after linking the standard libraries dynamically (instead of the default static linking), the access violation identified the std DLL in question, leading into the discovery of the wrong binaries in PATH.
Upvotes: 1
Reputation: 613572
As far as I can tell, the default calling convention for Mono on Unix platforms is cdecl
. Which presumably matches your C++ code. So I guess that's not the problem. Worth double-checking though. Perhaps specify the calling convention explicitly in your pinvoke.
I don't understand why you are using UnmanagedType.Struct
for the return value. So far as I can tell that is not correct. On .net that would result in the struct being marshalled as a VARIANT
which is absolutely not what you want. I'm not sure whether UnmanagedType.Struct
has any meaning for Mono pinvoke on Linux, but in any case it should not be present so you should remove it.
Nor is there any need to name the entry point. So I'd simplify the p/invoke.
[DllImport("somelib.so")]
public static extern some_struct some_function();
The other obvious potential mismatch would be between your native and managed struct definitions. You said:
As you can see, there are no parameters that need marshaling, so the problem isn't with data that is being passed in.
That's true, but what about the return value being passed out. That needs marshalling too, and you've got to get it right. Without being able to see definitions of your struct types (both native and managed), we're in no position to judge. If you cannot show them here, then you'll have to check that they match.
Finally, it is conceivable the your compiler and the pinvoke marshaller assume a different ABI for struct return values. That is always a grey area. I would always recommend simple types as return values and return your struct as an out parameter.
If I had to bet, I'd say that the problem lies in the struct definitions, which sadly you have not shown.
Update
Your question edit indicates that you have problems with a function that has void
return type. In which case the only sane conclusion is that the problem is in your native code and not in the p/invoke. To track this down further, you'll need to look at the native code.
Upvotes: 3