1 #include <cassert>
2 #include <cstring>
3 #include <stdexcept>
4 #include "MachoObjectFile.h"
5
6 using namespace Jitter;
7
8 template <typename MachoTraits>
CMachoObjectFile(CPU_ARCH cpuArch)9 CMachoObjectFile<MachoTraits>::CMachoObjectFile(CPU_ARCH cpuArch)
10 : CObjectFile(cpuArch)
11 {
12
13 }
14
15 template <typename MachoTraits>
~CMachoObjectFile()16 CMachoObjectFile<MachoTraits>::~CMachoObjectFile()
17 {
18
19 }
20
21 template <typename MachoTraits>
Write(Framework::CStream & stream)22 void CMachoObjectFile<MachoTraits>::Write(Framework::CStream& stream)
23 {
24 auto internalSymbolInfos = InternalSymbolInfoArray(m_internalSymbols.size());
25 auto externalSymbolInfos = ExternalSymbolInfoArray(m_externalSymbols.size());
26
27 StringTable stringTable;
28 stringTable.push_back(0x00);
29 FillStringTable(stringTable, m_internalSymbols, internalSymbolInfos);
30 FillStringTable(stringTable, m_externalSymbols, externalSymbolInfos);
31
32 auto textSection = BuildSection(m_internalSymbols, internalSymbolInfos, INTERNAL_SYMBOL_LOCATION_TEXT);
33 auto dataSection = BuildSection(m_internalSymbols, internalSymbolInfos, INTERNAL_SYMBOL_LOCATION_DATA);
34
35 //Make sure section size is aligned
36 textSection.data.resize((textSection.data.size() + 0x03) & ~0x03);
37 dataSection.data.resize((dataSection.data.size() + 0x01) & ~0x01);
38
39 auto symbols = BuildSymbols(m_internalSymbols, internalSymbolInfos, m_externalSymbols, externalSymbolInfos,
40 0, static_cast<uint32>(textSection.data.size()));
41
42 auto textSectionRelocations = BuildRelocations(textSection, internalSymbolInfos, externalSymbolInfos);
43 auto dataSectionRelocations = BuildRelocations(dataSection, internalSymbolInfos, externalSymbolInfos);
44
45 uint32 sectionCount = 2;
46 SectionArray sections;
47
48 uint32 sizeofCommands = sizeof(typename MachoTraits::SEGMENT_COMMAND) + (sizeof(typename MachoTraits::SECTION) * sectionCount) +
49 sizeof(Macho::SYMTAB_COMMAND);
50 uint32 headerSize = sizeof(typename MachoTraits::MACH_HEADER) + sizeofCommands;
51 uint32 segmentDataSize = static_cast<uint32>(textSection.data.size()) + static_cast<uint32>(dataSection.data.size());
52 uint32 segmentAndRelocDataSize = segmentDataSize + (textSectionRelocations.size() + dataSectionRelocations.size()) * sizeof(Macho::RELOCATION_INFO);
53
54 {
55 typename MachoTraits::SECTION section;
56 memset(§ion, 0, sizeof(typename MachoTraits::SECTION));
57 strncpy(section.sectionName, "__text", 0x10);
58 strncpy(section.segmentName, "__TEXT", 0x10);
59
60 section.sectionName[0x0F] = 0;
61 section.segmentName[0x0F] = 0;
62
63 section.address = 0;
64 section.size = static_cast<uint32>(textSection.data.size());
65 section.offset = headerSize;
66 section.align = 0x02;
67 section.relocationOffset = headerSize + segmentDataSize;
68 section.relocationCount = static_cast<uint32>(textSectionRelocations.size());
69 section.flags = Macho::S_ATTR_SOME_INSTRUCTIONS | Macho::S_ATTR_PURE_INSTRUCTIONS;
70 section.reserved1 = 0;
71 section.reserved2 = 0;
72
73 sections.push_back(section);
74 }
75
76 {
77 typename MachoTraits::SECTION section;
78 memset(§ion, 0, sizeof(typename MachoTraits::SECTION));
79 strncpy(section.sectionName, "__data", 0x10);
80 strncpy(section.segmentName, "__DATA", 0x10);
81
82 section.sectionName[0x0F] = 0;
83 section.segmentName[0x0F] = 0;
84
85 section.address = static_cast<uint32>(textSection.data.size());
86 section.size = static_cast<uint32>(dataSection.data.size());
87 section.offset = headerSize + static_cast<uint32>(textSection.data.size());
88 section.align = 0x01;
89 section.relocationOffset = headerSize + segmentDataSize + textSectionRelocations.size() * sizeof(Macho::RELOCATION_INFO);
90 section.relocationCount = static_cast<uint32>(dataSectionRelocations.size());
91 section.flags = 0;
92 section.reserved1 = 0;
93 section.reserved2 = 0;
94
95 sections.push_back(section);
96 }
97
98 typename MachoTraits::SEGMENT_COMMAND segmentCommand;
99 memset(&segmentCommand, 0, sizeof(typename MachoTraits::SEGMENT_COMMAND));
100 segmentCommand.cmd = MachoTraits::LC_SEGMENT;
101 segmentCommand.cmdSize = sizeof(typename MachoTraits::SEGMENT_COMMAND) + (sizeof(typename MachoTraits::SECTION) * sections.size());
102 segmentCommand.vmAddress = 0;
103 segmentCommand.vmSize = segmentDataSize;
104 segmentCommand.fileOffset = headerSize;
105 segmentCommand.fileSize = segmentDataSize;
106 segmentCommand.maxProtection = 0x07;
107 segmentCommand.initProtection = 0x07;
108 segmentCommand.sectionCount = sections.size();
109 segmentCommand.flags = 0;
110
111 Macho::SYMTAB_COMMAND symtabCommand;
112 memset(&symtabCommand, 0, sizeof(Macho::SYMTAB_COMMAND));
113 symtabCommand.cmd = Macho::LC_SYMTAB;
114 symtabCommand.cmdSize = sizeof(Macho::SYMTAB_COMMAND);
115 symtabCommand.stringsSize = stringTable.size();
116 symtabCommand.stringsOffset = headerSize + segmentAndRelocDataSize + (sizeof(typename MachoTraits::NLIST) * symbols.size());
117 symtabCommand.symbolCount = symbols.size();
118 symtabCommand.symbolsOffset = headerSize + segmentAndRelocDataSize;
119
120 typename MachoTraits::MACH_HEADER header = {};
121 header.magic = MachoTraits::HEADER_MAGIC;
122 switch(m_cpuArch)
123 {
124 case CPU_ARCH_X86:
125 header.cpuType = Macho::CPU_TYPE_I386;
126 header.cpuSubType = Macho::CPU_SUBTYPE_I386_ALL;
127 break;
128 case CPU_ARCH_ARM:
129 header.cpuType = Macho::CPU_TYPE_ARM;
130 header.cpuSubType = Macho::CPU_SUBTYPE_ARM_V7;
131 break;
132 case CPU_ARCH_ARM64:
133 header.cpuType = Macho::CPU_TYPE_ARM64;
134 header.cpuSubType = Macho::CPU_SUBTYPE_ARM64_ALL;
135 break;
136 default:
137 throw std::runtime_error("MachoObjectFile: Unsupported CPU architecture.");
138 break;
139 }
140 header.fileType = Macho::MH_OBJECT;
141 header.commandCount = 2; //SEGMENT_COMMMAND + SYMTAB_COMMAND
142 header.sizeofCommands = sizeofCommands;
143 header.flags = Macho::MH_NOMULTIDEFS;
144
145 stream.Write(&header, sizeof(typename MachoTraits::MACH_HEADER));
146 stream.Write(&segmentCommand, sizeof(typename MachoTraits::SEGMENT_COMMAND));
147 for(const auto& section : sections)
148 {
149 stream.Write(§ion, sizeof(typename MachoTraits::SECTION));
150 }
151 stream.Write(&symtabCommand, sizeof(Macho::SYMTAB_COMMAND));
152 stream.Write(textSection.data.data(), textSection.data.size());
153 stream.Write(dataSection.data.data(), dataSection.data.size());
154 stream.Write(textSectionRelocations.data(), textSectionRelocations.size() * sizeof(Macho::RELOCATION_INFO));
155 stream.Write(dataSectionRelocations.data(), dataSectionRelocations.size() * sizeof(Macho::RELOCATION_INFO));
156 stream.Write(symbols.data(), sizeof(typename MachoTraits::NLIST) * symbols.size());
157 stream.Write(stringTable.data(), stringTable.size());
158 }
159
160 template <typename MachoTraits>
FillStringTable(StringTable & stringTable,const InternalSymbolArray & internalSymbols,InternalSymbolInfoArray & internalSymbolInfos)161 void CMachoObjectFile<MachoTraits>::FillStringTable(StringTable& stringTable, const InternalSymbolArray& internalSymbols, InternalSymbolInfoArray& internalSymbolInfos)
162 {
163 uint32 stringTableSizeIncrement = 0;
164 for(const auto& internalSymbol : internalSymbols)
165 {
166 stringTableSizeIncrement += static_cast<uint32>(internalSymbol.name.length()) + 1;
167 }
168 stringTable.reserve(stringTable.size() + stringTableSizeIncrement);
169 for(uint32 i = 0; i < internalSymbols.size(); i++)
170 {
171 const auto& internalSymbol = internalSymbols[i];
172 auto& internalSymbolInfo = internalSymbolInfos[i];
173 internalSymbolInfo.nameOffset = static_cast<uint32>(stringTable.size());
174 stringTable.insert(std::end(stringTable), std::begin(internalSymbol.name), std::end(internalSymbol.name));
175 stringTable.push_back(0);
176 }
177 }
178
179 template <typename MachoTraits>
FillStringTable(StringTable & stringTable,const ExternalSymbolArray & externalSymbols,ExternalSymbolInfoArray & externalSymbolInfos)180 void CMachoObjectFile<MachoTraits>::FillStringTable(StringTable& stringTable, const ExternalSymbolArray& externalSymbols, ExternalSymbolInfoArray& externalSymbolInfos)
181 {
182 uint32 stringTableSizeIncrement = 0;
183 for(const auto& externalSymbol : externalSymbols)
184 {
185 stringTableSizeIncrement += static_cast<uint32>(externalSymbol.name.length()) + 1;
186 }
187 stringTable.reserve(stringTable.size() + stringTableSizeIncrement);
188 for(uint32 i = 0; i < externalSymbols.size(); i++)
189 {
190 const auto& externalSymbol = externalSymbols[i];
191 auto& externalSymbolInfo = externalSymbolInfos[i];
192 externalSymbolInfo.nameOffset = static_cast<uint32>(stringTable.size());
193 stringTable.insert(std::end(stringTable), std::begin(externalSymbol.name), std::end(externalSymbol.name));
194 stringTable.push_back(0);
195 }
196 }
197
198 template <typename MachoTraits>
BuildSection(const InternalSymbolArray & internalSymbols,InternalSymbolInfoArray & internalSymbolInfos,INTERNAL_SYMBOL_LOCATION location)199 typename CMachoObjectFile<MachoTraits>::SECTION CMachoObjectFile<MachoTraits>::BuildSection(const InternalSymbolArray& internalSymbols, InternalSymbolInfoArray& internalSymbolInfos, INTERNAL_SYMBOL_LOCATION location)
200 {
201 SECTION section;
202 auto& sectionData(section.data);
203 uint32 sectionSize = 0;
204 for(const auto& internalSymbol : internalSymbols)
205 {
206 sectionSize += static_cast<uint32>(internalSymbol.data.size());
207 }
208 sectionData.reserve(sectionSize);
209 for(uint32 i = 0; i < internalSymbols.size(); i++)
210 {
211 const auto& internalSymbol = internalSymbols[i];
212 if(internalSymbol.location != location) continue;
213
214 auto& internalSymbolInfo = internalSymbolInfos[i];
215 internalSymbolInfo.dataOffset = static_cast<uint32>(sectionData.size());
216 for(const auto& symbolReference : internalSymbol.symbolReferences)
217 {
218 SYMBOL_REFERENCE newReference;
219 newReference.offset = symbolReference.offset + internalSymbolInfo.dataOffset;
220 newReference.symbolIndex = symbolReference.symbolIndex;
221 newReference.type = symbolReference.type;
222 section.symbolReferences.push_back(newReference);
223 }
224 sectionData.insert(std::end(sectionData), std::begin(internalSymbol.data), std::end(internalSymbol.data));
225 }
226 return section;
227 }
228
229 template <typename MachoTraits>
BuildSymbols(const InternalSymbolArray & internalSymbols,InternalSymbolInfoArray & internalSymbolInfos,const ExternalSymbolArray & externalSymbols,ExternalSymbolInfoArray & externalSymbolInfos,uint32 textSectionVa,uint32 dataSectionVa)230 typename CMachoObjectFile<MachoTraits>::SymbolArray CMachoObjectFile<MachoTraits>::BuildSymbols(
231 const InternalSymbolArray& internalSymbols, InternalSymbolInfoArray& internalSymbolInfos,
232 const ExternalSymbolArray& externalSymbols, ExternalSymbolInfoArray& externalSymbolInfos,
233 uint32 textSectionVa, uint32 dataSectionVa
234 )
235 {
236 SymbolArray symbols;
237 symbols.reserve(internalSymbols.size() + externalSymbols.size());
238
239 //Internal symbols
240 for(uint32 i = 0; i < internalSymbols.size(); i++)
241 {
242 const auto& internalSymbol = internalSymbols[i];
243 auto& internalSymbolInfo = internalSymbolInfos[i];
244 internalSymbolInfo.symbolIndex = static_cast<uint32>(symbols.size());
245
246 typename MachoTraits::NLIST symbol = {};
247 symbol.stringTableIndex = internalSymbolInfo.nameOffset;
248 symbol.type = 0x1F; // N_SECT | N_PEXT | N_EXT
249 symbol.value = internalSymbolInfo.dataOffset;
250 symbol.section = (internalSymbol.location == CObjectFile::INTERNAL_SYMBOL_LOCATION_TEXT) ? 1 : 2; //.text or .data section
251 symbol.value += (internalSymbol.location == CObjectFile::INTERNAL_SYMBOL_LOCATION_TEXT) ? textSectionVa : dataSectionVa;
252 symbol.desc = 0;
253 symbols.push_back(symbol);
254 }
255
256 //External symbols
257 for(uint32 i = 0; i < externalSymbols.size(); i++)
258 {
259 const auto& externalSymbol = externalSymbols[i];
260 auto& externalSymbolInfo = externalSymbolInfos[i];
261 externalSymbolInfo.symbolIndex = static_cast<uint32>(symbols.size());
262
263 typename MachoTraits::NLIST symbol = {};
264 symbol.stringTableIndex = externalSymbolInfo.nameOffset;
265 symbol.value = 0;
266 symbol.type = 0x01; // N_UNDF | N_EXT
267 symbol.section = 0;
268 symbol.desc = 0;
269 symbols.push_back(symbol);
270 }
271
272 return symbols;
273 }
274
275 template <typename MachoTraits>
BuildRelocations(SECTION & section,const InternalSymbolInfoArray & internalSymbolInfos,const ExternalSymbolInfoArray & externalSymbolInfos) const276 typename CMachoObjectFile<MachoTraits>::RelocationArray CMachoObjectFile<MachoTraits>::BuildRelocations(
277 SECTION& section, const InternalSymbolInfoArray& internalSymbolInfos,
278 const ExternalSymbolInfoArray& externalSymbolInfos) const
279 {
280 RelocationArray relocations;
281 relocations.reserve(section.symbolReferences.size() * 4); //Times 4 because of possible HALF/PAIR relocs
282
283 for(uint32 i = 0; i < section.symbolReferences.size(); i++)
284 {
285 const auto& symbolReference = section.symbolReferences[i];
286 uint32 symbolIndex = (symbolReference.type == SYMBOL_TYPE_INTERNAL) ?
287 internalSymbolInfos[symbolReference.symbolIndex].symbolIndex :
288 externalSymbolInfos[symbolReference.symbolIndex].symbolIndex;
289
290 if((m_cpuArch == CObjectFile::CPU_ARCH_ARM) && (symbolReference.type == SYMBOL_TYPE_EXTERNAL))
291 {
292 //Dirty hack: We assume that all external symbols are coming from the OP_CALL emitter on ARM
293 //which uses MOVW and MOVT
294
295 //Length field for half relocs
296 //Bit 0: 0=movw,1=movt; Bit 1: 0=arm,1=thumb
297
298 {
299 unsigned int symbolLength = 0;
300
301 {
302 auto relocation = Macho::RELOCATION_INFO();
303 relocation.type = Macho::ARM_RELOC_HALF;
304 relocation.symbolIndex = symbolIndex;
305 relocation.address = symbolReference.offset + 0;
306 relocation.isExtern = 1;
307 relocation.pcRel = 0;
308 relocation.length = symbolLength;
309 relocations.push_back(relocation);
310 }
311
312 {
313 auto relocation = Macho::RELOCATION_INFO();
314 relocation.type = Macho::ARM_RELOC_PAIR;
315 relocation.symbolIndex = 0xFFFFFF;
316 relocation.address = 0;
317 relocation.isExtern = 0;
318 relocation.pcRel = 0;
319 relocation.length = symbolLength;
320 relocations.push_back(relocation);
321 }
322 }
323
324 {
325 unsigned int symbolLength = 1;
326
327 {
328 auto relocation = Macho::RELOCATION_INFO();
329 relocation.type = Macho::ARM_RELOC_HALF;
330 relocation.symbolIndex = symbolIndex;
331 relocation.address = symbolReference.offset + 4;
332 relocation.isExtern = 1;
333 relocation.pcRel = 0;
334 relocation.length = symbolLength;
335 relocations.push_back(relocation);
336 }
337
338 {
339 auto relocation = Macho::RELOCATION_INFO();
340 relocation.type = Macho::ARM_RELOC_PAIR;
341 relocation.symbolIndex = 0xFFFFFF;
342 relocation.address = 0;
343 relocation.isExtern = 0;
344 relocation.pcRel = 0;
345 relocation.length = symbolLength;
346 relocations.push_back(relocation);
347 }
348 }
349
350 *reinterpret_cast<uint32*>(section.data.data() + symbolReference.offset + 0) &= ~0xF0FFF;
351 *reinterpret_cast<uint32*>(section.data.data() + symbolReference.offset + 4) &= ~0xF0FFF;
352 }
353 else if((m_cpuArch == CObjectFile::CPU_ARCH_ARM64) && (symbolReference.type == SYMBOL_TYPE_EXTERNAL))
354 {
355 //We assume that the instruction that references this symbol is BL.
356
357 auto relocation = Macho::RELOCATION_INFO();
358 relocation.type = Macho::ARM64_RELOC_BRANCH26;
359 relocation.symbolIndex = symbolIndex;
360 relocation.address = symbolReference.offset;
361 relocation.isExtern = 1;
362 relocation.pcRel = 1;
363 relocation.length = 2; //4 bytes
364 relocations.push_back(relocation);
365
366 *reinterpret_cast<uint32*>(section.data.data() + symbolReference.offset) &= ~0x03FFFFFF;
367 }
368 else
369 {
370 auto relocation = Macho::RELOCATION_INFO();
371 relocation.type = Macho::GENERIC_RELOC_VANILLA;
372 relocation.symbolIndex = symbolIndex;
373 relocation.address = symbolReference.offset;
374 relocation.isExtern = 1;
375 relocation.pcRel = 0;
376 switch(MachoTraits::POINTER_SIZE)
377 {
378 case 4:
379 relocation.length = 2; //4 bytes
380 *reinterpret_cast<uint32*>(section.data.data() + symbolReference.offset) = 0;
381 break;
382 case 8:
383 relocation.length = 3; //8 bytes
384 *reinterpret_cast<uint64*>(section.data.data() + symbolReference.offset) = 0;
385 break;
386 default:
387 assert(false);
388 break;
389 }
390 relocations.push_back(relocation);
391 }
392 }
393
394 return relocations;
395 }
396
397 template class Jitter::CMachoObjectFile<MACHO_TRAITS_32>;
398 template class Jitter::CMachoObjectFile<MACHO_TRAITS_64>;
399