1 /*****************************************************************************/
5 /* Target configuration file for the ld65 linker */
9 /* (C) 1998-2010, Ullrich von Bassewitz */
10 /* Roemerstrasse 52 */
11 /* D-70794 Filderstadt */
12 /* EMail: uz@cc65.org */
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 /*****************************************************************************/
66 /*****************************************************************************/
68 /*****************************************************************************/
72 /* Remember which sections we had encountered */
81 } SectionsEncountered = SE_NONE;
86 static Collection FileList = STATIC_COLLECTION_INITIALIZER;
89 static Collection MemoryAreas = STATIC_COLLECTION_INITIALIZER;
91 /* Memory attributes */
92 #define MA_START 0x0001
93 #define MA_SIZE 0x0002
94 #define MA_TYPE 0x0004
95 #define MA_FILE 0x0008
96 #define MA_DEFINE 0x0010
97 #define MA_FILL 0x0020
98 #define MA_FILLVAL 0x0040
101 static Collection SegDescList = STATIC_COLLECTION_INITIALIZER;
103 /* Segment attributes */
104 #define SA_TYPE 0x0001
105 #define SA_LOAD 0x0002
106 #define SA_RUN 0x0004
107 #define SA_ALIGN 0x0008
108 #define SA_ALIGN_LOAD 0x0010
109 #define SA_DEFINE 0x0020
110 #define SA_OFFSET 0x0040
111 #define SA_START 0x0080
112 #define SA_OPTIONAL 0x0100
114 /* Symbol structure. It is used for o65 imports and exports, but also for
115 * symbols from the SYMBOLS sections (symbols defined in the config file or
118 typedef struct Symbol Symbol;
120 const char* CfgName; /* Config file name */
121 unsigned CfgLine; /* Config file position */
123 unsigned Name; /* Symbol name */
124 unsigned Flags; /* Symbol flags */
125 long Val; /* Symbol value if any */
128 /* Collections with symbols */
129 static Collection O65Imports = STATIC_COLLECTION_INITIALIZER;
130 static Collection O65Exports = STATIC_COLLECTION_INITIALIZER;
131 static Collection Symbols = STATIC_COLLECTION_INITIALIZER;
134 #define SYM_NONE 0x00 /* No special meaning */
135 #define SYM_DEF 0x01 /* Symbol defined in the config file */
136 #define SYM_WEAK 0x02 /* Defined symbol is weak */
137 #define SYM_IMPORT 0x04 /* A forced import */
139 /* Descriptor holding information about the binary formats */
140 static BinDesc* BinFmtDesc = 0;
141 static O65Desc* O65FmtDesc = 0;
145 /*****************************************************************************/
147 /*****************************************************************************/
151 static File* NewFile (unsigned Name);
152 /* Create a new file descriptor and insert it into the list */
156 /*****************************************************************************/
157 /* List management */
158 /*****************************************************************************/
162 static File* FindFile (unsigned Name)
163 /* Find a file with a given name. */
166 for (I = 0; I < CollCount (&FileList); ++I) {
167 File* F = CollAtUnchecked (&FileList, I);
168 if (F->Name == Name) {
177 static File* GetFile (unsigned Name)
178 /* Get a file entry with the given name. Create a new one if needed. */
180 File* F = FindFile (Name);
182 /* Create a new one */
190 static void FileInsert (File* F, MemoryArea* M)
191 /* Insert the memory area into the files list */
194 CollAppend (&F->MemoryAreas, M);
199 static MemoryArea* CfgFindMemory (unsigned Name)
200 /* Find the memory are with the given name. Return NULL if not found */
203 for (I = 0; I < CollCount (&MemoryAreas); ++I) {
204 MemoryArea* M = CollAtUnchecked (&MemoryAreas, I);
205 if (M->Name == Name) {
214 static MemoryArea* CfgGetMemory (unsigned Name)
215 /* Find the memory are with the given name. Print an error on an invalid name */
217 MemoryArea* M = CfgFindMemory (Name);
219 CfgError ("Invalid memory area `%s'", GetString (Name));
226 static SegDesc* CfgFindSegDesc (unsigned Name)
227 /* Find the segment descriptor with the given name, return NULL if not found. */
230 for (I = 0; I < CollCount (&SegDescList); ++I) {
231 SegDesc* S = CollAtUnchecked (&SegDescList, I);
232 if (S->Name == Name) {
244 static void MemoryInsert (MemoryArea* M, SegDesc* S)
245 /* Insert the segment descriptor into the memory area list */
247 /* Insert the segment into the segment list of the memory area */
248 CollAppend (&M->SegList, S);
253 /*****************************************************************************/
254 /* Constructors/Destructors */
255 /*****************************************************************************/
259 static Symbol* NewSymbol (unsigned Name, unsigned Flags, long Val)
260 /* Create a new Symbol structure with the given name name and flags. The
261 * current config file position is recorded in the returned struct.
264 /* Allocate memory */
265 Symbol* Sym = xmalloc (sizeof (Symbol));
267 /* Initialize the fields */
268 Sym->CfgName = CfgGetName ();
269 Sym->CfgLine = CfgErrorLine;
270 Sym->CfgCol = CfgErrorCol;
275 /* Return the initialized struct */
281 static Symbol* NewO65Symbol (void)
282 /* Create a new Symbol structure with the name in the current CfgSVal variable
283 * ready for use as an o65 symbol. The current config file position is recorded
284 * in the returned struct.
287 return NewSymbol (GetStrBufId (&CfgSVal), SYM_NONE, 0);
292 static File* NewFile (unsigned Name)
293 /* Create a new file descriptor and insert it into the list */
295 /* Allocate memory */
296 File* F = xmalloc (sizeof (File));
298 /* Initialize the fields */
301 F->Format = BINFMT_DEFAULT;
302 InitCollection (&F->MemoryAreas);
304 /* Insert the struct into the list */
305 CollAppend (&FileList, F);
307 /* ...and return it */
313 static MemoryArea* CreateMemoryArea (unsigned Name)
314 /* Create a new memory area and insert it into the list */
316 /* Check for duplicate names */
317 MemoryArea* M = CfgFindMemory (Name);
319 CfgError ("Memory area `%s' defined twice", GetString (Name));
322 /* Create a new memory area */
323 M = NewMemoryArea (Name);
325 /* Insert the struct into the list ... */
326 CollAppend (&MemoryAreas, M);
328 /* ...and return it */
334 static SegDesc* NewSegDesc (unsigned Name)
335 /* Create a segment descriptor and insert it into the list */
338 /* Check for duplicate names */
339 SegDesc* S = CfgFindSegDesc (Name);
341 CfgError ("Segment `%s' defined twice", GetString (Name));
344 /* Allocate memory */
345 S = xmalloc (sizeof (SegDesc));
347 /* Initialize the fields */
354 /* Insert the struct into the list ... */
355 CollAppend (&SegDescList, S);
357 /* ...and return it */
363 static void FreeSegDesc (SegDesc* S)
364 /* Free a segment descriptor */
371 /*****************************************************************************/
372 /* Config file parsing */
373 /*****************************************************************************/
377 static void FlagAttr (unsigned* Flags, unsigned Mask, const char* Name)
378 /* Check if the item is already defined. Print an error if so. If not, set
379 * the marker that we have a definition now.
383 CfgError ("%s is already defined", Name);
390 static void AttrCheck (unsigned Attr, unsigned Mask, const char* Name)
391 /* Check that a mandatory attribute was given */
393 if ((Attr & Mask) == 0) {
394 CfgError ("%s attribute is missing", Name);
400 static void ParseMemory (void)
401 /* Parse a MEMORY section */
403 static const IdentTok Attributes [] = {
404 { "START", CFGTOK_START },
405 { "SIZE", CFGTOK_SIZE },
406 { "TYPE", CFGTOK_TYPE },
407 { "FILE", CFGTOK_FILE },
408 { "DEFINE", CFGTOK_DEFINE },
409 { "FILL", CFGTOK_FILL },
410 { "FILLVAL", CFGTOK_FILLVAL },
412 static const IdentTok Types [] = {
417 while (CfgTok == CFGTOK_IDENT) {
419 /* Create a new entry on the heap */
420 MemoryArea* M = CreateMemoryArea (GetStrBufId (&CfgSVal));
422 /* Skip the name and the following colon */
426 /* Read the attributes */
427 while (CfgTok == CFGTOK_IDENT) {
429 /* Map the identifier to a token */
431 CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
434 /* An optional assignment follows */
436 CfgOptionalAssign ();
438 /* Check which attribute was given */
442 FlagAttr (&M->Attr, MA_START, "START");
443 M->StartExpr = CfgExpr ();
447 FlagAttr (&M->Attr, MA_SIZE, "SIZE");
448 M->SizeExpr = CfgExpr ();
452 FlagAttr (&M->Attr, MA_TYPE, "TYPE");
453 CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type");
454 if (CfgTok == CFGTOK_RO) {
461 FlagAttr (&M->Attr, MA_FILE, "FILE");
463 /* Get the file entry and insert the memory area */
464 FileInsert (GetFile (GetStrBufId (&CfgSVal)), M);
469 FlagAttr (&M->Attr, MA_DEFINE, "DEFINE");
470 /* Map the token to a boolean */
472 if (CfgTok == CFGTOK_TRUE) {
473 M->Flags |= MF_DEFINE;
479 FlagAttr (&M->Attr, MA_FILL, "FILL");
480 /* Map the token to a boolean */
482 if (CfgTok == CFGTOK_TRUE) {
489 FlagAttr (&M->Attr, MA_FILLVAL, "FILLVAL");
490 M->FillVal = (unsigned char) CfgCheckedConstExpr (0, 0xFF);
494 FAIL ("Unexpected attribute token");
498 /* Skip an optional comma */
502 /* Skip the semicolon */
505 /* Check for mandatory parameters */
506 AttrCheck (M->Attr, MA_START, "START");
507 AttrCheck (M->Attr, MA_SIZE, "SIZE");
509 /* If we don't have a file name for output given, use the default
512 if ((M->Attr & MA_FILE) == 0) {
513 FileInsert (GetFile (GetStringId (OutputName)), M);
517 /* Remember we had this section */
518 SectionsEncountered |= SE_MEMORY;
523 static void ParseFiles (void)
524 /* Parse a FILES section */
526 static const IdentTok Attributes [] = {
527 { "FORMAT", CFGTOK_FORMAT },
529 static const IdentTok Formats [] = {
530 { "O65", CFGTOK_O65 },
531 { "BIN", CFGTOK_BIN },
532 { "BINARY", CFGTOK_BIN },
536 /* The MEMORY section must preceed the FILES section */
537 if ((SectionsEncountered & SE_MEMORY) == 0) {
538 CfgError ("MEMORY must precede FILES");
541 /* Parse all files */
542 while (CfgTok != CFGTOK_RCURLY) {
546 /* We expect a string value here */
549 /* Search for the file, it must exist */
550 F = FindFile (GetStrBufId (&CfgSVal));
552 CfgError ("File `%s' not found in MEMORY section",
553 SB_GetConstBuf (&CfgSVal));
556 /* Skip the token and the following colon */
560 /* Read the attributes */
561 while (CfgTok == CFGTOK_IDENT) {
563 /* Map the identifier to a token */
565 CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
568 /* An optional assignment follows */
570 CfgOptionalAssign ();
572 /* Check which attribute was given */
576 if (F->Format != BINFMT_DEFAULT) {
577 /* We've set the format already! */
578 Error ("Cannot set a file format twice");
580 /* Read the format token */
581 CfgSpecialToken (Formats, ENTRY_COUNT (Formats), "Format");
585 F->Format = BINFMT_BINARY;
589 F->Format = BINFMT_O65;
593 Error ("Unexpected format token");
598 FAIL ("Unexpected attribute token");
602 /* Skip the attribute value and an optional comma */
607 /* Skip the semicolon */
612 /* Remember we had this section */
613 SectionsEncountered |= SE_FILES;
618 static void ParseSegments (void)
619 /* Parse a SEGMENTS section */
621 static const IdentTok Attributes [] = {
622 { "ALIGN", CFGTOK_ALIGN },
623 { "ALIGN_LOAD", CFGTOK_ALIGN_LOAD },
624 { "DEFINE", CFGTOK_DEFINE },
625 { "LOAD", CFGTOK_LOAD },
626 { "OFFSET", CFGTOK_OFFSET },
627 { "OPTIONAL", CFGTOK_OPTIONAL },
628 { "RUN", CFGTOK_RUN },
629 { "START", CFGTOK_START },
630 { "TYPE", CFGTOK_TYPE },
632 static const IdentTok Types [] = {
635 { "BSS", CFGTOK_BSS },
642 /* The MEMORY section must preceed the SEGMENTS section */
643 if ((SectionsEncountered & SE_MEMORY) == 0) {
644 CfgError ("MEMORY must precede SEGMENTS");
647 while (CfgTok == CFGTOK_IDENT) {
651 /* Create a new entry on the heap */
652 S = NewSegDesc (GetStrBufId (&CfgSVal));
654 /* Skip the name and the following colon */
658 /* Read the attributes */
659 while (CfgTok == CFGTOK_IDENT) {
661 /* Map the identifier to a token */
663 CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
666 /* An optional assignment follows */
668 CfgOptionalAssign ();
670 /* Check which attribute was given */
674 FlagAttr (&S->Attr, SA_ALIGN, "ALIGN");
675 Val = CfgCheckedConstExpr (1, 0x10000);
676 S->Align = BitFind (Val);
677 if ((0x01L << S->Align) != Val) {
678 CfgError ("Alignment must be a power of 2");
680 S->Flags |= SF_ALIGN;
683 case CFGTOK_ALIGN_LOAD:
684 FlagAttr (&S->Attr, SA_ALIGN_LOAD, "ALIGN_LOAD");
685 Val = CfgCheckedConstExpr (1, 0x10000);
686 S->AlignLoad = BitFind (Val);
687 if ((0x01L << S->AlignLoad) != Val) {
688 CfgError ("Alignment must be a power of 2");
690 S->Flags |= SF_ALIGN_LOAD;
694 FlagAttr (&S->Attr, SA_DEFINE, "DEFINE");
695 /* Map the token to a boolean */
697 if (CfgTok == CFGTOK_TRUE) {
698 S->Flags |= SF_DEFINE;
704 FlagAttr (&S->Attr, SA_LOAD, "LOAD");
705 S->Load = CfgGetMemory (GetStrBufId (&CfgSVal));
710 FlagAttr (&S->Attr, SA_OFFSET, "OFFSET");
711 S->Addr = CfgCheckedConstExpr (1, 0x1000000);
712 S->Flags |= SF_OFFSET;
715 case CFGTOK_OPTIONAL:
716 FlagAttr (&S->Attr, SA_OPTIONAL, "OPTIONAL");
718 if (CfgTok == CFGTOK_TRUE) {
719 S->Flags |= SF_OPTIONAL;
725 FlagAttr (&S->Attr, SA_RUN, "RUN");
726 S->Run = CfgGetMemory (GetStrBufId (&CfgSVal));
731 FlagAttr (&S->Attr, SA_START, "START");
732 S->Addr = CfgCheckedConstExpr (1, 0x1000000);
733 S->Flags |= SF_START;
737 FlagAttr (&S->Attr, SA_TYPE, "TYPE");
738 CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type");
740 case CFGTOK_RO: S->Flags |= SF_RO; break;
741 case CFGTOK_RW: /* Default */ break;
742 case CFGTOK_BSS: S->Flags |= SF_BSS; break;
743 case CFGTOK_ZP: S->Flags |= (SF_BSS | SF_ZP); break;
744 default: Internal ("Unexpected token: %d", CfgTok);
750 FAIL ("Unexpected attribute token");
754 /* Skip an optional comma */
758 /* Check for mandatory parameters */
759 AttrCheck (S->Attr, SA_LOAD, "LOAD");
761 /* Set defaults for stuff not given */
762 if ((S->Attr & SA_RUN) == 0) {
767 /* An attribute of ALIGN_LOAD doesn't make sense if there are no
768 * separate run and load memory areas.
770 if ((S->Flags & SF_ALIGN_LOAD) != 0 && (S->Load == S->Run)) {
771 Warning ("%s(%u): ALIGN_LOAD attribute specified, but no separate "
772 "LOAD and RUN memory areas assigned",
773 CfgGetName (), CfgErrorLine);
774 /* Remove the flag */
775 S->Flags &= ~SF_ALIGN_LOAD;
778 /* If the segment is marked as BSS style, it may not have separate
779 * load and run memory areas, because it's is never written to disk.
781 if ((S->Flags & SF_BSS) != 0 && (S->Load != S->Run)) {
782 Warning ("%s(%u): Segment with type `bss' has both LOAD and RUN "
783 "memory areas assigned", CfgGetName (), CfgErrorLine);
786 /* Don't allow read/write data to be put into a readonly area */
787 if ((S->Flags & SF_RO) == 0) {
788 if (S->Run->Flags & MF_RO) {
789 CfgError ("Cannot put r/w segment `%s' in r/o memory area `%s'",
790 GetString (S->Name), GetString (S->Run->Name));
794 /* Only one of ALIGN, START and OFFSET may be used */
795 Count = ((S->Flags & SF_ALIGN) != 0) +
796 ((S->Flags & SF_OFFSET) != 0) +
797 ((S->Flags & SF_START) != 0);
799 CfgError ("Only one of ALIGN, START, OFFSET may be used");
802 /* Skip the semicolon */
806 /* Remember we had this section */
807 SectionsEncountered |= SE_SEGMENTS;
812 static void ParseO65 (void)
813 /* Parse the o65 format section */
815 static const IdentTok Attributes [] = {
816 { "EXPORT", CFGTOK_EXPORT },
817 { "IMPORT", CFGTOK_IMPORT },
818 { "TYPE", CFGTOK_TYPE },
821 { "VERSION", CFGTOK_VERSION },
823 static const IdentTok Types [] = {
824 { "SMALL", CFGTOK_SMALL },
825 { "LARGE", CFGTOK_LARGE },
827 static const IdentTok OperatingSystems [] = {
828 { "LUNIX", CFGTOK_LUNIX },
829 { "OSA65", CFGTOK_OSA65 },
830 { "CC65", CFGTOK_CC65 },
831 { "OPENCBM", CFGTOK_OPENCBM },
834 /* Bitmask to remember the attributes we got already */
838 atOSVersion = 0x0002,
845 unsigned AttrFlags = atNone;
847 /* Remember the attributes read */
848 unsigned OS = 0; /* Initialize to keep gcc happy */
849 unsigned Version = 0;
851 /* Read the attributes */
852 while (CfgTok == CFGTOK_IDENT) {
854 /* Map the identifier to a token */
856 CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
859 /* An optional assignment follows */
861 CfgOptionalAssign ();
863 /* Check which attribute was given */
867 /* Remember we had this token (maybe more than once) */
868 AttrFlags |= atExport;
869 /* We expect an identifier */
871 /* Remember it as an export for later */
872 CollAppend (&O65Exports, NewO65Symbol ());
873 /* Eat the identifier token */
878 /* Remember we had this token (maybe more than once) */
879 AttrFlags |= atImport;
880 /* We expect an identifier */
882 /* Remember it as an import for later */
883 CollAppend (&O65Imports, NewO65Symbol ());
884 /* Eat the identifier token */
889 /* Cannot have this attribute twice */
890 FlagAttr (&AttrFlags, atType, "TYPE");
891 /* Get the type of the executable */
892 CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type");
896 O65SetSmallModel (O65FmtDesc);
900 O65SetLargeModel (O65FmtDesc);
904 CfgError ("Unexpected type token");
906 /* Eat the attribute token */
911 /* Cannot use this attribute twice */
912 FlagAttr (&AttrFlags, atOS, "OS");
913 /* Get the operating system. It may be specified as name or
914 * as a number in the range 1..255.
916 if (CfgTok == CFGTOK_INTCON) {
917 CfgRangeCheck (O65OS_MIN, O65OS_MAX);
918 OS = (unsigned) CfgIVal;
920 CfgSpecialToken (OperatingSystems, ENTRY_COUNT (OperatingSystems), "OS type");
922 case CFGTOK_LUNIX: OS = O65OS_LUNIX; break;
923 case CFGTOK_OSA65: OS = O65OS_OSA65; break;
924 case CFGTOK_CC65: OS = O65OS_CC65; break;
925 case CFGTOK_OPENCBM: OS = O65OS_OPENCBM; break;
926 default: CfgError ("Unexpected OS token");
933 /* Cannot have this attribute twice */
934 FlagAttr (&AttrFlags, atID, "ID");
935 /* We're expecting a number in the 0..$FFFF range*/
936 ModuleId = (unsigned) CfgCheckedConstExpr (0, 0xFFFF);
940 /* Cannot have this attribute twice */
941 FlagAttr (&AttrFlags, atVersion, "VERSION");
942 /* We're expecting a number in byte range */
943 Version = (unsigned) CfgCheckedConstExpr (0, 0xFF);
947 FAIL ("Unexpected attribute token");
951 /* Skip an optional comma */
955 /* Check if we have all mandatory attributes */
956 AttrCheck (AttrFlags, atOS, "OS");
958 /* Check for attributes that may not be combined */
959 if (OS == O65OS_CC65) {
960 if ((AttrFlags & (atImport | atExport)) != 0 && ModuleId < 0x8000) {
961 CfgError ("OS type CC65 may not have imports or exports for ids < $8000");
964 if (AttrFlags & atID) {
965 CfgError ("Operating system does not support the ID attribute");
969 /* Set the O65 operating system to use */
970 O65SetOS (O65FmtDesc, OS, Version, ModuleId);
975 static void ParseFormats (void)
976 /* Parse a target format section */
978 static const IdentTok Formats [] = {
979 { "O65", CFGTOK_O65 },
980 { "BIN", CFGTOK_BIN },
981 { "BINARY", CFGTOK_BIN },
984 while (CfgTok == CFGTOK_IDENT) {
986 /* Map the identifier to a token */
988 CfgSpecialToken (Formats, ENTRY_COUNT (Formats), "Format");
991 /* Skip the name and the following colon */
995 /* Parse the format options */
1003 /* No attribibutes available */
1007 Error ("Unexpected format token");
1010 /* Skip the semicolon */
1015 /* Remember we had this section */
1016 SectionsEncountered |= SE_FORMATS;
1021 static void ParseConDes (void)
1022 /* Parse the CONDES feature */
1024 static const IdentTok Attributes [] = {
1025 { "SEGMENT", CFGTOK_SEGMENT },
1026 { "LABEL", CFGTOK_LABEL },
1027 { "COUNT", CFGTOK_COUNT },
1028 { "TYPE", CFGTOK_TYPE },
1029 { "ORDER", CFGTOK_ORDER },
1032 static const IdentTok Types [] = {
1033 { "CONSTRUCTOR", CFGTOK_CONSTRUCTOR },
1034 { "DESTRUCTOR", CFGTOK_DESTRUCTOR },
1035 { "INTERRUPTOR", CFGTOK_INTERRUPTOR },
1038 static const IdentTok Orders [] = {
1039 { "DECREASING", CFGTOK_DECREASING },
1040 { "INCREASING", CFGTOK_INCREASING },
1043 /* Attribute values. */
1044 unsigned SegName = INVALID_STRING_ID;
1045 unsigned Label = INVALID_STRING_ID;
1046 unsigned Count = INVALID_STRING_ID;
1047 /* Initialize to avoid gcc warnings: */
1049 ConDesOrder Order = cdIncreasing;
1051 /* Bitmask to remember the attributes we got already */
1060 unsigned AttrFlags = atNone;
1062 /* Parse the attributes */
1065 /* Map the identifier to a token */
1067 CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
1070 /* An optional assignment follows */
1072 CfgOptionalAssign ();
1074 /* Check which attribute was given */
1077 case CFGTOK_SEGMENT:
1078 /* Don't allow this twice */
1079 FlagAttr (&AttrFlags, atSegName, "SEGMENT");
1080 /* We expect an identifier */
1082 /* Remember the value for later */
1083 SegName = GetStrBufId (&CfgSVal);
1087 /* Don't allow this twice */
1088 FlagAttr (&AttrFlags, atLabel, "LABEL");
1089 /* We expect an identifier */
1091 /* Remember the value for later */
1092 Label = GetStrBufId (&CfgSVal);
1096 /* Don't allow this twice */
1097 FlagAttr (&AttrFlags, atCount, "COUNT");
1098 /* We expect an identifier */
1100 /* Remember the value for later */
1101 Count = GetStrBufId (&CfgSVal);
1105 /* Don't allow this twice */
1106 FlagAttr (&AttrFlags, atType, "TYPE");
1107 /* The type may be given as id or numerical */
1108 if (CfgTok == CFGTOK_INTCON) {
1109 CfgRangeCheck (CD_TYPE_MIN, CD_TYPE_MAX);
1110 Type = (int) CfgIVal;
1112 CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type");
1114 case CFGTOK_CONSTRUCTOR: Type = CD_TYPE_CON; break;
1115 case CFGTOK_DESTRUCTOR: Type = CD_TYPE_DES; break;
1116 case CFGTOK_INTERRUPTOR: Type = CD_TYPE_INT; break;
1117 default: FAIL ("Unexpected type token");
1123 /* Don't allow this twice */
1124 FlagAttr (&AttrFlags, atOrder, "ORDER");
1125 CfgSpecialToken (Orders, ENTRY_COUNT (Orders), "Order");
1127 case CFGTOK_DECREASING: Order = cdDecreasing; break;
1128 case CFGTOK_INCREASING: Order = cdIncreasing; break;
1129 default: FAIL ("Unexpected order token");
1134 FAIL ("Unexpected attribute token");
1138 /* Skip the attribute value */
1141 /* Semicolon ends the ConDes decl, otherwise accept an optional comma */
1142 if (CfgTok == CFGTOK_SEMI) {
1144 } else if (CfgTok == CFGTOK_COMMA) {
1149 /* Check if we have all mandatory attributes */
1150 AttrCheck (AttrFlags, atSegName, "SEGMENT");
1151 AttrCheck (AttrFlags, atLabel, "LABEL");
1152 AttrCheck (AttrFlags, atType, "TYPE");
1154 /* Check if the condes has already attributes defined */
1155 if (ConDesHasSegName(Type) || ConDesHasLabel(Type)) {
1156 CfgError ("CONDES attributes for type %d are already defined", Type);
1159 /* Define the attributes */
1160 ConDesSetSegName (Type, SegName);
1161 ConDesSetLabel (Type, Label);
1162 if (AttrFlags & atCount) {
1163 ConDesSetCountSym (Type, Count);
1165 if (AttrFlags & atOrder) {
1166 ConDesSetOrder (Type, Order);
1172 static void ParseStartAddress (void)
1173 /* Parse the STARTADDRESS feature */
1175 static const IdentTok Attributes [] = {
1176 { "DEFAULT", CFGTOK_DEFAULT },
1180 /* Attribute values. */
1181 unsigned long DefStartAddr = 0;
1183 /* Bitmask to remember the attributes we got already */
1188 unsigned AttrFlags = atNone;
1190 /* Parse the attributes */
1193 /* Map the identifier to a token */
1195 CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
1198 /* An optional assignment follows */
1200 CfgOptionalAssign ();
1202 /* Check which attribute was given */
1205 case CFGTOK_DEFAULT:
1206 /* Don't allow this twice */
1207 FlagAttr (&AttrFlags, atDefault, "DEFAULT");
1208 /* We expect a numeric expression */
1209 DefStartAddr = CfgCheckedConstExpr (0, 0xFFFFFF);
1213 FAIL ("Unexpected attribute token");
1217 /* Semicolon ends the ConDes decl, otherwise accept an optional comma */
1218 if (CfgTok == CFGTOK_SEMI) {
1220 } else if (CfgTok == CFGTOK_COMMA) {
1225 /* Check if we have all mandatory attributes */
1226 AttrCheck (AttrFlags, atDefault, "DEFAULT");
1228 /* If no start address was given on the command line, use the one given
1231 if (!HaveStartAddr) {
1232 StartAddr = DefStartAddr;
1238 static void ParseFeatures (void)
1239 /* Parse a features section */
1241 static const IdentTok Features [] = {
1242 { "CONDES", CFGTOK_CONDES },
1243 { "STARTADDRESS", CFGTOK_STARTADDRESS },
1246 while (CfgTok == CFGTOK_IDENT) {
1248 /* Map the identifier to a token */
1249 cfgtok_t FeatureTok;
1250 CfgSpecialToken (Features, ENTRY_COUNT (Features), "Feature");
1251 FeatureTok = CfgTok;
1253 /* Skip the name and the following colon */
1257 /* Parse the format options */
1258 switch (FeatureTok) {
1264 case CFGTOK_STARTADDRESS:
1265 ParseStartAddress ();
1270 FAIL ("Unexpected feature token");
1273 /* Skip the semicolon */
1277 /* Remember we had this section */
1278 SectionsEncountered |= SE_FEATURES;
1283 static void ParseSymbols (void)
1284 /* Parse a symbols section */
1286 static const IdentTok Attributes[] = {
1287 { "VALUE", CFGTOK_VALUE },
1288 { "WEAK", CFGTOK_WEAK },
1291 while (CfgTok == CFGTOK_IDENT) {
1294 unsigned Flags = SYM_NONE;
1296 /* Remember the name */
1297 unsigned Name = GetStrBufId (&CfgSVal);
1300 /* Support both, old and new syntax here. New syntax is a colon
1301 * followed by an attribute list, old syntax is an optional equal
1302 * sign plus a value.
1304 if (CfgTok != CFGTOK_COLON) {
1308 /* Allow an optional assignment */
1309 CfgOptionalAssign ();
1311 /* Make sure the next token is an integer expression, read and
1314 Val = CfgConstExpr ();
1316 /* This is a defined symbol */
1321 /* Bitmask to remember the attributes we got already */
1327 unsigned AttrFlags = atNone;
1330 /* New syntax - skip the colon */
1333 /* Parse the attributes */
1336 /* Map the identifier to a token */
1338 CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
1341 /* Skip the attribute name */
1344 /* An optional assignment follows */
1345 CfgOptionalAssign ();
1347 /* Check which attribute was given */
1351 /* Don't allow this twice */
1352 FlagAttr (&AttrFlags, atValue, "VALUE");
1353 /* We expect a numeric expression */
1354 Val = CfgConstExpr ();
1355 /* Symbol is defined */
1360 /* Don't allow this twice */
1361 FlagAttr (&AttrFlags, atWeak, "WEAK");
1363 if (CfgTok == CFGTOK_TRUE) {
1370 FAIL ("Unexpected attribute token");
1374 /* Semicolon ends the decl, otherwise accept an optional comma */
1375 if (CfgTok == CFGTOK_SEMI) {
1377 } else if (CfgTok == CFGTOK_COMMA) {
1382 /* Check if we have all mandatory attributes */
1383 AttrCheck (AttrFlags, atValue, "VALUE");
1386 /* Remember the symbol for later */
1387 CollAppend (&Symbols, NewSymbol (Name, Flags, Val));
1389 /* Skip the semicolon */
1393 /* Remember we had this section */
1394 SectionsEncountered |= SE_SYMBOLS;
1399 static void ParseConfig (void)
1400 /* Parse the config file */
1402 static const IdentTok BlockNames [] = {
1403 { "MEMORY", CFGTOK_MEMORY },
1404 { "FILES", CFGTOK_FILES },
1405 { "SEGMENTS", CFGTOK_SEGMENTS },
1406 { "FORMATS", CFGTOK_FORMATS },
1407 { "FEATURES", CFGTOK_FEATURES },
1408 { "SYMBOLS", CFGTOK_SYMBOLS },
1414 /* Read the block ident */
1415 CfgSpecialToken (BlockNames, ENTRY_COUNT (BlockNames), "Block identifier");
1419 /* Expected a curly brace */
1420 CfgConsume (CFGTOK_LCURLY, "`{' expected");
1422 /* Read the block */
1433 case CFGTOK_SEGMENTS:
1437 case CFGTOK_FORMATS:
1441 case CFGTOK_FEATURES:
1445 case CFGTOK_SYMBOLS:
1450 FAIL ("Unexpected block token");
1454 /* Skip closing brace */
1455 CfgConsume (CFGTOK_RCURLY, "`}' expected");
1457 } while (CfgTok != CFGTOK_EOF);
1463 /* Read the configuration */
1465 /* Create the descriptors for the binary formats */
1466 BinFmtDesc = NewBinDesc ();
1467 O65FmtDesc = NewO65Desc ();
1469 /* If we have a config name given, open the file, otherwise we will read
1474 /* Parse the file */
1477 /* Close the input file */
1483 /*****************************************************************************/
1484 /* Config file processing */
1485 /*****************************************************************************/
1489 static void ProcessMemory (void)
1490 /* Process the MEMORY section */
1492 /* Walk over the list with the memory areas */
1494 for (I = 0; I < CollCount (&MemoryAreas); ++I) {
1496 /* Get the next memory area */
1497 MemoryArea* M = CollAtUnchecked (&MemoryAreas, I);
1499 /* Remember if this is a relocatable memory area */
1500 M->Relocatable = RelocatableBinFmt (M->F->Format);
1502 /* Resolve the expressions */
1503 if (!IsConstExpr (M->StartExpr)) {
1504 Error ("Start address of memory area `%s' is not constant",
1505 GetString (M->Name));
1507 M->Start = GetExprVal (M->StartExpr);
1509 if (!IsConstExpr (M->SizeExpr)) {
1510 Error ("Size of memory area `%s' is not constant",
1511 GetString (M->Name));
1513 M->Size = GetExprVal (M->SizeExpr);
1515 /* Mark the memory area as placed */
1516 M->Flags |= MF_PLACED;
1522 static void ProcessSegments (void)
1523 /* Process the SEGMENTS section */
1527 /* Walk over the list of segment descriptors */
1529 while (I < CollCount (&SegDescList)) {
1531 /* Get the next segment descriptor */
1532 SegDesc* S = CollAtUnchecked (&SegDescList, I);
1534 /* Search for the actual segment in the input files. The function may
1535 * return NULL (no such segment), this is checked later.
1537 S->Seg = SegFind (S->Name);
1539 /* If the segment is marked as BSS style, and if the segment exists
1540 * in any of the object file, check that there's no initialized data
1543 if ((S->Flags & SF_BSS) != 0 && S->Seg != 0 && !IsBSSType (S->Seg)) {
1544 Warning ("Segment `%s' with type `bss' contains initialized data",
1545 GetString (S->Name));
1548 /* If this segment does exist in any of the object files, insert the
1549 * segment into the load/run memory areas. Otherwise print a warning
1550 * and discard it, because the segment pointer in the descriptor is
1555 /* Insert the segment into the memory area list */
1556 MemoryInsert (S->Run, S);
1557 if (S->Load != S->Run) {
1558 /* We have separate RUN and LOAD areas */
1559 MemoryInsert (S->Load, S);
1562 /* Process the next segment descriptor in the next run */
1567 /* Print a warning if the segment is not optional */
1568 if ((S->Flags & SF_OPTIONAL) == 0) {
1569 CfgWarning ("Segment `%s' does not exist", GetString (S->Name));
1572 /* Discard the descriptor and remove it from the collection */
1574 CollDelete (&SegDescList, I);
1581 static void ProcessO65 (void)
1582 /* Process the o65 format section */
1586 /* Walk over the imports, check and add them to the o65 data */
1587 for (I = 0; I < CollCount (&O65Imports); ++I) {
1589 /* Get the import */
1590 Symbol* Sym = CollAtUnchecked (&O65Imports, I);
1592 /* Check if we have this symbol defined already. The entry
1593 * routine will check this also, but we get a more verbose
1594 * error message when checking it here.
1596 if (O65GetImport (O65FmtDesc, Sym->Name) != 0) {
1597 Error ("%s(%u): Duplicate imported o65 symbol: `%s'",
1598 Sym->CfgName, Sym->CfgLine, GetString (Sym->Name));
1601 /* Insert the symbol into the table */
1602 O65SetImport (O65FmtDesc, Sym->Name);
1605 /* Walk over the exports, check and add them to the o65 data */
1606 for (I = 0; I < CollCount (&O65Exports); ++I) {
1608 /* Get the export */
1609 Symbol* Sym = CollAtUnchecked (&O65Exports, I);
1611 /* Check if the export symbol is also defined as an import. */
1612 if (O65GetImport (O65FmtDesc, Sym->Name) != 0) {
1613 Error ("%s(%u): Exported o65 symbol `%s' cannot also be an o65 import",
1614 Sym->CfgName, Sym->CfgLine, GetString (Sym->Name));
1617 /* Check if we have this symbol defined already. The entry
1618 * routine will check this also, but we get a more verbose
1619 * error message when checking it here.
1621 if (O65GetExport (O65FmtDesc, Sym->Name) != 0) {
1622 Error ("%s(%u): Duplicate exported o65 symbol: `%s'",
1623 Sym->CfgName, Sym->CfgLine, GetString (Sym->Name));
1626 /* Insert the symbol into the table */
1627 O65SetExport (O65FmtDesc, Sym->Name);
1633 static void ProcessBin (void)
1634 /* Process the bin format section */
1640 static void ProcessFormats (void)
1641 /* Process the target format section */
1649 static void ProcessSymbols (void)
1650 /* Process the SYMBOLS section */
1654 /* Walk over all symbols */
1656 for (I = 0; I < CollCount (&Symbols); ++I) {
1658 /* Get the next symbol */
1659 Symbol* Sym = CollAtUnchecked (&Symbols, I);
1661 /* Do we define this symbol? */
1662 if ((Sym->Flags & SYM_DEF) != 0) {
1663 /* Check if the symbol is already defined somewhere else */
1664 if ((E = FindExport (Sym->Name)) != 0 && !IsUnresolvedExport (E)) {
1665 /* If the symbol is not marked as weak, this is an error.
1666 * Otherwise ignore the symbol from the config.
1668 if ((Sym->Flags & SYM_WEAK) == 0) {
1669 CfgError ("Symbol `%s' is already defined",
1670 GetString (Sym->Name));
1673 /* The symbol is undefined, generate an export */
1674 CreateConstExport (Sym->Name, Sym->Val);
1686 static void CreateRunDefines (SegDesc* S, unsigned long SegAddr)
1687 /* Create the defines for a RUN segment */
1689 StrBuf Buf = STATIC_STRBUF_INITIALIZER;
1691 SB_Printf (&Buf, "__%s_RUN__", GetString (S->Name));
1692 CreateMemoryExport (GetStrBufId (&Buf), S->Run, SegAddr - S->Run->Start);
1693 SB_Printf (&Buf, "__%s_SIZE__", GetString (S->Name));
1694 CreateConstExport (GetStrBufId (&Buf), S->Seg->Size);
1695 S->Flags |= SF_RUN_DEF;
1701 static void CreateLoadDefines (SegDesc* S, unsigned long SegAddr)
1702 /* Create the defines for a LOAD segment */
1704 StrBuf Buf = STATIC_STRBUF_INITIALIZER;
1706 SB_Printf (&Buf, "__%s_LOAD__", GetString (S->Name));
1707 CreateMemoryExport (GetStrBufId (&Buf), S->Load, SegAddr - S->Load->Start);
1708 S->Flags |= SF_LOAD_DEF;
1714 unsigned CfgProcess (void)
1715 /* Process the config file after reading in object files and libraries. This
1716 * includes postprocessing of the config file data but also assigning segments
1717 * and defining segment/memory area related symbols. The function will return
1718 * the number of memory area overflows (so zero means anything went ok).
1719 * In case of overflows, a short mapfile can be generated later, to ease the
1720 * task of rearranging segments for the user.
1723 unsigned Overflows = 0;
1726 /* Do postprocessing of the config file data */
1727 ProcessSymbols (); /* ######## */
1732 /* Walk through each of the memory sections. Add up the sizes and check
1733 * for an overflow of the section. Assign the start addresses of the
1734 * segments while doing this.
1736 for (I = 0; I < CollCount (&MemoryAreas); ++I) {
1740 /* Get this entry */
1741 MemoryArea* M = CollAtUnchecked (&MemoryAreas, I);
1743 /* Get the start address of this memory area */
1744 unsigned long Addr = M->Start;
1746 /* Walk through the segments in this memory area */
1747 for (J = 0; J < CollCount (&M->SegList); ++J) {
1749 /* Get the segment */
1750 SegDesc* S = CollAtUnchecked (&M->SegList, J);
1752 /* Some actions depend on wether this is the load or run memory
1757 /* This is the run (and maybe load) memory area. Handle
1758 * alignment and explict start address and offset.
1760 if (S->Flags & SF_ALIGN) {
1761 /* Align the address */
1762 unsigned long Val = (0x01UL << S->Align) - 1;
1763 Addr = (Addr + Val) & ~Val;
1764 } else if (S->Flags & (SF_OFFSET | SF_START)) {
1765 /* Give the segment a fixed starting address */
1766 unsigned long NewAddr = S->Addr;
1767 if (S->Flags & SF_OFFSET) {
1768 /* An offset was given, no address, make an address */
1769 NewAddr += M->Start;
1771 if (Addr > NewAddr) {
1772 /* Offset already too large */
1773 if (S->Flags & SF_OFFSET) {
1774 Error ("Offset too small in `%s', segment `%s'",
1775 GetString (M->Name), GetString (S->Name));
1777 Error ("Start address too low in `%s', segment `%s'",
1778 GetString (M->Name), GetString (S->Name));
1784 /* Set the start address of this segment, set the readonly flag
1785 * in the segment and and remember if the segment is in a
1786 * relocatable file or not.
1789 S->Seg->ReadOnly = (S->Flags & SF_RO) != 0;
1790 S->Seg->Relocatable = M->Relocatable;
1792 /* Remember that this segment is placed */
1795 } else if (S->Load == M) {
1797 /* This is the load memory area, *and* run and load are
1798 * different (because of the "else" above). Handle alignment.
1800 if (S->Flags & SF_ALIGN_LOAD) {
1801 /* Align the address */
1802 unsigned long Val = (0x01UL << S->AlignLoad) - 1;
1803 Addr = (Addr + Val) & ~Val;
1808 /* Increment the fill level of the memory area and check for an
1811 M->FillLevel = Addr + S->Seg->Size - M->Start;
1812 if (M->FillLevel > M->Size && (M->Flags & MF_OVERFLOW) == 0) {
1814 M->Flags |= MF_OVERFLOW;
1815 Warning ("Memory area overflow in `%s', segment `%s' (%lu bytes)",
1816 GetString (M->Name), GetString (S->Name),
1817 M->FillLevel - M->Size);
1820 /* If requested, define symbols for the start and size of the
1823 if (S->Flags & SF_DEFINE) {
1824 if (S->Run == M && (S->Flags & SF_RUN_DEF) == 0) {
1825 CreateRunDefines (S, Addr);
1827 if (S->Load == M && (S->Flags & SF_LOAD_DEF) == 0) {
1828 CreateLoadDefines (S, Addr);
1832 /* Calculate the new address */
1833 Addr += S->Seg->Size;
1837 /* If requested, define symbols for start and size of the memory area */
1838 if (M->Flags & MF_DEFINE) {
1839 StrBuf Buf = STATIC_STRBUF_INITIALIZER;
1840 SB_Printf (&Buf, "__%s_START__", GetString (M->Name));
1841 CreateMemoryExport (GetStrBufId (&Buf), M, 0);
1842 SB_Printf (&Buf, "__%s_SIZE__", GetString (M->Name));
1843 CreateConstExport (GetStrBufId (&Buf), M->Size);
1844 SB_Printf (&Buf, "__%s_LAST__", GetString (M->Name));
1845 CreateMemoryExport (GetStrBufId (&Buf), M, M->FillLevel);
1851 /* Return the number of memory area overflows */
1857 void CfgWriteTarget (void)
1858 /* Write the target file(s) */
1862 /* Walk through the files list */
1863 for (I = 0; I < CollCount (&FileList); ++I) {
1865 /* Get this entry */
1866 File* F = CollAtUnchecked (&FileList, I);
1868 /* We don't need to look at files with no memory areas */
1869 if (CollCount (&F->MemoryAreas) > 0) {
1871 /* Is there an output file? */
1872 if (SB_GetLen (GetStrBuf (F->Name)) > 0) {
1874 /* Assign a proper binary format */
1875 if (F->Format == BINFMT_DEFAULT) {
1876 F->Format = DefaultBinFmt;
1879 /* Call the apropriate routine for the binary format */
1880 switch (F->Format) {
1883 BinWriteTarget (BinFmtDesc, F);
1887 O65WriteTarget (O65FmtDesc, F);
1891 Internal ("Invalid binary format: %u", F->Format);
1897 /* No output file. Walk through the list and mark all segments
1898 * loading into these memory areas in this file as dumped.
1901 for (J = 0; J < CollCount (&F->MemoryAreas); ++J) {
1905 /* Get this entry */
1906 MemoryArea* M = CollAtUnchecked (&F->MemoryAreas, J);
1909 Print (stdout, 2, "Skipping `%s'...\n", GetString (M->Name));
1911 /* Walk throught the segments */
1912 for (K = 0; K < CollCount (&M->SegList); ++K) {
1913 SegDesc* S = CollAtUnchecked (&M->SegList, K);
1915 /* Load area - mark the segment as dumped */