MWCC is a commercial compiler by Metrowerks (now owned by Freescale) as part of the CodeWarrior line of compilers and tools available for a large number of platforms. It is a proprietary compiler platform, unlike GCC, which was licensed out to developers for use on their respective consoles.
A decompilation of Mac MWCC compiler is underway.
For unstripped binaries, some versions of the linker (MWLD) may include a comment string in a .comment section that can be used to indentify that the binary has been linked with a Metrowerks linker. An example of such a string on the PS2 is the following: MW MIPS C Compiler (2.4.1.01). Note that this does not identify the exact version of MWCC used, as this version number is generated by MWLD and a version number was often shared by multiple releases of CodeWarrior. Further investigation will be required to identify the exact compiler version used.
This section is not present in GameCube releases of MWCC.
The default linker script for PS2 MWLD joins the text, data, and rodata sections into a single section called main. This can be an indicator that the binary may have been linked by MWLD.
Overlays can be identified by their headers which start with an MWo identifier.
For PS2 ELF binaries that have been compiled with debug information that has not been stripped the information is stored in a .debug section containing DWARF 1 format debug information.
GameCube/Wii releases of MWCC supported generating debug information in both DWARF 1 format as well as DWARF 2 as of version 3.0. DWARF 1 and 2 debug information utilize a different structure of sections in the linked binary.
.comment MW compiler string..comment MW compiler string..comment MW compiler string..comment MW compiler string..comment MW compiler string..comment MW compiler string..comment MW compiler string (found in SLUS-20767)..comment MW compiler string (found in SLES_50412)..comment MW compiler string..comment MW compiler string..debug section.In cases where matching a greater/lesser than if conditional that uses the AT register rather than a standard variable register, it may be required to swap the positions of the literal and the variable.
For example, take the following C code:
Example C:
if (tmp >= 0x100) {
tmp = 0xFF;
}
The above C code will generate MIPS asm that makes use of an available variable register by default. However, in order to produce the AT register to be used, the C will need to be adjusted to the following:
if (0xFF < tmp) {
tmp = 0xFF;
}
And here is a comparison of the two conditionals and the MIPS assembly output:
| if (tmp >= 0x100) | if (0xFF < tmp) |
|---|---|
| slti v0,s0,0x100 bnez at,40 nop |
slti at,s0,0x100 bnez at,40 nop |
There are cases where a function cannot be properly matched on its own. With static functions, MWCC can perform optimizations using information about the whole compilation unit.
int flowers = 0;
void plant(int count) {
flowers += count;
}
void harvest(int count) {
flowers -= count;
}
void garden(int count) {
plant(count);
harvest(count);
}
In this example, when plant is not static, MWCC treats the function as opaque and must not assume it leaves caller-saved registers such as a0 unchanged. So the compiler restores a0 from the callee-saved temporary s0 before calling harvest, as shown in the assembly below.
addiu sp, sp, -0x20
sd ra, 0x10(sp)
sq s0, 0x0(sp)
daddu s0, a0, zero
jal plant
nop
daddu a0, s0, zero
jal harvest
nop
ld ra, 0x10(sp)
lq s0, 0x0(sp)
addiu sp, sp, 0x20
jr ra
nop
However, making plant static within the same C file allows MWCC to prove that the function does not modify its argument.
static void plant(int count) {
flowers += count;
}
Then MWCC omits the daddu a0, s0, zero instructions, as a0 already holds the correct argument value.
addiu sp, sp, -0x10
sd ra, 0x0(sp)
jal plant
nop
jal harvest
nop
ld ra, 0x0(sp)
addiu sp, sp, 0x10
jr ra
nop
Note that if plant does clobber a0, MWCC cannot perform this optimization.
This behavior can arise at all optimization levels above -O0 on MWCCPS2 2.4 and 3.0.3.
When compiling C++ code, MWCC emits .init and .ctor sections implementing static initialization. Certain compiler versions, such as MWCCPSP 3.0.1 219, generate bugged symbols for these sections.
In the MWCC Mac decomp, the code generating these symbols is:
char buf[100];
Str255 fname;
COS_FileGetFSSpecInfo(&cparamblkptr->sourcefile, NULL, NULL, fname);
sprintf(buf, "__sinit_%*.*s", -fname[0], fname[0], &fname[1]);
where fname is a length-prefixed dynamic string.
In the bugged compilers, the code generating these symbols is something like:
char *buf;
char fname[0x100];
GetFileName(&sourcefile, ..., fname, 0x100);
buf = alloc(fname[0] + 10);
sprintf(buf, "__sinit_%*.*s", -fname[0], fname[0], fname);
where fname is a null-terminated string, not length-prefixed.
Because fname is not length-prefixed, static initializer symbols are padded (or truncated) based on the first character of the filename.