1 /*****************************************************************************/
5 /* Target configuration file for the ld65 linker */
9 /* (C) 1998-2000 Ullrich von Bassewitz */
11 /* D-70597 Stuttgart */
12 /* EMail: uz@musoftware.de */
15 /* This software is provided 'as-is', without any expressed or implied */
16 /* warranty. In no event will the authors be held liable for any damages */
17 /* arising from the use of this software. */
19 /* Permission is granted to anyone to use this software for any purpose, */
20 /* including commercial applications, and to alter it and redistribute it */
21 /* freely, subject to the following restrictions: */
23 /* 1. The origin of this software must not be misrepresented; you must not */
24 /* claim that you wrote the original software. If you use this software */
25 /* in a product, an acknowledgment in the product documentation would be */
26 /* appreciated but is not required. */
27 /* 2. Altered source versions must be plainly marked as such, and must not */
28 /* be misrepresented as being the original software. */
29 /* 3. This notice may not be removed or altered from any source */
32 /*****************************************************************************/
41 #include "../common/bitops.h"
42 #include "../common/xmalloc.h"
55 /*****************************************************************************/
57 /*****************************************************************************/
62 static File* FileList; /* Single linked list */
63 static unsigned FileCount; /* Number of entries in the list */
68 static Memory* MemoryList; /* Single linked list */
69 static Memory* MemoryLast; /* Last element in list */
70 static unsigned MemoryCount; /* Number of entries in the list */
72 /* Memory attributes */
73 #define MA_START 0x0001
74 #define MA_SIZE 0x0002
75 #define MA_TYPE 0x0004
76 #define MA_FILE 0x0008
77 #define MA_DEFINE 0x0010
78 #define MA_FILL 0x0020
79 #define MA_FILLVAL 0x0040
84 SegDesc* SegDescList; /* Single linked list */
85 unsigned SegDescCount; /* Number of entries in list */
87 /* Segment attributes */
88 #define SA_TYPE 0x0001
89 #define SA_LOAD 0x0002
91 #define SA_ALIGN 0x0008
92 #define SA_DEFINE 0x0010
93 #define SA_OFFSET 0x0020
94 #define SA_START 0x0040
98 /* Descriptor holding information about the binary formats */
99 static BinDesc* BinFmtDesc = 0;
100 static O65Desc* O65FmtDesc = 0;
102 /* Attributes for the o65 format */
103 static unsigned O65Attr = 0;
105 #define OA_TYPE 0x0002
106 #define OA_VERSION 0x0004
107 #define OA_OSVERSION 0x0008
108 #define OA_TEXT 0x0010
109 #define OA_DATA 0x0020
110 #define OA_BSS 0x0040
115 /*****************************************************************************/
116 /* Constructors/Destructors */
117 /*****************************************************************************/
121 static File* NewFile (const char* Name)
122 /* Create a new file descriptor and insert it into the list */
124 /* Get the length of the name */
125 unsigned Len = strlen (Name);
127 /* Allocate memory */
128 File* F = xmalloc (sizeof (File) + Len);
130 /* Initialize the fields */
132 F->Format = BINFMT_DEFAULT;
135 memcpy (F->Name, Name, Len);
136 F->Name [Len] = '\0';
138 /* Insert the struct into the list */
143 /* ...and return it */
149 static Memory* NewMemory (const char* Name)
150 /* Create a new memory section and insert it into the list */
152 /* Get the length of the name */
153 unsigned Len = strlen (Name);
155 /* Check for duplicate names */
156 Memory* M = MemoryList;
158 if (strcmp (M->Name, Name) == 0) {
159 CfgError ("Memory area `%s' defined twice", Name);
165 /* Allocate memory */
166 M = xmalloc (sizeof (Memory) + Len);
168 /* Initialize the fields */
180 memcpy (M->Name, Name, Len);
181 M->Name [Len] = '\0';
183 /* Insert the struct into the list */
184 if (MemoryLast == 0) {
188 MemoryLast->Next = M;
193 /* ...and return it */
199 static SegDesc* NewSegDesc (const char* Name)
200 /* Create a segment descriptor */
204 /* Get the length of the name */
205 unsigned Len = strlen (Name);
207 /* Check for duplicate names */
208 SegDesc* S = SegDescList;
210 if (strcmp (S->Name, Name) == 0) {
211 CfgError ("Segment `%s' defined twice", Name);
217 /* Verify that the given segment does really exist */
218 Seg = SegFind (Name);
220 CfgWarning ("Segment `%s' does not exist", Name);
223 /* Allocate memory */
224 S = xmalloc (sizeof (SegDesc) + Len);
226 /* Initialize the fields */
232 memcpy (S->Name, Name, Len);
233 S->Name [Len] = '\0';
235 /* ...and return it */
241 static void FreeSegDesc (SegDesc* S)
242 /* Free a segment descriptor */
249 /*****************************************************************************/
251 /*****************************************************************************/
255 static void FlagAttr (unsigned* Flags, unsigned Mask, const char* Name)
256 /* Check if the item is already defined. Print an error if so. If not, set
257 * the marker that we have a definition now.
261 CfgError ("%s is already defined", Name);
268 static void AttrCheck (unsigned Attr, unsigned Mask, const char* Name)
269 /* Check that a mandatory attribute was given */
271 if ((Attr & Mask) == 0) {
272 CfgError ("%s attribute is missing", Name);
278 static File* FindFile (const char* Name)
279 /* Find a file with a given name. */
283 if (strcmp (F->Name, Name) == 0) {
293 static File* GetFile (const char* Name)
294 /* Get a file entry with the given name. Create a new one if needed. */
296 File* F = FindFile (Name);
298 /* Create a new one */
306 static void FileInsert (File* F, Memory* M)
307 /* Insert the memory area into the files list */
310 if (F->MemList == 0) {
314 F->MemLast->FNext = M;
321 static void ParseMemory (void)
322 /* Parse a MEMORY section */
324 static const IdentTok Attributes [] = {
325 { "START", CFGTOK_START },
326 { "SIZE", CFGTOK_SIZE },
327 { "TYPE", CFGTOK_TYPE },
328 { "FILE", CFGTOK_FILE },
329 { "DEFINE", CFGTOK_DEFINE },
330 { "FILL", CFGTOK_FILL },
331 { "FILLVAL", CFGTOK_FILLVAL },
333 static const IdentTok Types [] = {
338 while (CfgTok == CFGTOK_IDENT) {
340 /* Create a new entry on the heap */
341 Memory* M = NewMemory (CfgSVal);
343 /* Skip the name and the following colon */
347 /* Read the attributes */
348 while (CfgTok == CFGTOK_IDENT) {
350 /* Map the identifier to a token */
352 CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
355 /* An optional assignment follows */
357 CfgOptionalAssign ();
359 /* Check which attribute was given */
363 FlagAttr (&M->Attr, MA_START, "START");
369 FlagAttr (&M->Attr, MA_SIZE, "SIZE");
375 FlagAttr (&M->Attr, MA_TYPE, "TYPE");
376 CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type");
377 if (CfgTok == CFGTOK_RO) {
383 FlagAttr (&M->Attr, MA_FILE, "FILE");
385 /* Get the file entry and insert the memory area */
386 FileInsert (GetFile (CfgSVal), M);
390 FlagAttr (&M->Attr, MA_DEFINE, "DEFINE");
391 /* Map the token to a boolean */
393 if (CfgTok == CFGTOK_TRUE) {
394 M->Flags |= MF_DEFINE;
399 FlagAttr (&M->Attr, MA_FILL, "FILL");
400 /* Map the token to a boolean */
402 if (CfgTok == CFGTOK_TRUE) {
408 FlagAttr (&M->Attr, MA_FILLVAL, "FILLVAL");
410 CfgRangeCheck (0, 0xFF);
411 M->FillVal = (unsigned char) CfgIVal;
415 FAIL ("Unexpected attribute token");
419 /* Skip the attribute value and an optional comma */
424 /* Skip the semicolon */
427 /* Check for mandatory parameters */
428 AttrCheck (M->Attr, MA_START, "START");
429 AttrCheck (M->Attr, MA_SIZE, "SIZE");
431 /* If we don't have a file name for output given, use the default
434 if ((M->Attr & MA_FILE) == 0) {
435 FileInsert (GetFile (OutputName), M);
442 static void ParseFiles (void)
443 /* Parse a FILES section */
445 static const IdentTok Attributes [] = {
446 { "FORMAT", CFGTOK_FORMAT },
448 static const IdentTok Formats [] = {
449 { "O65", CFGTOK_O65 },
450 { "BIN", CFGTOK_BIN },
451 { "BINARY", CFGTOK_BIN },
455 /* Parse all files */
456 while (CfgTok != CFGTOK_RCURLY) {
460 /* We expect a string value here */
463 /* Search for the file, it must exist */
464 F = FindFile (CfgSVal);
466 CfgError ("No such file: `%s'", CfgSVal);
469 /* Skip the token and the following colon */
473 /* Read the attributes */
474 while (CfgTok == CFGTOK_IDENT) {
476 /* Map the identifier to a token */
478 CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
481 /* An optional assignment follows */
483 CfgOptionalAssign ();
485 /* Check which attribute was given */
489 if (F->Format != BINFMT_DEFAULT) {
490 /* We've set the format already! */
491 Error ("Cannot set a file format twice");
493 /* Read the format token */
494 CfgSpecialToken (Formats, ENTRY_COUNT (Formats), "Format");
498 F->Format = BINFMT_BINARY;
502 F->Format = BINFMT_O65;
506 Error ("Unexpected format token");
511 FAIL ("Unexpected attribute token");
515 /* Skip the attribute value and an optional comma */
520 /* Skip the semicolon */
528 static Memory* CfgFindMemory (const char* Name)
529 /* Find the memory are with the given name. Return NULL if not found */
531 Memory* M = MemoryList;
533 if (strcmp (M->Name, Name) == 0) {
543 static Memory* CfgGetMemory (const char* Name)
544 /* Find the memory are with the given name. Print an error on an invalid name */
546 Memory* M = CfgFindMemory (Name);
548 CfgError ("Invalid memory area `%s'", Name);
555 static void SegDescInsert (SegDesc* S)
556 /* Insert a segment descriptor into the list of segment descriptors */
558 /* Insert the struct into the list */
559 S->Next = SegDescList;
566 static void MemoryInsert (Memory* M, SegDesc* S)
567 /* Insert the segment descriptor into the memory area list */
569 /* Create a new node for the entry */
570 MemListNode* N = xmalloc (sizeof (MemListNode));
574 if (M->SegLast == 0) {
578 M->SegLast->Next = N;
585 static void ParseSegments (void)
586 /* Parse a SEGMENTS section */
588 static const IdentTok Attributes [] = {
589 { "LOAD", CFGTOK_LOAD },
590 { "RUN", CFGTOK_RUN },
591 { "TYPE", CFGTOK_TYPE },
592 { "ALIGN", CFGTOK_ALIGN },
593 { "DEFINE", CFGTOK_DEFINE },
594 { "OFFSET", CFGTOK_OFFSET },
595 { "START", CFGTOK_START },
597 static const IdentTok Types [] = {
600 { "BSS", CFGTOK_BSS },
602 { "WP", CFGTOK_WPROT },
603 { "WPROT", CFGTOK_WPROT },
608 while (CfgTok == CFGTOK_IDENT) {
612 /* Create a new entry on the heap */
613 S = NewSegDesc (CfgSVal);
615 /* Skip the name and the following colon */
619 /* Read the attributes */
620 while (CfgTok == CFGTOK_IDENT) {
622 /* Map the identifier to a token */
624 CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
627 /* An optional assignment follows */
629 CfgOptionalAssign ();
631 /* Check which attribute was given */
635 FlagAttr (&S->Attr, SA_LOAD, "LOAD");
636 S->Load = CfgGetMemory (CfgSVal);
640 FlagAttr (&S->Attr, SA_RUN, "RUN");
641 S->Run = CfgGetMemory (CfgSVal);
645 FlagAttr (&S->Attr, SA_TYPE, "TYPE");
646 CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type");
648 case CFGTOK_RO: S->Flags |= SF_RO; break;
649 case CFGTOK_BSS: S->Flags |= SF_BSS; break;
650 case CFGTOK_ZP: S->Flags |= (SF_BSS | SF_ZP); break;
651 case CFGTOK_WPROT: S->Flags |= (SF_RO | SF_WPROT); break;
657 FlagAttr (&S->Attr, SA_ALIGN, "ALIGN");
658 CfgRangeCheck (1, 0x10000);
659 S->Align = BitFind (CfgIVal);
660 if ((0x01UL << S->Align) != CfgIVal) {
661 CfgError ("Alignment must be a power of 2");
663 S->Flags |= SF_ALIGN;
667 FlagAttr (&S->Attr, SA_DEFINE, "DEFINE");
668 /* Map the token to a boolean */
670 if (CfgTok == CFGTOK_TRUE) {
671 S->Flags |= SF_DEFINE;
677 FlagAttr (&S->Attr, SA_OFFSET, "OFFSET");
678 CfgRangeCheck (1, 0x1000000);
680 S->Flags |= SF_OFFSET;
685 FlagAttr (&S->Attr, SA_START, "START");
686 CfgRangeCheck (1, 0x1000000);
688 S->Flags |= SF_START;
692 FAIL ("Unexpected attribute token");
696 /* Skip the attribute value and an optional comma */
701 /* Skip the semicolon */
704 /* Check for mandatory parameters */
705 AttrCheck (S->Attr, SA_LOAD, "LOAD");
707 /* Set defaults for stuff not given */
708 if ((S->Attr & SA_RUN) == 0) {
712 /* Both attributes given */
713 S->Flags |= SF_LOAD_AND_RUN;
715 if ((S->Attr & SA_ALIGN) == 0) {
720 /* If the segment is marked as BSS style, check that there's no
721 * initialized data in the segment.
723 if ((S->Flags & SF_BSS) != 0 && !IsBSSType (S->Seg)) {
724 Warning ("%s(%u): Segment with type `bss' contains initialized data",
725 CfgGetName (), CfgErrorLine);
728 /* Don't allow read/write data to be put into a readonly area */
729 if ((S->Flags & SF_RO) == 0) {
730 if (S->Run->Flags & MF_RO) {
731 CfgError ("Cannot put r/w segment `%s' in r/o memory area `%s'",
732 S->Name, S->Run->Name);
736 /* Only one of ALIGN, START and OFFSET may be used */
737 Count = ((S->Flags & SF_ALIGN) != 0) +
738 ((S->Flags & SF_OFFSET) != 0) +
739 ((S->Flags & SF_START) != 0);
741 CfgError ("Only one of ALIGN, START, OFFSET may be used");
744 /* If this segment does exist in any of the object files, insert the
745 * descriptor into the list of segment descriptors. Otherwise discard
746 * it silently, because the segment pointer in the descriptor is
750 /* Insert the descriptor into the list of all descriptors */
752 /* Insert the segment into the memory area list */
753 MemoryInsert (S->Run, S);
754 if ((S->Flags & SF_LOAD_AND_RUN) != 0) {
755 /* We have a separate RUN area given */
756 MemoryInsert (S->Load, S);
759 /* Segment does not exist, discard the descriptor */
767 static void ParseO65 (void)
768 /* Parse the o65 format section */
770 static const IdentTok Attributes [] = {
771 { "EXPORT", CFGTOK_EXPORT },
772 { "IMPORT", CFGTOK_IMPORT },
773 { "TYPE", CFGTOK_TYPE },
776 static const IdentTok Types [] = {
777 { "SMALL", CFGTOK_SMALL },
778 { "LARGE", CFGTOK_LARGE },
780 static const IdentTok OperatingSystems [] = {
781 { "LUNIX", CFGTOK_LUNIX },
782 { "OSA65", CFGTOK_OSA65 },
785 while (CfgTok == CFGTOK_IDENT) {
787 /* Map the identifier to a token */
789 CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
792 /* An optional assignment follows */
794 CfgOptionalAssign ();
796 /* Check which attribute was given */
800 /* We expect an identifier */
802 /* Check if we have this symbol defined already. The entry
803 * routine will check this also, but we get a more verbose
804 * error message when checking it here.
806 if (O65GetExport (O65FmtDesc, CfgSVal) != 0) {
807 CfgError ("Duplicate exported symbol: `%s'", CfgSVal);
809 /* Insert the symbol into the table */
810 O65SetExport (O65FmtDesc, CfgSVal);
814 /* We expect an identifier */
816 /* Check if we have this symbol defined already. The entry
817 * routine will check this also, but we get a more verbose
818 * error message when checking it here.
820 if (O65GetImport (O65FmtDesc, CfgSVal) != 0) {
821 CfgError ("Duplicate imported symbol: `%s'", CfgSVal);
823 /* Insert the symbol into the table */
824 O65SetImport (O65FmtDesc, CfgSVal);
828 /* Cannot have this attribute twice */
829 FlagAttr (&O65Attr, OA_TYPE, "TYPE");
830 /* Get the type of the executable */
831 CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type");
835 /* Default, nothing to do */
839 O65SetLargeModel (O65FmtDesc);
843 Error ("Unexpected type token");
848 /* Cannot use this attribute twice */
849 FlagAttr (&O65Attr, OA_OS, "OS");
850 /* Get the operating system */
851 CfgSpecialToken (OperatingSystems, ENTRY_COUNT (OperatingSystems), "OS type");
855 O65SetOS (O65FmtDesc, O65OS_LUNIX);
859 O65SetOS (O65FmtDesc, O65OS_OSA65);
863 Error ("Unexpected OS token");
868 FAIL ("Unexpected attribute token");
872 /* Skip the attribute value and an optional comma */
880 static void ParseFormats (void)
881 /* Parse a target format section */
883 static const IdentTok Formats [] = {
884 { "O65", CFGTOK_O65 },
885 { "BIN", CFGTOK_BIN },
886 { "BINARY", CFGTOK_BIN },
889 while (CfgTok == CFGTOK_IDENT) {
891 /* Map the identifier to a token */
893 CfgSpecialToken (Formats, ENTRY_COUNT (Formats), "Format");
896 /* Skip the name and the following colon */
900 /* Parse the format options */
908 /* No attribibutes available */
912 Error ("Unexpected format token");
915 /* Skip the semicolon */
922 static void ParseConfig (void)
923 /* Parse the config file */
925 static const IdentTok BlockNames [] = {
926 { "MEMORY", CFGTOK_MEMORY },
927 { "FILES", CFGTOK_FILES },
928 { "SEGMENTS", CFGTOK_SEGMENTS },
929 { "FORMATS", CFGTOK_FORMATS },
935 /* Read the block ident */
936 CfgSpecialToken (BlockNames, ENTRY_COUNT (BlockNames), "Block identifier");
940 /* Expected a curly brace */
941 CfgConsume (CFGTOK_LCURLY, "`{' expected");
954 case CFGTOK_SEGMENTS:
963 FAIL ("Unexpected block token");
967 /* Skip closing brace */
968 CfgConsume (CFGTOK_RCURLY, "`}' expected");
970 } while (CfgTok != CFGTOK_EOF);
976 /* Read the configuration */
978 /* Create the descriptors for the binary formats */
979 BinFmtDesc = NewBinDesc ();
980 O65FmtDesc = NewO65Desc ();
982 /* If we have a config name given, open the file, otherwise we will read
990 /* Close the input file */
996 static void CreateRunDefines (Memory* M, SegDesc* S, unsigned long Addr)
997 /* Create the defines for a RUN segment */
1001 sprintf (Buf, "__%s_RUN__", S->Name);
1002 CreateMemExport (Buf, M, Addr - M->Start);
1003 sprintf (Buf, "__%s_SIZE__", S->Name);
1004 CreateConstExport (Buf, S->Seg->Size);
1005 S->Flags |= SF_RUN_DEF;
1010 static void CreateLoadDefines (Memory* M, SegDesc* S, unsigned long Addr)
1011 /* Create the defines for a LOAD segment */
1015 sprintf (Buf, "__%s_LOAD__", S->Name);
1016 CreateMemExport (Buf, M, Addr - M->Start);
1017 S->Flags |= SF_LOAD_DEF;
1022 void CfgAssignSegments (void)
1023 /* Assign segments, define linker symbols where requested */
1025 /* Walk through each of the memory sections. Add up the sizes and check
1026 * for an overflow of the section. Assign the start addresses of the
1027 * segments while doing this.
1029 Memory* M = MemoryList;
1032 /* Get the start address of this memory area */
1033 unsigned long Addr = M->Start;
1035 /* Walk through the segments in this memory area */
1036 MemListNode* N = M->SegList;
1039 /* Get the segment from the node */
1040 SegDesc* S = N->Seg;
1042 /* Handle ALIGN and OFFSET/START */
1043 if (S->Flags & SF_ALIGN) {
1044 /* Align the address */
1045 unsigned long Val = (0x01UL << S->Align) - 1;
1046 Addr = (Addr + Val) & ~Val;
1047 } else if (S->Flags & (SF_OFFSET | SF_START)) {
1048 /* Give the segment a fixed starting address */
1049 unsigned long NewAddr = S->Addr;
1050 if (S->Flags & SF_OFFSET) {
1051 /* An offset was given, no address, make an address */
1052 NewAddr += M->Start;
1054 if (Addr > NewAddr) {
1055 /* Offset already too large */
1056 if (S->Flags & SF_OFFSET) {
1057 Error ("Offset too small in `%s', segment `%s'",
1060 Error ("Start address too low in `%s', segment `%s'",
1067 /* If this is the run area, set the start address of this segment */
1072 /* Increment the fill level of the memory area and check for an
1075 M->FillLevel = Addr + S->Seg->Size - M->Start;
1076 if (M->FillLevel > M->Size) {
1077 Error ("Memory area overflow in `%s', segment `%s' (%lu bytes)",
1078 M->Name, S->Name, M->FillLevel - M->Size);
1081 /* If requested, define symbols for the start and size of the
1084 if (S->Flags & SF_DEFINE) {
1085 if ((S->Flags & SF_LOAD_AND_RUN) && S->Run == S->Load) {
1086 /* RUN and LOAD given and in one memory area.
1087 * Be careful: We will encounter this code twice, the
1088 * first time when walking the RUN list, second time when
1089 * walking the LOAD list. Be sure to define only the
1090 * relevant symbols on each walk.
1093 if ((S->Flags & SF_LOAD_DEF) == 0) {
1094 CreateLoadDefines (M, S, Addr);
1096 CHECK ((S->Flags & SF_RUN_DEF) == 0);
1097 CreateRunDefines (M, S, Addr);
1101 /* RUN and LOAD in different memory areas, or RUN not
1102 * given, so RUN defaults to LOAD. In the latter case, we
1103 * have only one copy of the segment in the area.
1106 CreateRunDefines (M, S, Addr);
1109 CreateLoadDefines (M, S, Addr);
1114 /* Calculate the new address */
1115 Addr += S->Seg->Size;
1121 /* If requested, define symbols for start and size of the memory area */
1122 if (M->Flags & MF_DEFINE) {
1124 sprintf (Buf, "__%s_START__", M->Name);
1125 CreateMemExport (Buf, M, 0);
1126 sprintf (Buf, "__%s_SIZE__", M->Name);
1127 CreateConstExport (Buf, M->Size);
1128 sprintf (Buf, "__%s_LAST__", M->Name);
1129 CreateConstExport (Buf, M->FillLevel);
1132 /* Next memory area */
1139 void CfgWriteTarget (void)
1140 /* Write the target file(s) */
1144 /* Walk through the files list */
1147 /* We don't need to look at files with no memory areas */
1150 /* Is there an output file? */
1151 if (strlen (F->Name) > 0) {
1153 /* Assign a proper binary format */
1154 if (F->Format == BINFMT_DEFAULT) {
1155 F->Format = DefaultBinFmt;
1158 /* Call the apropriate routine for the binary format */
1159 switch (F->Format) {
1162 BinWriteTarget (BinFmtDesc, F);
1166 O65WriteTarget (O65FmtDesc, F);
1170 Internal ("Invalid binary format: %u", F->Format);
1176 /* No output file. Walk through the list and mark all segments
1177 * assigned to the memory areas in this file as dumped.
1181 /* Walk throught the segments */
1182 MemListNode* N = M->SegList;
1184 /* Mark the segment as dumped */
1185 N->Seg->Seg->Dumped = 1;
1187 /* Next segment node */
1190 /* Next memory area */