newlib porting challenges

Maximus32

Developer
Introduction
For a long time there have been 2 libc libraries for ps2 development: newlib and ps2sdk/ee/libc. They have lots of duplicate functionality and duplicate header files, but with different implementations. The newlib one is more standard (posix) compliant, but the ps2 port has always been incomplete and broken. While the ps2sdk one is not broken, but it's not feature complete and not very posix compliant.

To fix this issue I decided to complete the newlib port. This seems to be the best way forward, since having a good libc library also means we can compile software not written for the ps2. Normally this would require "porting" to get these programs working with all ps2 specific header files and functions. But with a good libc library, a lot of porting is no longer needed. As a good example of this I've been working together with fjtrujy to get more emulators (cores) running in the ps2 port of RetroArch. A previously difficult to compile core with lots of errors (snes9x) now compiles AND RUNS without any porting.

Currently the port is not only feature complete, but also more complete than both libc libraries together ever have been. With an accurate nanosleep function. A time function (GMT/UTC only for now). And open/read/write/close automatically mapping to fioOpen/fileXioOpen/etc... Also all directory browsing with opendit/readdir/closedir works, as well as requisting file information using stat.

The good thing is standard applications work very good, making possible many other programs on the ps2. The downside is that ps2 applications are made to work with non-standard headers and functions, so some ps2 specific projects need to be 'ported' to posix. And another problem I run into is bugs, lots of bugs. Not just in the newlib port itself, but also in other ps2 projects. These bugs where not causing crashes before, but with the newlib port they somehow are. For instance freeing the same piece of memory twice, or freeing a static piece of memory didn't cause a crash before, but it does now! I've already fixed a few of these issues, but I can't get OPL to function properly becouse of it. Delaying the introduction of the newlib port, since I think OPL compatibility is important.
I'm hoping you can help in improving the newlib port, and fix the problems with OPL.

For more info on the OPL issue, I've created an issue here:
https://gitlab.com/ps2max/ps2sdk/issues/6

To get started with the newlib port you'll need to use my development environment, which is loosly based on the development environment used for developing android. Once you get to know it you'll love it. Here goes...

Getting the sources and compiling for the first time
Extra requirements for when you already have a ps2dev environment:
  • packages: repo, cmake
  • remove all your $PS2DEV,$PS2SDK,etc,etc... environment variables
  • preferably remove or rename your /usr/local/ps2dev folder, just to be sure you're not still using it
Using my build system you can have multiple toolchains running on the same system. That's why you need to remove all your old environment variables to $PS2DEV,$PS2SDK,etc,etc. In this short tutorial I'll explain how to get the normal toolchain (with some minor additions). And then the newlib toolchain. Both toolchains can then be used at the same time.

First we need to get the old toolchain up and running:

mkdir ps2dev
cd ps2dev
repo init -u https://gitlab.com/ps2max/ps2dev-repo -b ps2max
repo sync -j4
. envsetup.sh
./build-all.sh


NOTE1: add "--depth=1" to the "repo init" command if you're not interested in the git history. This will make cloning all git repositories significantly faster.
NOTE2: add "-c --no-tags" to the repo sync command to get only the current branch without tags. This will make cloning all git repositories significantly faster.
NOTE3: The ". envsetup.sh" sets all environment variables in the current terminal session. You should ALWAYS execute this command first after opening a new terminal. This is also what makes it possible to use multiple toolchains on the same pc. Those familiar to android development will see a lot of similarities :).
NOTE4: If you watched closely you'll have seen some project being built with cmake (the green lines with the percentages). These git projects build without any changes to the sources. Not even a Makefile is added or changed to compile these libraries.

When ready this should result in not only the toolchain, but also all libraries, tools and some ps2 applications like OPL. Let's see if OPL works:

cd apps/open-ps2-loader
make clean all # already performed by ./build-all.sh but lets do this anyway
make run # will send opl.elf to your ps2 using ps2client
make sim # will try to start opl.elf using PCSX2


Now that we have the basics working we can continue to the newlib port. Open a second terminal session:
mkdir ps2dev-newlib

cd ps2dev-newlib
repo init -u https://gitlab.com/ps2max/ps2dev-repo -b move-libc-to-newlib
repo sync -j4
. envsetup.sh
./build-all.sh


You'll now have all my latest newlib work compiled. Try again if OPL works in the same way as described above. HINT: do not use a HIRES resolution, and do not load any artwork (jpg).

Working with repo and the toolchain(s)
Updating to the latest changes is simple, assuming you don't have any local changes:
repo sync # will fetch all the latest sources
./build-all.sh # will rebuild everything, so only use when needed


