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