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, "%s V%s\n", ProgName, GetVersionAsString ());
552 static void CmdlOptStartGroup (const char* Opt attribute ((unused)),
553 const char* Arg attribute ((unused)))
554 /* Remember 'start group' occurrence in input files array */
556 InputFiles[InputFilesCount].Type = INPUT_FILES_SGROUP;
557 InputFiles[InputFilesCount].FileName = Arg; /* Unused */
558 if (++InputFilesCount >= MAX_INPUTFILES)
559 Error ("Too many input files");
564 static void CmdlOptEndGroup (const char* Opt attribute ((unused)),
565 const char* Arg attribute ((unused)))
566 /* Remember 'end group' occurrence in input files array */
568 InputFiles[InputFilesCount].Type = INPUT_FILES_EGROUP;
569 InputFiles[InputFilesCount].FileName = Arg; /* Unused */
570 if (++InputFilesCount >= MAX_INPUTFILES)
571 Error ("Too many input files");
576 static void CmdlOptConfig (const char* Opt attribute ((unused)), const char* Arg)
577 /* Set 'config file' command line parameter */
579 if (CmdlineCfgFile || CmdlineTarget) {
580 Error ("Cannot use -C/-t twice");
582 CmdlineCfgFile = Arg;
587 static void CmdlOptTarget (const char* Opt attribute ((unused)), const char* Arg)
588 /* Set 'target' command line parameter */
590 if (CmdlineCfgFile || CmdlineTarget) {
591 Error ("Cannot use -C/-t twice");
598 static void ParseCommandLine(void)
600 /* Program long options */
601 static const LongOpt OptTab[] = {
602 { "--cfg-path", 1, OptCfgPath },
603 { "--config", 1, CmdlOptConfig },
604 { "--dbgfile", 1, OptDbgFile },
605 { "--define", 1, OptDefine },
606 { "--end-group", 0, CmdlOptEndGroup },
607 { "--force-import", 1, OptForceImport },
608 { "--help", 0, OptHelp },
609 { "--lib", 1, OptLib },
610 { "--lib-path", 1, OptLibPath },
611 { "--mapfile", 1, OptMapFile },
612 { "--module-id", 1, OptModuleId },
613 { "--obj", 1, OptObj },
614 { "--obj-path", 1, OptObjPath },
615 { "--start-addr", 1, OptStartAddr },
616 { "--start-group", 0, CmdlOptStartGroup },
617 { "--target", 1, CmdlOptTarget },
618 { "--version", 0, OptVersion },
622 unsigned LabelFileGiven = 0;
624 /* Allocate memory for input file array */
625 InputFiles = xmalloc (MAX_INPUTFILES * sizeof (struct InputFile));
627 /* Defer setting of config/target and input files until all options are parsed */
629 while (I < ArgCount) {
631 /* Get the argument */
632 const char* Arg = ArgVec[I];
634 /* Check for an option */
635 if (Arg [0] == '-') {
641 LongOption (&I, OptTab, sizeof(OptTab)/sizeof(OptTab[0]));
645 CmdlOptStartGroup (Arg, 0);
649 CmdlOptEndGroup (Arg, 0);
658 OptMapFile (Arg, GetArg (&I, 2));
662 OptOutputName (NULL, GetArg (&I, 2));
666 CmdlOptTarget (Arg, GetArg (&I, 2));
670 OptForceImport (Arg, GetArg (&I, 2));
675 case 'm': VerboseMap = 1; break;
676 case '\0': ++Verbosity; break;
677 default: UnknownOption (Arg);
682 CmdlOptConfig (Arg, GetArg (&I, 2));
686 OptDefine (Arg, GetArg (&I, 2));
692 /* ## This one is obsolete and will go */
693 if (LabelFileGiven) {
694 Error ("Cannot use -Ln twice");
697 LabelFileName = GetArg (&I, 3);
700 OptLibPath (Arg, GetArg (&I, 2));
706 OptStartAddr (Arg, GetArg (&I, 2));
721 InputFiles[InputFilesCount].Type = INPUT_FILES_FILE;
722 InputFiles[InputFilesCount].FileName = Arg;
723 if (++InputFilesCount >= MAX_INPUTFILES)
724 Error ("Too many input files");
733 OptTarget (NULL, CmdlineTarget);
734 } else if (CmdlineCfgFile) {
735 OptConfig (NULL, CmdlineCfgFile);
738 /* Process input files */
739 for (I = 0; I < InputFilesCount; ++I) {
740 switch (InputFiles[I].Type) {
741 case INPUT_FILES_FILE:
742 LinkFile (InputFiles[I].FileName, FILETYPE_UNKNOWN);
744 case INPUT_FILES_FILE_LIB:
745 LinkFile (InputFiles[I].FileName, FILETYPE_LIB);
747 case INPUT_FILES_FILE_OBJ:
748 LinkFile (InputFiles[I].FileName, FILETYPE_OBJ);
750 case INPUT_FILES_SGROUP:
751 OptStartGroup (NULL, 0);
753 case INPUT_FILES_EGROUP:
754 OptEndGroup (NULL, 0);
761 /* Free memory used for input file array */
767 int main (int argc, char* argv [])
768 /* Linker main program */
770 unsigned MemoryAreaOverflows;
772 /* Initialize the cmdline module */
773 InitCmdLine (&argc, &argv, "ld65");
775 /* Initialize the input file search paths */
778 /* Initialize the string pool */
781 /* Initialize the type pool */
784 /* Parse the command line */
787 /* Check if we had any object files */
789 Error ("No object files to link");
792 /* Check if we have a valid configuration */
794 Error ("Memory configuration missing");
797 /* Check if we have open library groups */
800 /* Create the condes tables if requested */
803 /* Process data from the config file. Assign start addresses for the
804 ** segments, define linker symbols. The function will return the number
805 ** of memory area overflows (zero on success).
807 MemoryAreaOverflows = CfgProcess ();
809 /* Check module assertions */
812 /* Check for import/export mismatches */
815 /* If we had a memory area overflow before, we cannot generate the output
816 ** file. However, we will generate a short map file if requested, since
817 ** this will help the user to rearrange segments and fix the overflow.
819 if (MemoryAreaOverflows) {
821 CreateMapFile (SHORT_MAPFILE);
823 Error ("Cannot generate most of the files due to memory area overflow%c",
824 (MemoryAreaOverflows > 1) ? 's' : ' ');
827 /* Create the output file */
830 /* Check for segments not written to the output file */
833 /* If requested, create a map file and a label file for VICE */
835 CreateMapFile (LONG_MAPFILE);
844 /* Dump the data for debugging */
850 /* Return an apropriate exit code */