1 /*****************************************************************************/
5 /* Main program for the ld65 linker */
9 /* (C) 1998-2013, 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 /*****************************************************************************/
74 /*****************************************************************************/
76 /*****************************************************************************/
80 static unsigned ObjFiles = 0; /* Count of object files linked */
81 static unsigned LibFiles = 0; /* Count of library files linked */
83 /* struct InputFile.Type definitions */
84 #define INPUT_FILES_FILE 0 /* Entry is a file (unknown type) */
85 #define INPUT_FILES_FILE_OBJ 1 /* Entry is a object file */
86 #define INPUT_FILES_FILE_LIB 2 /* Entry is a library file */
87 #define INPUT_FILES_SGROUP 3 /* Entry is 'StartGroup' */
88 #define INPUT_FILES_EGROUP 4 /* Entry is 'EndGroup' */
90 #define MAX_INPUTFILES 256
92 /* Array of inputs (libraries and object files) */
93 static struct InputFile {
97 static unsigned InputFilesCount = 0;
98 static const char *CmdlineCfgFile = NULL,
99 *CmdlineTarget = NULL;
103 /*****************************************************************************/
105 /*****************************************************************************/
109 static void Usage (void)
110 /* Print usage information and exit */
112 printf ("Usage: %s [options] module ...\n"
114 " -(\t\t\tStart a library group\n"
115 " -)\t\t\tEnd a library group\n"
116 " -C name\t\tUse linker config file\n"
117 " -D sym=val\t\tDefine a symbol\n"
118 " -L path\t\tSpecify a library search path\n"
119 " -Ln name\t\tCreate a VICE label file\n"
120 " -S addr\t\tSet the default start address\n"
121 " -V\t\t\tPrint the linker version\n"
122 " -h\t\t\tHelp (this text)\n"
123 " -m name\t\tCreate a map file\n"
124 " -o name\t\tName the default output file\n"
125 " -t sys\t\tSet the target system\n"
126 " -u sym\t\tForce an import of symbol `sym'\n"
127 " -v\t\t\tVerbose mode\n"
128 " -vm\t\t\tVerbose map file\n"
131 " --cfg-path path\tSpecify a config file search path\n"
132 " --config name\t\tUse linker config file\n"
133 " --dbgfile name\tGenerate debug information\n"
134 " --define sym=val\tDefine a symbol\n"
135 " --end-group\t\tEnd a library group\n"
136 " --force-import sym\tForce an import of symbol `sym'\n"
137 " --help\t\tHelp (this text)\n"
138 " --lib file\t\tLink this library\n"
139 " --lib-path path\tSpecify a library search path\n"
140 " --mapfile name\tCreate a map file\n"
141 " --module-id id\tSpecify a module id\n"
142 " --obj file\t\tLink this object file\n"
143 " --obj-path path\tSpecify an object file search path\n"
144 " --start-addr addr\tSet the default start address\n"
145 " --start-group\t\tStart a library group\n"
146 " --target sys\t\tSet the target system\n"
147 " --version\t\tPrint the linker version\n",
153 static unsigned long CvtNumber (const char* Arg, const char* Number)
154 /* Convert a number from a string. Allow '$' and '0x' prefixes for hex
162 if (*Number == '$') {
164 Converted = sscanf (Number, "%lx", &Val);
166 Converted = sscanf (Number, "%li", (long*)&Val);
169 /* Check if we do really have a number */
170 if (Converted != 1) {
171 Error ("Invalid number given in argument: %s\n", Arg);
174 /* Return the result */
180 static void LinkFile (const char* Name, FILETYPE Type)
181 /* Handle one file */
188 /* If we don't know the file type, determine it from the extension */
189 if (Type == FILETYPE_UNKNOWN) {
190 Type = GetFileType (Name);
193 /* For known file types, search the file in the directory list */
197 PathName = SearchFile (LibSearchPath, Name);
199 PathName = SearchFile (LibDefaultPath, Name);
204 PathName = SearchFile (ObjSearchPath, Name);
206 PathName = SearchFile (ObjDefaultPath, Name);
211 PathName = xstrdup (Name); /* Use the name as is */
215 /* We must have a valid name now */
217 Error ("Input file `%s' not found", Name);
220 /* Try to open the file */
221 F = fopen (PathName, "rb");
223 Error ("Cannot open `%s': %s", PathName, strerror (errno));
226 /* Read the magic word */
229 /* Check the magic for known file types. The handling is somewhat weird
230 ** since we may have given a file with a ".lib" extension, which was
231 ** searched and found in a directory for library files, but we now find
232 ** out (by looking at the magic) that it's indeed an object file. We just
233 ** ignore the problem and hope no one will notice...
238 ObjAdd (F, PathName);
243 LibAdd (F, PathName);
249 Error ("File `%s' has unknown type", PathName);
253 /* Free allocated memory. */
259 static void DefineSymbol (const char* Def)
260 /* Define a symbol from the command line */
264 StrBuf SymName = AUTO_STRBUF_INITIALIZER;
267 /* The symbol must start with a character or underline */
268 if (Def [0] != '_' && !IsAlpha (Def [0])) {
273 /* Copy the symbol, checking the remainder */
274 while (IsAlNum (*P) || *P == '_') {
275 SB_AppendChar (&SymName, *P++);
277 SB_Terminate (&SymName);
279 /* Do we have a value given? */
283 /* We have a value */
287 if (sscanf (P, "%lx", &Val) != 1) {
291 if (sscanf (P, "%li", &Val) != 1) {
297 /* Define the new symbol */
298 CreateConstExport (GetStringId (SB_GetConstBuf (&SymName)), Val);
303 static void OptCfgPath (const char* Opt attribute ((unused)), const char* Arg)
304 /* Specify a config file search path */
306 AddSearchPath (CfgSearchPath, Arg);
311 static void OptConfig (const char* Opt attribute ((unused)), const char* Arg)
312 /* Define the config file */
317 Error ("Cannot use -C/-t twice");
319 /* Search for the file */
320 PathName = SearchFile (CfgSearchPath, Arg);
322 PathName = SearchFile (CfgDefaultPath, Arg);
325 Error ("Cannot find config file `%s'", Arg);
328 /* Read the config */
329 CfgSetName (PathName);
335 static void OptDbgFile (const char* Opt attribute ((unused)), const char* Arg)
336 /* Give the name of the debug file */
343 static void OptDefine (const char* Opt attribute ((unused)), const char* Arg)
344 /* Define a symbol on the command line */
351 static void OptEndGroup (const char* Opt attribute ((unused)),
352 const char* Arg attribute ((unused)))
353 /* End a library group */
360 static void OptForceImport (const char* Opt attribute ((unused)), const char* Arg)
361 /* Force an import of a symbol */
363 /* An optional address size may be specified */
364 const char* ColPos = strchr (Arg, ':');
367 /* Use default address size (which for now is always absolute
370 InsertImport (GenImport (GetStringId (Arg), ADDR_SIZE_ABS));
376 /* Get the address size and check it */
377 unsigned char AddrSize = AddrSizeFromStr (ColPos+1);
378 if (AddrSize == ADDR_SIZE_INVALID) {
379 Error ("Invalid address size `%s'", ColPos+1);
382 /* Create a copy of the argument */
385 /* We need just the symbol */
386 A[ColPos - Arg] = '\0';
388 /* Generate the import */
389 InsertImport (GenImport (GetStringId (A), AddrSize));
391 /* Delete the copy of the argument */
398 static void OptHelp (const char* Opt attribute ((unused)),
399 const char* Arg attribute ((unused)))
400 /* Print usage information and exit */
408 static void OptLib (const char* Opt attribute ((unused)), const char* Arg)
411 InputFiles[InputFilesCount].Type = INPUT_FILES_FILE_LIB;
412 InputFiles[InputFilesCount].FileName = Arg;
413 if (++InputFilesCount >= MAX_INPUTFILES)
414 Error ("Too many input files");
419 static void OptLibPath (const char* Opt attribute ((unused)), const char* Arg)
420 /* Specify a library file search path */
422 AddSearchPath (LibSearchPath, Arg);
427 static void OptMapFile (const char* Opt attribute ((unused)), const char* Arg)
428 /* Give the name of the map file */
431 Error ("Cannot use -m twice");
438 static void OptModuleId (const char* Opt, const char* Arg)
439 /* Specify a module id */
441 unsigned long Id = CvtNumber (Opt, Arg);
443 Error ("Range error in module id");
445 ModuleId = (unsigned) Id;
450 static void OptObj (const char* Opt attribute ((unused)), const char* Arg)
451 /* Link an object file */
453 InputFiles[InputFilesCount].Type = INPUT_FILES_FILE_OBJ;
454 InputFiles[InputFilesCount].FileName = Arg;
455 if (++InputFilesCount >= MAX_INPUTFILES)
456 Error ("Too many input files");
461 static void OptObjPath (const char* Opt attribute ((unused)), const char* Arg)
462 /* Specify an object file search path */
464 AddSearchPath (ObjSearchPath, Arg);
469 static void OptOutputName (const char* Opt attribute ((unused)), const char* Arg)
470 /* Give the name of the output file */
472 static int OutputNameSeen = 0;
473 if (OutputNameSeen) {
474 Error ("Cannot use -o twice");
482 static void OptStartAddr (const char* Opt, const char* Arg)
483 /* Set the default start address */
486 Error ("Cannot use -S twice");
488 StartAddr = CvtNumber (Opt, Arg);
494 static void OptStartGroup (const char* Opt attribute ((unused)),
495 const char* Arg attribute ((unused)))
496 /* Start a library group */
503 static void OptTarget (const char* Opt attribute ((unused)), const char* Arg)
504 /* Set the target system */
506 StrBuf FileName = STATIC_STRBUF_INITIALIZER;
509 /* Map the target name to a target id */
510 Target = FindTarget (Arg);
511 if (Target == TGT_UNKNOWN) {
512 Error ("Invalid target name: `%s'", Arg);
515 /* Set the target binary format */
516 DefaultBinFmt = GetTargetProperties (Target)->BinFmt;
518 /* Build config file name from target name */
519 SB_CopyStr (&FileName, GetTargetName (Target));
520 SB_AppendStr (&FileName, ".cfg");
521 SB_Terminate (&FileName);
523 /* Search for the file */
524 PathName = SearchFile (CfgSearchPath, SB_GetBuf (&FileName));
526 PathName = SearchFile (CfgDefaultPath, SB_GetBuf (&FileName));
529 Error ("Cannot find config file `%s'", SB_GetBuf (&FileName));
532 /* Free file name memory */
536 CfgSetName (PathName);
542 static void OptVersion (const char* Opt attribute ((unused)),
543 const char* Arg attribute ((unused)))
544 /* Print the assembler version */
546 fprintf (stderr, "ld65 V%s\n", GetVersionAsString ());
551 static void CmdlOptStartGroup (const char* Opt attribute ((unused)),
552 const char* Arg attribute ((unused)))
553 /* Remember 'start group' occurrence in input files array */
555 InputFiles[InputFilesCount].Type = INPUT_FILES_SGROUP;
556 InputFiles[InputFilesCount].FileName = Arg; /* Unused */
557 if (++InputFilesCount >= MAX_INPUTFILES)
558 Error ("Too many input files");
563 static void CmdlOptEndGroup (const char* Opt attribute ((unused)),
564 const char* Arg attribute ((unused)))
565 /* Remember 'end group' occurrence in input files array */
567 InputFiles[InputFilesCount].Type = INPUT_FILES_EGROUP;
568 InputFiles[InputFilesCount].FileName = Arg; /* Unused */
569 if (++InputFilesCount >= MAX_INPUTFILES)
570 Error ("Too many input files");
575 static void CmdlOptConfig (const char* Opt attribute ((unused)), const char* Arg)
576 /* Set 'config file' command line parameter */
578 if (CmdlineCfgFile || CmdlineTarget) {
579 Error ("Cannot use -C/-t twice");
581 CmdlineCfgFile = Arg;
586 static void CmdlOptTarget (const char* Opt attribute ((unused)), const char* Arg)
587 /* Set 'target' command line parameter */
589 if (CmdlineCfgFile || CmdlineTarget) {
590 Error ("Cannot use -C/-t twice");
597 static void ParseCommandLine(void)
599 /* Program long options */
600 static const LongOpt OptTab[] = {
601 { "--cfg-path", 1, OptCfgPath },
602 { "--config", 1, CmdlOptConfig },
603 { "--dbgfile", 1, OptDbgFile },
604 { "--define", 1, OptDefine },
605 { "--end-group", 0, CmdlOptEndGroup },
606 { "--force-import", 1, OptForceImport },
607 { "--help", 0, OptHelp },
608 { "--lib", 1, OptLib },
609 { "--lib-path", 1, OptLibPath },
610 { "--mapfile", 1, OptMapFile },
611 { "--module-id", 1, OptModuleId },
612 { "--obj", 1, OptObj },
613 { "--obj-path", 1, OptObjPath },
614 { "--start-addr", 1, OptStartAddr },
615 { "--start-group", 0, CmdlOptStartGroup },
616 { "--target", 1, CmdlOptTarget },
617 { "--version", 0, OptVersion },
621 unsigned LabelFileGiven = 0;
623 /* Allocate memory for input file array */
624 InputFiles = xmalloc (MAX_INPUTFILES * sizeof (struct InputFile));
626 /* Defer setting of config/target and input files until all options are parsed */
628 while (I < ArgCount) {
630 /* Get the argument */
631 const char* Arg = ArgVec[I];
633 /* Check for an option */
634 if (Arg [0] == '-') {
640 LongOption (&I, OptTab, sizeof(OptTab)/sizeof(OptTab[0]));
644 CmdlOptStartGroup (Arg, 0);
648 CmdlOptEndGroup (Arg, 0);
657 OptMapFile (Arg, GetArg (&I, 2));
661 OptOutputName (NULL, GetArg (&I, 2));
665 CmdlOptTarget (Arg, GetArg (&I, 2));
669 OptForceImport (Arg, GetArg (&I, 2));
674 case 'm': VerboseMap = 1; break;
675 case '\0': ++Verbosity; break;
676 default: UnknownOption (Arg);
681 CmdlOptConfig (Arg, GetArg (&I, 2));
685 OptDefine (Arg, GetArg (&I, 2));
691 /* ## This one is obsolete and will go */
692 if (LabelFileGiven) {
693 Error ("Cannot use -Ln twice");
696 LabelFileName = GetArg (&I, 3);
699 OptLibPath (Arg, GetArg (&I, 2));
705 OptStartAddr (Arg, GetArg (&I, 2));
720 InputFiles[InputFilesCount].Type = INPUT_FILES_FILE;
721 InputFiles[InputFilesCount].FileName = Arg;
722 if (++InputFilesCount >= MAX_INPUTFILES)
723 Error ("Too many input files");
732 OptTarget (NULL, CmdlineTarget);
733 } else if (CmdlineCfgFile) {
734 OptConfig (NULL, CmdlineCfgFile);
737 /* Process input files */
738 for (I = 0; I < InputFilesCount; ++I) {
739 switch (InputFiles[I].Type) {
740 case INPUT_FILES_FILE:
741 LinkFile (InputFiles[I].FileName, FILETYPE_UNKNOWN);
743 case INPUT_FILES_FILE_LIB:
744 LinkFile (InputFiles[I].FileName, FILETYPE_LIB);
746 case INPUT_FILES_FILE_OBJ:
747 LinkFile (InputFiles[I].FileName, FILETYPE_OBJ);
749 case INPUT_FILES_SGROUP:
750 OptStartGroup (NULL, 0);
752 case INPUT_FILES_EGROUP:
753 OptEndGroup (NULL, 0);
760 /* Free memory used for input file array */
766 int main (int argc, char* argv [])
767 /* Linker main program */
769 unsigned MemoryAreaOverflows;
771 /* Initialize the cmdline module */
772 InitCmdLine (&argc, &argv, "ld65");
774 /* Initialize the input file search paths */
777 /* Initialize the string pool */
780 /* Initialize the type pool */
783 /* Parse the command line */
786 /* Check if we had any object files */
788 Error ("No object files to link");
791 /* Check if we have a valid configuration */
793 Error ("Memory configuration missing");
796 /* Check if we have open library groups */
799 /* Create the condes tables if requested */
802 /* Process data from the config file. Assign start addresses for the
803 ** segments, define linker symbols. The function will return the number
804 ** of memory area overflows (zero on success).
806 MemoryAreaOverflows = CfgProcess ();
808 /* Check module assertions */
811 /* Check for import/export mismatches */
814 /* If we had a memory area overflow before, we cannot generate the output
815 ** file. However, we will generate a short map file if requested, since
816 ** this will help the user to rearrange segments and fix the overflow.
818 if (MemoryAreaOverflows) {
820 CreateMapFile (SHORT_MAPFILE);
822 Error ("Cannot generate output due to memory area overflow%s",
823 (MemoryAreaOverflows > 1)? "s" : "");
826 /* Create the output file */
829 /* Check for segments not written to the output file */
832 /* If requested, create a map file and a label file for VICE */
834 CreateMapFile (LONG_MAPFILE);
843 /* Dump the data for debugging */
849 /* Return an apropriate exit code */