1 #include "stdafx.h"
2 #include "PsxRelocator.h"
3 #include "Core/Misc.h"
4 #include "Core/Common.h"
5 #include <map>
6 #include "Util/CRC.h"
7 #include "Core/FileManager.h"
8 #include "Util/Util.h"
9
10 struct PsxLibEntry
11 {
12 std::wstring name;
13 ByteArray data;
14 };
15
16 const unsigned char psxObjectFileMagicNum[6] = { 'L', 'N', 'K', '\x02', '\x2E', '\x07' };
17
loadPsxLibrary(const std::wstring & inputName)18 std::vector<PsxLibEntry> loadPsxLibrary(const std::wstring& inputName)
19 {
20 ByteArray input = ByteArray::fromFile(inputName);
21 std::vector<PsxLibEntry> result;
22
23 if (input.size() == 0)
24 return result;
25
26 if (memcmp(input.data(),psxObjectFileMagicNum,sizeof(psxObjectFileMagicNum)) == 0)
27 {
28 PsxLibEntry entry;
29 entry.name = getFileNameFromPath(inputName);
30 entry.data = input;
31 result.push_back(entry);
32 return result;
33 }
34
35 if (memcmp(input.data(),"LIB\x01",4) != 0)
36 return result;
37
38 size_t pos = 4;
39 while (pos < input.size())
40 {
41 PsxLibEntry entry;
42
43 for (int i = 0; i < 16 && input[pos+i] != ' '; i++)
44 {
45 entry.name += input[pos+i];
46 }
47
48 int size = input.getDoubleWord(pos+16);
49 int skip = 20;
50
51 while (input[pos+skip] != 0)
52 {
53 skip += 1+input[pos+skip];
54 }
55
56 skip++;
57
58 entry.data = input.mid(pos+skip,size-skip);
59 pos += size;
60
61 result.push_back(entry);
62 }
63
64 return result;
65 }
66
loadString(ByteArray & data,size_t pos,std::wstring & dest)67 size_t PsxRelocator::loadString(ByteArray& data, size_t pos, std::wstring& dest)
68 {
69 dest = L"";
70 int len = data[pos++];
71
72 for (int i = 0; i < len; i++)
73 {
74 dest += data[pos++];
75 }
76
77 return len+1;
78 }
79
parseObject(ByteArray data,PsxRelocatorFile & dest)80 bool PsxRelocator::parseObject(ByteArray data, PsxRelocatorFile& dest)
81 {
82 if (memcmp(data.data(),psxObjectFileMagicNum,sizeof(psxObjectFileMagicNum)) != 0)
83 return false;
84
85 size_t pos = 6;
86
87 std::vector<PsxSegment>& segments = dest.segments;
88 std::vector<PsxSymbol>& syms = dest.symbols;
89
90 int activeSegment = -1;
91 int lastSegmentPartStart = -1;
92 while (pos < data.size())
93 {
94 switch (data[pos])
95 {
96 case 0x10: // segment definition
97 {
98 PsxSegment seg;
99 seg.id = data.getDoubleWord(pos+1);
100 segments.push_back(seg);
101 pos += 5;
102
103 if (data[pos] != 8)
104 return false;
105
106 std::wstring& name = segments[segments.size()-1].name;
107 pos += 1 + loadString(data,pos+1,name);
108 }
109 break;
110 case 0x14: // group?
111 pos += data[pos+4]+5;
112 break;
113 case 0x1C: // source file name
114 pos += data[pos+3]+4;
115 break;
116
117 case 0x06: // set segment id
118 {
119 int id = data.getWord(pos+1);
120 pos += 3;
121
122 int num = -1;
123 for (size_t i = 0; i < segments.size(); i++)
124 {
125 if (segments[i].id == id)
126 {
127 num = (int) i;
128 break;
129 }
130 }
131
132 activeSegment = num;
133 }
134 break;
135 case 0x02: // append to data segment
136 {
137 int size = data.getWord(pos+1);
138 pos += 3;
139
140 ByteArray d = data.mid(pos,size);
141 pos += size;
142
143 lastSegmentPartStart = (int) segments[activeSegment].data.size();
144 segments[activeSegment].data.append(d);
145 }
146 break;
147 case 0x08: // append zeroes data segment
148 {
149 int size = data.getWord(pos+1);
150 pos += 3;
151
152 ByteArray d;
153 d.reserveBytes(size);
154 segments[activeSegment].data.append(d);
155 }
156 break;
157 case 0x0A: // relocation data
158 {
159 int type = data[pos+1];
160 pos += 2;
161
162 PsxRelocation rel;
163 rel.relativeOffset = 0;
164 rel.filePos = (int) pos-2;
165
166 switch (type)
167 {
168 case 0x10: // 32 bit word
169 rel.type = PsxRelocationType::WordLiteral;
170 rel.segmentOffset = data.getWord(pos);
171 pos += 2;
172 break;
173 case 0x4A: // jal
174 rel.type = PsxRelocationType::FunctionCall;
175 rel.segmentOffset = data.getWord(pos);
176 pos += 2;
177 break;
178 case 0x52: // upper immerdiate
179 rel.type = PsxRelocationType::UpperImmediate;
180 rel.segmentOffset = data.getWord(pos);
181 pos += 2;
182 break;
183 case 0x54: // lower immediate (add)
184 rel.type = PsxRelocationType::LowerImmediate;
185 rel.segmentOffset = data.getWord(pos);
186 pos += 2;
187 break;
188 default:
189 return false;
190 }
191
192 rel.segmentOffset += lastSegmentPartStart;
193 checkothertype:
194 int otherType = data[pos++];
195 switch (otherType)
196 {
197 case 0x02: // reference to symbol with id num
198 rel.refType = PsxRelocationRefType::SymblId;
199 rel.referenceId = data.getWord(pos);
200 pos += 2;
201 break;
202 case 0x2C: // ref to other segment?
203 rel.refType = PsxRelocationRefType::SegmentOffset;
204
205 switch (data[pos++])
206 {
207 case 0x00:
208 rel.relativeOffset = data.getDoubleWord(pos);
209 pos += 4;
210 goto checkothertype;
211 case 0x04:
212 rel.referenceId = data.getWord(pos); // segment id
213 pos += 2;
214
215 if (data[pos++] != 0x00)
216 {
217 return false;
218 }
219
220 rel.referencePos = data.getDoubleWord(pos);
221 pos += 4;
222 break;
223 default:
224 return false;
225 }
226 break;
227 case 0x2E: // negative ref?
228 rel.refType = PsxRelocationRefType::SegmentOffset;
229
230 switch (data[pos++])
231 {
232 case 0x00:
233 rel.relativeOffset = -data.getDoubleWord(pos);
234 pos += 4;
235 goto checkothertype;
236 default:
237 return false;
238 }
239 break;
240 default:
241 return false;
242 }
243
244 segments[activeSegment].relocations.push_back(rel);
245 }
246 break;
247 case 0x12: // internal symbol
248 {
249 PsxSymbol sym;
250 sym.type = PsxSymbolType::Internal;
251 sym.segment = data.getWord(pos+1);
252 sym.offset = data.getDoubleWord(pos+3);
253 pos += 7 + loadString(data,pos+7,sym.name);
254 syms.push_back(sym);
255 }
256 break;
257 case 0x0E: // external symbol
258 {
259 PsxSymbol sym;
260 sym.type = PsxSymbolType::External;
261 sym.id = data.getWord(pos+1);
262 pos += 3 + loadString(data,pos+3,sym.name);
263 syms.push_back(sym);
264 }
265 break;
266 case 0x30: // bss symbol?
267 {
268 PsxSymbol sym;
269 sym.type = PsxSymbolType::BSS;
270 sym.id = data.getWord(pos+1);
271 sym.segment = data.getWord(pos+3);
272 sym.size = data.getDoubleWord(pos+5);
273 pos += 9 + loadString(data,pos+9,sym.name);
274 syms.push_back(sym);
275 }
276 break;
277 case 0x0C: // internal with id
278 {
279 PsxSymbol sym;
280 sym.type = PsxSymbolType::InternalID;
281 sym.id = data.getWord(pos+1);
282 sym.segment = data.getWord(pos+3);
283 sym.offset = data.getDoubleWord(pos+5);
284 pos += 9 + loadString(data,pos+9,sym.name);
285 syms.push_back(sym);
286 }
287 break;
288 case 0x4A: // function
289 {
290 PsxSymbol sym;
291 sym.type = PsxSymbolType::Function;
292 sym.segment = data.getWord(pos+1);
293 sym.offset = data.getDoubleWord(pos+3);
294 pos += 0x1D + loadString(data,pos+0x1D,sym.name);
295 syms.push_back(sym);
296 }
297 break;
298 case 0x4C: // function size
299 pos += 11;
300 break;
301 case 0x3C: // ??
302 pos += 3;
303 break;
304 case 0x00: // ??
305 pos++;
306 break;
307 case 0x32: // ??
308 pos += 3;
309 break;
310 case 0x3A: // ??
311 pos += 9;
312 break;
313 default:
314 return false;
315 }
316 }
317
318 return true;
319 }
320
init(const std::wstring & inputName)321 bool PsxRelocator::init(const std::wstring& inputName)
322 {
323 auto inputFiles = loadPsxLibrary(inputName);
324 if (inputFiles.size() == 0)
325 {
326 Logger::printError(Logger::Error,L"Could not load library");
327 return false;
328 }
329
330 reloc = new MipsElfRelocator();
331
332 for (PsxLibEntry& entry: inputFiles)
333 {
334 PsxRelocatorFile file;
335 file.name = entry.name;
336
337 if (parseObject(entry.data,file) == false)
338 {
339 Logger::printError(Logger::Error,L"Could not load object file %s",entry.name);
340 return false;
341 }
342
343 // init symbols
344 for (PsxSymbol& sym: file.symbols)
345 {
346 std::wstring lowered = sym.name;
347 std::transform(lowered.begin(), lowered.end(), lowered.begin(), ::towlower);
348
349 sym.label = Global.symbolTable.getLabel(lowered,-1,-1);
350 if (sym.label == nullptr)
351 {
352 Logger::printError(Logger::Error,L"Invalid label name \"%s\"",sym.name);
353 continue;
354 }
355
356 if (sym.label->isDefined() && sym.type != PsxSymbolType::External)
357 {
358 Logger::printError(Logger::Error,L"Label \"%s\" already defined",sym.name);
359 continue;
360 }
361
362 sym.label->setOriginalName(sym.name);
363 }
364
365 files.push_back(file);
366 }
367
368 return true;
369 }
370
relocateFile(PsxRelocatorFile & file,int & relocationAddress)371 bool PsxRelocator::relocateFile(PsxRelocatorFile& file, int& relocationAddress)
372 {
373 std::map<int,int> relocationOffsets;
374 std::map<int,int> symbolOffsets;
375 int start = relocationAddress;
376
377 // assign addresses to segments
378 for (PsxSegment& seg: file.segments)
379 {
380 int index = seg.id;
381 size_t size = seg.data.size();
382
383 relocationOffsets[index] = relocationAddress;
384 relocationAddress += (int) size;
385
386 while (relocationAddress % 4)
387 relocationAddress++;
388 }
389
390 // parse/add/relocate symbols
391 bool error = false;
392 for (PsxSymbol& sym: file.symbols)
393 {
394 int pos;
395 switch (sym.type)
396 {
397 case PsxSymbolType::Internal:
398 case PsxSymbolType::Function:
399 sym.label->setValue(relocationOffsets[sym.segment]+sym.offset);
400 sym.label->setDefined(true);
401 break;
402 case PsxSymbolType::InternalID:
403 pos = relocationOffsets[sym.segment]+sym.offset;
404 sym.label->setValue(pos);
405 sym.label->setDefined(true);
406 symbolOffsets[sym.id] = pos;
407 break;
408 case PsxSymbolType::BSS:
409 sym.label->setValue(relocationAddress);
410 sym.label->setDefined(true);
411 symbolOffsets[sym.id] = relocationAddress;
412 relocationAddress += sym.size;
413
414 while (relocationAddress % 4)
415 relocationAddress++;
416 break;
417 case PsxSymbolType::External:
418 if (sym.label->isDefined() == false)
419 {
420 Logger::queueError(Logger::Error,L"Undefined external symbol %s in file %s",sym.name,file.name);
421 error = true;
422 continue;
423 }
424
425 symbolOffsets[sym.id] = (int) sym.label->getValue();
426 break;
427 }
428 }
429
430 if (error)
431 return false;
432
433 size_t dataStart = outputData.size();
434 outputData.reserveBytes(relocationAddress-start);
435
436 // load code and data
437 for (PsxSegment& seg: file.segments)
438 {
439 // relocate
440 ByteArray sectionData = seg.data;
441 for (PsxRelocation& rel: seg.relocations)
442 {
443 RelocationData relData;
444 int pos = rel.segmentOffset;
445 relData.opcode = sectionData.getDoubleWord(pos);
446
447 switch (rel.refType)
448 {
449 case PsxRelocationRefType::SymblId:
450 relData.relocationBase = symbolOffsets[rel.referenceId]+rel.relativeOffset;
451 break;
452 case PsxRelocationRefType::SegmentOffset:
453 relData.relocationBase = relocationOffsets[rel.referenceId] + rel.referencePos+rel.relativeOffset;
454 break;
455 }
456
457 switch (rel.type)
458 {
459 case PsxRelocationType::WordLiteral:
460 reloc->relocateOpcode(R_MIPS_32,relData);
461 break;
462 case PsxRelocationType::UpperImmediate:
463 reloc->relocateOpcode(R_MIPS_HI16,relData);
464 break;
465 case PsxRelocationType::LowerImmediate:
466 reloc->relocateOpcode(R_MIPS_LO16,relData);
467 break;
468 case PsxRelocationType::FunctionCall:
469 reloc->relocateOpcode(R_MIPS_26,relData);
470 break;
471 }
472
473 sectionData.replaceDoubleWord(pos,relData.opcode);
474 }
475
476 size_t arrayStart = dataStart+relocationOffsets[seg.id]-start;
477 memcpy(outputData.data(arrayStart),sectionData.data(),sectionData.size());
478 }
479
480 return true;
481 }
482
relocate(int & memoryAddress)483 bool PsxRelocator::relocate(int& memoryAddress)
484 {
485 int oldCrc = getCrc32(outputData.data(),outputData.size());
486 outputData.clear();
487 dataChanged = false;
488
489 bool error = false;
490 int start = memoryAddress;
491
492 for (PsxRelocatorFile& file: files)
493 {
494 if (relocateFile(file,memoryAddress) == false)
495 error = true;
496 }
497
498 int newCrc = getCrc32(outputData.data(),outputData.size());
499 if (oldCrc != newCrc)
500 dataChanged = true;
501
502 memoryAddress -= start;
503 return !error;
504 }
505
506
writeSymbols(SymbolData & symData) const507 void PsxRelocator::writeSymbols(SymbolData& symData) const
508 {
509 for (const PsxRelocatorFile& file: files)
510 {
511 for (const PsxSymbol& sym: file.symbols)
512 {
513 if (sym.type != PsxSymbolType::External)
514 symData.addLabel(sym.label->getValue(),sym.name.c_str());
515 }
516 }
517 }
518
519 //
520 // DirectivePsxObjImport
521 //
522
DirectivePsxObjImport(const std::wstring & fileName)523 DirectivePsxObjImport::DirectivePsxObjImport(const std::wstring& fileName)
524 {
525 if (rel.init(fileName))
526 {
527 }
528 }
529
Validate()530 bool DirectivePsxObjImport::Validate()
531 {
532 int memory = (int) g_fileManager->getVirtualAddress();
533 rel.relocate(memory);
534 g_fileManager->advanceMemory(memory);
535 return rel.hasDataChanged();
536 }
537
Encode() const538 void DirectivePsxObjImport::Encode() const
539 {
540 const ByteArray& data = rel.getData();
541 g_fileManager->write(data.data(),data.size());
542 }
543
writeSymData(SymbolData & symData) const544 void DirectivePsxObjImport::writeSymData(SymbolData& symData) const
545 {
546 rel.writeSymbols(symData);
547 }
548