├── .github └── workflows │ └── compilation.yml ├── .gitignore ├── Makefile ├── Readme.md └── main.c /.github/workflows/compilation.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | tags: 8 | - v* 9 | pull_request: 10 | repository_dispatch: 11 | types: [run_build] 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | strategy: 17 | matrix: 18 | docker_tag: [v1.0, v1.1, v1.2.0, latest] 19 | flags: [ 20 | DUMMY_LIBC_INIT=1, 21 | DUMMY_TIMEZONE=1, 22 | KERNEL_NOPATCH=1, 23 | NEWLIB_NANO=1, 24 | KERNEL_NOPATCH=1 NEWLIB_NANO=1, 25 | KERNEL_NOPATCH=1 NEWLIB_NANO=1 DUMMY_LIBC_INIT=1, 26 | KERNEL_NOPATCH=1 NEWLIB_NANO=1 DUMMY_TIMEZONE=1, 27 | DEFAULT=1 28 | ] 29 | 30 | container: ps2dev/ps2dev:${{ matrix.docker_tag }} 31 | steps: 32 | - uses: actions/checkout@v4 33 | 34 | - name: Install dependencies 35 | run: | 36 | apk add build-base 37 | 38 | - name: Compile project 39 | run: | 40 | make -j $(getconf _NPROCESSORS_ONLN) clean 41 | make -j $(getconf _NPROCESSORS_ONLN) all ${{ matrix.flags }} 42 | 43 | - name: Get short SHA 44 | id: slug 45 | run: echo "sha8=$(echo ${GITHUB_SHA} | cut -c1-8)" >> $GITHUB_OUTPUT 46 | 47 | - name: Upload artifacts 48 | if: ${{ success() }} 49 | uses: actions/upload-artifact@v4 50 | with: 51 | name: hello-${{ steps.slug.outputs.sha8 }}-${{matrix.docker_tag}}-${{ matrix.flags }} 52 | path: hello.elf 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # _____ ___ ____ ___ ____ 2 | # ____| | ____| | | |____| 3 | # | ___| |____ ___| ____| | \ PS2DEV Open Source Project. 4 | #----------------------------------------------------------------------- 5 | # Copyright 2001-2022, ps2dev - http://www.ps2dev.org 6 | # Licenced under Academic Free License version 2.0 7 | # Review ps2sdk README & LICENSE files for further details. 8 | 9 | EE_BIN = hello.elf 10 | 11 | # KERNEL_NOPATCH = 1 12 | # NEWLIB_NANO = 1 13 | 14 | EE_OBJS = main.o 15 | EE_CFLAGS += -fdata-sections -ffunction-sections 16 | EE_LDFLAGS += -Wl,--gc-sections 17 | 18 | ifeq ($(DUMMY_TIMEZONE), 1) 19 | EE_CFLAGS += -DDUMMY_TIMEZONE 20 | endif 21 | 22 | ifeq ($(DUMMY_LIBC_INIT), 1) 23 | EE_CFLAGS += -DDUMMY_LIBC_INIT 24 | endif 25 | 26 | ifeq ($(KERNEL_NOPATCH), 1) 27 | EE_CFLAGS += -DKERNEL_NOPATCH 28 | endif 29 | 30 | ifeq ($(DEBUG), 1) 31 | EE_CFLAGS += -DDEBUG -O0 -g 32 | else 33 | EE_CFLAGS += -Os 34 | EE_LDFLAGS += -s 35 | endif 36 | 37 | all: $(EE_BIN) 38 | 39 | clean: 40 | rm -rf $(EE_OBJS) $(EE_BIN) 41 | 42 | # Include makefiles 43 | include $(PS2SDK)/samples/Makefile.pref 44 | include $(PS2SDK)/samples/Makefile.eeglobal 45 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # HELLO WORLD & Binary Sizes for PS2 2 | 3 | ## Summary 4 | This repo was created with the objective of comparing binary sizes of a super dummy `elf` for `PS2`, as usual there is no better example than `Hello World!` 5 | 6 | The `ps2dev` community has been existing a lot of years, but just in the latest one (from 2019) the toolchain has been updated (`binutils, gcc and newlib`). The toolchain's upgrade bring a lot of new functionality & features, but on the other hands could bring as well bugs or unexpected behaviour. 7 | 8 | One of the thing that some PS2 Devs complained was about the binary size increased. 9 | 10 | ## Action 11 | How I was considering this size increase, something that shouldn't happen, I have been a couple of weeks investigating the root of this issue. 12 | 13 | ## Information 14 | Ideally when a binary is generated, the information that contains inside "should" be **JUST** the necessary functionality, which means the funcionality that the developer wrote in his program. 15 | 16 | Well, that sentency is partially true, let me explain it. 17 | Our program is just a hello world: 18 | ```c 19 | int main() { 20 | printf("Hello world!\n"); 21 | 22 | return 0; 23 | } 24 | ``` 25 | 26 | So, in theory the program should just contains required things to do a single `printf`, however we can't forget, that before this `main` function is called, there are tons of thing that need to be prepared in order to make this `printf` to work. Usually this "pre needed" things are know as "standard libs". In the case of the `PS2` what we have: 27 | 28 | ``` 29 | - newlib -> libc.a 30 | - ps2sdkc -> libps2sdkc.a 31 | - ps2 kernel -> libkernel.a 32 | ``` 33 | 34 | These 3 libraries, are the "guilties" of the exceed of the binary size. 35 | 36 | ## Why? 37 | These libraries contains the basic functionality to make to work every single PS2 app, and by default there are some functions that are called during initializacion of the program, in order to prepare the environment and basic functionality. 38 | 39 | Working in this way, it will work for majority of developers, they will have a environment to work with a lot of functionality ready to be used, but in cons... some devs are affected increasing binary sizes. 40 | 41 | ## How can we reduce it? 42 | So there are some ways of trying it. 43 | 44 | ### Nano newlib 45 | First of all is try to reduce the size of the libraries it self. The most critical one is `newlib`. In order to achieve this I have created a nano version of newlib. 46 | 47 | ### Override weak functions 48 | Secondly during the initalization of the `ps2sdkc` there are some `weaks` methods required by `newlib` they are: 49 | 50 | ```c 51 | void _libcglue_init(); 52 | void _libcglue_deinit(); 53 | 54 | .... 55 | void _libcglue_timezone_update(); // Called by _libcglue_init in the weak implementation 56 | ``` 57 | 58 | These functions prepare the app to use properly all the timing functionality (sleep, nanosleep, cpu_ticks, getlocaltimezone). 59 | 60 | If your app, is not going to use any functionality realted to this, you can "stop" the including of these code onto your main app. In order to do this, you can just create empty methods. 61 | 62 | If your app uses time function but is ignoring `timezones`: 63 | ```c 64 | void _libcglue_timezone_update() {} 65 | ``` 66 | 67 | If your app is not using time functions at all (this one also ignore `timezones`)): 68 | ```c 69 | void _libcglue_init() {} 70 | void _libcglue_deinit() {} 71 | ``` 72 | 73 | Adding these piece of code to your main app, will avoid to include unncesary initialization to your project. 74 | 75 | You can take a look about where these `weak functions` are here: 76 | https://github.com/ps2dev/ps2sdk/blob/master/ee/libc/src/init.c 77 | 78 | ### Kernel library 79 | The current PS2 toolchain expose 2 versions of the kerner library: 80 | ``` 81 | libkernel.a 82 | libkernel-nopatch.a 83 | ``` 84 | I'm not going to enter in details about what are they differences (honestly I did't take a look deeply), however I can tell you that if you are looking for reduce the binary size, you should give a try and use `libkernel-nopatch` 85 | 86 | ## Process 87 | In the CI (`Github Actions`), we have used the different toolchains versions, for generating the hello world example in order to compare sizes. 88 | https://github.com/fjtrujy/helloWorldPS2/actions 89 | 90 | Using the `matrix` option of `GitHub Actions` we have created all the different possibities. Here you have the different flags that we are passing to the hello world, to check sizes. The matrix also covers combinations between them. 91 | ``` 92 | DUMMY_LIBC_INIT 93 | DUMMY_TIMEZONE 94 | KERNEL_NOPATCH 95 | NEWLIB_NANO 96 | ``` 97 | 98 | ## Results 99 | Here you can check the different sizes based in the matrix option selected. 100 | 101 | | Flags/Dockers | v1.0 | v1.1 | v1.2.0 | latest | 102 | |-------------------------------------------------- |-------- |-------- |-------- |-------- | 103 | | DEFAULT=1 | 24 KB | 52.2 KB | 101 KB | 99.3 KB | 104 | | DUMMY_LIBC_INIT=1 | 21.9 KB | 51.5 KB | 53.7 KB | 30.8 KB | 105 | | DUMMY_TIMEZONE=1 | 24 KB | 52.2 KB | 54.6 KB | 31.9 KB | 106 | | KERNEL_NOPATCH=1 | 24 KB | 52.2 KB | 101 KB | 92.1 KB | 107 | | NEWLIB_NANO=1 | 24 KB | 52.2 KB | 101 KB | 39.1 KB | 108 | | KERNEL_NOPATCH=1 NEWLIB_NANO=1 | 24 KB | 52.2 KB | 101 KB | 31.9 KB | 109 | | KERNEL_NOPATCH=1 NEWLIB_NANO=1 DUMMY_TIMEZONE=1 | 24 KB | 52.2 KB | 54.6 KB | 15.2 KB | 110 | | KERNEL_NOPATCH=1 NEWLIB_NANO=1 DUMMY_LIBC_INIT=1 | 21.9 KB | 51.5 KB | 53.7 KB | 14.3 KB | 111 | 112 | ## How to use it in your project? 113 | `KERNEL_NOPATCH` and `NEWLIB_NANO` variables are ready to be used in your project if you use makefiles from `ps2sdk`. 114 | Just add in your `makefile`, 115 | ```makefile 116 | KERNEL_NOPATCH = 1 117 | ``` 118 | and/or 119 | ```makefile 120 | NEWLIB_NANO = 1 121 | ```` 122 | 123 | To override `weak functions` is up to you, just put them in your `main.c`. 124 | 125 | If you have doubts, please take a look to the `main.c` of this project. 126 | 127 | ## Conclusion 128 | The legacy toolchain was created eariler 2003 (almost 20 years ago), during that time the main tools used (binutils, gcc and newlib) have been improving and adding a lot of functionality. However this doesn't mean to increase binary sizes. 129 | 130 | One of the most important topic in the tools is to keep binaries as much as possible, having the best performance, and this is why even 20 years later just tunning up some flags and configuration we can have even a smaller binary. 131 | 132 | Let's go to move on in the same direction and keep improving our PS2 Environent. 133 | 134 | ## ENJOY! -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int printf(const char *format, ...); 4 | 5 | int main() 6 | { 7 | while (1) 8 | { 9 | printf("Hello, world!\n"); 10 | } 11 | 12 | return 0; 13 | } 14 | 15 | #if defined(DUMMY_TIMEZONE) 16 | void _libcglue_timezone_update() {} 17 | #endif 18 | 19 | #if defined(DUMMY_LIBC_INIT) 20 | void _libcglue_init() {} 21 | void _libcglue_deinit() {} 22 | void _libcglue_args_parse() {} 23 | #endif 24 | 25 | #if defined(KERNEL_NOPATCH) 26 | DISABLE_PATCHED_FUNCTIONS(); 27 | #endif --------------------------------------------------------------------------------