Page 1 of 1

crt0 not working correctly

Posted: Mon Jan 12, 2009 6:11 pm
by radad
crt0 is supposed to call _ps2sdk_args_parse which sets up the initial cwd according to argv[0] but it isnt doing this. To test this I added the following line to the hello sample:

Code: Select all

char cwd[256];
printf("cwd: %s\n", getcwd(cwd, 256));
And when I run the test the cwd is empty. If I add a call to _ps2sdk_args_parse at the start of main then the above code prints out the correct string proving its not been called.

Now if I remove _ps2sdk_args_parse again and comment out this line in crt0.s:

Code: Select all

.weak   _ps2sdk_args_parse
Then again the hello sample prints out the correct string. So it appears the weak attribute isnt working correctly in the compiler Im using. Im using build 28072007 from this site: http://lukasz.dk/playstation-2-programm ... -tutorial/

Is this working correctly with other peoples setup?

Posted: Mon Jan 12, 2009 6:25 pm
by radad
I have added a test to ps2sdk\ee\libc\regress so build and run this to see if it works on your system.

Posted: Mon Jan 12, 2009 6:46 pm
by Lukasz
I just tested this on Ubuntu, I modified your test_init to:

Code: Select all

static const char *test_init(void *arg)
{
    // _ps2sdk_args_parse should setup the initial cwd called from crt0.s
    char cwd[256];
    if (getcwd(cwd, 256)[0] == '\0')
        return "failed to setup cwd";
	else
		printf("(cwd: \"%s\")\n", getcwd(cwd,256));
        
    return NULL;
}
I get the following output when using PS2Link 1.46 and ps2client (latest version I think).

Code: Select all

Running 10 tests
Test 1: init (cwd: "host:")

Test 2: fopen, fclose open name host:testfiles/dummy flag 1 data 44078
open fd = 2

Test 3: fgets open name host:testfiles/dummy flag 1 data 44078
open fd = 2

Test 4: fread open name host:testfiles/dummy flag 1 data 44078
open fd = 2

Test 5: fgetc open name host:testfiles/dummy flag 1 data 44078
open fd = 2

Test 6: fseek, ftell open name host:testfiles/dummy flag 1 data 44078
open fd = 2

Test 7: stat stat() unimplemented
fn host:testfiles/dummy mode 0x0, size 0

Test 8: stat stat() unimplemented
fn host:testfiles mode 0x0, size 0
[expected directory, not regular file]
Test 9: mkdir mkdir name host:dummydir 

Test 10: rmdir rmdir name host:dummydir 


Total failures: 1

Appears to be working correctly.

Posted: Mon Jan 12, 2009 9:29 pm
by ragnarok2040
I'm getting the same behavior as you radad. When I commented out the .weak _ps2sdk_args_parse line in crt0.s, it worked. When I added a call to _ps2sdk_args_parse to main, getcwd worked.

I'm guessing what's happening is the linker doesn't explicitly look for the weak _ps2sdk_args_parse definition among library archives and since it's not explicitly called by any functions, the symbol is never actually added in the linking stage.

Posted: Mon Jan 12, 2009 9:35 pm
by radad
What compiler/OS are you using?

It is explicitly called in crt0.s, that should be enough for linker to make sure it is in there.

Posted: Mon Jan 12, 2009 10:35 pm
by ragnarok2040
Archlinux x86-64, using a 32-bit standard ps2toolchain compiler on a chroot.

The .weak pseudo-op just tells it to use the definition it has until a global definition supercedes it from user code, or another weak definition from user code, which has to be seen first, supercedes it. Otherwise the weak definition in crt0.s holds sway.

When _ps2sdk_args_parse is called by user code, then the linker searches for the symbol definition, finding the weak one in init.o. Since it finds the weak symbol linked in from init.o from libc first, it supercedes the one defined in crt0.s which is linked in later which causes the code to work.

It's just the matter of placing the other weak _ps2sdk_args_parse call somewhere so it gets linked in prior to crt0.s. Adding _ps2sdk_args_parse to _ps2sdk_stdio_init() seems to fix it, though.

Edit:
Just to make sure, I called it with some false args to see what would happen, but it parsed the args provided by ps2link correctly.

Posted: Tue Jan 13, 2009 6:29 am
by radad
Im having the same issue with _ps2sdk_stdio_init though. It too uses a weak definition in crt0.s.

Posted: Tue Jan 13, 2009 11:40 am
by ragnarok2040
Hrmm, using weak definitions in the crt0.s was a way to get the crt0.o to compile without ps2sdk as well, according to chp. Unfortunately, it depends on the proper definitions being defined prior to crt0.o being linked when compiling with ps2sdk.

Also defining the symbols in crt0.s as .globl prior to defining a .weak symbol might also be causing trouble since global definitions override weak definitions. It didn't seem to make any difference after changing them, though.

I changed it to use .weakext instead. It seems to work fine now. Your clock example was working, and only 2 tests failed, tests 6 & 8. I was getting 4 tests failing with _ps2sdk_args_parse not being defined, and 3 tests failing after that, so I guess libc wasn't being initialized quite right.

