1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* objfile.c */ |
4 | /* */ |
5 | /* Object file handling for the ar65 archiver */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 1998-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 <string.h> |
37 | #include <errno.h> |
38 | |
39 | /* common */ |
40 | #include "cddefs.h" |
41 | #include "exprdefs.h" |
42 | #include "filestat.h" |
43 | #include "filetime.h" |
44 | #include "fname.h" |
45 | #include "symdefs.h" |
46 | #include "xmalloc.h" |
47 | |
48 | /* ar65 */ |
49 | #include "error.h" |
50 | #include "objdata.h" |
51 | #include "fileio.h" |
52 | #include "library.h" |
53 | #include "objfile.h" |
54 | |
55 | |
56 | |
57 | /*****************************************************************************/ |
58 | /* Code */ |
59 | /*****************************************************************************/ |
60 | |
61 | |
62 | |
63 | static const char* GetModule (const char* Name) |
64 | /* Get a module name from the file name */ |
65 | { |
66 | /* Make a module name from the file name */ |
67 | const char* Module = FindName (Name); |
68 | |
69 | /* Must not end with a path separator */ |
70 | if (*Module == 0) { |
71 | Error ("Cannot make module name from '%s'" , Name); |
72 | } |
73 | |
74 | /* Done */ |
75 | return Module; |
76 | } |
77 | |
78 | |
79 | |
80 | static void (FILE* Obj, ObjHeader* H, const char* Name) |
81 | /* Read the header of the object file checking the signature */ |
82 | { |
83 | H->Magic = Read32 (Obj); |
84 | if (H->Magic != OBJ_MAGIC) { |
85 | Error ("'%s' is not an object file" , Name); |
86 | } |
87 | H->Version = Read16 (Obj); |
88 | if (H->Version != OBJ_VERSION) { |
89 | Error ("Object file '%s' has wrong version" , Name); |
90 | } |
91 | H->Flags = Read16 (Obj); |
92 | H->OptionOffs = Read32 (Obj); |
93 | H->OptionSize = Read32 (Obj); |
94 | H->FileOffs = Read32 (Obj); |
95 | H->FileSize = Read32 (Obj); |
96 | H->SegOffs = Read32 (Obj); |
97 | H->SegSize = Read32 (Obj); |
98 | H->ImportOffs = Read32 (Obj); |
99 | H->ImportSize = Read32 (Obj); |
100 | H->ExportOffs = Read32 (Obj); |
101 | H->ExportSize = Read32 (Obj); |
102 | H->DbgSymOffs = Read32 (Obj); |
103 | H->DbgSymSize = Read32 (Obj); |
104 | H->LineInfoOffs = Read32 (Obj); |
105 | H->LineInfoSize = Read32 (Obj); |
106 | H->StrPoolOffs = Read32 (Obj); |
107 | H->StrPoolSize = Read32 (Obj); |
108 | H->AssertOffs = Read32 (Obj); |
109 | H->AssertSize = Read32 (Obj); |
110 | H->ScopeOffs = Read32 (Obj); |
111 | H->ScopeSize = Read32 (Obj); |
112 | H->SpanOffs = Read32 (Obj); |
113 | H->SpanSize = Read32 (Obj); |
114 | } |
115 | |
116 | |
117 | |
118 | static void SkipExpr (FILE* F) |
119 | /* Skip an expression in F */ |
120 | { |
121 | /* Get the operation and skip it */ |
122 | unsigned char Op = Read8 (F); |
123 | |
124 | /* Handle then different expression nodes */ |
125 | switch (Op) { |
126 | |
127 | case EXPR_NULL: |
128 | break; |
129 | |
130 | case EXPR_LITERAL: |
131 | /* 32 bit literal value */ |
132 | (void) Read32 (F); |
133 | break; |
134 | |
135 | case EXPR_SYMBOL: |
136 | /* Variable seized symbol index */ |
137 | (void) ReadVar (F); |
138 | break; |
139 | |
140 | case EXPR_SECTION: |
141 | /* 8 bit segment number */ |
142 | (void) Read8 (F); |
143 | break; |
144 | |
145 | default: |
146 | /* What's left are unary and binary nodes */ |
147 | SkipExpr (F); /* Left */ |
148 | SkipExpr (F); /* right */ |
149 | break; |
150 | } |
151 | } |
152 | |
153 | |
154 | |
155 | static void SkipLineInfoList (FILE* F) |
156 | /* Skip a list of line infos in F */ |
157 | { |
158 | /* Number of indices preceeds the list */ |
159 | unsigned long Count = ReadVar (F); |
160 | |
161 | /* Skip indices */ |
162 | while (Count--) { |
163 | (void) ReadVar (F); |
164 | } |
165 | } |
166 | |
167 | |
168 | |
169 | void ObjReadData (FILE* F, ObjData* O) |
170 | /* Read object file data from the given file. The function expects the Name |
171 | ** and Start fields to be valid. Header and basic data are read. |
172 | */ |
173 | { |
174 | unsigned long Count; |
175 | |
176 | /* Seek to the start of the object file data */ |
177 | fseek (F, O->Start, SEEK_SET); |
178 | |
179 | /* Read the object file header */ |
180 | ObjReadHeader (F, &O->Header, O->Name); |
181 | |
182 | /* Read the string pool */ |
183 | fseek (F, O->Start + O->Header.StrPoolOffs, SEEK_SET); |
184 | Count = ReadVar (F); |
185 | CollGrow (&O->Strings, Count); |
186 | while (Count--) { |
187 | CollAppend (&O->Strings, ReadStr (F)); |
188 | } |
189 | |
190 | /* Read the exports */ |
191 | fseek (F, O->Start + O->Header.ExportOffs, SEEK_SET); |
192 | Count = ReadVar (F); |
193 | CollGrow (&O->Exports, Count); |
194 | while (Count--) { |
195 | |
196 | unsigned char ConDes[CD_TYPE_COUNT]; |
197 | |
198 | /* Skip data until we get to the name */ |
199 | unsigned Type = ReadVar (F); |
200 | (void) Read8 (F); /* AddrSize */ |
201 | ReadData (F, ConDes, SYM_GET_CONDES_COUNT (Type)); |
202 | |
203 | /* Now this is what we actually need: The name of the export */ |
204 | CollAppend (&O->Exports, CollAt (&O->Strings, ReadVar (F))); |
205 | |
206 | /* Skip the export value */ |
207 | if (SYM_IS_EXPR (Type)) { |
208 | /* Expression tree */ |
209 | SkipExpr (F); |
210 | } else { |
211 | /* Literal value */ |
212 | (void) Read32 (F); |
213 | } |
214 | |
215 | /* Skip the size if necessary */ |
216 | if (SYM_HAS_SIZE (Type)) { |
217 | (void) ReadVar (F); |
218 | } |
219 | |
220 | /* Line info indices */ |
221 | SkipLineInfoList (F); |
222 | SkipLineInfoList (F); |
223 | } |
224 | } |
225 | |
226 | |
227 | |
228 | void ObjAdd (const char* Name) |
229 | /* Add an object file to the library */ |
230 | { |
231 | struct stat StatBuf; |
232 | const char* Module; |
233 | ObjHeader H; |
234 | ObjData* O; |
235 | |
236 | /* Open the object file */ |
237 | FILE* Obj = fopen (Name, "rb" ); |
238 | if (Obj == 0) { |
239 | Error ("Could not open '%s': %s" , Name, strerror (errno)); |
240 | } |
241 | |
242 | /* Get the modification time of the object file. There's a race condition |
243 | ** here, since we cannot use fileno() (non-standard identifier in standard |
244 | ** header file), and therefore not fstat. When using stat with the |
245 | ** file name, there's a risk that the file was deleted and recreated |
246 | ** while it was open. Since mtime and size are only used to check |
247 | ** if a file has changed in the debugger, we will ignore this problem |
248 | ** here. |
249 | */ |
250 | if (FileStat (Name, &StatBuf) != 0) { |
251 | Error ("Cannot stat object file '%s': %s" , Name, strerror (errno)); |
252 | } |
253 | |
254 | /* Read and check the header */ |
255 | ObjReadHeader (Obj, &H, Name); |
256 | |
257 | /* Make a module name from the file name */ |
258 | Module = GetModule (Name); |
259 | |
260 | /* Check if we already have a module with this name */ |
261 | O = FindObjData (Module); |
262 | if (O == 0) { |
263 | /* Not found, create a new entry */ |
264 | O = NewObjData (); |
265 | } else { |
266 | /* Found - check the file modification times of the internal copy |
267 | ** and the external one. |
268 | */ |
269 | if (difftime ((time_t)O->MTime, StatBuf.st_mtime) > 0.0) { |
270 | Warning ("Replacing module '%s' by older version in library '%s'" , |
271 | O->Name, LibName); |
272 | } |
273 | |
274 | /* Free data */ |
275 | ClearObjData (O); |
276 | } |
277 | |
278 | /* Initialize the object module data structure */ |
279 | O->Name = xstrdup (Module); |
280 | O->Flags = OBJ_HAVEDATA; |
281 | O->MTime = (unsigned long) StatBuf.st_mtime; |
282 | O->Start = 0; |
283 | |
284 | /* Determine the file size. Note: Race condition here */ |
285 | fseek (Obj, 0, SEEK_END); |
286 | O->Size = ftell (Obj); |
287 | |
288 | /* Read the basic data from the object file */ |
289 | ObjReadData (Obj, O); |
290 | |
291 | /* Copy the complete object data to the library file and update the |
292 | ** starting offset |
293 | */ |
294 | fseek (Obj, 0, SEEK_SET); |
295 | O->Start = LibCopyTo (Obj, O->Size); |
296 | |
297 | /* Done, close the file (we read it only, so no error check) */ |
298 | fclose (Obj); |
299 | } |
300 | |
301 | |
302 | |
303 | void (const char* Name) |
304 | /* Extract a module from the library */ |
305 | { |
306 | FILE* Obj; |
307 | |
308 | /* Make a module name from the file name */ |
309 | const char* Module = GetModule (Name); |
310 | |
311 | /* Try to find the module in the library */ |
312 | const ObjData* O = FindObjData (Module); |
313 | |
314 | /* Bail out if the module does not exist */ |
315 | if (O == 0) { |
316 | Error ("Module '%s' not found in library '%s'" , Module, LibName); |
317 | } |
318 | |
319 | /* Open the output file */ |
320 | Obj = fopen (Name, "w+b" ); |
321 | if (Obj == 0) { |
322 | Error ("Cannot open target file '%s': %s" , Name, strerror (errno)); |
323 | } |
324 | |
325 | /* Copy the complete object file data from the library to the new object |
326 | ** file. |
327 | */ |
328 | LibCopyFrom (O->Start, O->Size, Obj); |
329 | |
330 | /* Close the file */ |
331 | if (fclose (Obj) != 0) { |
332 | Error ("Problem closing object file '%s': %s" , Name, strerror (errno)); |
333 | } |
334 | |
335 | /* Set access and modification time */ |
336 | if (SetFileTimes (Name, O->MTime) != 0) { |
337 | Error ("Cannot set mod time on '%s': %s" , Name, strerror (errno)); |
338 | } |
339 | } |
340 | |