forums.ps2dev.org Forum Index forums.ps2dev.org
Homebrew PS2, PSP & PS3 Development Discussions
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Proper C++ support in ee-gcc?

 
Post new topic   Reply to topic    forums.ps2dev.org Forum Index -> PS2 Development
View previous topic :: View next topic  
Author Message
stefan



Joined: 14 Nov 2004
Posts: 10

PostPosted: Tue Nov 16, 2004 3:33 pm    Post subject: Proper C++ support in ee-gcc? Reply with quote

What would it take to get fully functioning C++ support in GCC 3.2.2? I remember reading about crt0.s needing to call _init(), which seems easy enough to fix, but are there any other problems in the toolchain?

You don't have to be a toolchain hacker to respond, if you develop on PS2 using C++ and have run into problems please post them here. I'm willing to look into the major roadblocks so that we can have a robust C++ toolchain.

Thanks :)
Back to top
View user's profile Send private message
pixel



Joined: 30 Jan 2004
Posts: 791

PostPosted: Tue Nov 16, 2004 5:10 pm    Post subject: Reply with quote

About _init and _fini, those should be internal symbols only. That is, the usual crt mess from gcc/glibc is modular and comes in multiple files. A module file (.so under linux) may provide _init and _fini symbols, and when compiling a .so file, it will use some other crt modules providing these symbols as weak. Now I may be wrong, of course. That is such a mess ;) Well, we may try to introduce them if "really necessary" (that is, if somebody exhibits me some code which is really needing that _init symbol to be called). Well, anyway, that's not a big issue to implement... Only having to call _init and _fini in our crt0, and provide them as weak symbols into crt0.o.

IMHO, the biggest issue to support "full" C++ is the STL which requires alot of libc support. And I am one to be willing to definitively get rid of newlib's libc (I think libm may stay). Now, I don't really like STL too, and I have no idea about the problems linked to missing libc functions to our ps2sdk's libc when using STL. I think it should require "full" stream support (we are lacking the unputc functions), but apart of that, I don't know ;)
_________________
pixel: A mischievous magical spirit associated with screen displays. The computer industry has frequently borrowed from mythology. Witness the sprites in computer graphics, the demons in artificial intelligence and the trolls in the marketing department.
Back to top
View user's profile Send private message
mrbrown



Joined: 17 Jan 2004
Posts: 1536

PostPosted: Tue Nov 16, 2004 6:18 pm    Post subject: Reply with quote

_init() and _fini() are used to initialize global/static constructors and call global deconstructors in C++, so they are definitely required for "proper" C++ support. GCC also has an extension for C, where you can use the function attribute "constructor" to register a C function to be called automatically at program startup. That seems like it could be useful.

If I were adding calls to _init() and _fini() in crt0, I'd make sure I didn't break compatibility with other toolchains that don't use the _init/_fini system (e.g. the 2.9x EE toolchain). IMO, they must be added, because asking C++ developers to call _init() at the top of main() exposes parts of the compiler the developer shouldn't have to know (or care) about, and it's silly to ask such a thing when the fix is obvious. Just make sure it's a clean fix.

If you are going to be using C++ on PS2, then I strongly advise against using STL for anything. STL has the nasty habit of allocating objects behind your back, and besides GCC 3.2.2 isn't terribly efficient at removing duplicate template instantiations (IIRC) so you could end up with horribly bloated code. If you really do need STL I'd recommend using STLport.

Anyway, those are the only C++ issues I know of. It's also advisable to compile without exceptions (-fno-exceptions), as including them without using them can generate unwanted references to GCC's internal libraries. Using -fno-rtti is a good idea too (if you are trying to use dynamic casts in C++ on PS2, then you're using "too much" C++).

IMO besides the issues mentioned above, C++ support is already solid in GCC 3.2.2.
_________________
"He was warned..."
Back to top
View user's profile Send private message
Lukasz



Joined: 19 Jan 2004
Posts: 248
Location: Denmark

PostPosted: Wed Nov 17, 2004 2:42 am    Post subject: Reply with quote

Here is my solution to the global constructors fix or non dynamic created classes as N!K would say.

I came up with this since _init() was just a jr ra, nop function, maybe that is fixed now, posting it even so :)

In my link file I've put in ctors and dtors as shown below