Code: Select all

# _____     ___ ____     ___ ____
#  ____|   |    ____|   |        | |____|
# |     ___|   |____ ___|    ____| |    \    PS2DEV Open Source Project.
#-----------------------------------------------------------------------
# Copyright (c) 2001-2007 ps2dev - http://www.ps2dev.org
# Licenced under Academic Free License version 2.0
# Review ps2sdk README & LICENSE files for further details.
#
# $Id: crt0.s 1466 2008-11-10 01:46:38Z oopo $
# Standard startup file.


   .weak   _init
   .type   _init, @function

   .weak   _fini
   .type   _fini, @function

   .extern   _heap_size
   .extern   _stack
   .extern   _stack_size

   .weakext  _ps2sdk_weak_args_parse, _ps2sdk_args_parse
   .globl    _ps2sdk_weak_args_parse
   .type   _ps2sdk_weak_args_parse, @function

   .weakext _ps2sdk_weak_libc_init, _ps2sdk_libc_init
   .globl   _ps2sdk_weak_libc_init
   .type   _ps2sdk_weak_libc_init, @function

   .weakext _ps2sdk_weak_libc_deinit, _ps2sdk_libc_deinit
   .globl   _ps2sdk_weak_libc_deinit
   .type   _ps2sdk_weak_libc_deinit, @function

   .set   noat
   .set   noreorder

   .text
   .align   2

   nop
   nop

   .globl   _start
   .ent   _start
_start:

zerobss:
   # clear bss area

   la   $2, _fbss
   la   $3, _end

1:
   sltu   $1, $2, $3
   beq   $1, $0, 2f
   nop
   sq   $0, ($2)
   addiu   $2, $2, 16
   j   1b
   nop
2:

   # store eventual loader arguments (passed via a0)

   la   $2, _loader_args
   sw   $4, ($2)

setupthread:
   # setup current thread

   la   $4, _gp
   la   $5, _stack
   la   $6, _stack_size
   la   $7, _args
   la   $8, _root
   move   $gp, $4
   addiu   $3, $0, 60
   syscall         # SetupThread(_gp, _stack, _stack_size, _args, _root)
   move   $sp, $2

   # initialize heap

   la   $4, _end
   la   $5, _heap_size
   addiu   $3, $0, 61
   syscall         # SetupHeap(_end, _heap_size)

   # writeback data cache

   move   $4, $0
   addiu   $3, $0, 100
   syscall         # FlushCache(0)

parseargs:
   # call ps2sdk argument parsing (weak)

   la   $8, _ps2sdk_weak_args_parse
   beqz   $8, 1f
   nop

   jal   _getargs
   nop
   jalr   $8      # _ps2sdk_weak_args_parse(argc, argv)
   nop
1:

libc_init:
   # initialize ps2sdk libc (weak)

   la   $8, _ps2sdk_weak_libc_init
   beqz   $8, 1f
   nop
   jalr   $8      # _ps2sdk_weak_libc_init()
   nop
1:

ctors:
   # call global constructors (weak)

   la   $8, _init
   beqz   $8, 1f
   nop
   jalr   $8      # _init()
   nop
1:

   # call main

   ei
   jal   _getargs
   nop
   jal   main      # main(argc, argv)
   nop

   # call _exit

   j   _exit      # _exit(retval)
   move   $4, $2
   .end   _start

   .align   3

   .globl   _exit
   .ent   _exit
   .text
_exit:
   la   $2, _retval
   sw   $4, ($2)

dtors:
   # call global destructors (weak)

   la   $8, _fini
   beqz   $8, 1f
   nop
   jalr   $8      # _fini()
   nop
1:

libc_uninit:
   # uninitialize ps2sdk libc (weak)

   la   $8, _ps2sdk_weak_libc_deinit
   beqz   $8, 1f
   nop
   jalr   $8      # _ps2sdk_weak_libc_deinit()
   nop
1:

   # conditional exit (depending on if we got arguments through the loader or not)

   la   $2, _retval
   lw   $4, ($2)

   la   $5, _loader_args
   lw   $6, ($5)
   beqz   $6, 1f
   nop

   # called from a loader, close thread

   lw   $7, ($6)
   sw   $0, ($7)   # clear thread id

   addiu   $3, $0, 36
   syscall         # ExitDeleteThread() (noreturn)
1:

   # not called from a loader, return to browser

   addiu   $3, $0, 4
   syscall         # Exit(retval) (noreturn)

   .end   _exit

   .ent   _root
_root:
   addiu   $3, $0, 35
   syscall         # ExitThread() (noreturn)
   .end   _root

   .ent   _getargs
_getargs:
   # check normal arguments

   la   $2, _args
   lw   $3, ($2)
   bnez   $3, 1f
   nop

   # check for arguments passed by a loader

   la   $2, _loader_args
   lw   $3, ($2)
   beqzl   $3, 2f
   addu   $4, $0, 0

   addiu   $2, $3, 4
