]> git.sur5r.net Git - cc65/blobdiff - src/ld65/main.c
src/ld65/main.c: fix copy'n'paste error in comment
[cc65] / src / ld65 / main.c
index b47831c396a3c18816d1843a31629b4015608485..a3fd81143972b657824a324c25f0573687196471 100644 (file)
 static unsigned         ObjFiles   = 0; /* Count of object files linked */
 static unsigned         LibFiles   = 0; /* Count of library files linked */
 
+/* struct InputFile.Type definitions */
+#define INPUT_FILES_FILE       0        /* Entry is a file (unknown type) */
+#define INPUT_FILES_FILE_OBJ   1        /* Entry is a object file */
+#define INPUT_FILES_FILE_LIB   2        /* Entry is a library file */
+#define INPUT_FILES_SGROUP     3        /* Entry is 'StartGroup' */
+#define INPUT_FILES_EGROUP     4        /* Entry is 'EndGroup' */
+
+#define MAX_INPUTFILES         256
+
+/* Array of inputs (libraries and object files) */
+static struct InputFile {
+    const char *FileName;
+    unsigned Type;
+}                              *InputFiles;
+static unsigned                InputFilesCount = 0;
+static const char              *CmdlineCfgFile = NULL,
+                               *CmdlineTarget = NULL;
+
 
 
 /*****************************************************************************/
@@ -105,28 +123,29 @@ static void Usage (void)
             "  -m name\t\tCreate a map file\n"
             "  -o name\t\tName the default output file\n"
             "  -t sys\t\tSet the target system\n"
-            "  -u sym\t\tForce an import of symbol `sym'\n"
+            "  -u sym\t\tForce an import of symbol 'sym'\n"
             "  -v\t\t\tVerbose mode\n"
             "  -vm\t\t\tVerbose map file\n"
             "\n"
             "Long options:\n"
-            "  --cfg-path path\tSpecify a config file search path\n"
-            "  --config name\t\tUse linker config file\n"
-            "  --dbgfile name\tGenerate debug information\n"
-            "  --define sym=val\tDefine a symbol\n"
-            "  --end-group\t\tEnd a library group\n"
-            "  --force-import sym\tForce an import of symbol `sym'\n"
-            "  --help\t\tHelp (this text)\n"
-            "  --lib file\t\tLink this library\n"
-            "  --lib-path path\tSpecify a library search path\n"
-            "  --mapfile name\tCreate a map file\n"
-            "  --module-id id\tSpecify a module id\n"
-            "  --obj file\t\tLink this object file\n"
-            "  --obj-path path\tSpecify an object file search path\n"
-            "  --start-addr addr\tSet the default start address\n"
-            "  --start-group\t\tStart a library group\n"
-            "  --target sys\t\tSet the target system\n"
-            "  --version\t\tPrint the linker version\n",
+            "  --allow-multiple-definition\tAllow multiple definitions\n"
+            "  --cfg-path path\t\tSpecify a config file search path\n"
+            "  --config name\t\t\tUse linker config file\n"
+            "  --dbgfile name\t\tGenerate debug information\n"
+            "  --define sym=val\t\tDefine a symbol\n"
+            "  --end-group\t\t\tEnd a library group\n"
+            "  --force-import sym\t\tForce an import of symbol 'sym'\n"
+            "  --help\t\t\tHelp (this text)\n"
+            "  --lib file\t\t\tLink this library\n"
+            "  --lib-path path\t\tSpecify a library search path\n"
+            "  --mapfile name\t\tCreate a map file\n"
+            "  --module-id id\t\tSpecify a module id\n"
+            "  --obj file\t\t\tLink this object file\n"
+            "  --obj-path path\t\tSpecify an object file search path\n"
+            "  --start-addr addr\t\tSet the default start address\n"
+            "  --start-group\t\t\tStart a library group\n"
+            "  --target sys\t\t\tSet the target system\n"
+            "  --version\t\t\tPrint the linker version\n",
             ProgName);
 }
 