Code:

   .text 0x100000  : {
      *(.rodata)
      *(.text)
   }
   
   .ctors ALIGN(16): { _ctors = .; *(.ctors); }
   .dtors ALIGN(16): { _dtors = .; *(.dtors); }

   .reginfo ALIGN(128) : {
      *(.reginfo)
   }


In main() I have :

Code:


typedef void (*cdtor)();

extern "C"
{
   // Global constructors/destructors, values defined in linkfile
   extern void* _ctors;
   extern void* _dtors;
}   

static u32* g_ctors = (u32*)&_ctors;
static u32* g_dtors = (u32*)&_dtors;

int main()
{
   cdtor ctor = (cdtor)*g_ctors;
   cdtor dtor = (cdtor)*g_dtors;
   
   // Call global constructors
   if(ctor) ctor();
   
..
<insert code here>
..

   // Call global destructors
   if(dtor) dtor();

   return 0;
}


This method isn't pretty, calling the constructors and destructors should really be done in crt0.s, it does however work :)
Back to top
View user's profile Send private message Visit poster's website
pixel



Joined: 30 Jan 2004
Posts: 791

PostPosted: Wed Nov 17, 2004 3:17 am    Post subject: Reply with quote

Lukasz wrote:
I came up with this since _init() was just a jr ra, nop function
Can somebody tell me where this symbol comes from exactly? :)

I mean, it's like it's provided by the linker or something... it's here in the final binary, but it seems it automagically went there, since it is not here in any file in the ps2sdk. Does the linker provide it automatically if it's not here, in order to avoid missing symbols ? Who/what should provide it anyway ? Should we provide it as a crtX.o ? I've never seen _init and _fini used elsewhere than in .so files... Seems I am getting confused there, and now I have to dig stuff ;)
_________________
pixel: A mischievous magical spirit associated with screen displays. The computer industry has frequently borrowed from mythology. Witness the sprites in computer graphics, the demons in artificial intelligence and the trolls in the marketing department.
Back to top
View user's profile Send private message
mrbrown



Joined: 17 Jan 2004
Posts: 1536

PostPosted: Wed Nov 17, 2004 3:29 am    Post subject: Reply with quote

It's in crti.o (or crtbegin.o), which is in GCC (gcc/config/mips/crti.asm and gcc/crtstuff.c).
_________________
"He was warned..."
Back to top
View user's profile Send private message
pixel



Joined: 30 Jan 2004
Posts: 791

PostPosted: Wed Nov 17, 2004 4:33 am    Post subject: Reply with quote

Damn me... why haven't I thought of it.
_________________
pixel: A mischievous magical spirit associated with screen displays. The computer industry has frequently borrowed from mythology. Witness the sprites in computer graphics, the demons in artificial intelligence and the trolls in the marketing department.
Back to top
View user's profile Send private message
Guest






PostPosted: Wed Nov 17, 2004 1:17 pm    Post subject: Reply with quote

pixel wrote:
Damn me... why haven't I thought of it.


Damn you, indeed.
Back to top
stefan



Joined: 14 Nov 2004
Posts: 10

PostPosted: Mon Nov 22, 2004 4:33 pm    Post subject: Reply with quote

Ok.

I have checked in support for C++ global/static constructors and destructors and in the process exposed a problem in how ps2sdk linked programs. First off, the change I made to crt0.s should be backwards compatible with older EE compilers if someone is still using them.

The problem was that ps2sdk used -nostartfiles in its Makefiles (Rules.make) when doing the final link of a program. This prevented crtbegin.o, crti.o, crtend.o, and crtn.o from being linked with the program. These files hold the magic of the static constructors. This is why Lukasz only saw _init() as jr $ra (I think).

Anyway, the result of me fixing that (using -mno-crt0 instead of -nostartfiles) means that normal C programs will now have .ctors and .dtors and also the _init()/_fini() magic. Note however that GCC supports static constructors and destructors in C, if you use the attributes that mrbrown mentioned (see the GCC manual). Those sections can be easily stripped from your executable, if you aren't using them (see ee-strip --help).

The only other weird thing right now is that the linker puts the destructor code before _start(). I tried a few things but couldn't fix it, but I'll get it sorted out soon enough. Please let me know if you have any problems with what I've checked in. I'd also like feedback from C++ users: get rid of the current _init()/_ctors() hack and let me know if it works for you. Thanks.
Back to top
View user's profile Send private message
mrbrown



Joined: 17 Jan 2004
Posts: 1536

PostPosted: Mon Nov 22, 2004 4:52 pm    Post subject: Reply with quote

