1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* main.c */ |
4 | /* */ |
5 | /* Main program for the ld65 linker */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 1998-2013, Ullrich von Bassewitz */ |
10 | /* Roemerstrasse 52 */ |
11 | /* D-70794 Filderstadt */ |
12 | /* EMail: uz@cc65.org */ |
13 | /* */ |
14 | /* */ |
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. */ |
18 | /* */ |
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: */ |
22 | /* */ |
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 */ |
30 | /* distribution. */ |
31 | /* */ |
32 | /*****************************************************************************/ |
33 | |
34 | |
35 | |
36 | #include <stdio.h> |
37 | #include <stdlib.h> |
38 | #include <string.h> |
39 | #include <errno.h> |
40 | |
41 | /* common */ |
42 | #include "addrsize.h" |
43 | #include "chartype.h" |
44 | #include "cmdline.h" |
45 | #include "filetype.h" |
46 | #include "libdefs.h" |
47 | #include "objdefs.h" |
48 | #include "print.h" |
49 | #include "target.h" |
50 | #include "version.h" |
51 | #include "xmalloc.h" |
52 | |
53 | /* ld65 */ |
54 | #include "asserts.h" |
55 | #include "binfmt.h" |
56 | #include "condes.h" |
57 | #include "config.h" |
58 | #include "dbgfile.h" |
59 | #include "error.h" |
60 | #include "exports.h" |
61 | #include "fileio.h" |
62 | #include "filepath.h" |
63 | #include "global.h" |
64 | #include "library.h" |
65 | #include "mapfile.h" |
66 | #include "objfile.h" |
67 | #include "scanner.h" |
68 | #include "segments.h" |
69 | #include "spool.h" |
70 | #include "tpool.h" |
71 | |
72 | |
73 | |
74 | /*****************************************************************************/ |
75 | /* Data */ |
76 | /*****************************************************************************/ |
77 | |
78 | |
79 | |
80 | static unsigned ObjFiles = 0; /* Count of object files linked */ |
81 | static unsigned LibFiles = 0; /* Count of library files linked */ |
82 | |
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' */ |
89 | |
90 | #define MAX_INPUTFILES 256 |
91 | |
92 | /* Array of inputs (libraries and object files) */ |
93 | static struct InputFile { |
94 | const char *FileName; |
95 | unsigned Type; |
96 | } *InputFiles; |
97 | static unsigned InputFilesCount = 0; |
98 | static const char *CmdlineCfgFile = NULL, |
99 | *CmdlineTarget = NULL; |
100 | |
101 | |
102 | |
103 | /*****************************************************************************/ |
104 | /* Code */ |
105 | /*****************************************************************************/ |
106 | |
107 | |
108 | |
109 | static void Usage (void) |
110 | /* Print usage information and exit */ |
111 | { |
112 | printf ("Usage: %s [options] module ...\n" |
113 | "Short options:\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" |
129 | "\n" |
130 | "Long options:\n" |
131 | " --allow-multiple-definition\tAllow multiple definitions\n" |
132 | " --cfg-path path\t\tSpecify a config file search path\n" |
133 | " --config name\t\t\tUse linker config file\n" |
134 | " --dbgfile name\t\tGenerate debug information\n" |
135 | " --define sym=val\t\tDefine a symbol\n" |
136 | " --end-group\t\t\tEnd a library group\n" |
137 | " --force-import sym\t\tForce an import of symbol 'sym'\n" |
138 | " --help\t\t\tHelp (this text)\n" |
139 | " --lib file\t\t\tLink this library\n" |
140 | " --lib-path path\t\tSpecify a library search path\n" |
141 | " --mapfile name\t\tCreate a map file\n" |
142 | " --module-id id\t\tSpecify a module id\n" |
143 | " --obj file\t\t\tLink this object file\n" |
144 | " --obj-path path\t\tSpecify an object file search path\n" |
145 | " --start-addr addr\t\tSet the default start address\n" |
146 | " --start-group\t\t\tStart a library group\n" |
147 | " --target sys\t\t\tSet the target system\n" |
148 | " --version\t\t\tPrint the linker version\n" , |
149 | ProgName); |
150 | } |
151 | |
152 | |
153 | |
154 | static unsigned long CvtNumber (const char* Arg, const char* Number) |
155 | /* Convert a number from a string. Allow '$' and '0x' prefixes for hex |
156 | ** numbers. |
157 | */ |
158 | { |
159 | unsigned long Val; |
160 | int Converted; |
161 | |
162 | /* Convert */ |
163 | if (*Number == '$') { |
164 | ++Number; |
165 | Converted = sscanf (Number, "%lx" , &Val); |
166 | } else { |
167 | Converted = sscanf (Number, "%li" , (long*)&Val); |
168 | } |
169 | |
170 | /* Check if we do really have a number */ |
171 | if (Converted != 1) { |
172 | Error ("Invalid number given in argument: %s\n" , Arg); |
173 | } |
174 | |
175 | /* Return the result */ |
176 | return Val; |
177 | } |
178 | |
179 | |
180 | |
181 | static void LinkFile (const char* Name, FILETYPE Type) |
182 | /* Handle one file */ |
183 | { |
184 | char* PathName; |
185 | FILE* F; |
186 | unsigned long Magic; |
187 | |
188 | |
189 | /* If we don't know the file type, determine it from the extension */ |
190 | if (Type == FILETYPE_UNKNOWN) { |
191 | Type = GetFileType (Name); |
192 | } |
193 | |
194 | /* For known file types, search the file in the directory list */ |
195 | switch (Type) { |
196 | |
197 | case FILETYPE_LIB: |
198 | PathName = SearchFile (LibSearchPath, Name); |
199 | if (PathName == 0) { |
200 | PathName = SearchFile (LibDefaultPath, Name); |
201 | } |
202 | break; |
203 | |
204 | case FILETYPE_OBJ: |
205 | PathName = SearchFile (ObjSearchPath, Name); |
206 | if (PathName == 0) { |
207 | PathName = SearchFile (ObjDefaultPath, Name); |
208 | } |
209 | break; |
210 | |
211 | default: |
212 | PathName = xstrdup (Name); /* Use the name as is */ |
213 | break; |
214 | } |
215 | |
216 | /* We must have a valid name now */ |
217 | if (PathName == 0) { |
218 | Error ("Input file '%s' not found" , Name); |
219 | } |
220 | |
221 | /* Try to open the file */ |
222 | F = fopen (PathName, "rb" ); |
223 | if (F == 0) { |
224 | Error ("Cannot open '%s': %s" , PathName, strerror (errno)); |
225 | } |
226 | |
227 | /* Read the magic word */ |
228 | Magic = Read32 (F); |
229 | |
230 | /* Check the magic for known file types. The handling is somewhat weird |
231 | ** since we may have given a file with a ".lib" extension, which was |
232 | ** searched and found in a directory for library files, but we now find |
233 | ** out (by looking at the magic) that it's indeed an object file. We just |
234 | ** ignore the problem and hope no one will notice... |
235 | */ |
236 | switch (Magic) { |
237 | |
238 | case OBJ_MAGIC: |
239 | ObjAdd (F, PathName); |
240 | ++ObjFiles; |
241 | break; |
242 | |
243 | case LIB_MAGIC: |
244 | LibAdd (F, PathName); |
245 | ++LibFiles; |
246 | break; |
247 | |
248 | default: |
249 | fclose (F); |
250 | Error ("File '%s' has unknown type" , PathName); |
251 | |
252 | } |
253 | |
254 | /* Free allocated memory. */ |
255 | xfree (PathName); |
256 | } |
257 | |
258 | |
259 | |
260 | static void DefineSymbol (const char* Def) |
261 | /* Define a symbol from the command line */ |
262 | { |
263 | const char* P; |
264 | long Val; |
265 | StrBuf SymName = AUTO_STRBUF_INITIALIZER; |
266 | |
267 | |
268 | /* The symbol must start with a character or underline */ |
269 | if (Def [0] != '_' && !IsAlpha (Def [0])) { |
270 | InvDef (Def); |
271 | } |
272 | P = Def; |
273 | |
274 | /* Copy the symbol, checking the remainder */ |
275 | while (IsAlNum (*P) || *P == '_') { |
276 | SB_AppendChar (&SymName, *P++); |
277 | } |
278 | SB_Terminate (&SymName); |
279 | |
280 | /* Do we have a value given? */ |
281 | if (*P != '=') { |
282 | InvDef (Def); |
283 | } else { |
284 | /* We have a value */ |
285 | ++P; |
286 | if (*P == '$') { |
287 | ++P; |
288 | if (sscanf (P, "%lx" , &Val) != 1) { |
289 | InvDef (Def); |
290 | } |
291 | } else { |
292 | if (sscanf (P, "%li" , &Val) != 1) { |
293 | InvDef (Def); |
294 | } |
295 | } |
296 | } |
297 | |
298 | /* Define the new symbol */ |
299 | CreateConstExport (GetStringId (SB_GetConstBuf (&SymName)), Val); |
300 | } |
301 | |
302 | |
303 | |
304 | static void OptCfgPath (const char* Opt attribute ((unused)), const char* Arg) |
305 | /* Specify a config file search path */ |
306 | { |
307 | AddSearchPath (CfgSearchPath, Arg); |
308 | } |
309 | |
310 | |
311 | |
312 | static void OptConfig (const char* Opt attribute ((unused)), const char* Arg) |
313 | /* Define the config file */ |
314 | { |
315 | char* PathName; |
316 | |
317 | if (CfgAvail ()) { |
318 | Error ("Cannot use -C/-t twice" ); |
319 | } |
320 | /* Search for the file */ |
321 | PathName = SearchFile (CfgSearchPath, Arg); |
322 | if (PathName == 0) { |
323 | PathName = SearchFile (CfgDefaultPath, Arg); |
324 | } |
325 | if (PathName == 0) { |
326 | Error ("Cannot find config file '%s'" , Arg); |
327 | } |
328 | |
329 | /* Read the config */ |
330 | CfgSetName (PathName); |
331 | CfgRead (); |
332 | } |
333 | |
334 | |
335 | |
336 | static void OptDbgFile (const char* Opt attribute ((unused)), const char* Arg) |
337 | /* Give the name of the debug file */ |
338 | { |
339 | DbgFileName = Arg; |
340 | } |
341 | |
342 | |
343 | |
344 | static void OptDefine (const char* Opt attribute ((unused)), const char* Arg) |
345 | /* Define a symbol on the command line */ |
346 | { |
347 | DefineSymbol (Arg); |
348 | } |
349 | |
350 | |
351 | |
352 | static void OptEndGroup (const char* Opt attribute ((unused)), |
353 | const char* Arg attribute ((unused))) |
354 | /* End a library group */ |
355 | { |
356 | LibEndGroup (); |
357 | } |
358 | |
359 | |
360 | |
361 | static void OptForceImport (const char* Opt attribute ((unused)), const char* Arg) |
362 | /* Force an import of a symbol */ |
363 | { |
364 | /* An optional address size may be specified */ |
365 | const char* ColPos = strchr (Arg, ':'); |
366 | if (ColPos == 0) { |
367 | |
368 | /* Use default address size (which for now is always absolute |
369 | ** addressing) |
370 | */ |
371 | InsertImport (GenImport (GetStringId (Arg), ADDR_SIZE_ABS)); |
372 | |
373 | } else { |
374 | |
375 | char* A; |
376 | |
377 | /* Get the address size and check it */ |
378 | unsigned char AddrSize = AddrSizeFromStr (ColPos+1); |
379 | if (AddrSize == ADDR_SIZE_INVALID) { |
380 | Error ("Invalid address size '%s'" , ColPos+1); |
381 | } |
382 | |
383 | /* Create a copy of the argument */ |
384 | A = xstrdup (Arg); |
385 | |
386 | /* We need just the symbol */ |
387 | A[ColPos - Arg] = '\0'; |
388 | |
389 | /* Generate the import */ |
390 | InsertImport (GenImport (GetStringId (A), AddrSize)); |
391 | |
392 | /* Delete the copy of the argument */ |
393 | xfree (A); |
394 | } |
395 | } |
396 | |
397 | |
398 | |
399 | static void OptHelp (const char* Opt attribute ((unused)), |
400 | const char* Arg attribute ((unused))) |
401 | /* Print usage information and exit */ |
402 | { |
403 | Usage (); |
404 | exit (EXIT_SUCCESS); |
405 | } |
406 | |
407 | |
408 | |
409 | static void OptLib (const char* Opt attribute ((unused)), const char* Arg) |
410 | /* Link a library */ |
411 | { |
412 | InputFiles[InputFilesCount].Type = INPUT_FILES_FILE_LIB; |
413 | InputFiles[InputFilesCount].FileName = Arg; |
414 | if (++InputFilesCount >= MAX_INPUTFILES) |
415 | Error ("Too many input files" ); |
416 | } |
417 | |
418 | |
419 | |
420 | static void OptLibPath (const char* Opt attribute ((unused)), const char* Arg) |
421 | /* Specify a library file search path */ |
422 | { |
423 | AddSearchPath (LibSearchPath, Arg); |
424 | } |
425 | |
426 | |
427 | |
428 | static void OptMapFile (const char* Opt attribute ((unused)), const char* Arg) |
429 | /* Give the name of the map file */ |
430 | { |
431 | if (MapFileName) { |
432 | Error ("Cannot use -m twice" ); |
433 | } |
434 | MapFileName = Arg; |
435 | } |
436 | |
437 | |
438 | |
439 | static void OptModuleId (const char* Opt, const char* Arg) |
440 | /* Specify a module id */ |
441 | { |
442 | unsigned long Id = CvtNumber (Opt, Arg); |
443 | if (Id > 0xFFFFUL) { |
444 | Error ("Range error in module id" ); |
445 | } |
446 | ModuleId = (unsigned) Id; |
447 | } |
448 | |
449 | |
450 | |
451 | static void OptObj (const char* Opt attribute ((unused)), const char* Arg) |
452 | /* Link an object file */ |
453 | { |
454 | InputFiles[InputFilesCount].Type = INPUT_FILES_FILE_OBJ; |
455 | InputFiles[InputFilesCount].FileName = Arg; |
456 | if (++InputFilesCount >= MAX_INPUTFILES) |
457 | Error ("Too many input files" ); |
458 | } |
459 | |
460 | |
461 | |
462 | static void OptObjPath (const char* Opt attribute ((unused)), const char* Arg) |
463 | /* Specify an object file search path */ |
464 | { |
465 | AddSearchPath (ObjSearchPath, Arg); |
466 | } |
467 | |
468 | |
469 | |
470 | static void OptOutputName (const char* Opt attribute ((unused)), const char* Arg) |
471 | /* Give the name of the output file */ |
472 | { |
473 | static int OutputNameSeen = 0; |
474 | if (OutputNameSeen) { |
475 | Error ("Cannot use -o twice" ); |
476 | } |
477 | OutputNameSeen = 1; |
478 | OutputName = Arg; |
479 | } |
480 | |
481 | |
482 | |
483 | static void OptStartAddr (const char* Opt, const char* Arg) |
484 | /* Set the default start address */ |
485 | { |
486 | if (HaveStartAddr) { |
487 | Error ("Cannot use -S twice" ); |
488 | } |
489 | StartAddr = CvtNumber (Opt, Arg); |
490 | HaveStartAddr = 1; |
491 | } |
492 | |
493 | |
494 | |
495 | static void OptStartGroup (const char* Opt attribute ((unused)), |
496 | const char* Arg attribute ((unused))) |
497 | /* Start a library group */ |
498 | { |
499 | LibStartGroup (); |
500 | } |
501 | |
502 | |
503 | |
504 | static void OptTarget (const char* Opt attribute ((unused)), const char* Arg) |
505 | /* Set the target system */ |
506 | { |
507 | StrBuf FileName = STATIC_STRBUF_INITIALIZER; |
508 | char* PathName; |
509 | |
510 | /* Map the target name to a target id */ |
511 | Target = FindTarget (Arg); |
512 | if (Target == TGT_UNKNOWN) { |
513 | Error ("Invalid target name: '%s'" , Arg); |
514 | } |
515 | |
516 | /* Set the target binary format */ |
517 | DefaultBinFmt = GetTargetProperties (Target)->BinFmt; |
518 | |
519 | /* Build config file name from target name */ |
520 | SB_CopyStr (&FileName, GetTargetName (Target)); |
521 | SB_AppendStr (&FileName, ".cfg" ); |
522 | SB_Terminate (&FileName); |
523 | |
524 | /* Search for the file */ |
525 | PathName = SearchFile (CfgSearchPath, SB_GetBuf (&FileName)); |
526 | if (PathName == 0) { |
527 | PathName = SearchFile (CfgDefaultPath, SB_GetBuf (&FileName)); |
528 | } |
529 | if (PathName == 0) { |
530 | Error ("Cannot find config file '%s'" , SB_GetBuf (&FileName)); |
531 | } |
532 | |
533 | /* Free file name memory */ |
534 | SB_Done (&FileName); |
535 | |
536 | /* Read the file */ |
537 | CfgSetName (PathName); |
538 | CfgRead (); |
539 | } |
540 | |
541 | |
542 | |
543 | static void OptVersion (const char* Opt attribute ((unused)), |
544 | const char* Arg attribute ((unused))) |
545 | /* Print the assembler version */ |
546 | { |
547 | fprintf (stderr, "%s V%s\n" , ProgName, GetVersionAsString ()); |
548 | exit(EXIT_SUCCESS); |
549 | } |
550 | |
551 | |
552 | |
553 | static void OptMultDef (const char* Opt attribute ((unused)), |
554 | const char* Arg attribute ((unused))) |
555 | /* Set flag to allow multiple definitions of a global symbol */ |
556 | { |
557 | AllowMultDef = 1; |
558 | } |
559 | |
560 | |
561 | |
562 | static void CmdlOptStartGroup (const char* Opt attribute ((unused)), |
563 | const char* Arg attribute ((unused))) |
564 | /* Remember 'start group' occurrence in input files array */ |
565 | { |
566 | InputFiles[InputFilesCount].Type = INPUT_FILES_SGROUP; |
567 | InputFiles[InputFilesCount].FileName = Arg; /* Unused */ |
568 | if (++InputFilesCount >= MAX_INPUTFILES) |
569 | Error ("Too many input files" ); |
570 | } |
571 | |
572 | |
573 | |
574 | static void CmdlOptEndGroup (const char* Opt attribute ((unused)), |
575 | const char* Arg attribute ((unused))) |
576 | /* Remember 'end group' occurrence in input files array */ |
577 | { |
578 | InputFiles[InputFilesCount].Type = INPUT_FILES_EGROUP; |
579 | InputFiles[InputFilesCount].FileName = Arg; /* Unused */ |
580 | if (++InputFilesCount >= MAX_INPUTFILES) |
581 | Error ("Too many input files" ); |
582 | } |
583 | |
584 | |
585 | |
586 | static void CmdlOptConfig (const char* Opt attribute ((unused)), const char* Arg) |
587 | /* Set 'config file' command line parameter */ |
588 | { |
589 | if (CmdlineCfgFile || CmdlineTarget) { |
590 | Error ("Cannot use -C/-t twice" ); |
591 | } |
592 | CmdlineCfgFile = Arg; |
593 | } |
594 | |
595 | |
596 | |
597 | static void CmdlOptTarget (const char* Opt attribute ((unused)), const char* Arg) |
598 | /* Set 'target' command line parameter */ |
599 | { |
600 | if (CmdlineCfgFile || CmdlineTarget) { |
601 | Error ("Cannot use -C/-t twice" ); |
602 | } |
603 | CmdlineTarget = Arg; |
604 | } |
605 | |
606 | |
607 | |
608 | static void ParseCommandLine(void) |
609 | { |
610 | /* Program long options */ |
611 | static const LongOpt OptTab[] = { |
612 | { "--allow-multiple-definition" , 0, OptMultDef }, |
613 | { "--cfg-path" , 1, OptCfgPath }, |
614 | { "--config" , 1, CmdlOptConfig }, |
615 | { "--dbgfile" , 1, OptDbgFile }, |
616 | { "--define" , 1, OptDefine }, |
617 | { "--end-group" , 0, CmdlOptEndGroup }, |
618 | { "--force-import" , 1, OptForceImport }, |
619 | { "--help" , 0, OptHelp }, |
620 | { "--lib" , 1, OptLib }, |
621 | { "--lib-path" , 1, OptLibPath }, |
622 | { "--mapfile" , 1, OptMapFile }, |
623 | { "--module-id" , 1, OptModuleId }, |
624 | { "--obj" , 1, OptObj }, |
625 | { "--obj-path" , 1, OptObjPath }, |
626 | { "--start-addr" , 1, OptStartAddr }, |
627 | { "--start-group" , 0, CmdlOptStartGroup }, |
628 | { "--target" , 1, CmdlOptTarget }, |
629 | { "--version" , 0, OptVersion }, |
630 | }; |
631 | |
632 | unsigned I; |
633 | unsigned LabelFileGiven = 0; |
634 | |
635 | /* Allocate memory for input file array */ |
636 | InputFiles = xmalloc (MAX_INPUTFILES * sizeof (struct InputFile)); |
637 | |
638 | /* Defer setting of config/target and input files until all options are parsed */ |
639 | I = 1; |
640 | while (I < ArgCount) { |
641 | |
642 | /* Get the argument */ |
643 | const char* Arg = ArgVec[I]; |
644 | |
645 | /* Check for an option */ |
646 | if (Arg [0] == '-') { |
647 | |
648 | /* An option */ |
649 | switch (Arg [1]) { |
650 | |
651 | case '-': |
652 | LongOption (&I, OptTab, sizeof(OptTab)/sizeof(OptTab[0])); |
653 | break; |
654 | |
655 | case '(': |
656 | CmdlOptStartGroup (Arg, 0); |
657 | break; |
658 | |
659 | case ')': |
660 | CmdlOptEndGroup (Arg, 0); |
661 | break; |
662 | |
663 | case 'h': |
664 | case '?': |
665 | OptHelp (Arg, 0); |
666 | break; |
667 | |
668 | case 'm': |
669 | OptMapFile (Arg, GetArg (&I, 2)); |
670 | break; |
671 | |
672 | case 'o': |
673 | OptOutputName (NULL, GetArg (&I, 2)); |
674 | break; |
675 | |
676 | case 't': |
677 | CmdlOptTarget (Arg, GetArg (&I, 2)); |
678 | break; |
679 | |
680 | case 'u': |
681 | OptForceImport (Arg, GetArg (&I, 2)); |
682 | break; |
683 | |
684 | case 'v': |
685 | switch (Arg [2]) { |
686 | case 'm': VerboseMap = 1; break; |
687 | case '\0': ++Verbosity; break; |
688 | default: UnknownOption (Arg); |
689 | } |
690 | break; |
691 | |
692 | case 'C': |
693 | CmdlOptConfig (Arg, GetArg (&I, 2)); |
694 | break; |
695 | |
696 | case 'D': |
697 | OptDefine (Arg, GetArg (&I, 2)); |
698 | break; |
699 | |
700 | case 'L': |
701 | switch (Arg [2]) { |
702 | case 'n': |
703 | /* ## This one is obsolete and will go */ |
704 | if (LabelFileGiven) { |
705 | Error ("Cannot use -Ln twice" ); |
706 | } |
707 | LabelFileGiven = 1; |
708 | LabelFileName = GetArg (&I, 3); |
709 | break; |
710 | default: |
711 | OptLibPath (Arg, GetArg (&I, 2)); |
712 | break; |
713 | } |
714 | break; |
715 | |
716 | case 'S': |
717 | OptStartAddr (Arg, GetArg (&I, 2)); |
718 | break; |
719 | |
720 | case 'V': |
721 | OptVersion (Arg, 0); |
722 | break; |
723 | |
724 | default: |
725 | UnknownOption (Arg); |
726 | break; |
727 | } |
728 | |
729 | } else { |
730 | |
731 | /* A filename */ |
732 | InputFiles[InputFilesCount].Type = INPUT_FILES_FILE; |
733 | InputFiles[InputFilesCount].FileName = Arg; |
734 | if (++InputFilesCount >= MAX_INPUTFILES) |
735 | Error ("Too many input files" ); |
736 | |
737 | } |
738 | |
739 | /* Next argument */ |
740 | ++I; |
741 | } |
742 | |
743 | if (CmdlineTarget) { |
744 | OptTarget (NULL, CmdlineTarget); |
745 | } else if (CmdlineCfgFile) { |
746 | OptConfig (NULL, CmdlineCfgFile); |
747 | } |
748 | |
749 | /* Process input files */ |
750 | for (I = 0; I < InputFilesCount; ++I) { |
751 | switch (InputFiles[I].Type) { |
752 | case INPUT_FILES_FILE: |
753 | LinkFile (InputFiles[I].FileName, FILETYPE_UNKNOWN); |
754 | break; |
755 | case INPUT_FILES_FILE_LIB: |
756 | LinkFile (InputFiles[I].FileName, FILETYPE_LIB); |
757 | break; |
758 | case INPUT_FILES_FILE_OBJ: |
759 | LinkFile (InputFiles[I].FileName, FILETYPE_OBJ); |
760 | break; |
761 | case INPUT_FILES_SGROUP: |
762 | OptStartGroup (NULL, 0); |
763 | break; |
764 | case INPUT_FILES_EGROUP: |
765 | OptEndGroup (NULL, 0); |
766 | break; |
767 | default: |
768 | abort (); |
769 | } |
770 | } |
771 | |
772 | /* Free memory used for input file array */ |
773 | xfree (InputFiles); |
774 | } |
775 | |
776 | |
777 | |
778 | int main (int argc, char* argv []) |
779 | /* Linker main program */ |
780 | { |
781 | unsigned MemoryAreaOverflows; |
782 | |
783 | /* Initialize the cmdline module */ |
784 | InitCmdLine (&argc, &argv, "ld65" ); |
785 | |
786 | /* Initialize the input file search paths */ |
787 | InitSearchPaths (); |
788 | |
789 | /* Initialize the string pool */ |
790 | InitStrPool (); |
791 | |
792 | /* Initialize the type pool */ |
793 | InitTypePool (); |
794 | |
795 | /* Parse the command line */ |
796 | ParseCommandLine (); |
797 | |
798 | /* Check if we had any object files */ |
799 | if (ObjFiles == 0) { |
800 | Error ("No object files to link" ); |
801 | } |
802 | |
803 | /* Check if we have a valid configuration */ |
804 | if (!CfgAvail ()) { |
805 | Error ("Memory configuration missing" ); |
806 | } |
807 | |
808 | /* Check if we have open library groups */ |
809 | LibCheckGroup (); |
810 | |
811 | /* Create the condes tables if requested */ |
812 | ConDesCreate (); |
813 | |
814 | /* Process data from the config file. Assign start addresses for the |
815 | ** segments, define linker symbols. The function will return the number |
816 | ** of memory area overflows (zero on success). |
817 | */ |
818 | MemoryAreaOverflows = CfgProcess (); |
819 | |
820 | /* Check module assertions */ |
821 | CheckAssertions (); |
822 | |
823 | /* Check for import/export mismatches */ |
824 | CheckExports (); |
825 | |
826 | /* If we had a memory area overflow before, we cannot generate the output |
827 | ** file. However, we will generate a short map file if requested, since |
828 | ** this will help the user to rearrange segments and fix the overflow. |
829 | */ |
830 | if (MemoryAreaOverflows) { |
831 | if (MapFileName) { |
832 | CreateMapFile (SHORT_MAPFILE); |
833 | } |
834 | Error ("Cannot generate most of the files due to memory area overflow%c" , |
835 | (MemoryAreaOverflows > 1) ? 's' : ' '); |
836 | } |
837 | |
838 | /* Create the output file */ |
839 | CfgWriteTarget (); |
840 | |
841 | /* Check for segments not written to the output file */ |
842 | CheckSegments (); |
843 | |
844 | /* If requested, create a map file and a label file for VICE */ |
845 | if (MapFileName) { |
846 | CreateMapFile (LONG_MAPFILE); |
847 | } |
848 | if (LabelFileName) { |
849 | CreateLabelFile (); |
850 | } |
851 | if (DbgFileName) { |
852 | CreateDbgFile (); |
853 | } |
854 | |
855 | /* Dump the data for debugging */ |
856 | if (Verbosity > 1) { |
857 | SegDump (); |
858 | ConDesDump (); |
859 | } |
860 | |
861 | /* Return an apropriate exit code */ |
862 | return EXIT_SUCCESS; |
863 | } |
864 | |