1 /* alink.c */
2 /****************************************************************************/
3 /* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */
4 /* */
5 /* AS-Portierung */
6 /* */
7 /* Linking of AS Code Files */
8 /* */
9 /* History: 2. 7.2000 begun */
10 /* 4. 7.2000 read symbols */
11 /* 6. 7.2000 start real relocation */
12 /* 7. 7.2000 simple relocations */
13 /* 30.10.2000 added 1-byte relocations, verbosity levels */
14 /* 14. 1.2001 silenced warnings about unused parameters */
15 /* */
16 /****************************************************************************/
17
18 #include "stdinc.h"
19 #include <string.h>
20 #include <ctype.h>
21
22 #include "version.h"
23
24 #include "endian.h"
25 #include "bpemu.h"
26 #include "strutil.h"
27 #include "nls.h"
28 #include "nlmessages.h"
29 #include "stringlists.h"
30 #include "cmdarg.h"
31 #include "toolutils.h"
32
33 #include "ioerrs.h"
34
35 #include "alink.rsc"
36
37 /****************************************************************************/
38 /* Macros */
39
40 /****************************************************************************/
41 /* Types */
42
43 typedef struct sPart
44 {
45 struct sPart *Next;
46 int FileNum, RecNum;
47 LargeWord CodeStart, CodeLen;
48 Byte Gran, Segment;
49 Boolean MustReloc;
50 PRelocInfo RelocInfo;
51 } TPart, *PPart;
52
53 /****************************************************************************/
54 /* Variables */
55
56 static Boolean DoubleErr;
57 static int Verbose;
58 static LongWord UndefErr;
59
60 static String TargName;
61
62 static CMDProcessed ParUnprocessed;
63
64 static PPart PartList, PartLast;
65
66 static FILE *TargFile;
67
68 static Byte *Buffer;
69 LongInt BufferSize;
70
71 static LargeWord SegStarts[PCMax + 1];
72
73 /****************************************************************************/
74 /* increase buffer if necessary */
75
BumpBuffer(LongInt MinSize)76 static void BumpBuffer(LongInt MinSize)
77 {
78 if (MinSize > BufferSize)
79 {
80 Buffer = (Byte*) ((BufferSize == 0) ? malloc(sizeof(Byte) * MinSize)
81 : realloc(Buffer, sizeof(Byte) * MinSize));
82 BufferSize = MinSize;
83 }
84 }
85
86 /****************************************************************************/
87 /* reading/patching in buffer */
88
GetValue(LongInt Type,LargeWord Offset)89 static LargeWord GetValue(LongInt Type, LargeWord Offset)
90 {
91 switch (Type & ~(RelocFlagSUB | RelocFlagPage))
92 {
93 case RelocTypeL8:
94 return MRead1L(Buffer + Offset);
95 case RelocTypeL16:
96 return MRead2L(Buffer + Offset);
97 case RelocTypeB16:
98 return MRead2B(Buffer + Offset);
99 case RelocTypeL32:
100 return MRead4L(Buffer + Offset);
101 case RelocTypeB32:
102 return MRead4B(Buffer + Offset);
103 #ifdef HAS64
104 case RelocTypeL64:
105 return MRead8L(Buffer + Offset);
106 case RelocTypeB64:
107 return MRead8B(Buffer + Offset);
108 #endif
109 }
110 fprintf(stderr, "unknown relocation type: 0x");
111 fprintf(stderr, LongIntFormat, Type);
112 fprintf(stderr, "\n");
113 exit(3);
114 }
115
PutValue(LargeWord Value,LongInt Type,LargeWord Offset)116 static void PutValue(LargeWord Value, LongInt Type, LargeWord Offset)
117 {
118 switch (Type & ~(RelocFlagSUB | RelocFlagPage))
119 {
120 case RelocTypeL8:
121 MWrite1L(Buffer + Offset, Value);
122 break;
123 case RelocTypeL16:
124 MWrite2L(Buffer + Offset, Value);
125 break;
126 case RelocTypeB16:
127 MWrite2B(Buffer + Offset, Value);
128 break;
129 case RelocTypeL32:
130 MWrite4L(Buffer + Offset, Value);
131 break;
132 case RelocTypeB32:
133 MWrite4B(Buffer + Offset, Value);
134 break;
135 #ifdef HAS64
136 case RelocTypeL64:
137 MWrite8L(Buffer + Offset, Value);
138 break;
139 case RelocTypeB64:
140 MWrite8B(Buffer + Offset, Value);
141 break;
142 #endif
143 default:
144 fprintf(stderr, "unknown relocation type: 0x");
145 fprintf(stderr, LongIntFormat, Type);
146 fprintf(stderr, "\n");
147 exit(3);
148 }
149 }
150
151 /****************************************************************************/
152 /* get the value of an exported symbol */
153
GetExport(char * Name,LargeWord * Result)154 static Boolean GetExport(char *Name, LargeWord *Result)
155 {
156 PPart PartRun;
157 LongInt z;
158
159 for (PartRun = PartList; PartRun; PartRun = PartRun->Next)
160 for (z = 0; z < PartRun->RelocInfo->ExportCount; z++)
161 if (!strcmp(Name, PartRun->RelocInfo->ExportEntries[z].Name))
162 {
163 *Result = PartRun->RelocInfo->ExportEntries[z].Value;
164 return True;
165 }
166
167 return False;
168 }
169
170 /****************************************************************************/
171 /* read the symbol exports/relocations from a file */
172
ReadSymbols(const char * pSrcName,int Index)173 static void ReadSymbols(const char *pSrcName, int Index)
174 {
175 FILE *f;
176 String SrcName;
177 Byte Header, CPU, Gran, Segment;
178 PPart PNew;
179 LongWord Addr;
180 Word Len;
181 int cnt;
182
183 /* open this file - we're only reading */
184
185 strmaxcpy(SrcName, pSrcName, STRINGSIZE);
186 DelSuffix(SrcName); AddSuffix(SrcName, STRINGSIZE, getmessage(Num_Suffix));
187 if (Verbose >= 2)
188 printf("%s '%s'...\n", getmessage(Num_InfoMsgGetSyms), SrcName);
189 f = fopen(SrcName, OPENRDMODE);
190 if (!f)
191 ChkIO(SrcName);
192
193 /* check magic */
194
195 if (!Read2(f, &Len)) ChkIO(SrcName);
196 if (Len != FileMagic)
197 FormatError(SrcName, getmessage(Num_FormatInvHeaderMsg));
198
199 /* step through records */
200
201 cnt = 0;
202 while (!feof(f))
203 {
204 /* read a record's header */
205
206 ReadRecordHeader(&Header, &CPU, &Segment, &Gran, SrcName, f);
207
208 /* for absolute records, only store address position */
209 /* for relocatable records, also read following relocation record */
210
211 if ((Header == FileHeaderDataRec) || (Header == FileHeaderRDataRec)
212 || (Header == FileHeaderRelocRec) || (Header == FileHeaderRRelocRec))
213 {
214 /* build up record */
215
216 PNew = (PPart) malloc(sizeof(TPart));
217 PNew->Next = NULL;
218 PNew->FileNum = Index;
219 PNew->RecNum = cnt++;
220 PNew->Gran = Gran;
221 PNew->Segment = Segment;
222 if (!Read4(f, &Addr))
223 ChkIO(SrcName);
224 PNew->CodeStart = Addr;
225 if (!Read2(f, &Len))
226 ChkIO(SrcName);
227 PNew->CodeLen = Len;
228 PNew->MustReloc = ((Header == FileHeaderRelocRec)
229 || (Header == FileHeaderRRelocRec));
230
231 /* skip code */
232
233 if (fseek(f, Len, SEEK_CUR) != 0)
234 ChkIO(SrcName);
235
236 /* relocatable record must be followed by relocation data */
237
238 if ((Header == FileHeaderRDataRec) || (Header == FileHeaderRRelocRec))
239 {
240 LongInt z;
241 LargeWord Dummy;
242
243 ReadRecordHeader(&Header, &CPU, &Segment, &Gran, SrcName, f);
244 if (Header != FileHeaderRelocInfo)
245 FormatError(SrcName, getmessage(Num_FormatRelocInfoMissing));
246 PNew->RelocInfo = ReadRelocInfo(f);
247 if (!PNew->RelocInfo)
248 ChkIO(SrcName);
249
250 /* check for double-defined symbols */
251
252 for (z = 0; z < PNew->RelocInfo->ExportCount; z++)
253 if (GetExport(PNew->RelocInfo->ExportEntries[z].Name, &Dummy))
254 {
255 fprintf(stderr, "%s: %s '%s'\n",
256 SrcName, getmessage(Num_DoubleDefSymbol),
257 PNew->RelocInfo->ExportEntries[z].Name);
258 DoubleErr = True;
259 }
260 }
261 else
262 PNew->RelocInfo = NULL;
263
264 /* put into list */
265
266 if (!PartList)
267 PartList = PNew;
268 else
269 PartLast->Next = PNew;
270 PartLast = PNew;
271 }
272
273 /* end of file ? */
274
275 else if (Header == FileHeaderEnd)
276 break;
277
278 /* skip everything else */
279
280 else
281 SkipRecord(Header, SrcName, f);
282 }
283
284 /* close again */
285
286 fclose(f);
287 }
288
289 /****************************************************************************/
290 /* do the relocation */
291
ProcessFile(const char * pSrcName,int Index)292 static void ProcessFile(const char *pSrcName, int Index)
293 {
294 FILE *f;
295 String SrcName;
296 PPart PartRun;
297 Byte Header, CPU, Gran, Segment;
298 LongInt Addr, z;
299 LargeWord Value, RelocVal, NRelocVal;
300 Word Len, Magic;
301 LongWord SumLen;
302 PRelocEntry PReloc;
303 Boolean UndefFlag, Found;
304
305 /* open this file - we're only reading */
306
307 strmaxcpy(SrcName, pSrcName, STRINGSIZE);
308 DelSuffix(SrcName); AddSuffix(SrcName, STRINGSIZE, getmessage(Num_Suffix));
309 if (Verbose >= 2)
310 printf("%s '%s'...", getmessage(Num_InfoMsgOpenSrc), SrcName);
311 else if (Verbose >= 1)
312 printf("%s", SrcName);
313 f = fopen(SrcName, OPENRDMODE);
314 if (!f)
315 ChkIO(SrcName);
316
317 /* check magic */
318
319 if (!Read2(f, &Magic))
320 ChkIO(SrcName);
321 if (Magic != FileMagic)
322 FormatError(SrcName, getmessage(Num_FormatInvHeaderMsg));
323
324 /* due to the way we built the part list, all parts of a file are
325 sequentially in the list. Therefore we only have to look for
326 the first part of this file in the list and know the remainders
327 will follow sequentially. */
328
329 for (PartRun = PartList; PartRun; PartRun = PartRun->Next)
330 if (PartRun->FileNum >= Index)
331 break;
332
333 /* now step through the records */
334
335 SumLen = 0;
336 while (!feof(f))
337 {
338 /* get header */
339
340 ReadRecordHeader(&Header, &CPU, &Segment, &Gran, SrcName, f);
341
342 /* records without relocation info do not need any processing - just
343 pass them through */
344
345 if ((Header == FileHeaderDataRec) || (Header == FileHeaderRelocRec))
346 {
347 if (!Read4(f, &Addr))
348 ChkIO(SrcName);
349 if (!Read2(f, &Len))
350 ChkIO(SrcName);
351 BumpBuffer(Len);
352 if (fread(Buffer, 1, Len, f) != Len)
353 ChkIO(SrcName);
354 WriteRecordHeader(&Header, &CPU, &Segment, &Gran, TargName, TargFile);
355 if (!Write4(TargFile, &Addr))
356 ChkIO(TargName);
357 if (!Write2(TargFile, &Len))
358 ChkIO(TargName);
359 if (fwrite(Buffer, 1, Len, TargFile) != Len)
360 ChkIO(TargName);
361 if (PartRun)
362 PartRun = PartRun->Next;
363 SumLen += Len;
364 }
365
366 /* records with relocation: basically the same, plus the real work...
367 the appended relocation info will be skipped in the next loop run. */
368
369 else if ((Header == FileHeaderRDataRec) || (Header == FileHeaderRRelocRec))
370 {
371 if (!Read4(f, &Addr))
372 ChkIO(SrcName);
373 if (!Read2(f, &Len))
374 ChkIO(SrcName);
375 BumpBuffer(Len);
376 if (fread(Buffer, 1, Len, f) != Len)
377 ChkIO(SrcName);
378
379 UndefFlag = False;
380 for (z = 0; z < PartRun->RelocInfo->RelocCount; z++)
381 {
382 PReloc = PartRun->RelocInfo->RelocEntries + z;
383 Found = True;
384 if (!strcmp(PReloc->Name, RelName_SegStart))
385 Value = PartRun->CodeStart;
386 else
387 Found = GetExport(PReloc->Name, &Value);
388 if (Found)
389 {
390 if (Verbose >= 2)
391 printf("%s 0x%x...", getmessage(Num_InfoMsgReading), (int)PReloc->Addr);
392 RelocVal = GetValue(PReloc->Type, PReloc->Addr - PartRun->CodeStart);
393 NRelocVal = (PReloc->Type & RelocFlagSUB) ? RelocVal - Value : RelocVal + Value;
394 PutValue(NRelocVal, PReloc->Type, PReloc->Addr - PartRun->CodeStart);
395 }
396 else
397 {
398 fprintf(stderr, "%s: %s(%s)\n", getmessage(Num_UndefSymbol),
399 PReloc->Name, SrcName);
400 UndefFlag = True;
401 UndefErr++;
402 }
403 }
404
405 if (!UndefFlag)
406 {
407 Header = FileHeaderDataRec;
408 WriteRecordHeader(&Header, &CPU, &Segment, &Gran, TargName, TargFile);
409 Addr = PartRun->CodeStart;
410 if (!Write4(TargFile, &Addr))
411 ChkIO(TargName);
412 if (!Write2(TargFile, &Len))
413 ChkIO(TargName);
414 if (fwrite(Buffer, 1, Len, TargFile) != Len)
415 ChkIO(TargName);
416 if (PartRun)
417 PartRun = PartRun->Next;
418 }
419 SumLen += Len;
420 }
421
422 /* all done? */
423
424 else if (Header == FileHeaderEnd)
425 break;
426
427 else
428 SkipRecord(Header, SrcName, f);
429 }
430
431 if (Verbose >= 1)
432 {
433 printf("(");
434 printf(Integ32Format, SumLen);
435 printf(" %s)\n", getmessage((SumLen == 1) ? Num_Byte : Num_Bytes));
436 }
437 }
438
439 /****************************************************************************/
440 /* command line processing */
441
CMD_Verbose(Boolean Negate,const char * Arg)442 static CMDResult CMD_Verbose(Boolean Negate, const char *Arg)
443 {
444 UNUSED(Arg);
445
446 if (Negate)
447 {
448 if (Verbose)
449 Verbose--;
450 }
451 else
452 Verbose++;
453
454 return CMDOK;
455 }
456
ParamError(Boolean InEnv,char * Arg)457 static void ParamError(Boolean InEnv, char *Arg)
458 {
459 printf("%s%s\n%s\n",
460 getmessage((InEnv) ? Num_ErrMsgInvEnvParam : Num_ErrMsgInvParam),
461 Arg, getmessage(Num_ErrMsgProgTerm));
462 exit(1);
463 }
464
465 #define ALINKParamCnt (sizeof(ALINKParams) / sizeof(*ALINKParams))
466
467 static CMDRec ALINKParams[] =
468 {
469 {"v", CMD_Verbose}
470 };
471
472 /****************************************************************************/
473
main(int argc,char ** argv)474 int main(int argc, char **argv)
475 {
476 String Ver;
477 int z;
478 Word LMagic;
479 Byte LHeader;
480 PPart PartRun;
481 LargeInt Diff;
482
483 /* the initialization orgy... */
484
485 nls_init();
486 if (!NLS_Initialize(&argc, argv))
487 exit(4);
488
489 endian_init();
490 bpemu_init();
491 strutil_init();
492
493 Buffer = NULL;
494 BufferSize = 0;
495
496 /* open message catalog */
497
498 nlmessages_init("alink.msg", *argv, MsgId1, MsgId2); ioerrs_init(*argv);
499 cmdarg_init(*argv);
500 toolutils_init(*argv);
501
502 as_snprintf(Ver, sizeof(Ver), "ALINK/C V%s", Version);
503 WrCopyRight(Ver);
504
505 /* no commandline arguments -->print help */
506
507 if (argc <= 1)
508 {
509 char *ph1, *ph2;
510
511 errno = 0;
512 printf("%s%s%s\n", getmessage(Num_InfoMessHead1), GetEXEName(argv[0]),
513 getmessage(Num_InfoMessHead2));
514 ChkIO(OutName);
515 for (ph1 = getmessage(Num_InfoMessHelp), ph2 = strchr(ph1,'\n'); ph2; ph1 = ph2+1, ph2 = strchr(ph1,'\n'))
516 {
517 *ph2 = '\0';
518 printf("%s\n", ph1);
519 *ph2 = '\n';
520 }
521 exit(1);
522 }
523
524 /* preinit commandline variables */
525
526 Verbose = 0;
527
528 /* process arguments */
529
530 ProcessCMD(argc, argv, ALINKParams, ALINKParamCnt, ParUnprocessed, "ALINKCMD", ParamError);
531
532 if ((Verbose >= 1) && (argc > 1))
533 printf("\n");
534
535 /* extract target file */
536
537 if (ProcessedEmpty(ParUnprocessed))
538 {
539 errno = 0;
540 printf("%s\n", getmessage(Num_ErrMsgTargMissing));
541 ChkIO(OutName);
542 exit(1);
543 }
544 for (z = argc - 1; z > 0; z--)
545 if (ParUnprocessed[z])
546 break;
547 strmaxcpy(TargName, argv[z], STRINGSIZE);
548 DelSuffix(TargName);
549 AddSuffix(TargName, STRINGSIZE, getmessage(Num_Suffix));
550 ParUnprocessed[z] = False;
551
552 /* walk over source file(s): */
553
554 if (ProcessedEmpty(ParUnprocessed))
555 {
556 errno = 0;
557 printf("%s\n", getmessage(Num_ErrMsgSrcMissing));
558 ChkIO(OutName);
559 exit(1);
560 }
561
562 /* read symbol info from all files */
563
564 DoubleErr = False;
565 PartList = NULL;
566 for (z = 1; z < argc; z++)
567 if (ParUnprocessed[z])
568 ReadSymbols(argv[z], z);
569
570 /* double-defined symbols? */
571
572 if (DoubleErr)
573 return 1;
574
575 /* arrange relocatable segments in memory, relocate global symbols */
576
577 for (PartRun = PartList; PartRun; PartRun = PartRun->Next)
578 if (PartRun->MustReloc)
579 {
580 Diff = SegStarts[PartRun->Segment] - PartRun->CodeStart;
581 PartRun->CodeStart += Diff;
582 if (Verbose >= 2)
583 printf("%s 0x%x\n", getmessage(Num_InfoMsgLocating), (int)PartRun->CodeStart);
584 if (PartRun->RelocInfo)
585 {
586 PExportEntry ExpRun, ExpEnd;
587 PRelocEntry RelRun, RelEnd;
588
589 ExpRun = PartRun->RelocInfo->ExportEntries;
590 ExpEnd = ExpRun + PartRun->RelocInfo->ExportCount;
591 for (; ExpRun < ExpEnd; ExpRun++)
592 if (ExpRun->Flags & RelFlag_Relative)
593 ExpRun->Value += Diff;
594 RelRun = PartRun->RelocInfo->RelocEntries;
595 RelEnd = RelRun + PartRun->RelocInfo->RelocCount;
596 for (; RelRun < RelEnd; RelRun++)
597 RelRun->Addr += Diff;
598 }
599 SegStarts[PartRun->Segment] += PartRun->CodeLen / PartRun->Gran;
600 }
601
602 /* open target file */
603
604 TargFile = fopen(TargName, OPENWRMODE);
605 if (!TargFile)
606 ChkIO(TargName);
607 LMagic = FileMagic;
608 if (!Write2(TargFile, &LMagic))
609 ChkIO(TargName);
610
611 /* do relocations, underwhile write target file */
612
613 UndefErr = 0;
614 for (z = 1; z < argc; z++)
615 if (ParUnprocessed[z])
616 ProcessFile(argv[z], z);
617
618 /* write final creator record */
619
620 LHeader = FileHeaderEnd;
621 if (fwrite(&LHeader, 1, 1, TargFile) != 1)
622 ChkIO(TargName);
623 as_snprintf( Ver, sizeof(Ver), "ALINK %s/%s-%s", Version, ARCHPRNAME, ARCHSYSNAME);
624 if (fwrite(Ver, 1, strlen(Ver), TargFile) != strlen(Ver))
625 ChkIO(TargName);
626
627 /* close target file and erase if undefined symbols */
628
629 fclose(TargFile);
630 if ((UndefErr > 0) || (Magic != 0))
631 unlink(TargName);
632 if (UndefErr > 0)
633 {
634 fprintf(stderr, "\n");
635 fprintf(stderr, LongIntFormat, UndefErr);
636 fprintf(stderr, " %s\n", getmessage((UndefErr == 1) ? Num_SumUndefSymbol : Num_SumUndefSymbols));
637 return 1;
638 }
639
640 return 0;
641 }
642