Skip to main content

Notice

Please note that most of the software linked on this forum is likely to be safe to use. If you are unsure, feel free to ask in the relevant topics, or send a private message to an administrator or moderator. To help curb the problems of false positives, or in the event that you do find actual malware, you can contribute through the article linked here.
Topic: Compiling a plugin using dev-c++/gcc (Read 6940 times) previous topic - next topic
0 Members and 1 Guest are viewing this topic.

Compiling a plugin using dev-c++/gcc

warning: I'm not very good at this. If you're easily offended by stupid mistakes, you should probably leave.

Ok, a while back someone tried to get the foobar sdk to compile using dev-c++, which uses gcc as its compiler. See here. Since I'd like to try to write a plugin sometime, but don't intend to buy and install a complete microsoft ide just for that, this interested me. Unfortunately, Ricky12369 didn't get very far. I got a bit further. The compiler managed to produce a foo_albumlist.dll. However, there are some important differences between "my" foo_albumlist.dll and the one supplied with foobar:

1. Foobar's dll is about 15 KB in size, mine is about 360 KB. I knew that the missing support for _declspec(novtable) was going to make a difference, but not this kind of a difference
2. Foobar's dll (obviously) works. Mine doesn't. It crashes foobar on startup.

Now there's actually something good about that. If I put a random dll or a random textfile in foobar's directory and call it foo_albumlist.dll, foobar refuses to open it. My dll is apparently recognized as an attempt at a foobar plugin. Now if it would just not crash foobar...

In case anybody is willing and able to help me here, I've put the dll, the dev-c++ project and the buildlog online. Warning: that log is rather large. It mainly consists of warnings about "taking address of temporary" and "base class `class blah' should be explicitly initialized in the copy constructor". Also some instances of "In constructor `string_printf::string_printf(const char*, ...)' char is promoted to int when passed through `...' (so you should pass `int' not `char' to `va_arg')". There might be some "real" warnings there I've missed though. I'll go through it again tomorrow.

In order to get it to compile, I have done the following:

First the project. This consists of the imported version of the dsp file in the foo_albumlist directory, changed to produce a dll (building foo_albumlist.exe fails miserably on a missing winmain) and changed to include all the cpp files in the pfc and sdk directories, and component_client.cpp, but not profiler.cpp and directory.cpp (these two wouldn't compile, and removing them didn't produce any problems in linking).

Then the source. I had to make the following changes. BEWARE: I'm a moron. I don't know enough about c++ to be safely allowed near this kind of source. This was your final warning

0. Added newlines to the end of the files. gcc doesn't like it when they're not there.
1. string.h: the typedef for WCHAR conflicts with a typedef wchar_t WCHAR, so I commented it out. Don't know why the #ifndef WCHAR didn't catch this.
2. string.h: void xor(myChar x) changed to operator void xor(myChar x). gcc claims xor is a c++ operator, equivalent to ^. This was the problem the previous builder of this stuff ran into.
3. changed #define NOVTABLE _declspec(novtable) into #define NOVTABLE in /pfc/cfg_var.h and /pfc/pfc.h (gcc doesn't support this).
4. included wctype.h in utf8.cpp, to get towlower and towupper.
5. included ctype.h in string.cpp, to get tolower and toupper.
6. playlist_loader.cpp: added casts. This is the spot I was least sure of myself. It now reads: string8 extension = (const char*)string_extension_8(filename); and string8 ext = (const char*)string_extension_8(filename); and it doesn't compile without those casts. I don't know if it'll do what it's supposed to with them, though. I hope it does.
7. albumlist.rc: changed afxres.h into winresrc.h (suggested by google) and added #define IDC_STATIC (-1). This define was #if 0'd out in my system headers.
8. main.cpp: made metadb_enum_callback::on_entry(metadb_handle*, file_info const*) pure virtual. The function is declared as ordinary virtual function in a class with NOVTABLE, and (as far as I can see) never implemented. There is a derived class that does. Without making it pure virtual, the linker catches it as an unresolved external.

That's it, I think. Now if someone could tell me what I'm doing wrong, or that this is just never going to work because the compilers are too incompatible, I'd be happy.

Oh, before I forget: dev-c++ version 4.9.8.0, gcc version 3.2, win2k.

Thanks in advance.

Compiling a plugin using dev-c++/gcc

Reply #1
Quote from: marienZ,May 15 2003 - 04:18 PM
The compiler managed to produce a foo_albumlist.dll. However, there are some important differences between "my" foo_albumlist.dll and the one supplied with foobar:

1. Foobar's dll is about 15 KB in size, mine is about 360 KB. I knew that the missing support for _declspec(novtable) was going to make a difference, but not this kind of a difference 

Did you create a debug build or something?  I wouldn't expect the novtable declarations to matter that much, either.

Quote
2. Foobar's dll (obviously) works. Mine doesn't. It crashes foobar on startup.

My first guess would be that gcc uses different name decoration.  I would assume that there would be some way to set it so that it use msvc name decoration.

But that may not be the problem at all.  I really don't know.

Quote
Now there's actually something good about that. If I put a random dll or a random textfile in foobar's directory and call it foo_albumlist.dll, foobar refuses to open it. My dll is apparently recognized as an attempt at a foobar plugin. Now if it would just not crash foobar...

Yes, foobar should be able to find your plugin, and recognize it as a plugin, so long as the "foobar2000_get_interface" function is available (and dllexported).

Quote
In order to get it to compile, I have done the following:
...
changed to include all the cpp files in the pfc and sdk directories
...

There's why your dll is so huge.  You shouldn't be including all the cpp files in your project.  They should be compiled as two separate projects (pfc.dsp and foobar2000_sdk.dsp) and linked to your dll.  A good linker will remove the unused functions.

Quote

Then the source. I had to make the following changes. BEWARE: I'm a moron. I don't know enough about c++ to be safely allowed near this kind of source. This was your final warning

0. Added newlines to the end of the files. gcc doesn't like it when they're not there.

because gcc is, as some might put it, "gay"

Quote

1. string.h: the typedef for WCHAR conflicts with a typedef wchar_t WCHAR, so I commented it out. Don't know why the #ifndef WCHAR didn't catch this.

because "#ifdef" only catches preprocessor defines.  It has no concept of "typedef", which is handled by the C++ compiler.

Quote

2. string.h: void xor(myChar x) changed to operator void xor(myChar x). gcc claims xor is a c++ operator, equivalent to ^. This was the problem the previous builder of this stuff ran into.

Yes, I mentioned that to him, too.  I looked it up later, and yes, xor is a c++ keyword.  MSVC does not treat it as such, though.

I'm not sure if changing that could cause problems or not.  I doubt it.

Quote

3. changed #define NOVTABLE _declspec(novtable) into #define NOVTABLE in /pfc/cfg_var.h and /pfc/pfc.h (gcc doesn't support this).

I think that should be just fine.  I don't know why it would cause problems.

Quote

4. included wctype.h in utf8.cpp, to get towlower and towupper.
5. included ctype.h in string.cpp, to get tolower and toupper.

should be fine, so far as I know.  Where were those functions being linked from originally, though?

Quote

6. playlist_loader.cpp: added casts. This is the spot I was least sure of myself. It now reads: string8 extension = (const char*)string_extension_8(filename); and string8 ext = (const char*)string_extension_8(filename); and it doesn't compile without those casts. I don't know if it'll do what it's supposed to with them, though. I hope it does.

I think that's going to cause some problems.  It looks to me like you're casting object pointers as const char pointers.  Can you post the snippet of code where you changed that, along with the original?

Quote

7. albumlist.rc: changed afxres.h into winresrc.h (suggested by google) and added #define IDC_STATIC (-1). This define was #if 0'd out in my system headers.

I have no idea about this one.

Quote

8. main.cpp: made metadb_enum_callback::on_entry(metadb_handle*, file_info const*) pure virtual. The function is declared as ordinary virtual function in a class with NOVTABLE, and (as far as I can see) never implemented. There is a derived class that does. Without making it pure virtual, the linker catches it as an unresolved external.

I think that should be fine.

Quote

That's it, I think. Now if someone could tell me what I'm doing wrong, or that this is just never going to work because the compilers are too incompatible, I'd be happy.

Oh, before I forget: dev-c++ version 4.9.8.0, gcc version 3.2, win2k.

Thanks in advance.

It should be possible to get it to work.  It might be a whole lot of hassle, though.

Compiling a plugin using dev-c++/gcc

Reply #2
Thanks for the (speedy) reply.
Quote

Did you create a debug build or something? I wouldn't expect the novtable declarations to matter that much, either.

As far as I know, I didn't. It's not optimised, but it's not debug either. However, using the "strip" tool to "strip unneeded symbols" brings the size down to about 130 kb. That's better already.

Quote