@@ -134,8 +153,8 @@ static void Usage (void)
 
 static unsigned long CvtNumber (const char* Arg, const char* Number)
 /* Convert a number from a string. Allow '$' and '0x' prefixes for hex
- * numbers.
- */
+** numbers.
+*/
 {
     unsigned long Val;
     int           Converted;
@@ -196,24 +215,24 @@ static void LinkFile (const char* Name, FILETYPE Type)
 
     /* We must have a valid name now */
     if (PathName == 0) {
-        Error ("Input file `%s' not found", Name);
+        Error ("Input file '%s' not found", Name);
     }
 
     /* Try to open the file */
     F = fopen (PathName, "rb");
     if (F == 0) {
-        Error ("Cannot open `%s': %s", PathName, strerror (errno));
+        Error ("Cannot open '%s': %s", PathName, strerror (errno));
     }
 
     /* Read the magic word */
     Magic = Read32 (F);
 
     /* Check the magic for known file types. The handling is somewhat weird
-     * since we may have given a file with a ".lib" extension, which was
-     * searched and found in a directory for library files, but we now find
-     * out (by looking at the magic) that it's indeed an object file. We just
-     * ignore the problem and hope no one will notice...
-     */
+    ** since we may have given a file with a ".lib" extension, which was
+    ** searched and found in a directory for library files, but we now find
+    ** out (by looking at the magic) that it's indeed an object file. We just
+    ** ignore the problem and hope no one will notice...
+    */
     switch (Magic) {
 
         case OBJ_MAGIC:
@@ -228,7 +247,7 @@ static void LinkFile (const char* Name, FILETYPE Type)
 
         default:
             fclose (F);
-            Error ("File `%s' has unknown type", PathName);
+            Error ("File '%s' has unknown type", PathName);
 
     }
 
@@ -304,7 +323,7 @@ static void OptConfig (const char* Opt attribute ((unused)), const char* Arg)
         PathName = SearchFile (CfgDefaultPath, Arg);
     }
     if (PathName == 0) {
-        Error ("Cannot find config file `%s'", Arg);
+        Error ("Cannot find config file '%s'", Arg);
     }
 
     /* Read the config */
