1 #include "stdafx.h"
2 #include "ElfRelocator.h"
3 #include "Core/Common.h"
4 #include "Core/Misc.h"
5 #include "Util/CRC.h"
6 #include "Util/Util.h"
7 #include "Commands/CAssemblerLabel.h"
8 
9 struct ArFileHeader
10 {
11 	char fileName[16];
12 	char modifactionTime[12];
13 	char ownerId[6];
14 	char groupId[6];
15 	char fileMode[8];
16 	char fileSize[10];
17 	char magic[2];
18 };
19 
20 struct ArFileEntry
21 {
22 	std::wstring name;
23 	ByteArray data;
24 };
25 
loadArArchive(const std::wstring & inputName)26 std::vector<ArFileEntry> loadArArchive(const std::wstring& inputName)
27 {
28 	ByteArray input = ByteArray::fromFile(inputName);
29 	std::vector<ArFileEntry> result;
30 
31 	if (input.size() < 8 || memcmp(input.data(),"!<arch>\n",8) != 0)
32 	{
33 		if (input.size() < 4 || memcmp(input.data(),"\x7F""ELF",4) != 0)
34 			return result;
35 
36 		ArFileEntry entry;
37 		entry.name = getFileNameFromPath(inputName);
38 		entry.data = input;
39 		result.push_back(entry);
40 		return result;
41 	}
42 
43 	size_t pos = 8;
44 	while (pos < input.size())
45 	{
46 		ArFileHeader* header = (ArFileHeader*) input.data(pos);
47 		pos += sizeof(ArFileHeader);
48 
49 		// get file size
50 		int size = 0;
51 		for (int i = 0; i < 10; i++)
52 		{
53 			if (header->fileSize[i] == ' ')
54 				break;
55 
56 			size = size*10;
57 			size += (header->fileSize[i]-'0');
58 		}
59 
60 		// only ELF files are actually interesting
61 		if (memcmp(input.data(pos),"\x7F""ELF",4) == 0)
62 		{
63 			// get file name
64 			char fileName[17];
65 			fileName[16] = 0;
66 			for (int i = 0; i < 16; i++)
67 			{
68 				if (header->fileName[i] == ' ')
69 				{
70 					// remove trailing slashes of file names
71 					if (i > 0 && fileName[i-1] == '/')
72 						i--;
73 					fileName[i] = 0;
74 					break;;
75 				}
76 
77 				fileName[i] = header->fileName[i];
78 			}
79 
80 			ArFileEntry entry;
81 			entry.name = convertUtf8ToWString(fileName);
82 			entry.data = input.mid(pos,size);
83 			result.push_back(entry);
84 		}
85 
86 		pos += size;
87 		if (pos % 2)
88 			pos++;
89 	}
90 
91 	return result;
92 }
93 
init(const std::wstring & inputName)94 bool ElfRelocator::init(const std::wstring& inputName)
95 {
96 	relocator = Arch->getElfRelocator();
97 	if (relocator == nullptr)
98 	{
99 		Logger::printError(Logger::Error,L"Object importing not supported for this architecture");
100 		return false;
101 	}
102 
103 	auto inputFiles = loadArArchive(inputName);
104 	if (inputFiles.size() == 0)
105 	{
106 		Logger::printError(Logger::Error,L"Could not load library");
107 		return false;
108 	}
109 
110 	for (ArFileEntry& entry: inputFiles)
111 	{
112 		ElfRelocatorFile file;
113 
114 		ElfFile* elf = new ElfFile();
115 		if (elf->load(entry.data,false) == false)
116 		{
117 			Logger::printError(Logger::Error,L"Could not load object file %s",entry.name);
118 			return false;
119 		}
120 
121 		if (elf->getType() != ET_REL)
122 		{
123 			Logger::printError(Logger::Error,L"Unexpected ELF type %d in object file %s",elf->getType(),entry.name);
124 			return false;
125 		}
126 
127 		if (elf->getMachine() != relocator->expectedMachine())
128 		{
129 			Logger::printError(Logger::Error,L"Unexpected ELF machine %d in object file %s",elf->getMachine(),entry.name);
130 			return false;
131 		}
132 
133 		if (elf->getEndianness() != Arch->getEndianness())
134 		{
135 			Logger::printError(Logger::Error,L"Incorrect endianness in object file %s",entry.name);
136 			return false;
137 		}
138 
139 		if (elf->getSegmentCount() != 0)
140 		{
141 			Logger::printError(Logger::Error,L"Unexpected segment count %d in object file %s",elf->getSegmentCount(),entry.name);
142 			return false;
143 		}
144 
145 
146 		// load all relevant sections of this file
147 		for (size_t s = 0; s < elf->getSegmentlessSectionCount(); s++)
148 		{
149 			ElfSection* sec = elf->getSegmentlessSection(s);
150 			if (!(sec->getFlags() & SHF_ALLOC))
151 				continue;
152 
153 			if (sec->getType() == SHT_PROGBITS || sec->getType() == SHT_NOBITS || sec->getType() == SHT_INIT_ARRAY)
154 			{
155 				ElfRelocatorSection sectionEntry;
156 				sectionEntry.section = sec;
157 				sectionEntry.index = s;
158 				sectionEntry.relSection = nullptr;
159 				sectionEntry.label = nullptr;
160 
161 				// search relocation section
162 				for (size_t k = 0; k < elf->getSegmentlessSectionCount(); k++)
163 				{
164 					ElfSection* relSection = elf->getSegmentlessSection(k);
165 					if (relSection->getType() != SHT_REL)
166 						continue;
167 					if (relSection->getInfo() != s)
168 						continue;
169 
170 					// got it
171 					sectionEntry.relSection = relSection;
172 					break;
173 				}
174 
175 				// keep track of constructor sections
176 				if (sec->getName() == ".ctors" || sec->getName() == ".init_array")
177 				{
178 					ElfRelocatorCtor ctor;
179 					ctor.symbolName = Global.symbolTable.getUniqueLabelName();
180 					ctor.size = sec->getSize();
181 
182 					sectionEntry.label = Global.symbolTable.getLabel(ctor.symbolName,-1,-1);
183 					sectionEntry.label->setDefined(true);
184 
185 					ctors.push_back(ctor);
186 				}
187 
188 				file.sections.push_back(sectionEntry);
189 			}
190 		}
191 
192 		// init exportable symbols
193 		for (int i = 0; i < elf->getSymbolCount(); i++)
194 		{
195 			Elf32_Sym symbol;
196 			elf->getSymbol(symbol, i);
197 
198 			if (ELF32_ST_BIND(symbol.st_info) == STB_GLOBAL && symbol.st_shndx != 0)
199 			{
200 				ElfRelocatorSymbol symEntry;
201 				symEntry.type = ELF32_ST_TYPE(symbol.st_info);
202 				symEntry.name = convertUtf8ToWString(elf->getStrTableString(symbol.st_name));
203 				symEntry.relativeAddress = symbol.st_value;
204 				symEntry.section = symbol.st_shndx;
205 				symEntry.size = symbol.st_size;
206 				symEntry.label = nullptr;
207 
208 				file.symbols.push_back(symEntry);
209 			}
210 		}
211 
212 		file.elf = elf;
213 		file.name = entry.name;
214 		files.push_back(file);
215 	}
216 
217 	return true;
218 }
219 
exportSymbols()220 bool ElfRelocator::exportSymbols()
221 {
222 	bool error = false;
223 
224 	for (ElfRelocatorFile& file: files)
225 	{
226 		for (ElfRelocatorSymbol& sym: file.symbols)
227 		{
228 			if (sym.label != nullptr)
229 				continue;
230 
231 			std::wstring lowered = sym.name;
232 			std::transform(lowered.begin(), lowered.end(), lowered.begin(), ::towlower);
233 
234 			sym.label = Global.symbolTable.getLabel(lowered,-1,-1);
235 			if (sym.label == nullptr)
236 			{
237 				Logger::printError(Logger::Error,L"Invalid label name \"%s\"",sym.name);
238 				error = true;
239 				continue;
240 			}
241 
242 			if (sym.label->isDefined())
243 			{
244 				Logger::printError(Logger::Error,L"Label \"%s\" already defined",sym.name);
245 				error = true;
246 				continue;
247 			}
248 
249 			RelocationData data;
250 			data.symbolAddress = sym.relativeAddress;
251 			relocator->setSymbolAddress(data,sym.relativeAddress,sym.type);
252 
253 			sym.relativeAddress = data.symbolAddress;
254 			sym.label->setInfo(data.targetSymbolInfo);
255 			sym.label->setIsData(sym.type == STT_OBJECT);
256 			sym.label->setUpdateInfo(false);
257 
258 			sym.label->setValue(0);
259 			sym.label->setDefined(true);
260 			sym.label->setOriginalName(sym.name);
261 		}
262 	}
263 
264 	return !error;
265 }
266 
generateCtor(const std::wstring & ctorName)267 std::unique_ptr<CAssemblerCommand> ElfRelocator::generateCtor(const std::wstring& ctorName)
268 {
269 	std::unique_ptr<CAssemblerCommand> content = relocator->generateCtorStub(ctors);
270 
271 	auto func = ::make_unique<CDirectiveFunction>(ctorName,ctorName);
272 	func->setContent(std::move(content));
273 	return std::move(func);
274 }
275 
loadRelocation(Elf32_Rel & rel,ByteArray & data,int offset,Endianness endianness)276 void ElfRelocator::loadRelocation(Elf32_Rel& rel, ByteArray& data, int offset, Endianness endianness)
277 {
278 	rel.r_offset = data.getDoubleWord(offset + 0x00, endianness);
279 	rel.r_info   = data.getDoubleWord(offset + 0x04, endianness);
280 }
281 
relocateFile(ElfRelocatorFile & file,int64_t & relocationAddress)282 bool ElfRelocator::relocateFile(ElfRelocatorFile& file, int64_t& relocationAddress)
283 {
284 	ElfFile* elf = file.elf;
285 	int64_t start = relocationAddress;
286 
287 	// calculate address for each section
288 	std::map<int64_t,int64_t> relocationOffsets;
289 	for (ElfRelocatorSection& entry: file.sections)
290 	{
291 		ElfSection* section = entry.section;
292 		size_t index = entry.index;
293 		int size = section->getSize();
294 
295 		while (relocationAddress % section->getAlignment())
296 			relocationAddress++;
297 
298 		if (entry.label != nullptr)
299 			entry.label->setValue(relocationAddress);
300 
301 		relocationOffsets[index] = relocationAddress;
302 		relocationAddress += size;
303 	}
304 
305 	size_t dataStart = outputData.size();
306 	outputData.reserveBytes((size_t)(relocationAddress-start));
307 
308 	// load sections
309 	bool error = false;
310 	for (ElfRelocatorSection& entry: file.sections)
311 	{
312 		ElfSection* section = entry.section;
313 		size_t index = entry.index;
314 
315 		if (section->getType() == SHT_NOBITS)
316 		{
317 			// reserveBytes initialized the data to 0 already
318 			continue;
319 		}
320 
321 		ByteArray sectionData = section->getData();
322 
323 		// relocate if necessary
324 		ElfSection* relSection = entry.relSection;
325 		if (relSection != nullptr)
326 		{
327 			for (unsigned int relOffset = 0; relOffset < relSection->getSize(); relOffset += sizeof(Elf32_Rel))
328 			{
329 				Elf32_Rel rel;
330 				loadRelocation(rel, relSection->getData(), relOffset, elf->getEndianness());
331 				int pos = rel.r_offset;
332 
333 				if (relocator->isDummyRelocationType(rel.getType()))
334 					continue;
335 
336 				int symNum = rel.getSymbolNum();
337 				if (symNum <= 0)
338 				{
339 					Logger::queueError(Logger::Warning,L"Invalid symbol num %06X",symNum);
340 					error = true;
341 					continue;
342 				}
343 
344 				Elf32_Sym sym;
345 				elf->getSymbol(sym, symNum);
346 				int symSection = sym.st_shndx;
347 
348 				RelocationData relData;
349 				relData.opcode = sectionData.getDoubleWord(pos, elf->getEndianness());
350 				relData.opcodeOffset = pos+relocationOffsets[index];
351 				relocator->setSymbolAddress(relData,sym.st_value,sym.st_info & 0xF);
352 
353 				// externs?
354 				if (sym.st_shndx == 0)
355 				{
356 					if (sym.st_name == 0)
357 					{
358 						Logger::queueError(Logger::Error, L"Symbol without a name");
359 						error = true;
360 						continue;
361 					}
362 
363 					std::wstring symName = toWLowercase(elf->getStrTableString(sym.st_name));
364 
365 					std::shared_ptr<Label> label = Global.symbolTable.getLabel(symName,-1,-1);
366 					if (label == nullptr)
367 					{
368 						Logger::queueError(Logger::Error,L"Invalid external symbol %s",symName);
369 						error = true;
370 						continue;
371 					}
372 					if (label->isDefined() == false)
373 					{
374 						Logger::queueError(Logger::Error,L"Undefined external symbol %s in file %s",symName,file.name);
375 						error = true;
376 						continue;
377 					}
378 
379 					relData.relocationBase = (unsigned int) label->getValue();
380 					relData.targetSymbolType = label->isData() ? STT_OBJECT : STT_FUNC;
381 					relData.targetSymbolInfo = label->getInfo();
382 				} else {
383 					relData.relocationBase = relocationOffsets[symSection]+relData.symbolAddress;
384 				}
385 
386 				if (relocator->relocateOpcode(rel.getType(),relData) == false)
387 				{
388 					Logger::queueError(Logger::Error,relData.errorMessage);
389 					error = true;
390 					continue;
391 				}
392 
393 				sectionData.replaceDoubleWord(pos,relData.opcode, elf->getEndianness());
394 			}
395 		}
396 
397 		size_t arrayStart = (size_t) (dataStart+relocationOffsets[index]-start);
398 		memcpy(outputData.data(arrayStart),sectionData.data(),sectionData.size());
399 	}
400 
401 	// now update symbols
402 	for (ElfRelocatorSymbol& sym: file.symbols)
403 	{
404 		int64_t oldAddress = sym.relocatedAddress;
405 
406 		switch (sym.section)
407 		{
408 		case SHN_ABS:		// address does not change
409 			sym.relocatedAddress = sym.relativeAddress;
410 			break;
411 		case SHN_COMMON:	// needs to be allocated. relativeAddress gives alignment constraint
412 			{
413 				int64_t start = relocationAddress;
414 
415 				while (relocationAddress % sym.relativeAddress)
416 					relocationAddress++;
417 
418 				sym.relocatedAddress = relocationAddress;
419 				relocationAddress += sym.size;
420 				outputData.reserveBytes((size_t)(relocationAddress-start));
421 			}
422 			break;
423 		default:			// normal relocated symbol
424 			sym.relocatedAddress = sym.relativeAddress+relocationOffsets[sym.section];
425 			break;
426 		}
427 
428 		if (sym.label != nullptr)
429 			sym.label->setValue(sym.relocatedAddress);
430 
431 		if (oldAddress != sym.relocatedAddress)
432 			dataChanged = true;
433 	}
434 
435 	return !error;
436 }
437 
relocate(int64_t & memoryAddress)438 bool ElfRelocator::relocate(int64_t& memoryAddress)
439 {
440 	int oldCrc = getCrc32(outputData.data(),outputData.size());
441 	outputData.clear();
442 	dataChanged = false;
443 
444 	bool error = false;
445 	int64_t start = memoryAddress;
446 
447 	for (ElfRelocatorFile& file: files)
448 	{
449 		if (relocateFile(file,memoryAddress) == false)
450 			error = true;
451 	}
452 
453 	int newCrc = getCrc32(outputData.data(),outputData.size());
454 	if (oldCrc != newCrc)
455 		dataChanged = true;
456 
457 	memoryAddress -= start;
458 	return !error;
459 }
460 
writeSymbols(SymbolData & symData) const461 void ElfRelocator::writeSymbols(SymbolData& symData) const
462 {
463 	for (const ElfRelocatorFile& file: files)
464 	{
465 		for (const ElfRelocatorSymbol& sym: file.symbols)
466 		{
467 			symData.addLabel(sym.relocatedAddress,sym.name);
468 
469 			switch (sym.type)
470 			{
471 			case STT_OBJECT:
472 				symData.addData(sym.relocatedAddress,sym.size,SymbolData::Data8);
473 				break;
474 			case STT_FUNC:
475 				symData.startFunction(sym.relocatedAddress);
476 				symData.endFunction(sym.relocatedAddress+sym.size);
477 				break;
478 			}
479 		}
480 	}
481 }
482