1 /*****************************************************************************/
5 /* Object file handling for the ar65 archiver */
9 /* (C) 1998-2003 Ullrich von Bassewitz */
11 /* D-70794 Filderstadt */
12 /* EMail: uz@cc65.org */
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. */
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: */
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 */
32 /*****************************************************************************/
38 #if defined(__WATCOMC__) || defined(_MSC_VER) || defined(__MINGW32__)
39 /* The Windows compilers have the file in the wrong directory */
40 # include <sys/utime.h>
42 # include <sys/types.h> /* FreeBSD needs this */
60 /*****************************************************************************/
62 /*****************************************************************************/
66 static const char* GetModule (const char* Name)
67 /* Get a module name from the file name */
69 /* Make a module name from the file name */
70 const char* Module = Name + strlen (Name);
71 while (Module > Name) {
73 if (*Module == '/' || *Module == '\\') {
79 Error ("Cannot make module name from `%s'", Name);
86 void ObjReadHeader (FILE* Obj, ObjHeader* H, const char* Name)
87 /* Read the header of the object file checking the signature */
89 H->Magic = Read32 (Obj);
90 if (H->Magic != OBJ_MAGIC) {
91 Error ("`%s' is not an object file", Name);
93 H->Version = Read16 (Obj);
94 if (H->Version != OBJ_VERSION) {
95 Error ("Object file `%s' has wrong version", Name);
97 H->Flags = Read16 (Obj);
98 H->OptionOffs = Read32 (Obj);
99 H->OptionSize = Read32 (Obj);
100 H->FileOffs = Read32 (Obj);
101 H->FileSize = Read32 (Obj);
102 H->SegOffs = Read32 (Obj);
103 H->SegSize = Read32 (Obj);
104 H->ImportOffs = Read32 (Obj);
105 H->ImportSize = Read32 (Obj);
106 H->ExportOffs = Read32 (Obj);
107 H->ExportSize = Read32 (Obj);
108 H->DbgSymOffs = Read32 (Obj);
109 H->DbgSymSize = Read32 (Obj);
110 H->LineInfoOffs = Read32 (Obj);
111 H->LineInfoSize = Read32 (Obj);
112 H->StrPoolOffs = Read32 (Obj);
113 H->StrPoolSize = Read32 (Obj);
114 H->AssertOffs = Read32 (Obj);
115 H->AssertSize = Read32 (Obj);
116 H->ScopeOffs = Read32 (Obj);
117 H->ScopeSize = Read32 (Obj);
122 void ObjWriteHeader (FILE* Obj, ObjHeader* H)
123 /* Write the header of the object file */
125 Write32 (Obj, H->Magic);
126 Write16 (Obj, H->Version);
127 Write16 (Obj, H->Flags);
128 Write32 (Obj, H->OptionOffs);
129 Write32 (Obj, H->OptionSize);
130 Write32 (Obj, H->FileOffs);
131 Write32 (Obj, H->FileSize);
132 Write32 (Obj, H->SegOffs);
133 Write32 (Obj, H->SegSize);
134 Write32 (Obj, H->ImportOffs);
135 Write32 (Obj, H->ImportSize);
136 Write32 (Obj, H->ExportOffs);
137 Write32 (Obj, H->ExportSize);
138 Write32 (Obj, H->DbgSymOffs);
139 Write32 (Obj, H->DbgSymSize);
140 Write32 (Obj, H->LineInfoOffs);
141 Write32 (Obj, H->LineInfoSize);
142 Write32 (Obj, H->StrPoolOffs);
143 Write32 (Obj, H->StrPoolSize);
144 Write32 (Obj, H->AssertOffs);
145 Write32 (Obj, H->AssertSize);
146 Write32 (Obj, H->ScopeOffs);
147 Write32 (Obj, H->ScopeSize);
152 void ObjAdd (const char* Name)
153 /* Add an object file to the library */
161 /* Open the object file */
162 FILE* Obj = fopen (Name, "rb");
164 Error ("Could not open `%s': %s", Name, strerror (errno));
167 /* Get the modification time of the object file. There a race condition
168 * here, since we cannot use fileno() (non standard identifier in standard
169 * header file), and therefore not fstat. When using stat with the
170 * file name, there's a risk that the file was deleted and recreated
171 * while it was open. Since mtime and size are only used to check
172 * if a file has changed in the debugger, we will ignore this problem
175 if (stat (Name, &StatBuf) != 0) {
176 Error ("Cannot stat object file `%s': %s", Name, strerror (errno));
179 /* Read and check the header */
180 ObjReadHeader (Obj, &H, Name);
182 /* Make a module name from the file name */
183 Module = GetModule (Name);
185 /* Check if we already have a module with this name */
186 O = FindObjData (Module);
188 /* Not found, create a new entry */
191 /* Found - check the file modification times of the internal copy
192 * and the external one.
194 if (difftime ((time_t)O->MTime, StatBuf.st_mtime) > 0.0) {
195 Warning ("Replacing module `%s' by older version", O->Name);
199 /* Initialize the object module data structure */
200 O->Name = xstrdup (Module);
201 O->Flags = OBJ_HAVEDATA;
202 O->MTime = StatBuf.st_mtime;
203 O->ImportSize = H.ImportSize;
204 O->Imports = xmalloc (O->ImportSize);
205 O->ExportSize = H.ExportSize;
206 O->Exports = xmalloc (O->ExportSize);
208 /* Read imports and exports */
209 fseek (Obj, H.ImportOffs, SEEK_SET);
210 ReadData (Obj, O->Imports, O->ImportSize);
211 fseek (Obj, H.ExportOffs, SEEK_SET);
212 ReadData (Obj, O->Exports, O->ExportSize);
214 /* Read the string pool */
215 fseek (Obj, H.StrPoolOffs, SEEK_SET);
216 O->StringCount = ReadVar (Obj);
217 O->Strings = xmalloc (O->StringCount * sizeof (char*));
218 for (I = 0; I < O->StringCount; ++I) {
219 O->Strings[I] = ReadStr (Obj);
222 /* Skip the object file header */
223 O->Start = ftell (NewLib);
224 fseek (NewLib, OBJ_HDR_SIZE, SEEK_CUR);
226 /* Copy the remaining sections */
227 fseek (Obj, H.DbgSymOffs, SEEK_SET);
228 H.DbgSymOffs = LibCopyTo (Obj, H.DbgSymSize) - O->Start;
229 fseek (Obj, H.OptionOffs, SEEK_SET);
230 H.OptionOffs = LibCopyTo (Obj, H.OptionSize) - O->Start;
231 fseek (Obj, H.SegOffs, SEEK_SET);
232 H.SegOffs = LibCopyTo (Obj, H.SegSize) - O->Start;
233 fseek (Obj, H.FileOffs, SEEK_SET);
234 H.FileOffs = LibCopyTo (Obj, H.FileSize) - O->Start;
235 fseek (Obj, H.LineInfoOffs, SEEK_SET);
236 H.LineInfoOffs = LibCopyTo (Obj, H.LineInfoSize) - O->Start;
237 fseek (Obj, H.AssertOffs, SEEK_SET);
238 H.AssertOffs = LibCopyTo (Obj, H.AssertSize) - O->Start;
239 fseek (Obj, H.ScopeOffs, SEEK_SET);
240 H.ScopeOffs = LibCopyTo (Obj, H.ScopeSize) - O->Start;
242 /* Calculate the amount of data written */
243 O->Size = ftell (NewLib) - O->Start;
245 /* Clear the remaining header fields */
246 H.ImportOffs = H.ImportSize = 0;
247 H.ExportOffs = H.ExportSize = 0;
248 H.StrPoolOffs = H.StrPoolSize = 0;
250 /* Seek back and write the updated header */
251 fseek (NewLib, O->Start, SEEK_SET);
252 ObjWriteHeader (NewLib, &H);
254 /* Now seek again to end of file */
255 fseek (NewLib, 0, SEEK_END);
257 /* Done, close the file (we read it only, so no error check) */
263 void ObjExtract (const char* Name)
264 /* Extract a module from the library */
266 unsigned long ImportStart;
267 unsigned long ExportStart;
268 unsigned long StrPoolStart;
269 unsigned long StrPoolSize;
275 /* Make a module name from the file name */
276 const char* Module = GetModule (Name);
278 /* Try to find the module in the library */
279 ObjData* O = FindObjData (Module);
281 /* Bail out if the module does not exist */
283 Error ("Module `%s' not found in library", Module);
286 /* Open the output file */
287 Obj = fopen (Name, "w+b");
289 Error ("Cannot open target file `%s': %s", Name, strerror (errno));
292 /* Copy anything to the new file that has no special handling */
293 LibCopyFrom (O->Start, O->Size, Obj);
295 /* Write imports and exports */
296 ImportStart = ftell (Obj);
297 WriteData (Obj, O->Imports, O->ImportSize);
298 ExportStart = ftell (Obj);
299 WriteData (Obj, O->Exports, O->ExportSize);
301 /* Write the string pool */
302 StrPoolStart = ftell (Obj);
303 WriteVar (Obj, O->StringCount);
304 for (I = 0; I < O->StringCount; ++I) {
305 WriteStr (Obj, O->Strings[I]);
307 StrPoolSize = ftell (Obj) - StrPoolStart;
309 /* Seek back and read the header */
310 fseek (Obj, 0, SEEK_SET);
311 ObjReadHeader (Obj, &H, Name);
313 /* Update the header fields */
314 H.ImportOffs = ImportStart;
315 H.ImportSize = O->ImportSize;
316 H.ExportOffs = ExportStart;
317 H.ExportSize = O->ExportSize;
318 H.StrPoolOffs = StrPoolStart;
319 H.StrPoolSize = StrPoolSize;
321 /* Write the changed header */
322 fseek (Obj, 0, SEEK_SET);
323 ObjWriteHeader (Obj, &H);
326 if (fclose (Obj) != 0) {
327 Error ("Problem closing object file `%s': %s", Name, strerror (errno));
330 /* Set access and modification time */
332 U.modtime = O->MTime;
333 if (utime (Name, &U) != 0) {
334 Error ("Cannot set mod time on `%s': %s", Name, strerror (errno));