1:
   lw   $4, ($2)
   addiu   $5, $2, 4
2:
   jr   $ra      # $4 = argc, $5 = argv
   nop
   .end   _getargs

   .bss
   .align   6
_args:
   .space   4+16*4+256   # argc, 16 arguments, 256 bytes payload
_loader_args:
   .space   4      # pointer to loader arguments: thread id, argc, argv
_retval:
   .space   4


Posted: Tue Jan 13, 2009 12:04 pm
by ragnarok2040
Looks like a small oversight in fseek causes it to fail when it actually works.

The line
if (ret > 0)
should be
if (ret >= 0)

Edit3:
Fseek regression test problem was related to my version of ps2client so I removed my previous code.

Posted: Thu Jan 15, 2009 7:01 pm
by radad
committed rev 1508

Posted: Thu Jan 15, 2009 7:27 pm
by radad
This has broken cpp builds for me. I get this error:

Code: Select all

C:/Devel/ps2/ps2msys/msys/local/ps2dev/ps2sdk/ee/startup/crt0.o(.text+0xd4): In
function `_start':
src/crt0.s:123: undefined reference to `_init'
C:/Devel/ps2/ps2msys/msys/local/ps2dev/ps2sdk/ee/startup/crt0.o(.text+0xd8):src/
crt0.s:123: undefined reference to `_init'
C:/Devel/ps2/ps2msys/msys/local/ps2dev/ps2sdk/ee/startup/crt0.o(.text+0x114): In
 function `_exit':
src/crt0.s:157: undefined reference to `_fini'
C:/Devel/ps2/ps2msys/msys/local/ps2dev/ps2sdk/ee/startup/crt0.o(.text+0x118):src
/crt0.s:158: undefined reference to `_fini'

Posted: Thu Jan 15, 2009 11:04 pm
by ragnarok2040
I'm pretty sure _init and _fini are defined in crti.o in ee/lib/gcc-lib/ee/3.2.2/. Are you using the -nostartfiles flag when linking instead of -mno-crt0?

The best option for building C++ projects with -nostartfiles right now is defining an empty _init and _fini in your project somewhere.

Code: Select all

extern "C" {
    void _init(void) {}
    void _fini(void) {}
}//extern "C"
Hrmm, it looks like C projects are being forced to include C++ symbols and sections as well, and fails with undefined references to _init and _fini with -nostartfiles, seems to be linker script problem. I'll examine it further.

Posted: Sat Jan 17, 2009 1:45 am
by ragnarok2040
Looks like that's normal, :?, never noticed it before, but I haven't dabbled with low-level C++ stuff. The linkmaps indicate that _init and _fini get defined normally by crti.o when using .weak _init and .weak _fini and when using the flag -mno-crt0. Changing them back to .weak shouldn't be a problem, :D.

I tested using some global class instances to make sure.

I pm'd oobles but haven't gotten a response yet. I made a patch, http://homebrew.thewaffleiron.net/ragna ... 0.patch.gz.

This should fix builds using -nostartfiles that don't rely on global objects.

Posted: Sat Jan 17, 2009 9:58 am
by radad
That fixes it, thanks. I have checked it in.

Give oobles a bit of time, he doesnt check very often.

On a slightly different topic. Anybody know why this section exists in iop/Rules.make

Code: Select all

ifeq ($(IOP_CC_VERSION),3.2.2)
CFLAGS_TARGET  = -fno-builtin
ASFLAGS_TARGET = -march=r3000
LDFLAGS_TARGET = -fno-builtin
endif
Why is version 3.2.2 special? Is this needed especially as LDFLAGS_TARGET already includes a '-fno-builtin'? Isnt '-fno-builtin' a link option anyway so why is it in CFLAGS_TARGET?

Posted: Sat Jan 17, 2009 9:56 pm
by ragnarok2040
Hrmm, weird. Since ps2sdk uses LDFLAGS for CFLAGS when linking and CFLAGS for CFLAGS when compiling object code, I'm guessing it's to ensure that gcc doesn't try to optimize code for their builtin functions during both stages. It should be part of the CFLAGS for the older toolchains as well, then. Perhaps the behavior changed where gcc-3.2.2 does some optimizing during object code generation for builtin functions.

I'm not sure why LDFLAGS_TARGET was added since "-fno-builtin" is already in the original LDFLAGS. I'm pretty sure -march wasn't supported by the mips target in older IOP toolchains and -mcpu was deprecated in gcc-3.2.2.

Changing it to:

Code: Select all

ifeq ($ (IOP_CC_VERSION), 3.2.2)
ASFLAGS_TARGET = -march=r3000
endif
And changing the flags to:

Code: Select all

IOP_CFLAGS  := -fno-builtin -O2 -G0 -c $(IOP_INCS) $(IOP_CFLAGS)
IOP_ASFLAGS := $(ASFLAGS_TARGET) -EL -G0 $(IOP_ASFLAGS)
IOP_LDFLAGS := -fno-builtin -nostdlib $(IOP_LDFLAGS)
Should work for gcc-3.2.2 and older toolchains.