| 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 | |