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 | /* If this is the run memory area, we must apply run alignment. If |
173 | ** this is not the run memory area but the load memory area (which |
174 | ** means that both are different), we must apply load alignment. |
175 | ** Beware: DoWrite may be true even if this is the run memory area, |
176 | ** because it may be also the load memory area. |
177 | */ |
178 | if (S->Run == M) { |
179 | |
180 | /* Handle ALIGN and OFFSET/START */ |
181 | if (S->Flags & SF_ALIGN) { |
182 | /* Align the address */ |
183 | unsigned long NewAddr = AlignAddr (Addr, S->RunAlignment); |
184 | if (DoWrite || (M->Flags & MF_FILL) != 0) { |
185 | WriteMult (D->F, M->FillVal, NewAddr - Addr); |
186 | PrintNumVal ("SF_ALIGN" , NewAddr - Addr); |
187 | } |
188 | Addr = NewAddr; |
189 | } else if (S->Flags & (SF_OFFSET | SF_START)) { |
190 | unsigned long NewAddr = S->Addr; |
191 | if (S->Flags & SF_OFFSET) { |
192 | /* It's an offset, not a fixed address, make an address */ |
193 | NewAddr += M->Start; |
194 | } |
195 | if (DoWrite || (M->Flags & MF_FILL) != 0) { |
196 | /* Seek in "overwrite" segments */ |
197 | if (S->Flags & SF_OVERWRITE) { |
198 | fseek (D->F, NewAddr - M->Start, SEEK_SET); |
199 | } else { |
200 | WriteMult (D->F, M->FillVal, NewAddr-Addr); |
201 | PrintNumVal ("SF_OFFSET" , NewAddr - Addr); |
202 | } |
203 | } |
204 | Addr = NewAddr; |
205 | } |
206 | |
207 | } else if (S->Load == M) { |
208 | |
209 | /* Handle ALIGN_LOAD */ |
210 | if (S->Flags & SF_ALIGN_LOAD) { |
211 | /* Align the address */ |
212 | unsigned long NewAddr = AlignAddr (Addr, S->LoadAlignment); |
213 | if (DoWrite || (M->Flags & MF_FILL) != 0) { |
214 | WriteMult (D->F, M->FillVal, NewAddr - Addr); |
215 | PrintNumVal ("SF_ALIGN_LOAD" , NewAddr - Addr); |
216 | } |
217 | Addr = NewAddr; |
218 | } |
219 | |
220 | } |
221 | |
222 | /* Now write the segment to disk if it is not a BSS type segment and |
223 | ** if the memory area is the load area. |
224 | */ |
225 | if (DoWrite) { |
226 | unsigned long P = ftell (D->F); |
227 | SegWrite (D->Filename, D->F, S->Seg, BinWriteExpr, D); |
228 | PrintNumVal ("Wrote" , (unsigned long) (ftell (D->F) - P)); |
229 | } else if (M->Flags & MF_FILL) { |
230 | WriteMult (D->F, S->Seg->FillVal, S->Seg->Size); |
231 | PrintNumVal ("Filled" , (unsigned long) S->Seg->Size); |
232 | } |
233 | |
234 | /* If this was the load memory area, mark the segment as dumped */ |
235 | if (S->Load == M) { |
236 | S->Seg->Dumped = 1; |
237 | } |
238 | |
239 | /* Calculate the new address */ |
240 | Addr += S->Seg->Size; |
241 | } |
242 | |
243 | /* If a fill was requested, fill the remaining space */ |
244 | if ((M->Flags & MF_FILL) != 0 && M->FillLevel < M->Size) { |
245 | unsigned long ToFill = M->Size - M->FillLevel; |
246 | Print (stdout, 2, " Filling 0x%lx bytes with 0x%02x\n" , |
247 | ToFill, M->FillVal); |
248 | WriteMult (D->F, M->FillVal, ToFill); |
249 | M->FillLevel = M->Size; |
250 | } |
251 | } |
252 | |
253 | |
254 | |
255 | static int BinUnresolved (unsigned Name attribute ((unused)), void* D) |
256 | /* Called if an unresolved symbol is encountered */ |
257 | { |
258 | /* Unresolved symbols are an error in binary format. Bump the counter |
259 | ** and return zero telling the caller that the symbol is indeed |
260 | ** unresolved. |
261 | */ |
262 | ((BinDesc*) D)->Undef++; |
263 | return 0; |
264 | } |
265 | |
266 | |
267 | |
268 | void BinWriteTarget (BinDesc* D, struct File* F) |
269 | /* Write a binary output file */ |
270 | { |
271 | unsigned I; |
272 | |
273 | /* Place the filename in the control structure */ |
274 | D->Filename = GetString (F->Name); |
275 | |
276 | /* Check for unresolved symbols. The function BinUnresolved is called |
277 | ** if we get an unresolved symbol. |
278 | */ |
279 | D->Undef = 0; /* Reset the counter */ |
280 | CheckUnresolvedImports (BinUnresolved, D); |
281 | if (D->Undef > 0) { |
282 | /* We had unresolved symbols, cannot create output file */ |
283 | Error ("%u unresolved external(s) found - cannot create output file" , D->Undef); |
284 | } |
285 | |
286 | /* Open the file */ |
287 | D->F = fopen (D->Filename, "wb" ); |
288 | if (D->F == 0) { |
289 | Error ("Cannot open '%s': %s" , D->Filename, strerror (errno)); |
290 | } |
291 | |
292 | /* Keep the user happy */ |
293 | Print (stdout, 1, "Opened '%s'...\n" , D->Filename); |
294 | |
295 | /* Dump all memory areas */ |
296 | for (I = 0; I < CollCount (&F->MemoryAreas); ++I) { |
297 | /* Get this entry */ |
298 | MemoryArea* M = CollAtUnchecked (&F->MemoryAreas, I); |
299 | Print (stdout, 1, " Dumping '%s'\n" , GetString (M->Name)); |
300 | BinWriteMem (D, M); |
301 | } |
302 | |
303 | /* Close the file */ |
304 | if (fclose (D->F) != 0) { |
305 | Error ("Cannot write to '%s': %s" , D->Filename, strerror (errno)); |
306 | } |
307 | |
308 | /* Reset the file and filename */ |
309 | D->F = 0; |
310 | D->Filename = 0; |
311 | } |
312 | |