Making changes and using the power of repo has the benefit of still being able to "repo sync" after making changes:
repo start my-fantastic-changes --all # create a branch in ALL git repositories, yes, do it!
# make some changes... in whatever git project...
git add X
git commit -m "Fantastic!"


Now to see in what projects you made your changes:
repo branch # view ALL branches in ALL projects YOU made
repo status # view ALL uncommitted changes in ALL projects
repo info -o # view the commits you made in ALL projects ahead of upstream, I love this command


If you want to sync your branch to whatever changes are made 'upstream':
repo sync # will fetch all the latest sources, and !REBASE! your work on top of it
./build-all.sh # will rebuild everything, so only use when needed


Good luck testing the newlib toolchain. If there's anything I can improve let me know. If you find any bugs you can report them in this thread or https://gitlab.com/ps2max/ps2sdk/issues. If you fix something you can also send a pull request https://gitlab.com/ps2max. If I don't respond, leave me a message here, becouse I'm not sure if all notification settings are setup correcly on gitlab ;).
 
Last edited by a moderator:
Thanks, @Maximus32 for your great collaboration improving our PS2 DEV environment & experience.

As you mentioned before, it is amazing now how easy is to compile POSIX software compliant, as for instance RetroArch.

Just to give you an overview, if I would start today, to fully port Retroarch from the scratch, it just could take a couple of hours, and not a few weeks as it was before.

I will try today to play around OPL and see if I can help to find unexpected behaviours.

Once again, thanks!
 
I just rebased both open-ps2-loader branches on top of the latest ifcaro/master. Just so we can be working with the latest version on this, and becouse I forgot to push my latest open-ps2-loader changes ;).

Since it's rebased, the normal "git pull" of "repo sync" commands will produce errors if you've already checked out the sources. If you've already synced all sources, you can update using:
cd apps/open-ps2-loader
git fetch ps2max
git reset --hard ps2max/move-libc-to-newlib # if you're in the newlib repo
git reset --hard ps2max/master-ps2max # if you're in the normal repo


For the next couple of weeks I hope I won't need to rebase again so we can keep development simple (it's already complicated enough as it is).
 
The bug in OPL, where loading a jpeg file would crash OPL, has been fixed by @fjtrujy in ps2sdk-ports/libjpeg. Dynamically allocated memory was not cleared, causing a pointer to be !=NULL. Freeing this pointer caused the crash.

This is not the first dynamic memory bug that's been fixed in the newlib port. Somehow it seems the old ps2sdk malloc/free was much more resistent against programming bugs. So on the one hand it's a good thing these bugs are now visible and fixed. But on the other hand it's also causing a lot of headaches...

On to the next bug...
 
Good work!

I once wrote a few function wrappers that check malloc memory overwriting (of the memory just before and after the buffer) and multiple calls to free() and also can list all currently allocated memory so it gives some basic statistics and can this way check if anything was not freed. This proved extremely helpful to me, as several program I wrote had multiple mistakes of this kind, so I think this might be of some use to you. There probably are better tools for that out there, but I haven't looked.

The overwriting check is done by placing four identical structures (each 0x10 in size) around the data - two before the start and two after the end. The second set is inverted, which makes it easier to see if overwriting really did happen.
The code makes free() very slow, because its wrapper checks all allocations for errors, but this has the positive side of finding bugs earlier.
It also prints both the name of the caller and the PC of the call, though for printing the name, the caller code must be recompiled (the call is made into a macro).
The code is attached. I have only tested it on PC, and I have not had any problems with it.
 

Attachments

...and it would also not find 'faulty functions' or those which have no boundary-checking (strcpy() and what not...) and other 'tests'!

However... I think @wisi's wrapper could be useful once in a while (+ function-checking could be added).
 
@wisi, thanks. I was looking for something like this a couple of weeks ago. So basically I can replace all calls to malloc with mallocStatAAlloc, and all calls to free with mallocStatAFree.

I only have to add something for calloc and memalign. Then check all libraries opl uses, and opl itself.
 
Yes. Though malloc has to be a macro, if you want to see the caller's name too. If that can't be done for some reason, then you can give a NULL or empty string for the name, but it has to be static if it is an empty string.

I have no idea how __builtin_return_address(0); works - most likely it reads $ra at the time, but if the function was called with a 'j' instruction, it will get the last jal.

The code near the very end:

#define malloc(x) mallocAndChkIn((x), (char*)__func__)
void *mallocAndChkIn(int sz, char *callerFuncName) {
void *cp = __builtin_return_address(0);
void *bf = mallocStatAAlloc(sz, cp, callerFuncName, 0); // arg 4 = 0: alloc without clearing
return bf;
}

int free(void *bf) {
mallocStatAFree(bf);
return 0;
}


mallocStatAFree() doesn't free NULL buffers, so if you want to detect them too, just add a printf or some other function to warn if the given buffer is NULL there.

After the program completes (or at any point in its run) you can call mallocStatAPrtAllocs(); and it will print all allocations that weren't freed and the amount of memory allocated (note that it prints them in reverse order - the most recent ones - first) . It also does the same check mallocStatAFree() does (mallocStatACheck) so it will also give info about any buffer getting overwritten before its beginning or after its end. Of course there is no way to detect overwrites outside the checked areas (0x20 before and after the buffer) so it it is some array that is not linearly filled, writing outside the buffer can go undetected.

The error reported when a buffer was freed more than once or somehow got missing from the linked-list is printf("\n %s: Entry found NOT one time = %d ", __func__, found); - and it prints the number of times that entry was found. So if you free some buffer that wasn't allocated or was already freed, this will print 0, and if something went very wrong with the linked-list it might print >1 (though that can probably never happen).

The functions require 0x4-alignment of the buffer, and I am not sure if the core *alloc function used ever returns something with lower alignment (probably not).
 
Update for those not watching the progress on gitlab and github:
- A jpeg bug has been fixed by @fjtrujy
- rmdir function was missing and has been added by @fjtrujy
- I've just added a docker build for the toolchain
- Latest newlib based OPL is now automatically built by docker image and can be downloaded from... errr still not allowed to post links
 
I've few errors with this new 1417 test version.
  1. OPL will not launch when 720p or 1080i res has been set previously in settings.
  2. I'm getting BSOD when I'll try to set 720p in settings.
  3. I'm getting strange video glitches when I'll set 1080i in settings
  4. I don't know why, but sometimes OPL will freeze when I want to save settings:
    new-error-OPL.png

    I do not fully remember what I've set\change in settings.
    However settings suppose to be saved on HDD, didn't tried MC so far.

Tested on SCPH-50004.
 
Last edited:
  • Like
Reactions: TnA
There was a problem with newlib not being built with the -G0 compiler flag. This is now fixed here:
https://gitlab.com/ps2max/ps2dev-scripts/commit/859311911319c5f01e8a6628fc54287dc2bd9314

Unfortunately this caused OPL to not start at all (black screen). I'm still trying to find the root-cause of this issue, but it looks like another random problem, not related to the -G0 change or the newlib port. So far I have found that "audsrv_init" fails. It is waiting forever for the IOP RPC service to become available, here:
https://github.com/ps2dev/ps2sdk/bl...df5070502/ee/rpc/audsrv/src/audsrv_rpc.c#L310

Commenting out the audio initialisation in OPL fixed the black screen, but no sfx obviously.

To be continued.
 
Silly question, do I need a gitlab account to send a pr or can one come from a github?

Also, does std::cout work or does it still need fixing?
 
Last edited:
Silly question, do I need a gitlab account to send a pr or can one come from a github?

Also, does std::count work or does it still need fixing?
You need a Gitlab account. However, you can use OAuth to log in with GitHub or other accounts.

std::count works, and also std::cout.
 
  • Like
Reactions: TnA
To create a gitlab pull request you need a gitlab account. But if you have changes on github or somewhere else we can also discuss them somewhere else (here for instance), and then I can merge the changes to gitlab.

I'm not aware of a std::count problem, but I think it's not newlib related. It's probably in the stlport library inside the ps2sdk-ports repository. It's an old port that needs to be updated. The newlib port also has problems compiling the stlport library.

Verstuurd vanaf mijn SM-G930F met Tapatalk
 
  • Like
Reactions: TnA
You need a Gitlab account. However, you can use OAuth to log in with GitHub or other accounts.

std::count works, and also std::cout.

To create a gitlab pull request you need a gitlab account. But if you have changes on github or somewhere else we can also discuss them somewhere else (here for instance), and then I can merge the changes to gitlab.

I'm not aware of a std::count problem, but I think it's not newlib related. It's probably in the stlport library inside the ps2sdk-ports repository. It's an old port that needs to be updated. The newlib port also has problems compiling the stlport library.

Verstuurd vanaf mijn SM-G930F met Tapatalk

Damn phones autocorrect meant std::cout lol.
 
