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(&section, 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(&section, 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(&section, 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