1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* library.c */ |
4 | /* */ |
5 | /* Library data structures and helpers for the ar65 archiver */ |
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 <string.h> |
38 | #include <errno.h> |
39 | |
40 | /* common */ |
41 | #include "cmdline.h" |
42 | #include "exprdefs.h" |
43 | #include "libdefs.h" |
44 | #include "print.h" |
45 | #include "symdefs.h" |
46 | #include "xmalloc.h" |
47 | |
48 | /* ar65 */ |
49 | #include "error.h" |
50 | #include "exports.h" |
51 | #include "fileio.h" |
52 | #include "global.h" |
53 | #include "library.h" |
54 | #include "objdata.h" |
55 | #include "objfile.h" |
56 | |
57 | |
58 | |
59 | /*****************************************************************************/ |
60 | /* Data */ |
61 | /*****************************************************************************/ |
62 | |
63 | |
64 | |
65 | /* Name of the library file */ |
66 | const char* LibName = 0; |
67 | static char* NewLibName = 0; |
68 | |
69 | /* File descriptor for the library file */ |
70 | static FILE* Lib = 0; |
71 | static FILE* NewLib = 0; |
72 | |
73 | /* The library header */ |
74 | static LibHeader = { |
75 | LIB_MAGIC, |
76 | LIB_VERSION, |
77 | 0, |
78 | 0 |
79 | }; |
80 | |
81 | |
82 | |
83 | /*****************************************************************************/ |
84 | /* Writing file data structures */ |
85 | /*****************************************************************************/ |
86 | |
87 | |
88 | |
89 | static void (void) |
90 | /* Read the header of a library file */ |
91 | { |
92 | /* Seek to position zero */ |
93 | fseek (Lib, 0, SEEK_SET); |
94 | |
95 | /* Read the header fields, checking magic and version */ |
96 | Header.Magic = Read32 (Lib); |
97 | if (Header.Magic != LIB_MAGIC) { |
98 | Error ("'%s' is not a valid library file" , LibName); |
99 | } |
100 | Header.Version = Read16 (Lib); |
101 | if (Header.Version != LIB_VERSION) { |
102 | Error ("Wrong data version in '%s'" , LibName); |
103 | } |
104 | Header.Flags = Read16 (Lib); |
105 | Header.IndexOffs = Read32 (Lib); |
106 | } |
107 | |
108 | |
109 | |
110 | static void ReadIndexEntry (void) |
111 | /* Read one entry in the index */ |
112 | { |
113 | /* Create a new entry and insert it into the list */ |
114 | ObjData* O = NewObjData (); |
115 | |
116 | /* Module name/flags/MTime/Start/Size */ |
117 | O->Name = ReadStr (Lib); |
118 | O->Flags = Read16 (Lib); |
119 | O->MTime = Read32 (Lib); |
120 | O->Start = Read32 (Lib); |
121 | O->Size = Read32 (Lib); |
122 | } |
123 | |
124 | |
125 | |
126 | static void ReadIndex (void) |
127 | /* Read the index of a library file */ |
128 | { |
129 | unsigned Count, I; |
130 | |
131 | /* Seek to the start of the index */ |
132 | fseek (Lib, Header.IndexOffs, SEEK_SET); |
133 | |
134 | /* Read the object file count and calculate the cross ref size */ |
135 | Count = ReadVar (Lib); |
136 | |
137 | /* Read all entries in the index */ |
138 | while (Count--) { |
139 | ReadIndexEntry (); |
140 | } |
141 | |
142 | /* Read basic object file data from the actual entries */ |
143 | for (I = 0; I < CollCount (&ObjPool); ++I) { |
144 | |
145 | /* Get the object file entry */ |
146 | ObjData* O = CollAtUnchecked (&ObjPool, I); |
147 | |
148 | /* Read data */ |
149 | ObjReadData (Lib, O); |
150 | } |
151 | } |
152 | |
153 | |
154 | |
155 | /*****************************************************************************/ |
156 | /* Writing file data structures */ |
157 | /*****************************************************************************/ |
158 | |
159 | |
160 | |
161 | static void (void) |
162 | /* Write the header to the library file */ |
163 | { |
164 | /* Seek to position zero */ |
165 | fseek (NewLib, 0, SEEK_SET); |
166 | |
167 | /* Write the header fields */ |
168 | Write32 (NewLib, Header.Magic); |
169 | Write16 (NewLib, Header.Version); |
170 | Write16 (NewLib, Header.Flags); |
171 | Write32 (NewLib, Header.IndexOffs); |
172 | } |
173 | |
174 | |
175 | |
176 | static void WriteIndexEntry (const ObjData* O) |
177 | /* Write one index entry */ |
178 | { |
179 | /* Module name/flags/MTime/start/size */ |
180 | WriteStr (NewLib, O->Name); |
181 | Write16 (NewLib, O->Flags & ~OBJ_HAVEDATA); |
182 | Write32 (NewLib, O->MTime); |
183 | Write32 (NewLib, O->Start); |
184 | Write32 (NewLib, O->Size); |
185 | } |
186 | |
187 | |
188 | |
189 | static void WriteIndex (void) |
190 | /* Write the index of a library file */ |
191 | { |
192 | unsigned I; |
193 | |
194 | /* Sync I/O in case the last operation was a read */ |
195 | fseek (NewLib, 0, SEEK_CUR); |
196 | |
197 | /* Remember the current offset in the header */ |
198 | Header.IndexOffs = ftell (NewLib); |
199 | |
200 | /* Write the object file count */ |
201 | WriteVar (NewLib, CollCount (&ObjPool)); |
202 | |
203 | /* Write the object files */ |
204 | for (I = 0; I < CollCount (&ObjPool); ++I) { |
205 | WriteIndexEntry (CollConstAt (&ObjPool, I)); |
206 | } |
207 | } |
208 | |
209 | |
210 | |
211 | /*****************************************************************************/ |
212 | /* High level stuff */ |
213 | /*****************************************************************************/ |
214 | |
215 | |
216 | |
217 | void LibOpen (const char* Name, int MustExist, int NeedTemp) |
218 | /* Open an existing library and a temporary copy. If MustExist is true, the |
219 | ** old library is expected to exist. If NeedTemp is true, a temporary library |
220 | ** is created. |
221 | */ |
222 | { |
223 | /* Remember the name */ |
224 | LibName = xstrdup (Name); |
225 | |
226 | /* Open the existing library for reading */ |
227 | Lib = fopen (Name, "rb" ); |
228 | if (Lib == 0) { |
229 | |
230 | /* File does not exist */ |
231 | if (MustExist) { |
232 | Error ("Library '%s' does not exist" , Name); |
233 | } else { |
234 | /* Announce the library's creation if ar65 is verbose. */ |
235 | Print (stdout, 1, |
236 | "%s: Library '%s' will be created.\n" , ProgName, Name); |
237 | } |
238 | |
239 | } else { |
240 | |
241 | /* We have an existing file: Read the header */ |
242 | ReadHeader (); |
243 | |
244 | /* Now read the existing index */ |
245 | ReadIndex (); |
246 | |
247 | } |
248 | |
249 | if (NeedTemp) { |
250 | |
251 | /* Create the temporary library name */ |
252 | NewLibName = xmalloc (strlen (Name) + strlen (".temp" ) + 1); |
253 | strcpy (NewLibName, Name); |
254 | strcat (NewLibName, ".temp" ); |
255 | |
256 | /* Create the temporary library */ |
257 | NewLib = fopen (NewLibName, "w+b" ); |
258 | if (NewLib == 0) { |
259 | Error ("Cannot create temporary library file: %s" , strerror (errno)); |
260 | } |
261 | |
262 | /* Write a dummy header to the temp file */ |
263 | WriteHeader (); |
264 | } |
265 | } |
266 | |
267 | |
268 | |
269 | unsigned long LibCopyTo (FILE* F, unsigned long Bytes) |
270 | /* Copy data from F to the temp library file, return the start position in |
271 | ** the temporary library file. |
272 | */ |
273 | { |
274 | unsigned char Buf [4096]; |
275 | |
276 | /* Remember the position */ |
277 | unsigned long Pos = ftell (NewLib); |
278 | |
279 | /* Copy loop */ |
280 | while (Bytes) { |
281 | unsigned Count = (Bytes > sizeof (Buf))? sizeof (Buf) : Bytes; |
282 | ReadData (F, Buf, Count); |
283 | WriteData (NewLib, Buf, Count); |
284 | Bytes -= Count; |
285 | } |
286 | |
287 | /* Return the start position */ |
288 | return Pos; |
289 | } |
290 | |
291 | |
292 | |
293 | void LibCopyFrom (unsigned long Pos, unsigned long Bytes, FILE* F) |
294 | /* Copy data from the library file into another file */ |
295 | { |
296 | unsigned char Buf [4096]; |
297 | |
298 | /* Seek to the correct position */ |
299 | fseek (Lib, Pos, SEEK_SET); |
300 | |
301 | /* Copy loop */ |
302 | while (Bytes) { |
303 | unsigned Count = (Bytes > sizeof (Buf))? sizeof (Buf) : Bytes; |
304 | ReadData (Lib, Buf, Count); |
305 | WriteData (F, Buf, Count); |
306 | Bytes -= Count; |
307 | } |
308 | } |
309 | |
310 | |
311 | |
312 | static void LibCheckExports (ObjData* O) |
313 | /* Insert all exports from the given object file into the global list |
314 | ** checking for duplicates. |
315 | */ |
316 | { |
317 | unsigned I; |
318 | |
319 | /* Let the user know what we do */ |
320 | Print (stdout, 2, "Module '%s' (%u exports):\n" , O->Name, CollCount (&O->Exports)); |
321 | |
322 | /* Insert the exports into the global table */ |
323 | for (I = 0; I < CollCount (&O->Exports); ++I) { |
324 | |
325 | /* Get the name of the export */ |
326 | const char* Name = CollConstAt (&O->Exports, I); |
327 | |
328 | /* Insert the name into the hash table */ |
329 | Print (stdout, 2, " %s\n" , Name); |
330 | ExpInsert (Name, O); |
331 | } |
332 | } |
333 | |
334 | |
335 | |
336 | void LibClose (void) |
337 | /* Write remaining data, close both files and copy the temp file to the old |
338 | ** filename |
339 | */ |
340 | { |
341 | /* Do we have a temporary library? */ |
342 | if (NewLib) { |
343 | |
344 | unsigned I; |
345 | unsigned char Buf [4096]; |
346 | size_t Count; |
347 | |
348 | /* Walk through the object file list, inserting exports into the |
349 | ** export list checking for duplicates. Copy any data that is still |
350 | ** in the old library into the new one. |
351 | */ |
352 | for (I = 0; I < CollCount (&ObjPool); ++I) { |
353 | |
354 | /* Get a pointer to the object */ |
355 | ObjData* O = CollAtUnchecked (&ObjPool, I); |
356 | |
357 | /* Check exports, make global export table */ |
358 | LibCheckExports (O); |
359 | |
360 | /* Copy data if needed */ |
361 | if ((O->Flags & OBJ_HAVEDATA) == 0) { |
362 | /* Data is still in the old library */ |
363 | fseek (Lib, O->Start, SEEK_SET); |
364 | O->Start = ftell (NewLib); |
365 | LibCopyTo (Lib, O->Size); |
366 | O->Flags |= OBJ_HAVEDATA; |
367 | } |
368 | } |
369 | |
370 | /* Write the index */ |
371 | WriteIndex (); |
372 | |
373 | /* Write the updated header */ |
374 | WriteHeader (); |
375 | |
376 | /* Close the file */ |
377 | if (Lib && fclose (Lib) != 0) { |
378 | Error ("Error closing library: %s" , strerror (errno)); |
379 | } |
380 | |
381 | /* Reopen the library and truncate it */ |
382 | Lib = fopen (LibName, "wb" ); |
383 | if (Lib == 0) { |
384 | Error ("Cannot open library '%s' for writing: %s" , |
385 | LibName, strerror (errno)); |
386 | } |
387 | |
388 | /* Copy the temporary library to the new one */ |
389 | fseek (NewLib, 0, SEEK_SET); |
390 | while ((Count = fread (Buf, 1, sizeof (Buf), NewLib)) != 0) { |
391 | if (fwrite (Buf, 1, Count, Lib) != Count) { |
392 | Error ("Cannot write to '%s': %s" , LibName, strerror (errno)); |
393 | } |
394 | } |
395 | } |
396 | |
397 | /* Close both files */ |
398 | if (Lib && fclose (Lib) != 0) { |
399 | Error ("Problem closing '%s': %s" , LibName, strerror (errno)); |
400 | } |
401 | if (NewLib && fclose (NewLib) != 0) { |
402 | Error ("Problem closing temporary library file: %s" , strerror (errno)); |
403 | } |
404 | if (NewLibName && remove (NewLibName) != 0) { |
405 | Error ("Problem deleting temporary library file: %s" , strerror (errno)); |
406 | } |
407 | } |
408 | |