]> git.sur5r.net Git - cc65/blob - src/ld65/main.c
support for .zeropage segment in GEOS
[cc65] / src / ld65 / main.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                  main.c                                   */
4 /*                                                                           */
5 /*                     Main program for the ld65 linker                      */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2002 Ullrich von Bassewitz                                       */
10 /*               Wacholderweg 14                                             */
11 /*               D-70597 Stuttgart                                           */
12 /* EMail:        uz@musoftware.de                                            */
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 "cmdline.h"
43 #include "libdefs.h"
44 #include "objdefs.h"
45 #include "print.h"
46 #include "target.h"
47 #include "version.h"
48 #include "xmalloc.h"
49
50 /* ld65 */
51 #include "binfmt.h"
52 #include "condes.h"
53 #include "config.h"
54 #include "error.h"
55 #include "exports.h"
56 #include "fileio.h"
57 #include "global.h"
58 #include "library.h"
59 #include "mapfile.h"
60 #include "objfile.h"
61 #include "scanner.h"
62 #include "segments.h"
63 #include "tgtcfg.h"
64
65
66
67 /*****************************************************************************/
68 /*                                   Data                                    */
69 /*****************************************************************************/
70
71
72
73 static unsigned         ObjFiles   = 0; /* Count of object files linked */
74 static unsigned         LibFiles   = 0; /* Count of library files linked */
75 static const char*      LibPath    = 0; /* Search path for modules */
76 static unsigned         LibPathLen = 0; /* Length of LibPath */
77
78
79
80 /*****************************************************************************/
81 /*                                   Code                                    */
82 /*****************************************************************************/
83
84
85
86 static void Usage (void)
87 /* Print usage information and exit */
88 {
89     fprintf (stderr,
90              "Usage: %s [options] module ...\n"
91              "Short options:\n"
92              "  -C name\t\tUse linker config file\n"
93              "  -Ln name\t\tCreate a VICE label file\n"
94              "  -Lp\t\t\tMark write protected segments as such (VICE)\n"
95              "  -S addr\t\tSet the default start address\n"
96              "  -V\t\t\tPrint the linker version\n"
97              "  -h\t\t\tHelp (this text)\n"
98              "  -m name\t\tCreate a map file\n"
99              "  -o name\t\tName the default output file\n"
100              "  -t sys\t\tSet the target system\n"
101              "  -v\t\t\tVerbose mode\n"
102              "  -vm\t\t\tVerbose map file\n"
103              "\n"
104              "Long options:\n"
105              "  --config name\t\tUse linker config file\n"
106              "  --help\t\tHelp (this text)\n"
107              "  --mapfile name\tCreate a map file\n"
108              "  --module-id id\tSpecify a module id\n"
109              "  --start-addr addr\tSet the default start address\n"
110              "  --target sys\t\tSet the target system\n"
111              "  --version\t\tPrint the linker version\n",
112              ProgName);
113 }
114
115
116
117 static unsigned long CvtNumber (const char* Arg, const char* Number)
118 /* Convert a number from a string. Allow '$' and '0x' prefixes for hex
119  * numbers.
120  */
121 {
122     unsigned long Val;
123     int           Converted;
124
125     /* Convert */
126     if (*Number == '$') {
127         ++Number;
128         Converted = sscanf (Number, "%lx", &Val);
129     } else {
130         Converted = sscanf (Number, "%li", (long*)&Val);
131     }
132
133     /* Check if we do really have a number */
134     if (Converted != 1) {
135         Error ("Invalid number given in argument: %s\n", Arg);
136     }
137
138     /* Return the result */
139     return Val;
140 }
141
142
143
144 static int HasPath (const char* Name)
145 /* Check if the given Name has a path component */
146 {
147     return strchr (Name, '/') != 0 || strchr (Name, '\\') != 0;
148 }
149
150
151
152 static void LinkFile (const char* Name)
153 /* Handle one file */
154 {
155     unsigned long Magic;
156     unsigned Len;
157     char* NewName = 0;
158
159     /* Try to open the file */
160     FILE* F = fopen (Name, "rb");
161     if (F == 0) {
162         /* We couldn't open the file. If the name doesn't have a path, and we
163          * have a search path given, try the name with the search path
164          * prepended.
165          */
166         if (LibPathLen > 0 && !HasPath (Name)) {
167             /* Allocate memory. Account for the trailing zero, and for a
168              * path separator character eventually needed.
169              */
170             Len = LibPathLen;
171             NewName = xmalloc (strlen (Name) + Len + 2);
172             /* Build the new name */
173             memcpy (NewName, LibPath, Len);
174             if (NewName [Len-1] != '/' && NewName [Len-1] != '\\') {
175                 /* We need an additional path separator */
176                 NewName [Len++] = '/';
177             }
178             strcpy (NewName + Len, Name);
179
180             /* Now try to open the new file */
181             F = fopen (NewName, "rb");
182         }
183
184         if (F == 0) {
185             Error ("Cannot open `%s': %s", Name, strerror (errno));
186         }
187     }
188
189     /* Read the magic word */
190     Magic = Read32 (F);
191
192     /* Do we know this type of file? */
193     switch (Magic) {
194
195         case OBJ_MAGIC:
196             ObjAdd (F, Name);
197             ++ObjFiles;
198             break;
199
200         case LIB_MAGIC:
201             LibAdd (F, Name);
202             ++LibFiles;
203             break;
204
205         default:
206             fclose (F);
207             Error ("File `%s' has unknown type", Name);
208
209     }
210
211     /* If we have allocated memory, free it here. Note: Memory will not always
212      * be freed if we run into an error, but that's no problem. Adding more
213      * code to work around it will use more memory than the chunk that's lost.
214      */
215     xfree (NewName);
216 }
217
218
219
220 static void OptConfig (const char* Opt attribute ((unused)), const char* Arg)
221 /* Define the config file */
222 {
223     if (CfgAvail ()) {
224         Error ("Cannot use -C/-t twice");
225     }
226     CfgSetName (Arg);
227 }
228
229
230
231 static void OptDbgFile (const char* Opt attribute ((unused)), const char* Arg)
232 /* Give the name of the debug file */
233 {
234     DbgFileName = Arg;
235 }
236
237
238
239 static void OptHelp (const char* Opt attribute ((unused)),
240                      const char* Arg attribute ((unused)))
241 /* Print usage information and exit */
242 {
243     Usage ();
244     exit (EXIT_SUCCESS);
245 }
246
247
248
249 static void OptMapFile (const char* Opt attribute ((unused)), const char* Arg)
250 /* Give the name of the map file */
251 {
252     MapFileName = Arg;
253 }
254
255
256
257 static void OptModuleId (const char* Opt, const char* Arg)
258 /* Specify a module id */
259 {
260     unsigned long Id = CvtNumber (Opt, Arg);
261     if (Id > 0xFFFFUL) {
262         Error ("Range error in module id");
263     }
264     ModuleId = (unsigned) Id;
265 }
266
267
268
269 static void OptStartAddr (const char* Opt, const char* Arg)
270 /* Set the default start address */
271 {
272     StartAddr = CvtNumber (Opt, Arg);
273     HaveStartAddr = 1;
274 }
275
276
277
278 static void OptTarget (const char* Opt attribute ((unused)), const char* Arg)
279 /* Set the target system */
280 {
281     const TargetDesc* D;
282
283     /* Map the target name to a target id */
284     Target = FindTarget (Arg);
285     if (Target == TGT_UNKNOWN) {
286         Error ("Invalid target name: `%s'", Arg);
287     }
288
289     /* Get the target description record */
290     D = &Targets[Target];
291
292     /* Set the target data */
293     DefaultBinFmt = D->BinFmt;
294     CfgSetBuf (D->Cfg);
295 }
296
297
298
299 static void OptVersion (const char* Opt attribute ((unused)),
300                         const char* Arg attribute ((unused)))
301 /* Print the assembler version */
302 {
303     fprintf (stderr,
304              "ld65 V%u.%u.%u - (C) Copyright 1998-2002 Ullrich von Bassewitz\n",
305              VER_MAJOR, VER_MINOR, VER_PATCH);
306 }
307
308
309
310 int main (int argc, char* argv [])
311 /* Assembler main program */
312 {
313     /* Program long options */
314     static const LongOpt OptTab[] = {
315         { "--config",           1,      OptConfig               },
316         { "--dbgfile",          1,      OptDbgFile              },
317         { "--help",             0,      OptHelp                 },
318         { "--mapfile",          1,      OptMapFile              },
319         { "--module-id",        1,      OptModuleId             },
320         { "--start-addr",       1,      OptStartAddr            },
321         { "--target",           1,      OptTarget               },
322         { "--version",          0,      OptVersion              },
323     };
324
325     unsigned I;
326
327     /* Initialize the cmdline module */
328     InitCmdLine (&argc, &argv, "ld65");
329
330     /* Evaluate the CC65_LIB environment variable */
331     LibPath = getenv ("CC65_LIB");
332     if (LibPath == 0) {
333         /* Use some default path */
334 #ifdef CC65_LIB
335         LibPath = CC65_LIB;
336 #else
337         LibPath = "/usr/lib/cc65/lib/";
338 #endif
339     }
340     LibPathLen = strlen (LibPath);
341
342     /* Check the parameters */
343     I = 1;
344     while (I < ArgCount) {
345
346         /* Get the argument */
347         const char* Arg = ArgVec[I];
348
349         /* Check for an option */
350         if (Arg [0] == '-') {
351
352             /* An option */
353             switch (Arg [1]) {
354
355                 case '-':
356                     LongOption (&I, OptTab, sizeof(OptTab)/sizeof(OptTab[0]));
357                     break;
358
359                 case 'h':
360                 case '?':
361                     OptHelp (Arg, 0);
362                     break;
363
364                 case 'm':
365                     OptMapFile (Arg, GetArg (&I, 2));
366                     break;
367
368                 case 'o':
369                     OutputName = GetArg (&I, 2);
370                     break;
371
372                 case 't':
373                     if (CfgAvail ()) {
374                         Error ("Cannot use -C/-t twice");
375                     }
376                     OptTarget (Arg, GetArg (&I, 2));
377                     break;
378
379                 case 'v':
380                     switch (Arg [2]) {
381                         case 'm':   VerboseMap = 1;     break;
382                         case '\0':  ++Verbosity;        break;
383                         default:    UnknownOption (Arg);
384                     }
385                     break;
386
387                 case 'C':
388                     OptConfig (Arg, GetArg (&I, 2));
389                     break;
390
391                 case 'L':
392                     switch (Arg [2]) {
393                         case 'n': LabelFileName = GetArg (&I, 3); break;
394                         case 'p': WProtSegs = 1;                  break;
395                         default:  UnknownOption (Arg);            break;
396                     }
397                     break;
398
399                 case 'S':
400                     OptStartAddr (Arg, GetArg (&I, 2));
401                     break;
402
403                 case 'V':
404                     OptVersion (Arg, 0);
405                     break;
406
407                 default:
408                     UnknownOption (Arg);
409                     break;
410             }
411
412         } else {
413
414             /* A filename */
415             LinkFile (Arg);
416
417         }
418
419         /* Next argument */
420         ++I;
421     }
422
423     /* Check if we had any object files */
424     if (ObjFiles == 0) {
425         Error ("No object files to link");
426     }
427
428     /* Check if we have a valid configuration */
429     if (!CfgAvail ()) {
430         Error ("Memory configuration missing");
431     }
432
433     /* Read the config file */
434     CfgRead ();
435
436     /* Create the condes tables if requested */
437     ConDesCreate ();
438
439     /* Assign start addresses for the segments, define linker symbols */
440     CfgAssignSegments ();
441
442     /* Create the output file */
443     CfgWriteTarget ();
444
445     /* Check for segments not written to the output file */
446     CheckSegments ();
447
448     /* If requested, create a map file and a label file for VICE */
449     if (MapFileName) {
450         CreateMapFile ();
451     }
452     if (LabelFileName) {
453         CreateLabelFile ();
454     }
455     if (DbgFileName) {
456         CreateDbgFile ();
457     }
458
459     /* Dump the data for debugging */
460     if (Verbosity > 1) {
461         SegDump ();
462         ConDesDump ();
463     }
464
465     /* Return an apropriate exit code */
466     return EXIT_SUCCESS;
467 }
468
469
470
471