1 
2 #include "relocation.h"
3 
4 #include <stddef.h>
5 #include <stdint.h>
6 #include <stdio.h>
7 
8 #include "pe.h"
9 
10 namespace {
11 
12 constexpr size_t BIT26 = 1 << 26;
13 constexpr size_t BIT11 = 1 << 11;
14 constexpr size_t BIT10 = 1 << 10;
15 
16 /**
17   Pass in a pointer to an ARM MOVT or MOVW immediate instruciton and
18   return the immediate data encoded in the instruction.
19 
20   @param  Instruction   Pointer to ARM MOVT or MOVW immediate instruction
21 
22   @return Immediate address encoded in the instruction
23 
24 **/
ThumbMovtImmediateAddress(const uint16_t * Instruction)25 uint16_t ThumbMovtImmediateAddress(const uint16_t *Instruction) {
26   uint32_t Movt{};
27   uint16_t Address{};
28 
29   // Thumb2 is two 16-bit instructions working together. Not a single 32-bit
30   // instruction Example MOVT R0, #0 is 0x0000f2c0 or 0xf2c0 0x0000
31   Movt = (*Instruction << 16) | (*(Instruction + 1));
32 
33   // imm16 = imm4:i:imm3:imm8
34   //         imm4 -> Bit19:Bit16
35   //         i    -> Bit26
36   //         imm3 -> Bit14:Bit12
37   //         imm8 -> Bit7:Bit0
38   Address = static_cast<uint16_t>(Movt & 0x000000ff);         // imm8
39   Address |= static_cast<uint16_t>((Movt >> 4) & 0x0000f700); // imm4 imm3
40   Address |= (((Movt & BIT26) != 0) ? BIT11 : 0);             // i
41   return Address;
42 }
43 
44 /**
45   Pass in a pointer to an ARM MOVW/MOVT instruciton pair and
46   return the immediate data encoded in the two` instruction.
47 
48   @param  Instructions  Pointer to ARM MOVW/MOVT insturction pair
49 
50   @return Immediate address encoded in the instructions
51 
52 **/
ThumbMovwMovtImmediateAddress(uint16_t * Instructions)53 uint32_t ThumbMovwMovtImmediateAddress(uint16_t *Instructions) {
54   uint16_t *Word{};
55   uint16_t *Top{};
56 
57   Word = Instructions; // MOVW
58   Top = Word + 2;      // MOVT
59 
60   return (ThumbMovtImmediateAddress(Top) << 16) +
61          ThumbMovtImmediateAddress(Word);
62 }
63 
64 /**
65   Update an ARM MOVT or MOVW immediate instruction immediate data.
66 
67   @param  Instruction   Pointer to ARM MOVT or MOVW immediate instruction
68   @param  Address       New addres to patch into the instruction
69 **/
ThumbMovtImmediatePatch(uint16_t * Instruction,uint16_t Address)70 void ThumbMovtImmediatePatch(uint16_t *Instruction, uint16_t Address) {
71   uint16_t Patch{};
72 
73   // First 16-bit chunk of instruciton
74   Patch = ((Address >> 12) & 0x000f);              // imm4
75   Patch |= (((Address & BIT11) != 0) ? BIT10 : 0); // i
76   // Mask out instruction bits and or in address
77   *(Instruction) = (*Instruction & ~0x040f) | Patch;
78 
79   // Second 16-bit chunk of instruction
80   Patch = Address & 0x000000ff;           // imm8
81   Patch |= ((Address << 4) & 0x00007000); // imm3
82   // Mask out instruction bits and or in address
83   Instruction++;
84   *Instruction = (*Instruction & ~0x70ff) | Patch;
85 }
86 
87 /**
88   Update an ARM MOVW/MOVT immediate instruction instruction pair.
89 
90   @param  Instructions  Pointer to ARM MOVW/MOVT instruction pair
91   @param  Address       New addres to patch into the instructions
92 **/
ThumbMovwMovtImmediatePatch(uint16_t * Instructions,uint32_t Address)93 void ThumbMovwMovtImmediatePatch(uint16_t *Instructions, uint32_t Address) {
94   uint16_t *Word{};
95   uint16_t *Top{};
96 
97   Word = Instructions; // MOVW
98   Top = Word + 2;      // MOVT
99 
100   ThumbMovtImmediatePatch(Word, static_cast<uint16_t>(Address & 0xffff));
101   ThumbMovtImmediatePatch(Top, static_cast<uint16_t>(Address >> 16));
102 }
103 
104 }  // namespace
105 
relocate_image(char * image)106 int relocate_image(char *image) {
107   const auto dos_header = reinterpret_cast<IMAGE_DOS_HEADER *>(image);
108   const auto pe_header = dos_header->GetPEHeader();
109   const auto optional_header = &pe_header->OptionalHeader;
110   const auto reloc_directory =
111       optional_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
112   if (reloc_directory.Size == 0) {
113     printf("Relocation section empty\n");
114     return 0;
115   }
116   auto RelocBase = reinterpret_cast<EFI_IMAGE_BASE_RELOCATION *>(
117       image + reloc_directory.VirtualAddress);
118   const auto RelocBaseEnd = reinterpret_cast<EFI_IMAGE_BASE_RELOCATION *>(
119       reinterpret_cast<char *>(RelocBase) + reloc_directory.Size);
120   const auto Adjust =
121       reinterpret_cast<size_t>(image - optional_header->ImageBase);
122   //
123   // Run this relocation record
124   //
125   while (RelocBase < RelocBaseEnd) {
126     auto Reloc =
127         reinterpret_cast<uint16_t *>(reinterpret_cast<char *>(RelocBase) +
128                                      sizeof(EFI_IMAGE_BASE_RELOCATION));
129     auto RelocEnd = reinterpret_cast<uint16_t *>(
130         reinterpret_cast<char *>(RelocBase) + RelocBase->SizeOfBlock);
131     if (RelocBase->SizeOfBlock == 0) {
132       printf("Found relocation block of size 0, this is wrong\n");
133       return -1;
134     }
135     while (Reloc < RelocEnd) {
136       auto Fixup = image + RelocBase->VirtualAddress + (*Reloc & 0xFFF);
137       if (Fixup == nullptr) {
138         return 0;
139       }
140 
141       auto Fixup16 = reinterpret_cast<uint16_t *>(Fixup);
142       auto Fixup32 = reinterpret_cast<uint32_t *>(Fixup);
143       auto Fixup64 = reinterpret_cast<uint64_t *>(Fixup);
144       uint32_t FixupVal = 0;
145       switch ((*Reloc) >> 12) {
146       case EFI_IMAGE_REL_BASED_ABSOLUTE:
147         break;
148 
149       case EFI_IMAGE_REL_BASED_HIGH:
150         *Fixup16 = static_cast<uint16_t>(
151             *Fixup16 +
152             (static_cast<uint16_t>(static_cast<uint32_t>(Adjust) >> 16)));
153 
154         break;
155 
156       case EFI_IMAGE_REL_BASED_LOW:
157         *Fixup16 = static_cast<uint16_t>(
158             *Fixup16 + (static_cast<uint16_t>(Adjust) & 0xffff));
159 
160         break;
161 
162       case EFI_IMAGE_REL_BASED_HIGHLOW:
163         *Fixup32 = *Fixup32 + static_cast<uint32_t>(Adjust);
164         break;
165 
166       case EFI_IMAGE_REL_BASED_DIR64:
167         *Fixup64 = *Fixup64 + static_cast<uint64_t>(Adjust);
168         break;
169 
170       case EFI_IMAGE_REL_BASED_ARM_MOV32T:
171         FixupVal = ThumbMovwMovtImmediateAddress(Fixup16) +
172                    static_cast<uint32_t>(Adjust);
173         ThumbMovwMovtImmediatePatch(Fixup16, FixupVal);
174 
175         break;
176 
177       case EFI_IMAGE_REL_BASED_ARM_MOV32A:
178         printf("Unsupported relocation type: EFI_IMAGE_REL_BASED_ARM_MOV32A\n");
179         // break omitted - ARM instruction encoding not implemented
180         break;
181 
182       default:
183         printf("Unsupported relocation type: %d\n", (*Reloc) >> 12);
184         return -1;
185       }
186 
187       //
188       // Next relocation record
189       //
190       Reloc += 1;
191     }
192     RelocBase = reinterpret_cast<EFI_IMAGE_BASE_RELOCATION *>(RelocEnd);
193   }
194   optional_header->ImageBase = reinterpret_cast<size_t>(image);
195   return 0;
196 }
197