@@ -347,8 +366,8 @@ static void OptForceImport (const char* Opt attribute ((unused)), const char* Ar
     if (ColPos == 0) {
 
         /* Use default address size (which for now is always absolute
-         * addressing)
-         */
+        ** addressing)
+        */
         InsertImport (GenImport (GetStringId (Arg), ADDR_SIZE_ABS));
 
     } else {
@@ -358,7 +377,7 @@ static void OptForceImport (const char* Opt attribute ((unused)), const char* Ar
         /* Get the address size and check it */
         unsigned char AddrSize = AddrSizeFromStr (ColPos+1);
         if (AddrSize == ADDR_SIZE_INVALID) {
-            Error ("Invalid address size `%s'", ColPos+1);
+            Error ("Invalid address size '%s'", ColPos+1);
         }
 
         /* Create a copy of the argument */
@@ -390,7 +409,10 @@ static void OptHelp (const char* Opt attribute ((unused)),
 static void OptLib (const char* Opt attribute ((unused)), const char* Arg)
 /* Link a library */
 {
-    LinkFile (Arg, FILETYPE_LIB);
+    InputFiles[InputFilesCount].Type = INPUT_FILES_FILE_LIB;
+    InputFiles[InputFilesCount].FileName = Arg;
+    if (++InputFilesCount >= MAX_INPUTFILES)
+        Error ("Too many input files");
 }
 
 
@@ -406,6 +428,9 @@ static void OptLibPath (const char* Opt attribute ((unused)), const char* Arg)
 static void OptMapFile (const char* Opt attribute ((unused)), const char* Arg)
 /* Give the name of the map file */
 {
+    if (MapFileName) {
+        Error ("Cannot use -m twice");
+    }
     MapFileName = Arg;
 }
 
@@ -426,7 +451,10 @@ static void OptModuleId (const char* Opt, const char* Arg)
 static void OptObj (const char* Opt attribute ((unused)), const char* Arg)
 /* Link an object file */
 {
-    LinkFile (Arg, FILETYPE_OBJ);
+    InputFiles[InputFilesCount].Type = INPUT_FILES_FILE_OBJ;
+    InputFiles[InputFilesCount].FileName = Arg;
+    if (++InputFilesCount >= MAX_INPUTFILES)
+        Error ("Too many input files");
 }
 
 
@@ -439,16 +467,14 @@ static void OptObjPath (const char* Opt attribute ((unused)), const char* Arg)
 
 
 
-static void OptOutputName (const char* Opt, const char* Arg)
+static void OptOutputName (const char* Opt attribute ((unused)), const char* Arg)
 /* Give the name of the output file */
 {
-    /* If the name of the output file has been used in the config before
-     * (by using %O) we're actually changing it later, which - in most cases -
-     * gives unexpected results, so emit a warning in this case.
-     */
-    if (OutputNameUsed) {
-        Warning ("Option `%s' should precede options `-t' or `-C'", Opt);
+    static int OutputNameSeen = 0;
+    if (OutputNameSeen) {
+        Error ("Cannot use -o twice");
     }
+    OutputNameSeen = 1;
     OutputName = Arg;
 }
 
@@ -457,6 +483,9 @@ static void OptOutputName (const char* Opt, const char* Arg)
 static void OptStartAddr (const char* Opt, const char* Arg)
 /* Set the default start address */
 {
+    if (HaveStartAddr) {
+        Error ("Cannot use -S twice");
+    }
     StartAddr = CvtNumber (Opt, Arg);
     HaveStartAddr = 1;
 }
@@ -481,7 +510,7 @@ static void OptTarget (const char* Opt attribute ((unused)), const char* Arg)
     /* Map the target name to a target id */
     Target = FindTarget (Arg);
     if (Target == TGT_UNKNOWN) {
-        Error ("Invalid target name: `%s'", Arg);
+        Error ("Invalid target name: '%s'", Arg);
     }
 
     /* Set the target binary format */
@@ -498,7 +527,7 @@ static void OptTarget (const char* Opt attribute ((unused)), const char* Arg)
         PathName = SearchFile (CfgDefaultPath, SB_GetBuf (&FileName));
     }
     if (PathName == 0) {
-        Error ("Cannot find config file `%s'", SB_GetBuf (&FileName));
+        Error ("Cannot find config file '%s'", SB_GetBuf (&FileName));
     }
 
     /* Free file name memory */
@@ -515,51 +544,98 @@ static void OptVersion (const char* Opt attribute ((unused)),
                         const char* Arg attribute ((unused)))
 /* Print the assembler version */
 {
-    fprintf (stderr, "ld65 V%s\n", GetVersionAsString ());
+    fprintf (stderr, "%s V%s\n", ProgName, GetVersionAsString ());
+    exit(EXIT_SUCCESS);
 }
 
 
 
-int main (int argc, char* argv [])
-/* Assembler main program */
+static void OptMultDef (const char* Opt attribute ((unused)),
+                        const char* Arg attribute ((unused)))
+/* Set flag to allow multiple definitions of a global symbol */
+{
+    AllowMultDef = 1;
+}
+
+
+
+static void CmdlOptStartGroup (const char* Opt attribute ((unused)),
+                               const char* Arg attribute ((unused)))
+/* Remember 'start group' occurrence in input files array */
+{
+    InputFiles[InputFilesCount].Type = INPUT_FILES_SGROUP;
+    InputFiles[InputFilesCount].FileName = Arg;  /* Unused */
+    if (++InputFilesCount >= MAX_INPUTFILES)
+        Error ("Too many input files");
+}
+
+
+
+static void CmdlOptEndGroup (const char* Opt attribute ((unused)),
+                             const char* Arg attribute ((unused)))
+/* Remember 'end group' occurrence in input files array */
+{
+    InputFiles[InputFilesCount].Type = INPUT_FILES_EGROUP;
+    InputFiles[InputFilesCount].FileName = Arg;  /* Unused */
+    if (++InputFilesCount >= MAX_INPUTFILES)
+        Error ("Too many input files");
+}
+
+
+
+static void CmdlOptConfig (const char* Opt attribute ((unused)), const char* Arg)
+/* Set 'config file' command line parameter */
+{
+    if (CmdlineCfgFile || CmdlineTarget) {
+        Error ("Cannot use -C/-t twice");
+    }
+    CmdlineCfgFile = Arg;
+}
+
+
+
+static void CmdlOptTarget (const char* Opt attribute ((unused)), const char* Arg)
+/* Set 'target' command line parameter */
+{
+    if (CmdlineCfgFile || CmdlineTarget) {
+        Error ("Cannot use -C/-t twice");
+    }
+    CmdlineTarget = Arg;
+}
+
+
+
+static void ParseCommandLine(void)
 {
     /* Program long options */
     static const LongOpt OptTab[] = {
-        { "--cfg-path",         1,      OptCfgPath              },
-        { "--config",           1,      OptConfig               },
-        { "--dbgfile",          1,      OptDbgFile              },
-        { "--define",           1,      OptDefine               },
-        { "--end-group",        0,      OptEndGroup             },
-        { "--force-import",     1,      OptForceImport          },
-        { "--help",             0,      OptHelp                 },
-        { "--lib",              1,      OptLib                  },
-        { "--lib-path",         1,      OptLibPath              },
-        { "--mapfile",          1,      OptMapFile              },
-        { "--module-id",        1,      OptModuleId             },
-        { "--obj",              1,      OptObj                  },
-        { "--obj-path",         1,      OptObjPath              },
-        { "--start-addr",       1,      OptStartAddr            },
-        { "--start-group",      0,      OptStartGroup           },
-        { "--target",           1,      OptTarget               },
-        { "--version",          0,      OptVersion              },
+        { "--allow-multiple-definition", 0,      OptMultDef              },
+        { "--cfg-path",                  1,      OptCfgPath              },
+        { "--config",                    1,      CmdlOptConfig           },
+        { "--dbgfile",                   1,      OptDbgFile              },
+        { "--define",                    1,      OptDefine               },
+        { "--end-group",                 0,      CmdlOptEndGroup         },
+        { "--force-import",              1,      OptForceImport          },
+        { "--help",                      0,      OptHelp                 },
+        { "--lib",                       1,      OptLib                  },
+        { "--lib-path",                  1,      OptLibPath              },
+        { "--mapfile",                   1,      OptMapFile              },
+        { "--module-id",                 1,      OptModuleId             },
+        { "--obj",                       1,      OptObj                  },
+        { "--obj-path",                  1,      OptObjPath              },
+        { "--start-addr",                1,      OptStartAddr            },
+        { "--start-group",               0,      CmdlOptStartGroup       },
+        { "--target",                    1,      CmdlOptTarget           },
+        { "--version",                   0,      OptVersion              },
     };
 
     unsigned I;
-    unsigned MemoryAreaOverflows;
+    unsigned LabelFileGiven = 0;
 
-    /* Initialize the cmdline module */
-    InitCmdLine (&argc, &argv, "ld65");
-
-    /* Initialize the input file search paths */
-    InitSearchPaths ();
+    /* Allocate memory for input file array */
+    InputFiles = xmalloc (MAX_INPUTFILES * sizeof (struct InputFile));
 
-    /* Initialize the string pool */
-    InitStrPool ();
-
-    /* Initialize the type pool */
-    InitTypePool ();
-
-    /* Check the parameters */
+    /* Defer setting of config/target and input files until all options are parsed */
     I = 1;
     while (I < ArgCount) {
 
@@ -577,11 +653,11 @@ int main (int argc, char* argv [])
                     break;
 
                 case '(':
-                    OptStartGroup (Arg, 0);
+                    CmdlOptStartGroup (Arg, 0);
                     break;
 
                 case ')':
-                    OptEndGroup (Arg, 0);
+                    CmdlOptEndGroup (Arg, 0);
                     break;
 
                 case 'h':
@@ -594,14 +670,11 @@ int main (int argc, char* argv [])
                     break;
 
                 case 'o':
-                    OptOutputName (Arg, GetArg (&I, 2));
+                    OptOutputName (NULL, GetArg (&I, 2));
                     break;
 
                 case 't':
-                    if (CfgAvail ()) {
-                        Error ("Cannot use -C/-t twice");
-                    }
-                    OptTarget (Arg, GetArg (&I, 2));
+                    CmdlOptTarget (Arg, GetArg (&I, 2));
                     break;
 
                 case 'u':
@@ -617,7 +690,7 @@ int main (int argc, char* argv [])
                     break;
 
                 case 'C':
-                    OptConfig (Arg, GetArg (&I, 2));
+                    CmdlOptConfig (Arg, GetArg (&I, 2));
                     break;
 
                 case 'D':
@@ -626,9 +699,17 @@ int main (int argc, char* argv [])
 
                 case 'L':
                     switch (Arg [2]) {
-                        /* ## The first one is obsolete and will go */
-                        case 'n': LabelFileName = GetArg (&I, 3);   break;
-                        default:  OptLibPath (Arg, GetArg (&I, 2)); break;
+                        case 'n':
+                            /* ## This one is obsolete and will go */
+                            if (LabelFileGiven) {
+                                Error ("Cannot use -Ln twice");
+                            }
+                            LabelFileGiven = 1;
+                            LabelFileName = GetArg (&I, 3);
+                            break;
+                        default:
+                            OptLibPath (Arg, GetArg (&I, 2));
+                            break;
                     }
                     break;
 
@@ -648,7 +729,10 @@ int main (int argc, char* argv [])
         } else {
 
             /* A filename */
-            LinkFile (Arg, FILETYPE_UNKNOWN);
+            InputFiles[InputFilesCount].Type = INPUT_FILES_FILE;
+            InputFiles[InputFilesCount].FileName = Arg;
+            if (++InputFilesCount >= MAX_INPUTFILES)
+                Error ("Too many input files");
 
         }
 
@@ -656,6 +740,61 @@ int main (int argc, char* argv [])
         ++I;
     }
 
+    if (CmdlineTarget) {
+        OptTarget (NULL, CmdlineTarget);
+    } else if (CmdlineCfgFile) {
+        OptConfig (NULL, CmdlineCfgFile);
+    }
+
+    /* Process input files */
+    for (I = 0; I < InputFilesCount; ++I) {
+        switch (InputFiles[I].Type) {
+            case INPUT_FILES_FILE:
+                LinkFile (InputFiles[I].FileName, FILETYPE_UNKNOWN);
+                break;
+            case INPUT_FILES_FILE_LIB:
+                LinkFile (InputFiles[I].FileName, FILETYPE_LIB);
+                break;
+            case INPUT_FILES_FILE_OBJ:
+                LinkFile (InputFiles[I].FileName, FILETYPE_OBJ);
+                break;
+            case INPUT_FILES_SGROUP:
+                OptStartGroup (NULL, 0);
+                break;
+            case INPUT_FILES_EGROUP:
+                OptEndGroup (NULL, 0);
+                break;
+            default:
+                abort ();
+        }
+    }
+
+    /* Free memory used for input file array */
+    xfree (InputFiles);
+}
+
+
+
+int main (int argc, char* argv [])
+/* Linker main program */
+{
+    unsigned MemoryAreaOverflows;
+
+    /* Initialize the cmdline module */
+    InitCmdLine (&argc, &argv, "ld65");
+
+    /* Initialize the input file search paths */
+    InitSearchPaths ();
+
+    /* Initialize the string pool */
+    InitStrPool ();
+
+    /* Initialize the type pool */
+    InitTypePool ();
+
+    /* Parse the command line */
+    ParseCommandLine ();
+
     /* Check if we had any object files */
     if (ObjFiles == 0) {
         Error ("No object files to link");
@@ -673,9 +812,9 @@ int main (int argc, char* argv [])
     ConDesCreate ();
 
     /* Process data from the config file. Assign start addresses for the
-     * segments, define linker symbols. The function will return the number
-     * of memory area overflows (zero on success).
-     */
+    ** segments, define linker symbols. The function will return the number
+    ** of memory area overflows (zero on success).
+    */
     MemoryAreaOverflows = CfgProcess ();
 
     /* Check module assertions */
@@ -685,15 +824,15 @@ int main (int argc, char* argv [])
     CheckExports ();
 
     /* If we had a memory area overflow before, we cannot generate the output
-     * file. However, we will generate a short map file if requested, since
-     * this will help the user to rearrange segments and fix the overflow.
-     */
+    ** file. However, we will generate a short map file if requested, since
+    ** this will help the user to rearrange segments and fix the overflow.
+    */
     if (MemoryAreaOverflows) {
         if (MapFileName) {
             CreateMapFile (SHORT_MAPFILE);
         }
-        Error ("Cannot generate output due to memory area overflow%s",
-               (MemoryAreaOverflows > 1)? "s" : "");
+        Error ("Cannot generate most of the files due to memory area overflow%c",
+               (MemoryAreaOverflows > 1) ? 's' : ' ');
     }
 
     /* Create the output file */