My first guess would be that gcc uses different name decoration. I would assume that there would be some way to set it so that it use msvc name decoration.

But that may not be the problem at all. I really don't know.
Quote

Yes, foobar should be able to find your plugin, and recognize it as a plugin, so long as the "foobar2000_get_interface" function is available (and dllexported).

Correct me if I'm wrong, but doesn't the fact that foobar recognizes it as a plugin show that the name decoration/mangling is not the problem? Or is it possible it works for the foobar2000_get_interface, but not for any other functions?

And it looks like foobar2000_get_interface is indeed exported. At least the build process creates a def file telling me so.
Quote

There's why your dll is so huge. You shouldn't be including all the cpp files in your project. They should be compiled as two separate projects (pfc.dsp and foobar2000_sdk.dsp) and linked to your dll. A good linker will remove the unused functions.

Ah, I see now. Apparently the dsp import function is far from perfect: it tried to create the sdk and pfc as exe files, which failed. I changed the projects to create two static libraries (pfc.a and foobar2000_SDK.a), and included them in the project for foo_albumlist. I was confused because I thought the readme said I should make the plugin's project "dependend upon" the sdk's and pfc's *project*. That's not possible in dev-c++, so I just imported all the sources as a workaround.

However, linking with those 2 static libraries still produces rather large dll's.

Quote

because gcc is, as some might put it, "gay"

Agreed
Quote

because "#ifdef" only catches preprocessor defines. It has no concept of "typedef", which is handled by the C++ compiler.

Thought so.
Quote

Quote

2. string.h: void xor(myChar x) changed to operator void xor(myChar x).

I'm not sure if changing that could cause problems or not. I doubt it.

So do I.
Quote

Quote

3. changed #define NOVTABLE _declspec(novtable) into #define NOVTABLE in /pfc/cfg_var.h and /pfc/pfc.h (gcc doesn't support this).

I think that should be just fine. I don't know why it would cause problems.

Actually, it didn't cause problems. Just lots of warnings about ignored directives, since it didn't know what to do with the novtable stuff.
Quote

Quote

4. included wctype.h in utf8.cpp, to get towlower and towupper.
5. included ctype.h in string.cpp, to get tolower and toupper.

should be fine, so far as I know. Where were those functions being linked from originally, though?

Don't know. Is it possible msvc's version of windows.h includes those files?
Quote

Quote

6. playlist_loader.cpp: added casts. This is the spot I was least sure of myself. It now reads: string8 extension = (const char*)string_extension_8(filename); and string8 ext = (const char*)string_extension_8(filename); and it doesn't compile without those casts. I don't know if it'll do what it's supposed to with them, though. I hope it does.

I think that's going to cause some problems. It looks to me like you're casting object pointers as const char pointers. Can you post the snippet of code where you changed that, along with the original?


The original is just the same as I've posted here, without the casts. In the original .62 sdk, it's lines 21 and 117 of foobar2000\sdk\playlist_loader.cpp. I didn't like this either, but it looks like the compiler understands what I want it to do. I think it's not "blindly" casting, since without the "const", I still get a compile time error. Also, both string8 and string_extension_8 derive from string_base<char>. This defines operator const myChar*() const, which (I believe) allows me to safely convert it to a const char*. string8 has a constructor and assignment operator taking a const char*. I don't think I can cast the string_extension_8 to a astring8 directly. If none of this makes any sense, tell me. Perhaps someone with msvc could make this change too and see if it compiles and runs?

Quote

Quote

7. albumlist.rc: changed afxres.h into winresrc.h (suggested by google) and added #define IDC_STATIC (-1). This define was #if 0'd out in my system headers.

I have no idea about this one.

I'm not entirely sure about changing the include file, but at least it compiles. Google suggested the afxres.h is automagically added by msvc, but is really only needed when you use mfc. Which Peter doesn't, AFAIK. The define should be ok: it was in the system headers, but disabled with a comment that it should be defined by the program, not the system headers. So I defined it in the program.

Quote

Quote

8. main.cpp: made metadb_enum_callback::on_entry(metadb_handle*, file_info const*) pure virtual.

I think that should be fine.

Quote

It should be possible to get it to work. It might be a whole lot of hassle, though.

Actually, after browsing through some faq's, I'm not so sure:
Quote

How can an MSVC program call a MinGW DLL, and vice versa?
<snip>
MSVC cannot use the MinGW library

