grumpycoders / pcsx-redux

The PCSX-Redux project is a collection of tools, research, hardware design, and libraries aiming at development and reverse engineering on the PlayStation 1. The core product itself, PCSX-Redux, is yet another fork of the Playstation emulator, PCSX.

Home Page:https://pcsx-redux.consoledev.net

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

PSYQ object conv fails on certain global var access patterns

MrSapps opened this issue · comments

Describe the bug

Converting some PSYQ objects with the PSYQ obj converter tool fails with: Compiler error: :: Conversion failed: Two-part relocation missing its second part.

Expected behavior

Object should convert without any errors.

Steps to reproduce the bug

Compile the following code to an object using PSYQ 4.1 (gcc 2.7.2) or PSYQ 4.6 (gcc 2.95.2) (note it MUST have -O2 passed else it will not repro).

extern int global_array[ 3 ];
extern int global_var;
extern int another_global;

void Func1(void)
{
    if ( another_global == 15 )
    {
        global_var = 3;
    }

    another_global = global_array[1];
}

Convert this object using the psyq object conv tool. This then fails.

Operating System

Any OS the PSYQ obj conv tool is built for

PCSX-Redux version

N/A - tool is separate from the emulator but happens in every version of it to date.

CPU model

N/A

GPU model & Drivers

N/A

BIOS version

N/A

Options

  • Dynarec CPU
  • 8MB
  • OpenGL GPU
  • Fastboot
  • Debugger

Iso checks

No response

Logs

Compiler error: :: Conversion failed: Two-part relocation missing its second part
:: Converting /tmp/object.o.obj to /tmp/object.o...
:: Generating sections
:: Generating section .text
:: Generating relocations - pass 1, local only
:: Generating relocation HI16 .text::00000000 another_global
:: Skipped for this pass
:: Generating relocation LO16 .text::00000004 another_global
:: Skipped for this pass
:: Generating relocation HI16 .text::00000010 global_array + 4
:: Skipped for this pass
:: Generating relocation HI16 .text::00000014 global_var
:: Skipped for this pass
:: Generating relocation LO16 .text::0000001c global_var
:: Skipped for this pass
:: Generating relocation HI16 .text::00000020 global_array + 4
:: Skipped for this pass
:: Generating relocation LO16 .text::00000024 global_array + 4
:: Skipped for this pass
:: Generating relocation LO16 .text::0000002c another_global
:: Skipped for this pass
:: Generating symbols
:: Generating symbol global_array 0 0
:: Generating symbol global_var 0 0
:: Generating symbol Func1 0 2
:: Generating symbol another_global 0 0
:: Generating relocations - pass 2, globals only
:: Generating relocation HI16 .text::00000000 another_global
:: Generating relocation LO16 .text::00000004 another_global
:: Generating relocation HI16 .text::00000010 global_array + 4
:: Delaying relocation, waiting for LO16
:: Generating relocation HI16 .text::00000014 global_var

Additional information

Doesn't happen with all compiler versions, you can easily test with this decomp.me scratch here: https://decomp.me/scratch/9p1HB

commented

Here is two objects files for compare, one has the relocations and trigger the error, the other is the final version without the relocations.
reloc_bug.zip

The contents of the object file in the zip file doesn't seem to match the fragment of source code you've given here. Could you give the faulty .obj file generated by the code you've shared in the bug?

commented

Here it is:
Func1.zip

Ah fuck, I see what the compiler is doing. It's trying to be too clever for its own good, and ends up emitting bad code anyway.

The list of relocations for this file is the following:

  :: Relocations

    type            section::offset    expression
    ------------------------------------------
    HI16              .text::00000000  another_global
    LO16              .text::00000004  another_global
    HI16              .text::00000010  global_array + 4
    HI16              .text::00000014  global_var
    LO16              .text::0000001c  global_var
    HI16              .text::00000020  global_array + 4
    LO16              .text::00000024  global_array + 4
    LO16              .text::0000002c  another_global

The bytes for the .text section is the following blob:

000000: 00 00 04 3c 00 00 83 8c 0f 00 02 24 05 00 62 14  ...<.......$..b.
000010: 00 00 02 3c 00 00 03 3c 03 00 02 24 00 00 62 ac  ...<...<...$..b.
000020: 00 00 02 3c 00 00 42 8c 08 00 e0 03 00 00 82 ac  ...<..B.........

Which disassembles to the following, taking relocations into account:

0x00:  00 00 04 3C    lui   $a0, 0              %hi(another_global)
0x04:  00 00 83 8C    lw    $v1, 0($a0)         %lo(another_global)
0x08:  0F 00 02 24    li    $v0, 15
0x0c:  05 00 62 14    bne   $v1, $v0, 0x24
0x10:  00 00 02 3C    lui   $v0, 0              %hi(global_array + 4)
0x14:  00 00 03 3C    lui   $v1, 0              %hi(global_var)
0x18:  03 00 02 24    li    $v0, 3
0x1c:  00 00 62 AC    sw    $v0, 0($v1)         %lo(global_var)
0x20:  00 00 02 3C    lui   $v0, 0              %hi(global_array + 4)
0x24:  00 00 42 8C    lw    $v0, 0($v0)         %lo(global_array + 4)
0x28:  08 00 E0 03    jr    $ra
0x2c:  00 00 82 AC    sw    $v0, 0($a0)         %lo(another_global)

What's going on here is that the broken relocation at 0x10 is inside the delay slot of the conditional branch at 0x0c, so that it can continue at 0x24 to finish the full relocation, and the instruction at 0x20 is a full duplicate of it.

The compiler could've instead saved one instruction and generate the branch to the instruction at 0x20, moving the loading of 3 into the delay slot as a throw away. Basically, the compiler COULD've generated this instead:

0x00:  00 00 04 3C    lui   $a0, 0              %hi(another_global)
0x04:  00 00 83 8C    lw    $v1, 0($a0)         %lo(another_global)
0x08:  0F 00 02 24    li    $v0, 15
0x0c:  05 00 62 14    bne   $v1, $v0, 0x1c
0x10:  03 00 02 24    li    $v0, 3
0x14:  00 00 03 3C    lui   $v1, 0              %hi(global_var)
0x18:  00 00 62 AC    sw    $v0, 0($v1)         %lo(global_var)
0x1c:  00 00 02 3C    lui   $v0, 0              %hi(global_array + 4)
0x20:  00 00 42 8C    lw    $v0, 0($v0)         %lo(global_array + 4)
0x24:  08 00 E0 03    jr    $ra
0x28:  00 00 82 AC    sw    $v0, 0($a0)         %lo(another_global)

Now, this would've taken care of this whole problem altogether. The mismatched relocation at the end isn't a problem because it doesn't have an offset. The main issue with the mismatched hi/lo relocation that the software is complaining about is that the ELF format doesn't handle relocations with addends in them, so it's altering the bytestream to insert the addends in the instructions instead.

Now, I probably was trying too hard to make this work. I made the code be a bit too paranoid. Looking back at this, I may have a way to make it less paranoid and still convert these properly, without enforcing that they are in a pair.

I think the fix is simple.