I'm attempting to debug the newlib fork of OPL on my DTL-T10000, but it doesn't even show anything on the screen after 1 minute.
Code:
[julian@devtool julian]$ dsedb -r
dsedb (Version 1.23.1 Wed Jun  4 13:16:01 JST 2003)
*** Resetting...
EE DECI2 Manager version 0.06 Oct 31 2003 19:02:01
  CPUID=2e14, BoardID=4126, ROMGEN=2005-1124, 128M
*** DBGP Version 3.20
(type `help' for getting help, or `help help' for getting help of help)
dsedb S> run opl.elf
Loading program (address=0x00100000 size=0x0027919c) ...
Loading 7649 symbols ...
Entry address = 0x00100108
GP value      = 0x00381010
*** Resetted
*** No Connect
*** Resetted
         
EE DECI2 Manager version 0.06 Oct 31 2003 19:02:01
  CPUID=2e14, BoardID=4126, ROMGEN=2005-1124, 128M
         
dsedb R> break
*** Breaked
 at=80020000  v0-1=000d499e,fffffffe  a0-3=00000006,00000185,00000004,00000044
 t0-7=00000000,a00267d0,00000040,a0026780, 203b8540,a0000000,00000004,0000001f
 s0-7=00390000,00395b40,00000000,00000000, 00000000,00000000,00000000,00000000
 t8=80025980 t9=00030400   k0=00000080 k1=00000000   gp=00381010 sp=07ffecd0
 fp=00000000 ra=00124330   lo=00000000 hi=00000018   sa=00000000 PC=00124358
 badvaddr=00005eb0 badpaddr=181c2440
 $cause   = 0x70008800 [ BD2 CE3 EXC2=Reset IP7 IP3 EXC="External Interrupt" ]
 $status  = 0x70030c13 [ Cu210 EDI EIE IM3 IM2 KSU=User EXL IE ]
  0x00124350: 0x00000000  nop
  0x00124354: 0x00000000  nop
->0x00124358: 0x00000000  nop
  0x0012435c: 0x2442ffff  addiu   $v0,$v0,-1
  0x00124360: 0x00000000  nop
  0x00124364: 0x1443fff8  bne     $v0,$v1,0x00124348
  0x00124368| 0x00000000  nop
dsedb S> bt
 $BT0=0x00124358
dsedb S>
Code:
Get Reboot Request From EE
*** Resetted
         
Update rebooting.. reset parameter for IOP=00000000_00000100
cdvdman Init
  use alternate ROM image
*** Resetted
         
Update reboot complete
         
IOP Realtime Kernel Ver.0.9.1
    Copyright 1999 (C) Sony Computer Entertainment Inc. 
         
IOP DECI2 manager Version 0.9.1
    Copyright 1999 (C) Sony Computer Entertainment Inc. 
         
DECI2 manager start.
Reboot service module.(99/11/10)
cdvd driver module version 0.1.1 (C)SCEI
Load File service.(99/11/05)
Multi Threaded Fileio module.(99/11/15) 
iop heap service (99/11/03)
fileXio: fileXio RPC Server v1.00
Copyright (c) 2003 adresd
Pad Driver for OSD
Poweroff installed
FreeUsbd v.0.1.2
USB HDD FileSystem Driver v1.6
ISOFS driver start.
FREESD v1.01
audsrv: greetings from version 0.92 !
audsrv_adpcm_init()
audsrv: rpc server thread 0xc3cf29 started
audsrv: creating rpc server
dsidb R> break
*** Breaked
 at=00000000  v0-1=00000000,00000000  a0-3=00000000,00000000,00000000,00000000
 t0-7=00000000,00000000,00000000,00000000, 00000000,00000000,00000000,00000000
 s0-7=00000000,00000000,00000000,00000000, 00000000,00000000,00000000,00000000
 t8=00000000 t9=00000000   k0=0000ae94 k1=00000000   gp=000191f0 sp=007ff0e0
 fp=007ff0e0 ra=0000bc6c   lo=00000000 hi=00000000   PC=0000ae98 bada=00001003
 $cr=0xc0000400 [ BD BT CE0 IP0 External interrupt ]
 $sr=0x00000404 [ IM0 IEp ]
  0x0000ae90| 0x27bd0018  addiu   $sp,$sp,0x18
  0x0000ae94: 0x08002ba5  j       0x0000ae94
->0x0000ae98| 0x00000000  nop
  0x0000ae9c: 0x24020020  li      $v0,0x20
  0x0000aea0: 0x0000000c  syscall 0x0
  0x0000aea4: 0x03e00008  jr      $ra
  0x0000aea8| 0x00000000  nop
dsidb S>
 

Similar threads

Back
Top