Inheritance: IInstructionReader
Example #1
0
		/// <summary>
		/// This function relocates all the instructions and pointers of the loading executable.
		/// </summary>
		/// <param name="Relocs"></param>
		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++;
			}
		}