Excellent work, stefan.

Note that stefan completed what MrHTFord started when he added support for _init and _fini to GCC 3.2.2. And if you don't like the extra sections, remember that in GCC 2.9EE instead of _init there was __main(), which was called at the top of main() regardless of whether or not your program was C or C++, or whether or not you had any global constructors. stefan's change only adds a few more bytes but brings the tools back into the state they were before I butchered them and MrHTFord started to fix them :P.

Now why did it take so long for someone to fix this?
_________________
"He was warned..."
Back to top
View user's profile Send private message
mrbrown



Joined: 17 Jan 2004
Posts: 1536

PostPosted: Mon Nov 22, 2004 4:58 pm    Post subject: Reply with quote

Sorry, one last thing: You can still specify -nostartfiles if you don't want any of the _init/_fini stuff. Just add it to EE_LDFLAGS in your Makefile.
_________________
"He was warned..."
Back to top
View user's profile Send private message
blackdroid



Joined: 17 Jan 2004
Posts: 564
Location: Sweden

PostPosted: Mon Nov 22, 2004 10:27 pm    Post subject: Reply with quote

mrbrown wrote:
Now why did it take so long for someone to fix this?


because you are the dark lord, and thee we fear.
_________________
Kung VU
Back to top
View user's profile Send private message Visit poster's website
ooPo
Site Admin


Joined: 17 Jan 2004
Posts: 2032
Location: Canada

PostPosted: Tue Nov 23, 2004 9:32 am    Post subject: Reply with quote

yea and verily
Back to top
View user's profile Send private message Visit poster's website
rasmus



Joined: 21 Jul 2004
Posts: 17
Location: Göteborg, Sweden

PostPosted: Tue Nov 23, 2004 10:05 am    Post subject: Reply with quote

The constructors and destructors are working very well for me now, thanks a lot Stefan!

The only thing I noticed is that static objects at function-level does not get destructed at all, ie

Code:

void foo() {
        static Bar x;

}


The Bar destructor is never called for x even though the constructor is called. This behaviour is differs from native gcc on Cygwin, but I suppose it doesn't matter very much. I just thought I should mention it.

Great work!
Back to top
View user's profile Send private message
mrbrown



Joined: 17 Jan 2004
Posts: 1536

PostPosted: Tue Nov 23, 2004 11:26 am    Post subject: Reply with quote

rasmus wrote:

The only thing I noticed is that static objects at function-level does not get destructed at all, ie

Code:

void foo() {
        static Bar x;

}


The Bar destructor is never called for x even though the constructor is called. This behaviour is differs from native gcc on Cygwin, but I suppose it doesn't matter very much. I just thought I should mention it.


Correct me if I'm wrong, but because you declared x as static, then it really has module-level (or global) scope, and it won't be destructed until your program exits. Can you let your program fall off via main() or call exit() and see if that's the case?

Any C++ gurus in the house? :P
_________________
"He was warned..."
Back to top
View user's profile Send private message
mharris



Joined: 25 Jan 2004
Posts: 155
Location: Annapolis, MD, USA

PostPosted: Tue Nov 23, 2004 12:38 pm    Post subject: Reply with quote

mrbrown wrote:
Correct me if I'm wrong, but because you declared x as static, then it really has module-level (or global) scope, and it won't be destructed until your program exits. Can you let your program fall off via main() or call exit() and see if that's the case?

