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