Tell me more ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I am writing a C# GUI for a C++ application of mine. I retrieve the details of a plugin by enumerating the available DLLs, dynamically loading them one by one, and calling a function called "getdescstring" which returns the plugin description concatenated into a string.

This works flawlessly on Windows. However, when I try to run it on a Linux box (I have tried it on a Linux Mint 64-bit (Mono 3.0.6), and a Xubuntu 32-bit (Mono 2.10.8) virtual machine, g++-4.7 on both) most of the returned strings are corrupted like this:

"grown|Barabasi-Albert grown network.|1,000|1.0|1.0|1.0||n|Number of nodes|numeric|k|Degree of neA\0\0…"

If I call the function from a standalone C++ test program, it works. No memory leak or corruption with Valgrind. When I try to run the whole program with Mono through Valgrind, it crashes on initialization, so I can't report on that.

So I suspect some memory corruption somewhere between Mono and the DLL, but I cannot identify the location.

Update: My intuition is that calling conventions get mixed up somehow. Since 64-bit programs have unique calling conventions, maybe Mono uses ms_abi, which may conflict with sysv_abi in Unix. However, there are no calling convention flags for these, so even if this it the problem, I cannot fix it. I can set the CC to stdcall in Mono, but g++ ignores any CC attibute on 64-bit CPUs. No. I tried setting the conventions to stdcall at both ends in a 32-bit virtual machine, but no change

Here is the code called in the DLL (the function "description" returns a std::vector of strings, but passing a std::vector directly to C# seemed quite nasty)

extern "C" const char* getdescstring()
{
vector<vector<string> > descvec=description();
string descstr;
for(unsigned i=0;i<descvec.size();i++)
{
    for(unsigned j=0;j<descvec[i].size();j++)
    {
        descstr+=descvec[i][j];
        descstr+="|";
    }
    descstr+="|";
}
return descstr.c_str();
}

And this is the receiving function which transforms the given string back:

public static List<List<string>> GetPluginDesc(string plugin)
   {
    List<List<string>> des = new List<List<string>>();
    DllLoadUtils dl;
    if (OS == platform.Windows) dl = new DllLoadUtilsWindows();
    else dl = new DllLoadUtilsLinux();
    IntPtr dllh = dl.LoadLibrary(plugin);
    if (dllh == IntPtr.Zero) return null;
    IntPtr proc = dl.GetProcAddress(dllh, "getdescstring");
    if (proc == IntPtr.Zero) return null;
    dsc descr = (dsc)Marshal.GetDelegateForFunctionPointer(proc, typeof(dsc));
    String descstr =  Marshal.PtrToStringAnsi(descr());
    string[] descelem = descstr.Split('|');
    List<string> ls = new List<string>();
    foreach (string s in descelem)
        {
            //double pipe means EOL
            if (s == "") { des.Add(ls); ls = new List<string>(); } 
            else ls.Add(s);
        }
    if (ls.Count > 0) des.Add(ls);
    dl.FreeLibrary(dllh);
    return des;
}

I have also tried this approach (modifying the delegate and the function in the DLL to accept a char* and a length as a parameter), but the results were even worse, no sensible data was transmitted:

dsc descr = (dsc)Marshal.GetDelegateForFunctionPointer (proc, typeof(dsc));
IntPtr sf = Marshal.AllocHGlobal(1024);    
descr (sf,1024);
String descstr =  Marshal.PtrToStringAnsi(sf);
Marshal.FreeHGlobal(sf);

I would appreciate any help. Thanks in advance!

I have also copied the DLL loaders and the delegate declaration here, if it can help:

interface DllLoadUtils {
            IntPtr LoadLibrary(string fileName);
            void FreeLibrary(IntPtr handle);
            IntPtr GetProcAddress(IntPtr dllHandle, string name);
        }


 public class DllLoadUtilsWindows : DllLoadUtils {
     void DllLoadUtils.FreeLibrary(IntPtr handle) {
         FreeLibrary(handle);
     }

     IntPtr DllLoadUtils.GetProcAddress(IntPtr dllHandle, string name) {
         return GetProcAddress(dllHandle, name);
     }

     IntPtr DllLoadUtils.LoadLibrary(string fileName) {
         return LoadLibrary(fileName);
     }

     [DllImport("kernel32")]
     private static extern IntPtr LoadLibrary(string fileName);

     [DllImport("kernel32.dll")]
     private static extern int FreeLibrary(IntPtr handle);

     [DllImport("kernel32.dll")]
     private static extern IntPtr GetProcAddress (IntPtr handle, string procedureName);
 }

 internal class DllLoadUtilsLinux : DllLoadUtils {
     public IntPtr LoadLibrary(string fileName) {
         return dlopen(fileName, RTLD_NOW);
     }

     public void FreeLibrary(IntPtr handle) {
         dlclose(handle);
     }

     public IntPtr GetProcAddress(IntPtr dllHandle, string name) {
         // clear previous errors if any
         dlerror();
         var res = dlsym(dllHandle, name);
         var errPtr = dlerror();
         if (errPtr != IntPtr.Zero) {
             MessageBox.Show("dlsym: " + Marshal.PtrToStringAnsi(errPtr));
         }
         return res;
     }

     const int RTLD_NOW = 2;

     [DllImport("libdl.so")]
     private static extern IntPtr dlopen(String fileName, int flags);

     [DllImport("libdl.so")]
     private static extern IntPtr dlsym(IntPtr handle, String symbol);

     [DllImport("libdl.so")]
     private static extern int dlclose(IntPtr handle);

     [DllImport("libdl.so")]
     private static extern IntPtr dlerror();
 }     

the delegate is simply:

public delegate IntPtr dsc();
share|improve this question

1 Answer

You are returning descstr.c_str() which is declared on the stack in your getdescstring function. The memory it refers to is invalid as soon as the function returns. Try making descstr.c_str() so it stays in scope (e.g. as a global variable).

share|improve this answer
Thanks for your input. I have written the following code: I know it's ugly, just wanted to try it quickly. char desc[2000]; extern "C" const char* getdescstring() { vector<vector<string> > descvec=description(); string descstr; for(unsigned i=0;i<descvec.size();i++) { for(unsigned j=0;j<descvec[i].size();j++) { descstr+=descvec[i][j]; descstr+="|"; } descstr+="|"; } strncpy(desc,descstr.c_str(),strlen(descstr.c_str())); return desc; } but it doesn't work, it also trashes the returned string in roughly 50% of the cases. Sorry for the missing breaks. – Kristóf Szalay 21 hours ago
strncpy does not null terminate the string. Try memsetting the desc buffer to binary 0 before use or else adding a binary 0 to the end of the copied string. – edtheprogrammerguy 21 hours ago
If this was the problem I should have seen the string itself completely with a lot of junk at the end, shouldn't I? But they are cut in half if not complete garbage. I did try it, but the results were the same. – Kristóf Szalay 20 hours ago

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.