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 FilePos Pos; /* Config file position */
121 unsigned Name; /* Symbol name */
122 unsigned Flags; /* Symbol flags */
123 long Val; /* Symbol value if any */
126 /* Collections with symbols */
127 static Collection O65Imports = STATIC_COLLECTION_INITIALIZER;
128 static Collection O65Exports = STATIC_COLLECTION_INITIALIZER;
129 static Collection Symbols = STATIC_COLLECTION_INITIALIZER;
132 #define SYM_NONE 0x00 /* No special meaning */
133 #define SYM_DEF 0x01 /* Symbol defined in the config file */
134 #define SYM_WEAK 0x02 /* Defined symbol is weak */
135 #define SYM_IMPORT 0x04 /* A forced import */
137 /* Descriptor holding information about the binary formats */
138 static BinDesc* BinFmtDesc = 0;
139 static O65Desc* O65FmtDesc = 0;
143 /*****************************************************************************/
145 /*****************************************************************************/
149 static File* NewFile (unsigned Name);
150 /* Create a new file descriptor and insert it into the list */
154 /*****************************************************************************/
155 /* List management */
156 /*****************************************************************************/
160 static File* FindFile (unsigned Name)
161 /* Find a file with a given name. */
164 for (I = 0; I < CollCount (&FileList); ++I) {
165 File* F = CollAtUnchecked (&FileList, I);
166 if (F->Name == Name) {
175 static File* GetFile (unsigned Name)
176 /* Get a file entry with the given name. Create a new one if needed. */
178 File* F = FindFile (Name);
180 /* Create a new one */
188 static void FileInsert (File* F, MemoryArea* M)
189 /* Insert the memory area into the files list */
192 CollAppend (&F->MemoryAreas, M);
197 static MemoryArea* CfgFindMemory (unsigned Name)
198 /* Find the memory are with the given name. Return NULL if not found */
201 for (I = 0; I < CollCount (&MemoryAreas); ++I) {
202 MemoryArea* M = CollAtUnchecked (&MemoryAreas, I);
203 if (M->Name == Name) {
212 static MemoryArea* CfgGetMemory (unsigned Name)
213 /* Find the memory are with the given name. Print an error on an invalid name */
215 MemoryArea* M = CfgFindMemory (Name);
217 CfgError (&CfgErrorPos, "Invalid memory area `%s'", GetString (Name));
224 static SegDesc* CfgFindSegDesc (unsigned Name)
225 /* Find the segment descriptor with the given name, return NULL if not found. */
228 for (I = 0; I < CollCount (&SegDescList); ++I) {
229 SegDesc* S = CollAtUnchecked (&SegDescList, I);
230 if (S->Name == Name) {
242 static void MemoryInsert (MemoryArea* M, SegDesc* S)
243 /* Insert the segment descriptor into the memory area list */
245 /* Insert the segment into the segment list of the memory area */
246 CollAppend (&M->SegList, S);
251 /*****************************************************************************/
252 /* Constructors/Destructors */
253 /*****************************************************************************/
257 static Symbol* NewSymbol (unsigned Name, unsigned Flags, long Val)
258 /* Create a new Symbol structure with the given name name and flags. The
259 * current config file position is recorded in the returned struct.
262 /* Allocate memory */
263 Symbol* Sym = xmalloc (sizeof (Symbol));
265 /* Initialize the fields */
266 Sym->Pos = CfgErrorPos;
271 /* Return the initialized struct */
277 static Symbol* NewO65Symbol (void)
278 /* Create a new Symbol structure with the name in the current CfgSVal variable
279 * ready for use as an o65 symbol. The current config file position is recorded
280 * in the returned struct.
283 return NewSymbol (GetStrBufId (&CfgSVal), SYM_NONE, 0);
288 static File* NewFile (unsigned Name)
289 /* Create a new file descriptor and insert it into the list */
291 /* Allocate memory */
292 File* F = xmalloc (sizeof (File));
294 /* Initialize the fields */
297 F->Format = BINFMT_DEFAULT;
298 InitCollection (&F->MemoryAreas);
300 /* Insert the struct into the list */
301 CollAppend (&FileList, F);
303 /* ...and return it */
309 static MemoryArea* CreateMemoryArea (const FilePos* Pos, unsigned Name)
310 /* Create a new memory area and insert it into the list */
312 /* Check for duplicate names */
313 MemoryArea* M = CfgFindMemory (Name);
315 CfgError (&CfgErrorPos,
316 "Memory area `%s' defined twice",
320 /* Create a new memory area */
321 M = NewMemoryArea (Pos, Name);
323 /* Insert the struct into the list ... */
324 CollAppend (&MemoryAreas, M);
326 /* ...and return it */
332 static SegDesc* NewSegDesc (unsigned Name)
333 /* Create a segment descriptor and insert it into the list */
336 /* Check for duplicate names */
337 SegDesc* S = CfgFindSegDesc (Name);
339 CfgError (&CfgErrorPos, "Segment `%s' defined twice", GetString (Name));
342 /* Allocate memory */
343 S = xmalloc (sizeof (SegDesc));
345 /* Initialize the fields */
352 /* Insert the struct into the list ... */
353 CollAppend (&SegDescList, S);
355 /* ...and return it */
361 static void FreeSegDesc (SegDesc* S)
362 /* Free a segment descriptor */
369 /*****************************************************************************/
370 /* Config file parsing */
371 /*****************************************************************************/
375 static void FlagAttr (unsigned* Flags, unsigned Mask, const char* Name)
376 /* Check if the item is already defined. Print an error if so. If not, set
377 * the marker that we have a definition now.
381 CfgError (&CfgErrorPos, "%s is already defined", Name);
388 static void AttrCheck (unsigned Attr, unsigned Mask, const char* Name)
389 /* Check that a mandatory attribute was given */
391 if ((Attr & Mask) == 0) {
392 CfgError (&CfgErrorPos, "%s attribute is missing", Name);
398 static void ParseMemory (void)
399 /* Parse a MEMORY section */
401 static const IdentTok Attributes [] = {
402 { "START", CFGTOK_START },
403 { "SIZE", CFGTOK_SIZE },
404 { "TYPE", CFGTOK_TYPE },
405 { "FILE", CFGTOK_FILE },
406 { "DEFINE", CFGTOK_DEFINE },
407 { "FILL", CFGTOK_FILL },
408 { "FILLVAL", CFGTOK_FILLVAL },
410 static const IdentTok Types [] = {
415 while (CfgTok == CFGTOK_IDENT) {
417 /* Create a new entry on the heap */
418 MemoryArea* M = CreateMemoryArea (&CfgErrorPos, GetStrBufId (&CfgSVal));
420 /* Skip the name and the following colon */
424 /* Read the attributes */
425 while (CfgTok == CFGTOK_IDENT) {
427 /* Map the identifier to a token */
429 CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
432 /* An optional assignment follows */
434 CfgOptionalAssign ();
436 /* Check which attribute was given */
440 FlagAttr (&M->Attr, MA_START, "START");
441 M->StartExpr = CfgExpr ();
445 FlagAttr (&M->Attr, MA_SIZE, "SIZE");
446 M->SizeExpr = CfgExpr ();
450 FlagAttr (&M->Attr, MA_TYPE, "TYPE");
451 CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type");
452 if (CfgTok == CFGTOK_RO) {
459 FlagAttr (&M->Attr, MA_FILE, "FILE");
461 /* Get the file entry and insert the memory area */
462 FileInsert (GetFile (GetStrBufId (&CfgSVal)), M);
467 FlagAttr (&M->Attr, MA_DEFINE, "DEFINE");
468 /* Map the token to a boolean */
470 if (CfgTok == CFGTOK_TRUE) {
471 M->Flags |= MF_DEFINE;
477 FlagAttr (&M->Attr, MA_FILL, "FILL");
478 /* Map the token to a boolean */
480 if (CfgTok == CFGTOK_TRUE) {
487 FlagAttr (&M->Attr, MA_FILLVAL, "FILLVAL");
488 M->FillVal = (unsigned char) CfgCheckedConstExpr (0, 0xFF);
492 FAIL ("Unexpected attribute token");
496 /* Skip an optional comma */
500 /* Skip the semicolon */
503 /* Check for mandatory parameters */
504 AttrCheck (M->Attr, MA_START, "START");
505 AttrCheck (M->Attr, MA_SIZE, "SIZE");
507 /* If we don't have a file name for output given, use the default
510 if ((M->Attr & MA_FILE) == 0) {
511 FileInsert (GetFile (GetStringId (OutputName)), M);
516 /* Remember we had this section */
517 SectionsEncountered |= SE_MEMORY;
522 static void ParseFiles (void)
523 /* Parse a FILES section */
525 static const IdentTok Attributes [] = {
526 { "FORMAT", CFGTOK_FORMAT },
528 static const IdentTok Formats [] = {
529 { "O65", CFGTOK_O65 },
530 { "BIN", CFGTOK_BIN },
531 { "BINARY", CFGTOK_BIN },
535 /* The MEMORY section must preceed the FILES section */
536 if ((SectionsEncountered & SE_MEMORY) == 0) {
537 CfgError (&CfgErrorPos, "MEMORY must precede FILES");
540 /* Parse all files */
541 while (CfgTok != CFGTOK_RCURLY) {
545 /* We expect a string value here */
548 /* Search for the file, it must exist */
549 F = FindFile (GetStrBufId (&CfgSVal));
551 CfgError (&CfgErrorPos,
552 "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 CfgError (&CfgErrorPos,
579 "Cannot set a file format twice");
581 /* Read the format token */
582 CfgSpecialToken (Formats, ENTRY_COUNT (Formats), "Format");
586 F->Format = BINFMT_BINARY;
590 F->Format = BINFMT_O65;
594 Error ("Unexpected format token");
599 FAIL ("Unexpected attribute token");
603 /* Skip the attribute value and an optional comma */
608 /* Skip the semicolon */
613 /* Remember we had this section */
614 SectionsEncountered |= SE_FILES;
619 static void ParseSegments (void)
620 /* Parse a SEGMENTS section */
622 static const IdentTok Attributes [] = {
623 { "ALIGN", CFGTOK_ALIGN },
624 { "ALIGN_LOAD", CFGTOK_ALIGN_LOAD },
625 { "DEFINE", CFGTOK_DEFINE },
626 { "LOAD", CFGTOK_LOAD },
627 { "OFFSET", CFGTOK_OFFSET },
628 { "OPTIONAL", CFGTOK_OPTIONAL },
629 { "RUN", CFGTOK_RUN },
630 { "START", CFGTOK_START },
631 { "TYPE", CFGTOK_TYPE },
633 static const IdentTok Types [] = {
636 { "BSS", CFGTOK_BSS },
643 /* The MEMORY section must preceed the SEGMENTS section */
644 if ((SectionsEncountered & SE_MEMORY) == 0) {
645 CfgError (&CfgErrorPos, "MEMORY must precede SEGMENTS");
648 while (CfgTok == CFGTOK_IDENT) {
652 /* Create a new entry on the heap */
653 S = NewSegDesc (GetStrBufId (&CfgSVal));
655 /* Skip the name and the following colon */
659 /* Read the attributes */
660 while (CfgTok == CFGTOK_IDENT) {
662 /* Map the identifier to a token */
664 CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
667 /* An optional assignment follows */
669 CfgOptionalAssign ();
671 /* Check which attribute was given */
675 FlagAttr (&S->Attr, SA_ALIGN, "ALIGN");
676 Val = CfgCheckedConstExpr (1, 0x10000);
677 S->Align = BitFind (Val);
678 if ((0x01L << S->Align) != Val) {
679 CfgError (&CfgErrorPos, "Alignment must be a power of 2");
681 S->Flags |= SF_ALIGN;
684 case CFGTOK_ALIGN_LOAD:
685 FlagAttr (&S->Attr, SA_ALIGN_LOAD, "ALIGN_LOAD");
686 Val = CfgCheckedConstExpr (1, 0x10000);
687 S->AlignLoad = BitFind (Val);
688 if ((0x01L << S->AlignLoad) != Val) {
689 CfgError (&CfgErrorPos, "Alignment must be a power of 2");
691 S->Flags |= SF_ALIGN_LOAD;
695 FlagAttr (&S->Attr, SA_DEFINE, "DEFINE");
696 /* Map the token to a boolean */
698 if (CfgTok == CFGTOK_TRUE) {
699 S->Flags |= SF_DEFINE;
705 FlagAttr (&S->Attr, SA_LOAD, "LOAD");
706 S->Load = CfgGetMemory (GetStrBufId (&CfgSVal));
711 FlagAttr (&S->Attr, SA_OFFSET, "OFFSET");
712 S->Addr = CfgCheckedConstExpr (1, 0x1000000);
713 S->Flags |= SF_OFFSET;
716 case CFGTOK_OPTIONAL:
717 FlagAttr (&S->Attr, SA_OPTIONAL, "OPTIONAL");
719 if (CfgTok == CFGTOK_TRUE) {
720 S->Flags |= SF_OPTIONAL;
726 FlagAttr (&S->Attr, SA_RUN, "RUN");
727 S->Run = CfgGetMemory (GetStrBufId (&CfgSVal));
732 FlagAttr (&S->Attr, SA_START, "START");
733 S->Addr = CfgCheckedConstExpr (1, 0x1000000);
734 S->Flags |= SF_START;
738 FlagAttr (&S->Attr, SA_TYPE, "TYPE");
739 CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type");
741 case CFGTOK_RO: S->Flags |= SF_RO; break;
742 case CFGTOK_RW: /* Default */ break;
743 case CFGTOK_BSS: S->Flags |= SF_BSS; break;
744 case CFGTOK_ZP: S->Flags |= (SF_BSS | SF_ZP); break;
745 default: Internal ("Unexpected token: %d", CfgTok);
751 FAIL ("Unexpected attribute token");
755 /* Skip an optional comma */
759 /* Check for mandatory parameters */
760 AttrCheck (S->Attr, SA_LOAD, "LOAD");
762 /* Set defaults for stuff not given */
763 if ((S->Attr & SA_RUN) == 0) {
768 /* An attribute of ALIGN_LOAD doesn't make sense if there are no
769 * separate run and load memory areas.
771 if ((S->Flags & SF_ALIGN_LOAD) != 0 && (S->Load == S->Run)) {
772 Warning ("%s(%lu): ALIGN_LOAD attribute specified, but no separate "
773 "LOAD and RUN memory areas assigned",
774 CfgGetName (), CfgErrorPos.Line);
775 /* Remove the flag */
776 S->Flags &= ~SF_ALIGN_LOAD;
779 /* If the segment is marked as BSS style, it may not have separate
780 * load and run memory areas, because it's is never written to disk.
782 if ((S->Flags & SF_BSS) != 0 && (S->Load != S->Run)) {
783 Warning ("%s(%lu): Segment with type `bss' has both LOAD and RUN "
784 "memory areas assigned", CfgGetName (), CfgErrorPos.Line);
787 /* Don't allow read/write data to be put into a readonly area */
788 if ((S->Flags & SF_RO) == 0) {
789 if (S->Run->Flags & MF_RO) {
790 CfgError (&CfgErrorPos,
791 "Cannot put r/w segment `%s' in r/o memory area `%s'",
792 GetString (S->Name), GetString (S->Run->Name));
796 /* Only one of ALIGN, START and OFFSET may be used */
797 Count = ((S->Flags & SF_ALIGN) != 0) +
798 ((S->Flags & SF_OFFSET) != 0) +
799 ((S->Flags & SF_START) != 0);
801 CfgError (&CfgErrorPos,
802 "Only one of ALIGN, START, OFFSET may be used");
805 /* Skip the semicolon */
809 /* Remember we had this section */
810 SectionsEncountered |= SE_SEGMENTS;
815 static void ParseO65 (void)
816 /* Parse the o65 format section */
818 static const IdentTok Attributes [] = {
819 { "EXPORT", CFGTOK_EXPORT },
820 { "IMPORT", CFGTOK_IMPORT },
821 { "TYPE", CFGTOK_TYPE },
824 { "VERSION", CFGTOK_VERSION },
826 static const IdentTok Types [] = {
827 { "SMALL", CFGTOK_SMALL },
828 { "LARGE", CFGTOK_LARGE },
830 static const IdentTok OperatingSystems [] = {
831 { "LUNIX", CFGTOK_LUNIX },
832 { "OSA65", CFGTOK_OSA65 },
833 { "CC65", CFGTOK_CC65 },
834 { "OPENCBM", CFGTOK_OPENCBM },
837 /* Bitmask to remember the attributes we got already */
841 atOSVersion = 0x0002,
848 unsigned AttrFlags = atNone;
850 /* Remember the attributes read */
851 unsigned OS = 0; /* Initialize to keep gcc happy */
852 unsigned Version = 0;
854 /* Read the attributes */
855 while (CfgTok == CFGTOK_IDENT) {
857 /* Map the identifier to a token */
859 CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
862 /* An optional assignment follows */
864 CfgOptionalAssign ();
866 /* Check which attribute was given */
870 /* Remember we had this token (maybe more than once) */
871 AttrFlags |= atExport;
872 /* We expect an identifier */
874 /* Remember it as an export for later */
875 CollAppend (&O65Exports, NewO65Symbol ());
876 /* Eat the identifier token */
881 /* Remember we had this token (maybe more than once) */
882 AttrFlags |= atImport;
883 /* We expect an identifier */
885 /* Remember it as an import for later */
886 CollAppend (&O65Imports, NewO65Symbol ());
887 /* Eat the identifier token */
892 /* Cannot have this attribute twice */
893 FlagAttr (&AttrFlags, atType, "TYPE");
894 /* Get the type of the executable */
895 CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type");
899 O65SetSmallModel (O65FmtDesc);
903 O65SetLargeModel (O65FmtDesc);
907 CfgError (&CfgErrorPos, "Unexpected type token");
909 /* Eat the attribute token */
914 /* Cannot use this attribute twice */
915 FlagAttr (&AttrFlags, atOS, "OS");
916 /* Get the operating system. It may be specified as name or
917 * as a number in the range 1..255.
919 if (CfgTok == CFGTOK_INTCON) {
920 CfgRangeCheck (O65OS_MIN, O65OS_MAX);
921 OS = (unsigned) CfgIVal;
923 CfgSpecialToken (OperatingSystems, ENTRY_COUNT (OperatingSystems), "OS type");
925 case CFGTOK_LUNIX: OS = O65OS_LUNIX; break;
926 case CFGTOK_OSA65: OS = O65OS_OSA65; break;
927 case CFGTOK_CC65: OS = O65OS_CC65; break;
928 case CFGTOK_OPENCBM: OS = O65OS_OPENCBM; break;
929 default: CfgError (&CfgErrorPos, "Unexpected OS token");
936 /* Cannot have this attribute twice */
937 FlagAttr (&AttrFlags, atID, "ID");
938 /* We're expecting a number in the 0..$FFFF range*/
939 ModuleId = (unsigned) CfgCheckedConstExpr (0, 0xFFFF);
943 /* Cannot have this attribute twice */
944 FlagAttr (&AttrFlags, atVersion, "VERSION");
945 /* We're expecting a number in byte range */
946 Version = (unsigned) CfgCheckedConstExpr (0, 0xFF);
950 FAIL ("Unexpected attribute token");
954 /* Skip an optional comma */
958 /* Check if we have all mandatory attributes */
959 AttrCheck (AttrFlags, atOS, "OS");
961 /* Check for attributes that may not be combined */
962 if (OS == O65OS_CC65) {
963 if ((AttrFlags & (atImport | atExport)) != 0 && ModuleId < 0x8000) {
964 CfgError (&CfgErrorPos,
965 "OS type CC65 may not have imports or exports for ids < $8000");
968 if (AttrFlags & atID) {
969 CfgError (&CfgErrorPos,
970 "Operating system does not support the ID attribute");
974 /* Set the O65 operating system to use */
975 O65SetOS (O65FmtDesc, OS, Version, ModuleId);
980 static void ParseFormats (void)
981 /* Parse a target format section */
983 static const IdentTok Formats [] = {
984 { "O65", CFGTOK_O65 },
985 { "BIN", CFGTOK_BIN },
986 { "BINARY", CFGTOK_BIN },
989 while (CfgTok == CFGTOK_IDENT) {
991 /* Map the identifier to a token */
993 CfgSpecialToken (Formats, ENTRY_COUNT (Formats), "Format");
996 /* Skip the name and the following colon */
1000 /* Parse the format options */
1001 switch (FormatTok) {
1008 /* No attribibutes available */
1012 Error ("Unexpected format token");
1015 /* Skip the semicolon */
1020 /* Remember we had this section */
1021 SectionsEncountered |= SE_FORMATS;
1026 static void ParseConDes (void)
1027 /* Parse the CONDES feature */
1029 static const IdentTok Attributes [] = {
1030 { "SEGMENT", CFGTOK_SEGMENT },
1031 { "LABEL", CFGTOK_LABEL },
1032 { "COUNT", CFGTOK_COUNT },
1033 { "TYPE", CFGTOK_TYPE },
1034 { "ORDER", CFGTOK_ORDER },
1037 static const IdentTok Types [] = {
1038 { "CONSTRUCTOR", CFGTOK_CONSTRUCTOR },
1039 { "DESTRUCTOR", CFGTOK_DESTRUCTOR },
1040 { "INTERRUPTOR", CFGTOK_INTERRUPTOR },
1043 static const IdentTok Orders [] = {
1044 { "DECREASING", CFGTOK_DECREASING },
1045 { "INCREASING", CFGTOK_INCREASING },
1048 /* Attribute values. */
1049 unsigned SegName = INVALID_STRING_ID;
1050 unsigned Label = INVALID_STRING_ID;
1051 unsigned Count = INVALID_STRING_ID;
1052 /* Initialize to avoid gcc warnings: */
1054 ConDesOrder Order = cdIncreasing;
1056 /* Bitmask to remember the attributes we got already */
1065 unsigned AttrFlags = atNone;
1067 /* Parse the attributes */
1070 /* Map the identifier to a token */
1072 CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
1075 /* An optional assignment follows */
1077 CfgOptionalAssign ();
1079 /* Check which attribute was given */
1082 case CFGTOK_SEGMENT:
1083 /* Don't allow this twice */
1084 FlagAttr (&AttrFlags, atSegName, "SEGMENT");
1085 /* We expect an identifier */
1087 /* Remember the value for later */
1088 SegName = GetStrBufId (&CfgSVal);
1092 /* Don't allow this twice */
1093 FlagAttr (&AttrFlags, atLabel, "LABEL");
1094 /* We expect an identifier */
1096 /* Remember the value for later */
1097 Label = GetStrBufId (&CfgSVal);
1101 /* Don't allow this twice */
1102 FlagAttr (&AttrFlags, atCount, "COUNT");
1103 /* We expect an identifier */
1105 /* Remember the value for later */
1106 Count = GetStrBufId (&CfgSVal);
1110 /* Don't allow this twice */
1111 FlagAttr (&AttrFlags, atType, "TYPE");
1112 /* The type may be given as id or numerical */
1113 if (CfgTok == CFGTOK_INTCON) {
1114 CfgRangeCheck (CD_TYPE_MIN, CD_TYPE_MAX);
1115 Type = (int) CfgIVal;
1117 CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type");
1119 case CFGTOK_CONSTRUCTOR: Type = CD_TYPE_CON; break;
1120 case CFGTOK_DESTRUCTOR: Type = CD_TYPE_DES; break;
1121 case CFGTOK_INTERRUPTOR: Type = CD_TYPE_INT; break;
1122 default: FAIL ("Unexpected type token");
1128 /* Don't allow this twice */
1129 FlagAttr (&AttrFlags, atOrder, "ORDER");
1130 CfgSpecialToken (Orders, ENTRY_COUNT (Orders), "Order");
1132 case CFGTOK_DECREASING: Order = cdDecreasing; break;
1133 case CFGTOK_INCREASING: Order = cdIncreasing; break;
1134 default: FAIL ("Unexpected order token");
1139 FAIL ("Unexpected attribute token");
1143 /* Skip the attribute value */
1146 /* Semicolon ends the ConDes decl, otherwise accept an optional comma */
1147 if (CfgTok == CFGTOK_SEMI) {
1149 } else if (CfgTok == CFGTOK_COMMA) {
1154 /* Check if we have all mandatory attributes */
1155 AttrCheck (AttrFlags, atSegName, "SEGMENT");
1156 AttrCheck (AttrFlags, atLabel, "LABEL");
1157 AttrCheck (AttrFlags, atType, "TYPE");
1159 /* Check if the condes has already attributes defined */
1160 if (ConDesHasSegName(Type) || ConDesHasLabel(Type)) {
1161 CfgError (&CfgErrorPos,
1162 "CONDES attributes for type %d are already defined",
1166 /* Define the attributes */
1167 ConDesSetSegName (Type, SegName);
1168 ConDesSetLabel (Type, Label);
1169 if (AttrFlags & atCount) {
1170 ConDesSetCountSym (Type, Count);
1172 if (AttrFlags & atOrder) {
1173 ConDesSetOrder (Type, Order);
1179 static void ParseStartAddress (void)
1180 /* Parse the STARTADDRESS feature */
1182 static const IdentTok Attributes [] = {
1183 { "DEFAULT", CFGTOK_DEFAULT },
1187 /* Attribute values. */
1188 unsigned long DefStartAddr = 0;
1190 /* Bitmask to remember the attributes we got already */
1195 unsigned AttrFlags = atNone;
1197 /* Parse the attributes */
1200 /* Map the identifier to a token */
1202 CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
1205 /* An optional assignment follows */
1207 CfgOptionalAssign ();
1209 /* Check which attribute was given */
1212 case CFGTOK_DEFAULT:
1213 /* Don't allow this twice */
1214 FlagAttr (&AttrFlags, atDefault, "DEFAULT");
1215 /* We expect a numeric expression */
1216 DefStartAddr = CfgCheckedConstExpr (0, 0xFFFFFF);
1220 FAIL ("Unexpected attribute token");
1224 /* Semicolon ends the ConDes decl, otherwise accept an optional comma */
1225 if (CfgTok == CFGTOK_SEMI) {
1227 } else if (CfgTok == CFGTOK_COMMA) {
1232 /* Check if we have all mandatory attributes */
1233 AttrCheck (AttrFlags, atDefault, "DEFAULT");
1235 /* If no start address was given on the command line, use the one given
1238 if (!HaveStartAddr) {
1239 StartAddr = DefStartAddr;
1245 static void ParseFeatures (void)
1246 /* Parse a features section */
1248 static const IdentTok Features [] = {
1249 { "CONDES", CFGTOK_CONDES },
1250 { "STARTADDRESS", CFGTOK_STARTADDRESS },
1253 while (CfgTok == CFGTOK_IDENT) {
1255 /* Map the identifier to a token */
1256 cfgtok_t FeatureTok;
1257 CfgSpecialToken (Features, ENTRY_COUNT (Features), "Feature");
1258 FeatureTok = CfgTok;
1260 /* Skip the name and the following colon */
1264 /* Parse the format options */
1265 switch (FeatureTok) {
1271 case CFGTOK_STARTADDRESS:
1272 ParseStartAddress ();
1277 FAIL ("Unexpected feature token");
1280 /* Skip the semicolon */
1284 /* Remember we had this section */
1285 SectionsEncountered |= SE_FEATURES;
1290 static void ParseSymbols (void)
1291 /* Parse a symbols section */
1293 static const IdentTok Attributes[] = {
1294 { "VALUE", CFGTOK_VALUE },
1295 { "WEAK", CFGTOK_WEAK },
1298 while (CfgTok == CFGTOK_IDENT) {
1301 unsigned Flags = SYM_NONE;
1303 /* Remember the name */
1304 unsigned Name = GetStrBufId (&CfgSVal);
1307 /* Support both, old and new syntax here. New syntax is a colon
1308 * followed by an attribute list, old syntax is an optional equal
1309 * sign plus a value.
1311 if (CfgTok != CFGTOK_COLON) {
1315 /* Allow an optional assignment */
1316 CfgOptionalAssign ();
1318 /* Make sure the next token is an integer expression, read and
1321 Val = CfgConstExpr ();
1323 /* This is a defined symbol */
1328 /* Bitmask to remember the attributes we got already */
1334 unsigned AttrFlags = atNone;
1337 /* New syntax - skip the colon */
1340 /* Parse the attributes */
1343 /* Map the identifier to a token */
1345 CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
1348 /* Skip the attribute name */
1351 /* An optional assignment follows */
1352 CfgOptionalAssign ();
1354 /* Check which attribute was given */
1358 /* Don't allow this twice */
1359 FlagAttr (&AttrFlags, atValue, "VALUE");
1360 /* We expect a numeric expression */
1361 Val = CfgConstExpr ();
1362 /* Symbol is defined */
1367 /* Don't allow this twice */
1368 FlagAttr (&AttrFlags, atWeak, "WEAK");
1370 if (CfgTok == CFGTOK_TRUE) {
1377 FAIL ("Unexpected attribute token");
1381 /* Semicolon ends the decl, otherwise accept an optional comma */
1382 if (CfgTok == CFGTOK_SEMI) {
1384 } else if (CfgTok == CFGTOK_COMMA) {
1389 /* Check if we have all mandatory attributes */
1390 AttrCheck (AttrFlags, atValue, "VALUE");
1393 /* Remember the symbol for later */
1394 CollAppend (&Symbols, NewSymbol (Name, Flags, Val));
1396 /* Skip the semicolon */
1400 /* Remember we had this section */
1401 SectionsEncountered |= SE_SYMBOLS;
1406 static void ParseConfig (void)
1407 /* Parse the config file */
1409 static const IdentTok BlockNames [] = {
1410 { "MEMORY", CFGTOK_MEMORY },
1411 { "FILES", CFGTOK_FILES },
1412 { "SEGMENTS", CFGTOK_SEGMENTS },
1413 { "FORMATS", CFGTOK_FORMATS },
1414 { "FEATURES", CFGTOK_FEATURES },
1415 { "SYMBOLS", CFGTOK_SYMBOLS },
1421 /* Read the block ident */
1422 CfgSpecialToken (BlockNames, ENTRY_COUNT (BlockNames), "Block identifier");
1426 /* Expected a curly brace */
1427 CfgConsume (CFGTOK_LCURLY, "`{' expected");
1429 /* Read the block */
1440 case CFGTOK_SEGMENTS:
1444 case CFGTOK_FORMATS:
1448 case CFGTOK_FEATURES:
1452 case CFGTOK_SYMBOLS:
1457 FAIL ("Unexpected block token");
1461 /* Skip closing brace */
1462 CfgConsume (CFGTOK_RCURLY, "`}' expected");
1464 } while (CfgTok != CFGTOK_EOF);
1470 /* Read the configuration */
1472 /* Create the descriptors for the binary formats */
1473 BinFmtDesc = NewBinDesc ();
1474 O65FmtDesc = NewO65Desc ();
1476 /* If we have a config name given, open the file, otherwise we will read
1481 /* Parse the file */
1484 /* Close the input file */
1490 /*****************************************************************************/
1491 /* Config file processing */
1492 /*****************************************************************************/
1496 static void ProcessMemory (void)
1497 /* Process the MEMORY section */
1499 /* Walk over the list with the memory areas */
1501 for (I = 0; I < CollCount (&MemoryAreas); ++I) {
1503 /* Get the next memory area */
1504 MemoryArea* M = CollAtUnchecked (&MemoryAreas, I);
1506 /* Remember if this is a relocatable memory area */
1507 M->Relocatable = RelocatableBinFmt (M->F->Format);
1509 /* Resolve the expressions */
1510 if (!IsConstExpr (M->StartExpr)) {
1512 "Start address of memory area `%s' is not constant",
1513 GetString (M->Name));
1515 M->Start = GetExprVal (M->StartExpr);
1517 if (!IsConstExpr (M->SizeExpr)) {
1519 "Size of memory area `%s' is not constant",
1520 GetString (M->Name));
1522 M->Size = GetExprVal (M->SizeExpr);
1524 /* Mark the memory area as placed */
1525 M->Flags |= MF_PLACED;
1531 static void ProcessSegments (void)
1532 /* Process the SEGMENTS section */
1536 /* Walk over the list of segment descriptors */
1538 while (I < CollCount (&SegDescList)) {
1540 /* Get the next segment descriptor */
1541 SegDesc* S = CollAtUnchecked (&SegDescList, I);
1543 /* Search for the actual segment in the input files. The function may
1544 * return NULL (no such segment), this is checked later.
1546 S->Seg = SegFind (S->Name);
1548 /* If the segment is marked as BSS style, and if the segment exists
1549 * in any of the object file, check that there's no initialized data
1552 if ((S->Flags & SF_BSS) != 0 && S->Seg != 0 && !IsBSSType (S->Seg)) {
1553 Warning ("Segment `%s' with type `bss' contains initialized data",
1554 GetString (S->Name));
1557 /* If this segment does exist in any of the object files, insert the
1558 * segment into the load/run memory areas. Otherwise print a warning
1559 * and discard it, because the segment pointer in the descriptor is
1564 /* Insert the segment into the memory area list */
1565 MemoryInsert (S->Run, S);
1566 if (S->Load != S->Run) {
1567 /* We have separate RUN and LOAD areas */
1568 MemoryInsert (S->Load, S);
1571 /* Process the next segment descriptor in the next run */
1576 /* Print a warning if the segment is not optional */
1577 if ((S->Flags & SF_OPTIONAL) == 0) {
1578 CfgWarning (&CfgErrorPos,
1579 "Segment `%s' does not exist",
1580 GetString (S->Name));
1583 /* Discard the descriptor and remove it from the collection */
1585 CollDelete (&SegDescList, I);
1592 static void ProcessO65 (void)
1593 /* Process the o65 format section */
1597 /* Walk over the imports, check and add them to the o65 data */
1598 for (I = 0; I < CollCount (&O65Imports); ++I) {
1600 /* Get the import */
1601 Symbol* Sym = CollAtUnchecked (&O65Imports, I);
1603 /* Check if we have this symbol defined already. The entry
1604 * routine will check this also, but we get a more verbose
1605 * error message when checking it here.
1607 if (O65GetImport (O65FmtDesc, Sym->Name) != 0) {
1608 CfgError (&Sym->Pos,
1609 "Duplicate imported o65 symbol: `%s'",
1610 GetString (Sym->Name));
1613 /* Insert the symbol into the table */
1614 O65SetImport (O65FmtDesc, Sym->Name);
1617 /* Walk over the exports, check and add them to the o65 data */
1618 for (I = 0; I < CollCount (&O65Exports); ++I) {
1620 /* Get the export */
1621 Symbol* Sym = CollAtUnchecked (&O65Exports, I);
1623 /* Check if the export symbol is also defined as an import. */
1624 if (O65GetImport (O65FmtDesc, Sym->Name) != 0) {
1625 CfgError (&Sym->Pos,
1626 "Exported o65 symbol `%s' cannot also be an o65 import",
1627 GetString (Sym->Name));
1630 /* Check if we have this symbol defined already. The entry
1631 * routine will check this also, but we get a more verbose
1632 * error message when checking it here.
1634 if (O65GetExport (O65FmtDesc, Sym->Name) != 0) {
1635 CfgError (&Sym->Pos,
1636 "Duplicate exported o65 symbol: `%s'",
1637 GetString (Sym->Name));
1640 /* Insert the symbol into the table */
1641 O65SetExport (O65FmtDesc, Sym->Name);
1647 static void ProcessBin (void)
1648 /* Process the bin format section */
1654 static void ProcessFormats (void)
1655 /* Process the target format section */
1663 static void ProcessSymbols (void)
1664 /* Process the SYMBOLS section */
1668 /* Walk over all symbols */
1670 for (I = 0; I < CollCount (&Symbols); ++I) {
1672 /* Get the next symbol */
1673 Symbol* Sym = CollAtUnchecked (&Symbols, I);
1675 /* Do we define this symbol? */
1676 if ((Sym->Flags & SYM_DEF) != 0) {
1677 /* Check if the symbol is already defined somewhere else */
1678 if ((E = FindExport (Sym->Name)) != 0 && !IsUnresolvedExport (E)) {
1679 /* If the symbol is not marked as weak, this is an error.
1680 * Otherwise ignore the symbol from the config.
1682 if ((Sym->Flags & SYM_WEAK) == 0) {
1683 CfgError (&CfgErrorPos,
1684 "Symbol `%s' is already defined",
1685 GetString (Sym->Name));
1688 /* The symbol is undefined, generate an export */
1689 CreateConstExport (Sym->Name, Sym->Val);
1701 static void CreateRunDefines (SegDesc* S, unsigned long SegAddr)
1702 /* Create the defines for a RUN segment */
1704 StrBuf Buf = STATIC_STRBUF_INITIALIZER;
1706 SB_Printf (&Buf, "__%s_RUN__", GetString (S->Name));
1707 CreateMemoryExport (GetStrBufId (&Buf), S->Run, SegAddr - S->Run->Start);
1708 SB_Printf (&Buf, "__%s_SIZE__", GetString (S->Name));
1709 CreateConstExport (GetStrBufId (&Buf), S->Seg->Size);
1710 S->Flags |= SF_RUN_DEF;
1716 static void CreateLoadDefines (SegDesc* S, unsigned long SegAddr)
1717 /* Create the defines for a LOAD segment */
1719 StrBuf Buf = STATIC_STRBUF_INITIALIZER;
1721 SB_Printf (&Buf, "__%s_LOAD__", GetString (S->Name));
1722 CreateMemoryExport (GetStrBufId (&Buf), S->Load, SegAddr - S->Load->Start);
1723 S->Flags |= SF_LOAD_DEF;
1729 unsigned CfgProcess (void)
1730 /* Process the config file after reading in object files and libraries. This
1731 * includes postprocessing of the config file data but also assigning segments
1732 * and defining segment/memory area related symbols. The function will return
1733 * the number of memory area overflows (so zero means anything went ok).
1734 * In case of overflows, a short mapfile can be generated later, to ease the
1735 * task of rearranging segments for the user.
1738 unsigned Overflows = 0;
1741 /* Do postprocessing of the config file data */
1742 ProcessSymbols (); /* ######## */
1747 /* Walk through each of the memory sections. Add up the sizes and check
1748 * for an overflow of the section. Assign the start addresses of the
1749 * segments while doing this.
1751 for (I = 0; I < CollCount (&MemoryAreas); ++I) {
1755 /* Get this entry */
1756 MemoryArea* M = CollAtUnchecked (&MemoryAreas, I);
1758 /* Get the start address of this memory area */
1759 unsigned long Addr = M->Start;
1761 /* Walk through the segments in this memory area */
1762 for (J = 0; J < CollCount (&M->SegList); ++J) {
1764 /* Get the segment */
1765 SegDesc* S = CollAtUnchecked (&M->SegList, J);
1767 /* Some actions depend on wether this is the load or run memory
1772 /* This is the run (and maybe load) memory area. Handle
1773 * alignment and explict start address and offset.
1775 if (S->Flags & SF_ALIGN) {
1776 /* Align the address */
1777 unsigned long Val = (0x01UL << S->Align) - 1;
1778 Addr = (Addr + Val) & ~Val;
1779 } else if (S->Flags & (SF_OFFSET | SF_START)) {
1780 /* Give the segment a fixed starting address */
1781 unsigned long NewAddr = S->Addr;
1782 if (S->Flags & SF_OFFSET) {
1783 /* An offset was given, no address, make an address */
1784 NewAddr += M->Start;
1786 if (Addr > NewAddr) {
1787 /* Offset already too large */
1788 if (S->Flags & SF_OFFSET) {
1790 "Offset too small in `%s', segment `%s'",
1791 GetString (M->Name),
1792 GetString (S->Name));
1795 "Start address too low in `%s', segment `%s'",
1796 GetString (M->Name),
1797 GetString (S->Name));
1803 /* Set the start address of this segment, set the readonly flag
1804 * in the segment and and remember if the segment is in a
1805 * relocatable file or not.
1808 S->Seg->ReadOnly = (S->Flags & SF_RO) != 0;
1809 S->Seg->Relocatable = M->Relocatable;
1811 /* Remember that this segment is placed */
1814 } else if (S->Load == M) {
1816 /* This is the load memory area, *and* run and load are
1817 * different (because of the "else" above). Handle alignment.
1819 if (S->Flags & SF_ALIGN_LOAD) {
1820 /* Align the address */
1821 unsigned long Val = (0x01UL << S->AlignLoad) - 1;
1822 Addr = (Addr + Val) & ~Val;
1827 /* Increment the fill level of the memory area and check for an
1830 M->FillLevel = Addr + S->Seg->Size - M->Start;
1831 if (M->FillLevel > M->Size && (M->Flags & MF_OVERFLOW) == 0) {
1833 M->Flags |= MF_OVERFLOW;
1834 Warning ("Memory area overflow in `%s', segment `%s' (%lu bytes)",
1835 GetString (M->Name), GetString (S->Name),
1836 M->FillLevel - M->Size);
1839 /* If requested, define symbols for the start and size of the
1842 if (S->Flags & SF_DEFINE) {
1843 if (S->Run == M && (S->Flags & SF_RUN_DEF) == 0) {
1844 CreateRunDefines (S, Addr);
1846 if (S->Load == M && (S->Flags & SF_LOAD_DEF) == 0) {
1847 CreateLoadDefines (S, Addr);
1851 /* Calculate the new address */
1852 Addr += S->Seg->Size;
1856 /* If requested, define symbols for start and size of the memory area */
1857 if (M->Flags & MF_DEFINE) {
1858 StrBuf Buf = STATIC_STRBUF_INITIALIZER;
1859 SB_Printf (&Buf, "__%s_START__", GetString (M->Name));
1860 CreateMemoryExport (GetStrBufId (&Buf), M, 0);
1861 SB_Printf (&Buf, "__%s_SIZE__", GetString (M->Name));
1862 CreateConstExport (GetStrBufId (&Buf), M->Size);
1863 SB_Printf (&Buf, "__%s_LAST__", GetString (M->Name));
1864 CreateMemoryExport (GetStrBufId (&Buf), M, M->FillLevel);
1870 /* Return the number of memory area overflows */
1876 void CfgWriteTarget (void)
1877 /* Write the target file(s) */
1881 /* Walk through the files list */
1882 for (I = 0; I < CollCount (&FileList); ++I) {
1884 /* Get this entry */
1885 File* F = CollAtUnchecked (&FileList, I);
1887 /* We don't need to look at files with no memory areas */
1888 if (CollCount (&F->MemoryAreas) > 0) {
1890 /* Is there an output file? */
1891 if (SB_GetLen (GetStrBuf (F->Name)) > 0) {
1893 /* Assign a proper binary format */
1894 if (F->Format == BINFMT_DEFAULT) {
1895 F->Format = DefaultBinFmt;
1898 /* Call the apropriate routine for the binary format */
1899 switch (F->Format) {
1902 BinWriteTarget (BinFmtDesc, F);
1906 O65WriteTarget (O65FmtDesc, F);
1910 Internal ("Invalid binary format: %u", F->Format);
1916 /* No output file. Walk through the list and mark all segments
1917 * loading into these memory areas in this file as dumped.
1920 for (J = 0; J < CollCount (&F->MemoryAreas); ++J) {
1924 /* Get this entry */
1925 MemoryArea* M = CollAtUnchecked (&F->MemoryAreas, J);
1928 Print (stdout, 2, "Skipping `%s'...\n", GetString (M->Name));
1930 /* Walk throught the segments */
1931 for (K = 0; K < CollCount (&M->SegList); ++K) {
1932 SegDesc* S = CollAtUnchecked (&M->SegList, K);
1934 /* Load area - mark the segment as dumped */