Ouch. They do describe how to make a .lib file that msvc understands (a .lib file is a "static library", right?) but that's obviously not what I want.
Well, I might mess about with this a bit further next week. It's probably "a whole lot of hassle", alright, but I consider getting, installing and using msvc "a whole lot of hassle" too

Compiling a plugin using dev-c++/gcc

Reply #3
I just did a real quick google, you might check out:
http://mywebpage.netscape.com/yongweiwu/stdcall.htm
I didn't read it too in depth, but it looks like it shows you how to make it so gcc dll's use the same naming as msvc ones by making a def file and then editing the def file to use msvc convetions.

good luck!

Compiling a plugin using dev-c++/gcc

Reply #4
Small tip for gcc and VC development: I'm not sure if dev-c++'s binutils package is the same as mingw32, but mingw32 understands the COFF object format differently than VC++, at least as far as non-initialized space is concerned. Don't try to feed VC++ compiled objects or libraries into binutils' linker.

Compiling a plugin using dev-c++/gcc

Reply #5
The quote function is b0rked again.

Quote
Correct me if I'm wrong, but doesn't the fact that foobar recognizes it as a plugin show that the name decoration/mangling is not the problem? Or is it possible it works for the foobar2000_get_interface, but not for any other functions?

And it looks like foobar2000_get_interface is indeed exported. At least the build process creates a def file telling me so.


No.  The "__cdecl __declspec(dllexport)" forces it to use the proper calling and naming conventions.

The standard naming conventions for gcc and VC are not the same at all (nor are they the same for VC and Borland C++).  This may or may not cause problems.  I couldn't really tell you.  I'm not sure exactly how virtual functions are located in C++.  If they depend on the decorated names, then yes, they are causing problems.  I think that the decorated names are stripped out at the linker-level, though, so that's probably not a problem at all.

As a side-note, you should find out what naming convention gcc uses by default.  If it's not using __cdecl, you nee to make it do so.

Quote
Ah, I see now. Apparently the dsp import function is far from perfect: it tried to create the sdk and pfc as exe files, which failed. I changed the projects to create two static libraries (pfc.a and foobar2000_SDK.a), and included them in the project for foo_albumlist. I was confused because I thought the readme said I should make the plugin's project "dependend upon" the sdk's and pfc's *project*. That's not possible in dev-c++, so I just imported all the sources as a workaround.

Yes, they should be lib files after compiling.

Quote
However, linking with those 2 static libraries still produces rather large dll's.

Using that strip functionality you mentioned is something MSVC does by default.  Use it, unless it's buggy.  It'll help.  I'm not sure if it'll bring it down to MSVC size (I doubt it).

Quote
The original is just the same as I've posted here, without the casts. In the original .62 sdk, it's lines 21 and 117 of foobar2000\sdk\playlist_loader.cpp. I didn't like this either, but it looks like the compiler understands what I want it to do. I think it's not "blindly" casting, since without the "const", I still get a compile time error. Also, both string8 and string_extension_8 derive from string_base<char>. This defines operator const myChar*() const, which (I believe) allows me to safely convert it to a const char*. string8 has a constructor and assignment operator taking a const char*. I don't think I can cast the string_extension_8 to a astring8 directly. If none of this makes any sense, tell me. Perhaps someone with msvc could make this change too and see if it compiles and runs?

I'll look into this later, but I'm pretty sure that's not a valid way to do the job.  C-style casts are pretty blind.  It just happens that they can't change the const-status of a variable (at least not in C++).

Compiling a plugin using dev-c++/gcc

Reply #6
string_base defines "operator const myChar*() const", so I'm not really sure why it's having the problem anyway.

Try just being more explicit.  Instead of trying to cast (which won't work), simply call the get_ptr() method.

i.e.
string8 extension = string_extension_8(filename);
becomes
string8 extension = string_extension_8(filename).get_ptr();

I believe that should work properly.

Compiling a plugin using dev-c++/gcc

Reply #7
I've found that MinGW32 strip tends to break executables linked with anything other than MinGW32, but maybe that's been resolved. Use at your own risk. (Should be safe for dev-c++, but not for VC++)

 

Compiling a plugin using dev-c++/gcc

Reply #8
Thanks for the replies.
Silly me forgot that that one function was forced to use the "right" calling convention. Will investigate later how to force the rest to use this.
Thanks for the warning about the strip utility.
Since I don't have msvc, I can't feed it's objects or libraries to anything. So at least that won't cause any problems
I'll really have to spend some more time to get this right. I'm a bit busy right now, so it'll have to wait for a bit.