CSPspEmu.Hle.Loader.ElfPspLoader.RelocateRelocs C# (CSharp) Method

RelocateRelocs() protected method

This function relocates all the instructions and pointers of the loading executable.
protected RelocateRelocs ( IEnumerable Relocs ) : void
Relocs IEnumerable
return void
        protected void RelocateRelocs(IEnumerable<Elf.Reloc> Relocs)
        {
            var InstructionReader = new InstructionStreamReader(ElfLoader.MemoryStream);

            /*
            Func<uint, Action<ref Instruction>> UpdateInstruction = (Address) =>
            {
            };
            */

            //var Hi16List = new List<uint>();

            ushort HiValue = 0;
            var DeferredHi16 = new LinkedList<uint>(); // We'll use this to relocate R_MIPS_HI16 when we get a R_MIPS_LO16

            int Index = 0;
            foreach (var Reloc in Relocs)
            {
                //Console.WriteLine(Reloc.ToStringDefault());
                //Console.WriteLine("   {0:X}", RelocatedAddress);

                // Check if R_TYPE is 0xFF (break code) and break the loop
                // immediately in order to avoid fetching non existent program headers.

                // Some games (e.g.: "Final Fantasy: Dissidia") use this kind of relocation
                // suggesting that the PSP's ELF Loader is capable of recognizing it and stop.
                if (Reloc.Type == Elf.Reloc.TypeEnum.StopRelocation)
                {
                    break;
                }

                var PointerBaseOffset = (uint)ElfLoader.ProgramHeaders[Reloc.PointerSectionHeaderBase].VirtualAddress;
                var PointeeBaseOffset = (uint)ElfLoader.ProgramHeaders[Reloc.PointeeSectionHeaderBase].VirtualAddress;

                // Address of data to relocate
                var RelocatedPointerAddress = (uint)(BaseAddress + Reloc.PointerAddress + PointerBaseOffset);

                // Value of data to relocate
                var Instruction = InstructionReader[RelocatedPointerAddress];
                var InstructionBefore = Instruction;

                var S = (uint)BaseAddress + PointeeBaseOffset;
                var GP_ADDR = (int)(BaseAddress + Reloc.PointerAddress);
                var GP_OFFSET = (int)GP_ADDR - ((int)BaseAddress & 0xFFFF0000);

                //Console.WriteLine(Reloc.Type);

                bool DebugReloc = (RelocatedPointerAddress >= 0x08809320 && RelocatedPointerAddress <= 0x08809320 + 0x100);
                //bool DebugReloc = false;

                if (DebugReloc)
                {
                    Console.WriteLine("{0:X8}[{1:X8}]: {2}", RelocatedPointerAddress, Instruction.Value, Reloc);
                }

                switch (Reloc.Type)
                {
                    // Tested on PSP: R_MIPS_NONE just returns 0.
                    case Elf.Reloc.TypeEnum.None: // 0
                        {
                        }
                        break;
                        /*
                    case Elf.Reloc.TypeEnum.Mips16: // 1
                        {
                            Instruction.IMMU += S;
                        }
                        break;
                        */
                    case Elf.Reloc.TypeEnum.Mips32: // 2
                        {
                            Instruction.Value += S;
                        }
                        break;
                    case Elf.Reloc.TypeEnum.MipsRel32: // 3;
                        {
                            throw (new NotImplementedException());
                        }
                    case Elf.Reloc.TypeEnum.Mips26: // 4
                        {
                            Instruction.JUMP_Real = Instruction.JUMP_Real + S;
                        }
                        break;
                    case Elf.Reloc.TypeEnum.MipsHi16: // 5
                        {
                            HiValue = (ushort)Instruction.IMMU;
                            DeferredHi16.AddLast(RelocatedPointerAddress);
                        }
                        break;
                    case Elf.Reloc.TypeEnum.MipsLo16: // 6
                        {
                            uint A = Instruction.IMMU;

                            Instruction.IMMU = ((uint)(HiValue << 16) | (uint)(A & 0x0000FFFF)) + S;

                            // Process deferred R_MIPS_HI16
                            foreach (var data_addr2 in DeferredHi16)
                            {
                                var data2 = InstructionReader[data_addr2];
                                uint result = ((data2.Value & 0x0000FFFF) << 16) + A + S;
                                // The low order 16 bits are always treated as a signed
                                // value. Therefore, a negative value in the low order bits
                                // requires an adjustment in the high order bits. We need
                                // to make this adjustment in two ways: once for the bits we
                                // took from the data, and once for the bits we are putting
                                // back in to the data.
                                if ((A & 0x8000) != 0) {
                                    result -= 0x10000;
                                }
                                if ((result & 0x8000) != 0) {
                                     result += 0x10000;
                                }
                                data2.IMMU = (result >> 16);
                                InstructionReader[data_addr2] = data2;
                            }
                            DeferredHi16.Clear();
                        }
                        break;
                    case Elf.Reloc.TypeEnum.MipsGpRel16: // 7
                        {
                            /*
                            int A = Instruction.IMM;
                            int result;
                            if (A == 0)
                            {
                                result = (int)S - (int)GP_ADDR;
                            }
                            else
                            {
                                result = (int)S + (int)GP_OFFSET + (int)(((A & 0x00008000) != 0) ? (((A & 0x00003FFF) + 0x4000) | 0xFFFF0000) : A) - (int)GP_ADDR;
                            }
                            if ((result < -32768) || (result > 32768))
                            {
                                Console.Error.WriteLine("Relocation overflow (R_MIPS_GPREL16) : '" + result + "'");
                            }
                            Instruction.IMMU = (uint)result;
                            */
                        }
                        break;
                    default:
                        throw(new NotImplementedException("Handling " + Reloc.Type + " not implemented"));
                }

                if (RelocOutput != null) RelocOutput.WriteLine(
                    "RELOC %06d : 0x%08X : 0x%08X -> 0x%08X".Sprintf(
                        Index,
                        RelocatedPointerAddress, InstructionBefore.Value, Instruction.Value
                    )
                );

                if (DebugReloc)
                {
                    Console.WriteLine("   -> {0:X8}", Instruction.Value);
                }

                /*
                log.error(String.format(
                    "RELOC %06d : 0x%08X : 0x%08X -> 0x%08X\n",
                    i, data_addr, data_prev, data
                ));
                */
                InstructionReader[RelocatedPointerAddress] = Instruction;
                Index++;
            }
        }