]> git.sur5r.net Git - cc65/blob - src/ld65/bin.c
Removed (pretty inconsistently used) tab chars from source code base.
[cc65] / src / ld65 / bin.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                   bin.c                                   */
4 /*                                                                           */
5 /*                  Module to handle the raw binary format                   */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1999-2012, 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 <string.h>
38 #include <errno.h>
39
40 /* common */
41 #include "alignment.h"
42 #include "print.h"
43 #include "xmalloc.h"
44
45 /* ld65 */
46 #include "bin.h"
47 #include "config.h"
48 #include "exports.h"
49 #include "expr.h"
50 #include "error.h"
51 #include "global.h"
52 #include "fileio.h"
53 #include "lineinfo.h"
54 #include "memarea.h"
55 #include "segments.h"
56 #include "spool.h"
57
58
59
60 /*****************************************************************************/
61 /*                                   Data                                    */
62 /*****************************************************************************/
63
64
65
66 struct BinDesc {
67     unsigned    Undef;          /* Count of undefined externals */
68     FILE*       F;              /* Output file */
69     const char* Filename;       /* Name of output file */
70 };
71
72
73
74 /*****************************************************************************/
75 /*                                   Code                                    */
76 /*****************************************************************************/
77
78
79
80 BinDesc* NewBinDesc (void)
81 /* Create a new binary format descriptor */
82 {
83     /* Allocate memory for a new BinDesc struct */
84     BinDesc* D = xmalloc (sizeof (BinDesc));
85
86     /* Initialize the fields */
87     D->Undef    = 0;
88     D->F        = 0;
89     D->Filename = 0;
90
91     /* Return the created struct */
92     return D;
93 }
94
95
96
97 void FreeBinDesc (BinDesc* D)
98 /* Free a binary format descriptor */
99 {
100     xfree (D);
101 }
102
103
104
105 static unsigned BinWriteExpr (ExprNode* E, int Signed, unsigned Size,
106                               unsigned long Offs attribute ((unused)),
107                               void* Data)
108 /* Called from SegWrite for an expression. Evaluate the expression, check the
109  * range and write the expression value to the file.
110  */
111 {
112     /* There's a predefined function to handle constant expressions */
113     return SegWriteConstExpr (((BinDesc*)Data)->F, E, Signed, Size);
114 }
115
116
117
118 static void PrintBoolVal (const char* Name, int B)
119 /* Print a boolean value for debugging */
120 {
121     Print (stdout, 2, "      %s = %s\n", Name, B? "true" : "false");
122 }
123
124
125
126 static void PrintNumVal (const char* Name, unsigned long V)
127 /* Print a numerical value for debugging */
128 {
129     Print (stdout, 2, "      %s = 0x%lx\n", Name, V);
130 }
131
132
133
134 static void BinWriteMem (BinDesc* D, MemoryArea* M)
135 /* Write the segments of one memory area to a file */
136 {
137     unsigned I;
138
139     /* Get the start address of this memory area */
140     unsigned long Addr = M->Start;
141
142     /* Debugging: Check that the file offset is correct */
143     if (ftell (D->F) != (long)M->FileOffs) {
144         Internal ("Invalid file offset for memory area %s: %ld/%lu",
145                   GetString (M->Name), ftell (D->F), M->FileOffs);
146     }
147
148     /* Walk over all segments in this memory area */
149     for (I = 0; I < CollCount (&M->SegList); ++I) {
150
151         int DoWrite;
152
153         /* Get the segment */
154         SegDesc* S = CollAtUnchecked (&M->SegList, I);
155
156         /* Keep the user happy */
157         Print (stdout, 1, "    Writing `%s'\n", GetString (S->Name));
158
159         /* Writes do only occur in the load area and not for BSS segments */
160         DoWrite = (S->Flags & SF_BSS) == 0      &&      /* No BSS segment */
161                    S->Load == M                 &&      /* LOAD segment */
162                    S->Seg->Dumped == 0;                 /* Not already written */
163
164         /* Output debugging stuff */
165         PrintBoolVal ("bss", S->Flags & SF_BSS);
166         PrintBoolVal ("LoadArea", S->Load == M);
167         PrintBoolVal ("Dumped", S->Seg->Dumped);
168         PrintBoolVal ("DoWrite", DoWrite);
169         PrintNumVal  ("Address", Addr);
170         PrintNumVal  ("FileOffs", (unsigned long) ftell (D->F));
171
172         /* Check if the alignment for the segment from the linker config is
173          * a multiple for that of the segment.
174          */
175         if ((S->RunAlignment % S->Seg->Alignment) != 0) {
176             /* Segment requires another alignment than configured
177              * in the linker.
178              */
179             Warning ("Segment `%s' is not aligned properly. Resulting "
180                      "executable may not be functional.",
181                      GetString (S->Name));
182         }
183
184         /* If this is the run memory area, we must apply run alignment. If
185          * this is not the run memory area but the load memory area (which
186          * means that both are different), we must apply load alignment.
187          * Beware: DoWrite may be true even if this is the run memory area,
188          * because it may be also the load memory area.
189          */
190         if (S->Run == M) {
191
192             /* Handle ALIGN and OFFSET/START */
193             if (S->Flags & SF_ALIGN) {
194                 /* Align the address */
195                 unsigned long NewAddr = AlignAddr (Addr, S->RunAlignment);
196                 if (DoWrite || (M->Flags & MF_FILL) != 0) {
197                     WriteMult (D->F, M->FillVal, NewAddr - Addr);
198                     PrintNumVal ("SF_ALIGN", NewAddr - Addr);
199                 }
200                 Addr = NewAddr;
201             } else if (S->Flags & (SF_OFFSET | SF_START)) {
202                 unsigned long NewAddr = S->Addr;
203                 if (S->Flags & SF_OFFSET) {
204                     /* It's an offset, not a fixed address, make an address */
205                     NewAddr += M->Start;
206                 }
207                 if (DoWrite || (M->Flags & MF_FILL) != 0) {
208                     WriteMult (D->F, M->FillVal, NewAddr-Addr);
209                     PrintNumVal ("SF_OFFSET", NewAddr - Addr);
210                 }
211                 Addr = NewAddr;
212             }
213
214         } else if (S->Load == M) {
215
216             /* Handle ALIGN_LOAD */
217             if (S->Flags & SF_ALIGN_LOAD) {
218                 /* Align the address */
219                 unsigned long NewAddr = AlignAddr (Addr, S->LoadAlignment);
220                 if (DoWrite || (M->Flags & MF_FILL) != 0) {
221                     WriteMult (D->F, M->FillVal, NewAddr - Addr);
222                     PrintNumVal ("SF_ALIGN_LOAD", NewAddr - Addr);
223                 }
224                 Addr = NewAddr;
225             }
226
227         }
228
229         /* Now write the segment to disk if it is not a BSS type segment and
230          * if the memory area is the load area.
231          */
232         if (DoWrite) {
233             unsigned long P = ftell (D->F);
234             SegWrite (D->Filename, D->F, S->Seg, BinWriteExpr, D);
235             PrintNumVal ("Wrote", (unsigned long) (ftell (D->F) - P));
236         } else if (M->Flags & MF_FILL) {
237             WriteMult (D->F, S->Seg->FillVal, S->Seg->Size);
238             PrintNumVal ("Filled", (unsigned long) S->Seg->Size);
239         }
240
241         /* If this was the load memory area, mark the segment as dumped */
242         if (S->Load == M) {
243             S->Seg->Dumped = 1;
244         }
245
246         /* Calculate the new address */
247         Addr += S->Seg->Size;
248     }
249
250     /* If a fill was requested, fill the remaining space */
251     if ((M->Flags & MF_FILL) != 0 && M->FillLevel < M->Size) {
252         unsigned long ToFill = M->Size - M->FillLevel;
253         Print (stdout, 2, "    Filling 0x%lx bytes with 0x%02x\n",
254                ToFill, M->FillVal);
255         WriteMult (D->F, M->FillVal, ToFill);
256         M->FillLevel = M->Size;
257     }
258 }
259
260
261
262 static int BinUnresolved (unsigned Name attribute ((unused)), void* D)
263 /* Called if an unresolved symbol is encountered */
264 {
265     /* Unresolved symbols are an error in binary format. Bump the counter
266      * and return zero telling the caller that the symbol is indeed
267      * unresolved.
268      */
269     ((BinDesc*) D)->Undef++;
270     return 0;
271 }
272
273
274
275 void BinWriteTarget (BinDesc* D, struct File* F)
276 /* Write a binary output file */
277 {
278     unsigned I;
279
280     /* Place the filename in the control structure */
281     D->Filename = GetString (F->Name);
282
283     /* Check for unresolved symbols. The function BinUnresolved is called
284      * if we get an unresolved symbol.
285      */
286     D->Undef = 0;               /* Reset the counter */
287     CheckUnresolvedImports (BinUnresolved, D);
288     if (D->Undef > 0) {
289         /* We had unresolved symbols, cannot create output file */
290         Error ("%u unresolved external(s) found - cannot create output file", D->Undef);
291     }
292
293     /* Open the file */
294     D->F = fopen (D->Filename, "wb");
295     if (D->F == 0) {
296         Error ("Cannot open `%s': %s", D->Filename, strerror (errno));
297     }
298
299     /* Keep the user happy */
300     Print (stdout, 1, "Opened `%s'...\n", D->Filename);
301
302     /* Dump all memory areas */
303     for (I = 0; I < CollCount (&F->MemoryAreas); ++I) {
304         /* Get this entry */
305         MemoryArea* M = CollAtUnchecked (&F->MemoryAreas, I);
306         Print (stdout, 1, "  Dumping `%s'\n", GetString (M->Name));
307         BinWriteMem (D, M);
308     }
309
310     /* Close the file */
311     if (fclose (D->F) != 0) {
312         Error ("Cannot write to `%s': %s", D->Filename, strerror (errno));
313     }
314
315     /* Reset the file and filename */
316     D->F        = 0;
317     D->Filename = 0;
318 }
319
320
321
322
323