Any C++ gurus in the house? :P
Yes, you've described what *should* happen. Although the scope of the variable is the enclosing block -- foo() in this case -- the object's lifetime is the program. The constructor can either be called the first time foo() is entered, or at program initialization (not sure if the C++ standard defines this behavior, but most implementations I've seen do it at program start).

In any event, the destructor should be called when the program completes, either by main() exiting, or by calling the exit() stdlib function (which does global destruction, as opposed to _exit(), which does not). There shouldn't be any difference in behavior between a privately-scoped static object and one with global scope.

However, in most ps2dev programs for the EE, there's a call to SleepThread() at the bottom of main() -- so main() never exits, and global dtors will not be called. This is obviously different from most other environments (i.e., ones that have an operating system to return to).

The short answer: if main() never exits, global destructors will never be called.
Back to top
View user's profile Send private message AIM Address
radad



Joined: 19 May 2004
Posts: 246
Location: Melbourne, Australia

PostPosted: Tue Nov 23, 2004 2:48 pm    Post subject: Reply with quote

mharris wrote:
The constructor can either be called the first time foo() is entered, or at program initialization (not sure if the C++ standard defines this behavior, but most implementations I've seen do it at program start).


I believe it is defined to be the first time the function is entered. I have even seen that behaviour used explicitly to ensure proper ordering of construction. The order of file scope static objects is undefined when they are in different compilation units. This behaviour gets around that.
Back to top
View user's profile Send private message
mrbrown



Joined: 17 Jan 2004
Posts: 1536

PostPosted: Tue Nov 23, 2004 4:02 pm    Post subject: Reply with quote

A note about calling exit() or returning from main() normally (without SleepThread()): At some point pukklink, and thus ps2link, was fixed up so that ps2link would handle a program falling off of main(), or one that called exit(). In the _exit() routine in crt0.s, there is cleanup code for the case where the program was executed by ps2link. This is also where stefan added code to call _fini() which will call global destructors. Because ps2sdk's libc exit() calls _exit(), global deconstructors should be called normally.

The other thing to consider is how your app will run. If you're writing a game or emulator or something along those lines, then you know you'll be running until the user hits reset. So there's never really a need to clean up behind yourself. But maybe one of these days someone will come up with a smooth way for apps to return to their parent, so that all of these ELF loading utilties that keep cropping up can execute a program and expect for it to clean up after itself before returning control to the loader.

I guess what I'm trying to say is IMO it's always a good idea to clean up after yourself. SleepThread() at the end of main() has been obsolete for some time now. :P
_________________
"He was warned..."
Back to top
View user's profile Send private message
rasmus



Joined: 21 Jul 2004
Posts: 17
Location: Göteborg, Sweden

PostPosted: Tue Nov 23, 2004 8:33 pm    Post subject: Reply with quote

I am sorry if I was unclear. I didn't expect x to be destroyed when the function exits, but when the program exits. A global instance of Bar is destructed properly after main() with Stefan's fixes. x does never get destructed though.

Please see this test program:
Code:

#ifdef _EE
#include <tamtypes.h>
#include <sifrpc.h>
#include <kernel.h>
#endif

#include <stdio.h>

class Bar {
public:
  Bar(int x) : m_x(x) {
    printf("Bar::Bar(%d)\n", x);
  }
  ~Bar() {
    printf("Bar::~Bar(%d)\n", m_x);
  }
  int m_x;
};

Bar x1(1);

void f() {
  static Bar x2(2);
  Bar x3(3);
}

int main() {
#ifdef _EE
  SifInitRpc(0);
#endif
  f();
  return 0;
}


The expected output (which I get when I compile with gcc in cygwin) is:
Code:

Bar::Bar(1)
Bar::Bar(2)
Bar::Bar(3)
Bar::~Bar(3)
End of main()
Bar::~Bar(2)
Bar::~Bar(1)


But on the PS2 the static object in the function (x2) is never destructed, even though I exit main() properly.
Code:

Bar::Bar(1)
Bar::Bar(2)
Bar::Bar(3)
Bar::~Bar(3)
End of main()
Bar::~Bar(1)
Back to top
View user's profile Send private message
mharris



Joined: 25 Jan 2004
Posts: 155
Location: Annapolis, MD, USA

PostPosted: Thu Nov 25, 2004 10:47 am    Post subject: Reply with quote

Hmm, I've seen odd stuff before when trying to trace global dtors -- since it's happening after the program ends, all bets are off. I ran into trouble a while ago on some unix machine where stdout was closed before the global dtors ran (so everything was actually working OK, it just ate my debug output).

I'm not saying that's the problem -- in fact it's probably not, since you're getting Bar::~Bar(1) in the output. But be aware that weird things can happen after program's end...
Back to top
View user's profile Send private message AIM Address
cheriff
Regular


Joined: 23 Jun 2004
Posts: 262
Location: Sydney.au

PostPosted: Sun Jun 19, 2005 11:38 am    Post subject: Reply with quote

Hey c++ ppl!
I'm trying to move some cpp code onto ps2 and it does use <vector>. I link against stlport and all seems fine. Untill I try to instansiate a class (thats has all methods implemented inline in the .h, if it makes a difference):

At the global scope:
myClass thingy;
is the line that does it. removing this and it compiles and links no worries.

this one line spews forth a barrage of undefined references:setlocale, __assert, _impure_ptr, atoi, mcSync. Even sscanf, when i don't use it myself.

the link command is:
Code:
ee-gcc -T/usr/local/ps2dev/ps2sdk/ee/startup/linkfile -L/usr/local/ps2dev/ps2sdk/ee/lib -L /usr/local/ps2dev/ps2sdk/ports/lib \
        -o demo.elf a.o b.o  -ldraw -lgraph -lmath3d -lmf -lpacket -ldma -lstdc++ -lc -lstlport -lm -lstlport -lstdc++ -lstlport -lc -lstlport -lstdc++ -lstdc++ -lc -lkernel


i know i've probably gone overboard with the -lstlport and -lstdc++ but am posting this after much trial & error (mostly error, but)

any ideas?
_________________
Damn, I need a decent signature!
Back to top
View user's profile Send private message
pixel



Joined: 30 Jan 2004
Posts: 791

PostPosted: Sun Jun 19, 2005 9:21 pm    Post subject: Reply with quote

First, consider using stlport. Second, some functions are indeed not defined, such as vsscanf, or setlocale. Just write stub functions for them, that'll do it for now...
_________________
pixel: A mischievous magical spirit associated with screen displays. The computer industry has frequently borrowed from mythology. Witness the sprites in computer graphics, the demons in artificial intelligence and the trolls in the marketing department.
Back to top
View user's profile Send private message
cheriff
Regular


Joined: 23 Jun 2004
Posts: 262
Location: Sydney.au

PostPosted: Tue Jun 21, 2005 10:24 pm    Post subject: Reply with quote

Thanks, i finally got it. I wasn't linking against libg. My link command:
Code:
ee-gcc -mno-crt0 -T/usr/local/ps2dev/ps2sdk/ee/startup/linkfile -L/usr/local/ps2dev/ps2sdk/ee/lib -L /usr/local/ps2dev/ps2sdk/ports/lib \
        -o demo.elf /usr/local/ps2dev/ps2sdk/ee/startup/crt0.o main.o interface_ps2.o modplayer.o modplayer_thread.o sjpcm_rpc.o vsync.o -lc -lstlport -lstdc++ -ldraw -lgraph -lmath3d -lmf -lpacket -ldma   -lstlport -lm -lstlport  -lstlport  -lstlport -lg -lmc -lc -lkernel

I've prolly got lots of redundant stuff in there, but this works and is good enough for me. Havent tested the stl stuff yet, but at least it compiles in just fine.

Also, in the makefile
include $(PS2SDK)/samples/Makefile.eeglobal_cpp causes bad startup code, when it gets to main():
Code:
001006b0 <main>:
  1006b0:   27bdffb0    addiu   sp,sp,-80
  1006b4:   3c040014    lui   a0,0x14
  1006b8:   ffb00000    sd   s0,0(sp)
CRASH
Address store exception. apparantly sp is 0x01ffffc0
but Makefile.eeglobal works fine, so no biggie there
_________________
Damn, I need a decent signature!
Back to top
View user's profile Send private message
pixel



Joined: 30 Jan 2004
Posts: 791

PostPosted: Wed Jun 22, 2005 3:32 am    Post subject: Reply with quote

"libg" is the debug version of newlib....... you just didn't read what I said earlier.



oh, well, after all, nevermind.
_________________
pixel: A mischievous magical spirit associated with screen displays. The computer industry has frequently borrowed from mythology. Witness the sprites in computer graphics, the demons in artificial intelligence and the trolls in the marketing department.
Back to top
View user's profile Send private message
mrbrown



Joined: 17 Jan 2004
Posts: 1536

PostPosted: Wed Jun 22, 2005 3:57 am    Post subject: Reply with quote

Actually libg is the exact same thing as libc. Some weird fruity GNU thing. I guess it's convenient when ps2sdk has a library called libc as well.
Back to top
View user's profile Send private message
pixel



Joined: 30 Jan 2004
Posts: 791

PostPosted: Wed Jun 22, 2005 4:27 am    Post subject: Reply with quote

I thought it was "the-same-but-built-without-O3", but it obviously seems I was wrong. Exactly the same binaries. *shrug*
_________________
pixel: A mischievous magical spirit associated with screen displays. The computer industry has frequently borrowed from mythology. Witness the sprites in computer graphics, the demons in artificial intelligence and the trolls in the marketing department.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    forums.ps2dev.org Forum Index -> PS2 Development All times are GMT + 10 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group