1 /* imp_amig.c: Routines to import an AmigaOS-hunks file
2 
3    Copyright (C) 2002-2005 Kevin Kofler
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2, or (at your option)
8    any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software Foundation,
17    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18 
19 #include "imp_amig.h"
20 
21 #ifdef AMIGAOS_SUPPORT
22 
23 #include "../formats/amigaos.h"
24 #include "../manip.h"
25 #include "../special.h"
26 #include "../bincode/fix_m68k.h"
27 
28 #include <stdlib.h>
29 #include <string.h>
30 
31 // reallocate array, free it if reallocation failed, zero out the added size
32 // otherwise
crealloc(void * array,SIZE oldSize,SIZE newSize)33 static void *crealloc(void *array, SIZE oldSize, SIZE newSize)
34 {
35 	void *p = realloc(array, newSize);
36 	if (!p) {
37 		if (array) {
38 			free(array);
39 		}
40 		return NULL;
41 	}
42 	if (newSize > oldSize) {
43 		memset(((I1*)p)+oldSize, 0, newSize-oldSize);
44 	}
45 	return p;
46 }
47 
48 // Defines by how much the reloc target hunks array is grown.
49 #define RELOC_TARGET_HUNKS_INCREMENT (5 * sizeof(SECTION*))
50 
51 // Import an AmigaOS-hunks file into the internal data structures.
ImportAmigaOSFile(PROGRAM * Program,const I1 * File,SIZE FileSize,const char * FileName)52 BOOLEAN ImportAmigaOSFile (PROGRAM *Program, const I1 *File, SIZE FileSize, const char *FileName)
53 {
54 	// Call this for a nice and clean failing exit.
55 #define Fail() ({if(currSectionData) free(currSectionData);if(hunkName) free(hunkName);if(symName) free(symName);if(relocTargetHunks) free(relocTargetHunks);return FALSE;})
56 #define FailWithError(ErrorMsg...) ({Error (FileName, ErrorMsg); Fail ();})
57 #define TestMem(Ptr) ({ if (!(Ptr)) {FailWithError ("Out of memory.");} })
58 
59 	// Check if a given object with a given type is completely inside the file.
60 #define IsInFile(Ptr,Type) ((Ptr) >= File && (Ptr) + sizeof (Type) <= File + FileSize)
61 #define TestInFile(Ptr,Type) ({ if (!(IsInFile (Ptr, Type))) {FailWithError ("Corrupt A68k object file.");} })
62 
63 	// Get next big-endian longint. We will need this in many places here, since
64 	// unlike COFF, AmigaOS-hunks is a sequential format, not a pointer-based one.
65 #define GetNextTI4(Ptr) ({register TI4 __TI4;TestInFile(Ptr,TI4);__TI4=(*((TI4*)Ptr));Ptr+=4;ReadTI4(__TI4);})
66 
67 	// Local Variables
68 	const I1 *ptr = File;
69 	I4 hunkSize;
70 	char *hunkName = NULL, *symName = NULL;
71 	SECTION *currSection = NULL;
72 	SYMBOL *currSymbol;
73 	I1 *currSectionData = NULL;
74 	OFFSET StartupNumber;
75 	BOOLEAN InitializeBSS = TRUE;
76 	BOOLEAN AllRelocs = FALSE;
77 	COUNT numRelocTargetHunks=0; // number of hunks which are candidates for reloc targets
78 	SECTION **relocTargetHunks=NULL;
79 	SIZE relocTargetHunksSize=0;
80 	const RELOC *relocHint=NULL;
81 
82 	// Read unit hunk
83 	if (GetNextTI4(ptr) != AMIGAOS_HUNK_UNIT)
84 		// This should already be trapped by IsAmigaOSFile, but just to make sure.
85 		FailWithError("Corrupt A68k object file (unit hunk missing).");
86 	hunkSize = GetNextTI4(ptr)<<2;
87 	TestInFile(ptr,I1[hunkSize]);
88 	ptr += hunkSize;
89 
90 	while (ptr < (File + FileSize)) {
91 		I4 hunkType = GetNextTI4(ptr);
92 
93 		// Hunk flags:
94 		// AMIGAOS_HUNK_FLAG_ADVISORY:
95 		//	 A68k never generates this one
96 		if (hunkType & AMIGAOS_HUNK_FLAG_ADVISORY)
97 			FailWithError("Unsupported hunk flag `advisory'.");
98 		// AMIGAOS_HUNK_FLAG_CHIP,
99 		// AMIGAOS_HUNK_FLAG_FAST:
100 		//	 simply ignore those flags, they have no meaning on TI calculators
101 		hunkType &= AMIGAOS_HUNK_TYPE_MASK;
102 
103 		switch (hunkType) {
104 			// beginning section - there should be only one of those, handled above
105 			case AMIGAOS_HUNK_UNIT:
106 				FailWithError("Corrupt A68k object file (duplicate unit hunk).");
107 				break;
108 
109 			// name of the section to follow - temporarily store a pointer to it
110 			case AMIGAOS_HUNK_NAME:
111 				hunkSize = GetNextTI4(ptr)<<2;
112 				TestInFile(ptr,I1[hunkSize]);
113 				hunkName = malloc(hunkSize+1);
114 				TestMem(hunkName);
115 				strncpy(hunkName,ptr,hunkSize);
116 				hunkName[hunkSize]=0;
117 				ptr += hunkSize;
118 				break;
119 
120 			case AMIGAOS_HUNK_CODE:
121 			case AMIGAOS_HUNK_DATA:
122 				// allocate space for at least one more reloc target hunk
123 				while ((SIZE) ((numRelocTargetHunks+1) * sizeof(SECTION*)) > relocTargetHunksSize) {
124 					relocTargetHunks = crealloc(relocTargetHunks, relocTargetHunksSize,
125 																			relocTargetHunksSize+RELOC_TARGET_HUNKS_INCREMENT);
126 					TestMem(relocTargetHunks);
127 					relocTargetHunksSize += RELOC_TARGET_HUNKS_INCREMENT;
128 				}
129 
130 				// invent a section name if no reasonable one given
131 				if (!hunkName || !*hunkName || *hunkName==' ') {
132 					if (hunkName) {
133 						free(hunkName);
134 					}
135 					hunkName = malloc(sizeof(".data"));
136 					TestMem(hunkName);
137 					strcpy(hunkName,((hunkType == AMIGAOS_HUNK_CODE)?".text":".data"));
138 				}
139 
140 				// determine whether it is a startup section
141 				// This is very important as we cannot omit it.
142 				StartupNumber = GetStartupSectionNumber (hunkName, strlen(hunkName));
143 
144 				hunkSize = GetNextTI4(ptr)<<2;
145 				// omit empty sections to simplify the output
146 				if ((!StartupNumber) && (!hunkSize)) {
147 					free(hunkName);
148 					hunkName=NULL;
149 					break; // no data to skip - there is none!
150 				}
151 
152 				// Section is not empty (or a startup section).
153 				// Try to allocate data for the section, if necessary.
154 				TestInFile(ptr,I1[hunkSize]);
155 				TestMem ((currSectionData = calloc (hunkSize, 1)));
156 				memcpy (currSectionData, ptr, hunkSize);
157 				ptr += hunkSize;
158 
159 				// create a new section, initialize it, and append it to the list of sections.
160 				if (relocTargetHunks[numRelocTargetHunks]) {
161 					currSection = relocTargetHunks[numRelocTargetHunks];
162 				} else {
163 					currSection = calloc (1, sizeof (SECTION));
164 					TestMem (currSection);
165 					currSection->Parent = Program;
166 					currSection->FileName = FileName;
167 				}
168 
169 				currSection->Data = currSectionData;
170 				currSection->Size = hunkSize;
171 				currSection->Code = (hunkType == AMIGAOS_HUNK_CODE);
172 				currSection->Initialized = TRUE;
173 				currSection->StartupNumber = StartupNumber;
174 				currSection->Constructors = (!(strcmp (hunkName, ".ctors")));
175 				currSection->Destructors = (!(strcmp (hunkName, ".dtors")));
176 				currSection->CanCutRanges = AllRelocs;
177 
178 				InsertSection (Program, currSection);
179 
180 				if (!(CreateSectionSymbol (currSection, hunkName)))
181 					Fail ();
182 
183 				relocTargetHunks[numRelocTargetHunks++] = currSection;
184 				currSectionData = NULL; // don't free handles already in linked list!
185 
186 				if (hunkType == AMIGAOS_HUNK_CODE && Program->OptimizeInfo->OptimizeNOPs)
187 					M68kRemoveTrailingNOP (currSection);
188 
189 				free(hunkName);
190 				hunkName = NULL;
191 				break;
192 
193 			case AMIGAOS_HUNK_BSS:
194 				// allocate space for at least one more reloc target hunk
195 				while ((SIZE) ((numRelocTargetHunks+1) * sizeof(SECTION*)) > relocTargetHunksSize) {
196 					relocTargetHunks = crealloc(relocTargetHunks, relocTargetHunksSize,
197 																			relocTargetHunksSize+RELOC_TARGET_HUNKS_INCREMENT);
198 					TestMem(relocTargetHunks);
199 					relocTargetHunksSize += RELOC_TARGET_HUNKS_INCREMENT;
200 				}
201 
202 				// invent a section name if no reasonable one given
203 				if (!hunkName || !*hunkName || *hunkName==' ') {
204 					if (hunkName) {
205 						free(hunkName);
206 					}
207 					hunkName = malloc(sizeof(".bss"));
208 					TestMem(hunkName);
209 					strcpy(hunkName,".bss");
210 				}
211 
212 				hunkSize = GetNextTI4(ptr)<<2;
213 				// omit empty sections to simplify the output
214 				if (!hunkSize) break; // no data to skip - there is none!
215 
216 				// Section is not empty.
217 				// create a new section, initialize it, and append it to the list of sections.
218 				if (relocTargetHunks[numRelocTargetHunks]) {
219 					currSection = relocTargetHunks[numRelocTargetHunks];
220 				} else {
221 					currSection = calloc (1, sizeof (SECTION));
222 					TestMem (currSection);
223 					currSection->Parent = Program;
224 					currSection->FileName = FileName;
225 				}
226 
227 				currSection->Initialized = InitializeBSS;
228 				currSection->Size = hunkSize;
229 				currSection->CanCutRanges = AllRelocs;
230 
231 				Append (Program->Sections, currSection);
232 
233 				if (!(CreateSectionSymbol (currSection, hunkName)))
234 					Fail ();
235 
236 				relocTargetHunks[numRelocTargetHunks++] = currSection;
237 
238 				free(hunkName);
239 				hunkName = NULL;
240 				break;
241 
242 			case AMIGAOS_HUNK_RELOC_REL1:
243 			case AMIGAOS_HUNK_RELOC_REL2:
244 			case AMIGAOS_HUNK_RELOC_REL4:
245 			case AMIGAOS_HUNK_RELOC_ABS2:
246 			case AMIGAOS_HUNK_RELOC_ABS4:
247 				// make sure we have a section to put those relocs into
248 				if (!currSection)
249 					FailWithError("Relocation hunk (type `0x%lX') without context.",(long)hunkType);
250 
251 				{
252 					SIZE relocSize = 0;
253 					BOOLEAN relative = FALSE;
254 					switch (hunkType) {
255 						case AMIGAOS_HUNK_RELOC_REL1:
256 							relative = TRUE;
257 							relocSize = 1;
258 							break;
259 						case AMIGAOS_HUNK_RELOC_REL2:
260 							relative = TRUE;
261 						case AMIGAOS_HUNK_RELOC_ABS2:
262 							relocSize = 2;
263 							break;
264 						case AMIGAOS_HUNK_RELOC_REL4:
265 							relative = TRUE;
266 						case AMIGAOS_HUNK_RELOC_ABS4:
267 							relocSize = 4;
268 							break;
269 					}
270 
271 					hunkSize = GetNextTI4(ptr); // hunkSize is the number of relocs here.
272 
273 					while (hunkSize) {
274 						OFFSET targetHunkNumber = GetNextTI4(ptr);
275 						SECTION *targetHunk;
276 						OFFSET i;
277 
278 						// allocate space for at least one more reloc target hunk
279 						while ((SIZE) ((targetHunkNumber+1) * sizeof(SECTION*)) > relocTargetHunksSize) {
280 							relocTargetHunks = crealloc(relocTargetHunks, relocTargetHunksSize,
281 														relocTargetHunksSize+RELOC_TARGET_HUNKS_INCREMENT);
282 							TestMem(relocTargetHunks);
283 							relocTargetHunksSize += RELOC_TARGET_HUNKS_INCREMENT;
284 						}
285 
286 						targetHunk = relocTargetHunks[targetHunkNumber];
287 						if (!targetHunk) {
288 							// create placeholder for section right now
289 							targetHunk = calloc (1, sizeof (SECTION));
290 							TestMem(targetHunk);
291 							// initialize the fields to something acceptable to the backend:
292 							// the dummy section is a 0-byte BSS section
293 							targetHunk->Parent = Program;
294 							targetHunk->FileName = FileName;
295 
296 							// This will get overwritten when the real section will be read in.
297 							// Otherwise, the "targetHunk" section reference was invalid, hence
298 							// the name of the dummy symbol.
299 							if (!(CreateSectionSymbol (targetHunk, "(invalid AmigaOS target section)")))
300 								Fail ();
301 
302 							relocTargetHunks[targetHunkNumber] = targetHunk;
303 						}
304 
305 						for (i=0; i<(SIZE)hunkSize; i++) {
306 							RELOC *newReloc;
307 							I4 location = GetNextTI4(ptr);
308 							OFFSET targetOffset = 0;
309 							BOOLEAN unoptimizable = FALSE;
310 #ifdef AMIGAOS_TIGCC_EXTENSIONS
311 							unoptimizable = !!(location&AMIGAOS_RELOC_UNOPTIMIZABLE);
312 							location &= ~AMIGAOS_RELOC_UNOPTIMIZABLE;
313 #endif
314 							newReloc = calloc (1, sizeof (RELOC));
315 							TestMem (newReloc);
316 							newReloc->Parent = currSection;
317 							newReloc->Location = location;
318 							newReloc->Target.Symbol = targetHunk->SectionSymbol;
319 							newReloc->Target.SymbolName = newReloc->Target.Symbol->Name;
320 							newReloc->Size = relocSize;
321 							newReloc->Relative = relative;
322 							newReloc->Unoptimizable = unoptimizable;
323 							if ((OFFSET) (location+relocSize) <= currSection->Size) {
324 								if (currSection->Data) {
325 									targetOffset = ReadSTI(currSection->Data+location,relocSize);
326 									// Zero out the section contents.
327 									memset(currSection->Data+location,0,relocSize);
328 									// We have to guess the first bytes of the targetOffset. This is
329 									// the easiest way to do it.
330 									if (targetHunk != currSection) {
331 										OFFSET maxDistance = 1 << ((relocSize * 8) - 1);
332 										// If the section is in front of the current section,
333 										// set the reference location as close to the end as possible.
334 										if (targetHunkNumber < (numRelocTargetHunks - 1)) {
335 											location = targetHunk->Size - (maxDistance - 1);
336 										// Otherwise, set the reference location as close to the start as possible.
337 										} else {
338 											location = maxDistance;
339 										}
340 									}
341 									{
342 										SI4 difference = targetOffset - location;
343 										if (relocSize <= 1) {
344 											targetOffset = location + ((SI1) difference);
345 										} else if (relocSize <= 2) {
346 											targetOffset = location + ((SI2) difference);
347 										}
348 									}
349 								} else {
350 									Warning(FileName,"Adding reloc at 0x%lX in section `%s' without data to section `%s'.",(long)location,currSection->SectionSymbol->Name,newReloc->Target.SymbolName);
351 								}
352 							} else {
353 								Warning(FileName,"Invalid reloc location `0x%lX' in size 4 reloc table for origin section `%s' and target section `%s'",(long)location,currSection->SectionSymbol->Name,newReloc->Target.SymbolName);
354 							}
355 
356 							// Apply architecture-specific fixes to the offset.
357 							newReloc->Target.Offset = ((currSection->Code && newReloc->Target.Symbol->Parent->Code) ? M68kFixTargetOffset (targetOffset, newReloc->Size, newReloc->Relative) : targetOffset);
358 
359 							// Calculate the remaining part of the offset.
360 							newReloc->FixedOffset = targetOffset - newReloc->Target.Offset;
361 
362 							// Put this reloc into the linked list.
363 							InsertReloc(currSection,newReloc);
364 						}
365 
366 						hunkSize = GetNextTI4(ptr);
367 					}
368 				}
369 				break;
370 
371 #ifdef AMIGAOS_TIGCC_EXTENSIONS
372 			case AMIGAOS_HUNK_RELOC_ABS1_POSNEG:
373 			case AMIGAOS_HUNK_RELOC_ABS2_POSNEG:
374 			case AMIGAOS_HUNK_RELOC_ABS4_POSNEG:
375 				// make sure we have a section to put those relocs into
376 				if (!currSection)
377 					FailWithError("Relocation hunk (type `0x%lX') without context.",(long)hunkType);
378 
379 				{
380 					OFFSET i;
381 					SIZE relocSize = 0;
382 
383 					switch (hunkType) {
384 						case AMIGAOS_HUNK_RELOC_ABS1_POSNEG:
385 							relocSize = 1;
386 							break;
387 						case AMIGAOS_HUNK_RELOC_ABS2_POSNEG:
388 							relocSize = 2;
389 							break;
390 						case AMIGAOS_HUNK_RELOC_ABS4_POSNEG:
391 							relocSize = 4;
392 							break;
393 					}
394 
395 					hunkSize = GetNextTI4(ptr); // hunkSize is the number of relocs here.
396 
397 					for (i=0; i<(SIZE)hunkSize; i++) {
398 						RELOC *newReloc;
399 						OFFSET targetHunkNumber = GetNextTI4(ptr);
400 						SECTION *targetHunk;
401 						I4 location;
402 						OFFSET targetOffset;
403 						BOOLEAN unoptimizable;
404 
405 						// allocate space for at least one more reloc target hunk
406 						while ((SIZE) ((targetHunkNumber+1) * sizeof(SECTION*)) > relocTargetHunksSize) {
407 							relocTargetHunks = crealloc(relocTargetHunks, relocTargetHunksSize,
408 														relocTargetHunksSize+RELOC_TARGET_HUNKS_INCREMENT);
409 							TestMem(relocTargetHunks);
410 							relocTargetHunksSize += RELOC_TARGET_HUNKS_INCREMENT;
411 						}
412 
413 						targetHunk = relocTargetHunks[targetHunkNumber];
414 						if (!targetHunk) {
415 							// create placeholder for section right now
416 							targetHunk = calloc (1, sizeof (SECTION));
417 							TestMem(targetHunk);
418 							// initialize the fields to something acceptable to the backend:
419 							// the dummy section is a 0-byte BSS section
420 							targetHunk->Parent = Program;
421 							targetHunk->FileName = FileName;
422 
423 							// This will get overwritten when the real section will be read in.
424 							// Otherwise, the "targetHunk" section reference was invalid, hence
425 							// the name of the dummy symbol.
426 							if (!(CreateSectionSymbol (targetHunk, "(invalid AmigaOS target section)")))
427 								Fail ();
428 
429 							relocTargetHunks[targetHunkNumber] = targetHunk;
430 						}
431 
432 						location = GetNextTI4(ptr);
433 						unoptimizable = !!(location&AMIGAOS_RELOC_UNOPTIMIZABLE);
434 						location &= ~AMIGAOS_RELOC_UNOPTIMIZABLE;
435 						targetOffset = GetNextTI4(ptr);
436 						if (GetNextTI4(ptr)) { // negative reloc
437 							RELOC *PositiveReloc = NULL;
438 
439 							// Find a matching positive reloc.
440 							PositiveReloc = FindMatchingReloc (currSection, location, relocSize, FALSE, NULL, relocHint);
441 
442 							if (PositiveReloc) {
443 								LOCATION *relation = calloc (1, sizeof (LOCATION));
444 								TestMem (relation);
445 								relation->Symbol = targetHunk->SectionSymbol;
446 								relation->SymbolName = relation->Symbol->Name;
447 								relation->Offset = targetOffset;
448 								PositiveReloc->Relative = TRUE;
449 								PositiveReloc->Relation = relation;
450 								HandleLocation(PositiveReloc, relation);
451 
452 								relocHint = PositiveReloc;
453 							} else {
454 								Warning (FileName, "Removing negative reloc at 0x%lX with no matching positive reloc.", (long) location);
455 							}
456 						} else { // positive reloc
457 							newReloc = calloc (1, sizeof (RELOC));
458 							TestMem (newReloc);
459 							newReloc->Parent = currSection;
460 							newReloc->Location = location;
461 							newReloc->Target.Symbol = targetHunk->SectionSymbol;
462 							newReloc->Target.SymbolName = newReloc->Target.Symbol->Name;
463 							newReloc->Target.Offset = targetOffset;
464 							newReloc->Size = relocSize;
465 							newReloc->Relative = FALSE;
466 							newReloc->Unoptimizable = unoptimizable;
467 
468 							// Those hunks put the target offset in the relocation table. However, we have to
469 							// read a possible FixedOffset from the data stream.
470 							if ((OFFSET) (location+relocSize) <= currSection->Size) {
471 								if (currSection->Data) {
472 									newReloc->FixedOffset = ReadSTI(currSection->Data+location,relocSize);
473 									// Zero out the section contents.
474 									memset(currSection->Data+location,0,relocSize);
475 								} else {
476 									Warning(FileName,"Adding reloc at 0x%lX in section `%s' without data to section `%s'.",(long)location,currSection->SectionSymbol->Name,newReloc->Target.SymbolName);
477 								}
478 							} else {
479 								Warning(FileName,"Invalid reloc location `0x%lX' in extended size %ld reloc table for origin section `%s' and target section `%s'",(long)location,(long)relocSize,currSection->SectionSymbol->Name,newReloc->Target.SymbolName);
480 							}
481 
482 							// Put this reloc into the linked list.
483 							InsertReloc(currSection,newReloc);
484 						}
485 					}
486 				}
487 				break;
488 #endif
489 
490 			case AMIGAOS_HUNK_END:
491 				// Do nothing - we already skipped the hunk type, and that's all we need
492 				// to do here.
493 				break;
494 
495 			case AMIGAOS_HUNK_EXT:
496 				// make sure we have a section to put those symbols into
497 				if (!currSection)
498 					FailWithError("Symbol import/export hunk without context.");
499 				hunkSize = GetNextTI4(ptr);
500 				while (hunkSize) {
501 					// The most significant byte of the size longword encodes the symbol type.
502 					hunkType = hunkSize >> 24;
503 					hunkSize = (hunkSize&0xffffffL)<<2;
504 
505 					TestInFile(ptr,I1[hunkSize]);
506 					symName = malloc(hunkSize+1);
507 					TestMem(symName);
508 					strncpy(symName,ptr,hunkSize);
509 					symName[hunkSize]=0;
510 					ptr+=hunkSize;
511 
512 					switch (hunkType) {
513 						// Definitions:
514 						// Absolute definition: we cannot use those, but ignoring them
515 						// should not be fatal, so we will ignore them with a warning.
516 						case AMIGAOS_EXT_ABS:
517 							Warning(FileName,"Cannot handle absolute symbol `%s'.",symName);
518 							TestInFile(ptr,TI4);
519 							ptr+=4;
520 							break;
521 
522 						// Standard definition (offset from a section):
523 						case AMIGAOS_EXT_DEF: {
524 							I4 location = GetNextTI4(ptr);
525 							if (HandleSpecialSymbol (Program, symName)) {
526 								// This is the best we can get without
527 								// adding too much extra code: Omit the
528 								// initialization for all following BSS
529 								// sections.
530 								if (!(strcmp(symName,SYM_OMIT_BSS_INIT))) {
531 									InitializeBSS=FALSE;
532 								}
533 								// __ld_all_relocs has to be exported from the
534 								// first section. So we'll set CanCutRanges to
535 								// TRUE for the section it appears in, and
536 								// the variable AllRelocs will handle the rest.
537 								else if (!(strcmp(symName,SYM_ALL_RELOCS))) {
538 									currSection->CanCutRanges = TRUE;
539 									AllRelocs = TRUE;
540 								}
541 								break;
542 							}
543 							currSymbol = calloc (1, sizeof (SYMBOL));
544 							TestMem(currSymbol);
545 							currSymbol->Parent = currSection;
546 							currSymbol->Location = location;
547 							strncpy(currSymbol->Name,symName,MAX_SYM_LEN);
548 							currSymbol->Exported = TRUE;
549 							InsertSymbol(currSection,currSymbol);
550 							break; }
551 
552 						// References:
553 						case AMIGAOS_EXT_REF_ABS1:
554 						case AMIGAOS_EXT_REF_ABS2:
555 						case AMIGAOS_EXT_REF_ABS4:
556 						case AMIGAOS_EXT_REF_REL1:
557 						case AMIGAOS_EXT_REF_REL2:
558 						case AMIGAOS_EXT_REF_REL4: {
559 							// Those are actually relocs in ld-tigcc terms.
560 							OFFSET i;
561 
562 							SIZE relocSize = 0;
563 							BOOLEAN relative = FALSE;
564 							switch (hunkType) {
565 								case AMIGAOS_EXT_REF_REL1:
566 									relative = TRUE;
567 								case AMIGAOS_EXT_REF_ABS1:
568 									relocSize = 1;
569 									break;
570 								case AMIGAOS_EXT_REF_REL2:
571 									relative = TRUE;
572 								case AMIGAOS_EXT_REF_ABS2:
573 									relocSize = 2;
574 									break;
575 								case AMIGAOS_EXT_REF_REL4:
576 									relative = TRUE;
577 								case AMIGAOS_EXT_REF_ABS4:
578 									relocSize = 4;
579 									break;
580 							}
581 
582 							hunkSize = GetNextTI4(ptr); // hunkSize is the number of relocs here.
583 
584 							for (i=0; i<(SIZE)hunkSize; i++) {
585 								RELOC *newReloc;
586 								I4 location = GetNextTI4(ptr);
587 								BOOLEAN unoptimizable = FALSE;
588 								char *newName = malloc(strlen(symName)+1);
589 								TestMem(newName);
590 								strcpy(newName,symName);
591 #ifdef AMIGAOS_TIGCC_EXTENSIONS
592 								unoptimizable = !!(location&AMIGAOS_RELOC_UNOPTIMIZABLE);
593 								location &= ~AMIGAOS_RELOC_UNOPTIMIZABLE;
594 #endif
595 								newReloc = calloc (1, sizeof (RELOC));
596 								TestMem(newReloc);
597 								newReloc->Parent = currSection;
598 								newReloc->Location = location;
599 								newReloc->Target.SymbolName = newName;
600 								newReloc->Size = relocSize;
601 								newReloc->Relative = relative;
602 								newReloc->Unoptimizable = unoptimizable;
603 								if ((OFFSET) (location+relocSize) <= currSection->Size) {
604 									if (currSection->Data) {
605 										newReloc->FixedOffset = ReadSTI(currSection->Data+location,relocSize);
606 										memset(currSection->Data+location,0,relocSize);
607 									} else {
608 										Warning(FileName,"Adding reloc at 0x%lX in section `%s' without data to symbol `%s'.",(long)location,currSection->SectionSymbol->Name,symName);
609 									}
610 								} else {
611 									Warning(FileName,"Invalid reloc location `0x%lX' in size %ld reference table for origin section `%s' and target symbol `%s'",(long)location,(long)relocSize,currSection->SectionSymbol->Name,symName);
612 								}
613 
614 								// Put this reloc into the linked list
615 								InsertReloc(currSection,newReloc);
616 							}
617 							// The (unused) last copy of the symbol name will be freed below.
618 							break; }
619 
620 						// Other symbol types are never used by A68k.
621 						default:
622 							FailWithError("Unsupported AmigaOS symbol type `0x%lX'.",(unsigned long)hunkType);
623 							break;
624 					}
625 
626 					free(symName);
627 					symName = NULL; // Prevent symName from getting freed again.
628 					hunkSize = GetNextTI4(ptr);
629 				}
630 				break;
631 
632 			case AMIGAOS_HUNK_SYMBOL:
633 				// debugging symbol table - contains local (non-exported) symbols,
634 				// which are in the object file for debugging purposes only
635 
636 				// make sure we have a section to put those symbols into
637 				if (!currSection)
638 					FailWithError("Symbol table hunk without context.");
639 
640 				hunkSize = GetNextTI4(ptr)<<2;
641 				while (hunkSize) {
642 					SIZE symSize;
643 					I4 location;
644 					SYMBOL *symbol;
645 					BOOLEAN found=FALSE;
646 
647 					TestInFile(ptr,I1[hunkSize]);
648 					symSize = (hunkSize<MAX_SYM_LEN)?hunkSize:MAX_SYM_LEN;
649 
650 					for_each(symbol,currSection->Symbols) {
651 						if (!strncmp(symbol->Name,ptr,symSize) && !(ptr[symSize])) {
652 							found = TRUE;
653 							break;
654 						}
655 					}
656 
657 					if (found) { // symbol already present - it was already defined as global
658 						ptr+=hunkSize;
659 
660 						// skip location
661 						TestInFile(ptr,TI4);
662 						ptr+=4;
663 					} else {
664 						// copy symbol name
665 						currSymbol = calloc (1, sizeof (SYMBOL));
666 						TestMem(currSymbol);
667 						currSymbol->Parent = currSection;
668 						strncpy(currSymbol->Name,ptr,symSize);
669 						currSymbol->Name[symSize+1]=0;
670 						ptr+=hunkSize;
671 						// Do NOT handle special symbols specially here! Those symbols are
672 						// local and should NOT be interpreted by the linker.
673 
674 						// copy location
675 						location = GetNextTI4(ptr);
676 						currSymbol->Location = location;
677 
678 						//register symbol
679 						InsertSymbol(currSection,currSymbol);
680 					}
681 
682 					hunkSize = GetNextTI4(ptr)<<2;
683 				}
684 				break;
685 
686 			// The other hunk types are not well-documented or not applicable here, and A68k never generates them anyway.
687 			default:
688 				FailWithError("Unsupported AmigaOS hunk type `0x%lX'.",(unsigned long)hunkType);
689 				break;
690 		}
691 	}
692 
693 	// free allocated memory
694 	if (hunkName) {
695 		Warning(FileName,"Hunk name (`%s') with no corresponding hunk.",hunkName);
696 		free(hunkName);
697 	}
698 	if (relocTargetHunks) {
699 		free(relocTargetHunks);
700 	}
701 
702 	return TRUE; // success
703 
704 #undef GetNextTI4
705 #undef TestInFile
706 #undef IsInFile
707 #undef TestMem
708 #undef FailWithError
709 #undef Fail
710 }
711 
712 #endif /* AMIGAOS_SUPPORT */
713