1 /* 2 * Copyright 2007, Ingo Weinhold <bonefish@cs.tu-berlin.de>. 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 6 #include <errno.h> 7 #include <fcntl.h> 8 #include <inttypes.h> 9 #include <stdarg.h> 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <string.h> 13 #include <unistd.h> 14 15 #include <algorithm> 16 #include <string> 17 18 // We use htonl(), which is defined in <ByteOrder.h> on BeOS R5. 19 #ifdef HAIKU_HOST_PLATFORM_BEOS 20 #include <ByteOrder.h> 21 #else 22 #include <arpa/inet.h> 23 #endif 24 25 26 using std::string; 27 using std::max; 28 using std::min; 29 30 31 // #pragma mark - ELF definitions 32 33 34 // types 35 typedef uint32_t Elf32_Addr; 36 typedef uint16_t Elf32_Half; 37 typedef uint32_t Elf32_Off; 38 typedef int32_t Elf32_Sword; 39 typedef uint32_t Elf32_Word; 40 41 // e_ident indices 42 #define EI_MAG0 0 43 #define EI_MAG1 1 44 #define EI_MAG2 2 45 #define EI_MAG3 3 46 #define EI_CLASS 4 47 #define EI_DATA 5 48 #define EI_VERSION 6 49 #define EI_PAD 7 50 #define EI_NIDENT 16 51 52 // object file header 53 typedef struct { 54 unsigned char e_ident[EI_NIDENT]; 55 Elf32_Half e_type; 56 Elf32_Half e_machine; 57 Elf32_Word e_version; 58 Elf32_Addr e_entry; 59 Elf32_Off e_phoff; 60 Elf32_Off e_shoff; 61 Elf32_Word e_flags; 62 Elf32_Half e_ehsize; 63 Elf32_Half e_phentsize; 64 Elf32_Half e_phnum; 65 Elf32_Half e_shentsize; 66 Elf32_Half e_shnum; 67 Elf32_Half e_shstrndx; 68 } Elf32_Ehdr; 69 70 // e_ident EI_CLASS and EI_DATA values 71 #define ELFCLASSNONE 0 72 #define ELFCLASS32 1 73 #define ELFCLASS64 2 74 #define ELFDATANONE 0 75 #define ELFDATA2LSB 1 76 #define ELFDATA2MSB 2 77 78 // program header 79 typedef struct { 80 Elf32_Word p_type; 81 Elf32_Off p_offset; 82 Elf32_Addr p_vaddr; 83 Elf32_Addr p_paddr; 84 Elf32_Word p_filesz; 85 Elf32_Word p_memsz; 86 Elf32_Word p_flags; 87 Elf32_Word p_align; 88 } Elf32_Phdr; 89 90 // p_type 91 #define PT_NULL 0 92 #define PT_LOAD 1 93 #define PT_DYNAMIC 2 94 #define PT_INTERP 3 95 #define PT_NOTE 4 96 #define PT_SHLIB 5 97 #define PT_PHDIR 6 98 #define PT_LOPROC 0x70000000 99 #define PT_HIPROC 0x7fffffff 100 101 // section header 102 typedef struct { 103 Elf32_Word sh_name; 104 Elf32_Word sh_type; 105 Elf32_Word sh_flags; 106 Elf32_Addr sh_addr; 107 Elf32_Off sh_offset; 108 Elf32_Word sh_size; 109 Elf32_Word sh_link; 110 Elf32_Word sh_info; 111 Elf32_Word sh_addralign; 112 Elf32_Word sh_entsize; 113 } Elf32_Shdr; 114 115 // sh_type values 116 #define SHT_NULL 0 117 #define SHT_PROGBITS 1 118 #define SHT_SYMTAB 2 119 #define SHT_STRTAB 3 120 #define SHT_RELA 4 121 #define SHT_HASH 5 122 #define SHT_DYNAMIC 6 123 #define SHT_NOTE 7 124 #define SHT_NOBITS 8 125 #define SHT_REL 9 126 #define SHT_SHLIB 10 127 #define SHT_DYNSYM 11 128 #define SHT_LOPROC 0x70000000 129 #define SHT_HIPROC 0x7fffffff 130 #define SHT_LOUSER 0x80000000 131 #define SHT_HIUSER 0xffffffff 132 133 // special section indexes 134 #define SHN_UNDEF 0 135 136 static const uint32_t kMaxELFHeaderSize = sizeof(Elf32_Ehdr) + 32; 137 static const char kELFFileMagic[4] = { 0x7f, 'E', 'L', 'F' }; 138 139 140 // #pragma mark - Usage 141 142 // usage 143 static const char *kUsage = 144 "Usage: %s <file> <revision>\n" 145 "\n" 146 "Finds the haiku revision section in ELF object file <file> and replaces the\n" 147 "writes the number given by <revision> into the first 32 bits of the\n" 148 "section.\n" 149 ; 150 151 // command line args 152 static int sArgc; 153 static const char *const *sArgv; 154 155 // print_usage 156 void 157 print_usage(bool error) 158 { 159 // get nice program name 160 const char *programName = (sArgc > 0 ? sArgv[0] : "resattr"); 161 if (const char *lastSlash = strrchr(programName, '/')) 162 programName = lastSlash + 1; 163 164 // print usage 165 fprintf((error ? stderr : stdout), kUsage, programName); 166 } 167 168 // print_usage_and_exit 169 static 170 void 171 print_usage_and_exit(bool error) 172 { 173 print_usage(error); 174 exit(error ? 1 : 0); 175 } 176 177 178 // #pragma mark - Exception 179 180 181 class Exception { 182 public: 183 // constructor 184 Exception() 185 : fError(errno), 186 fDescription() 187 { 188 } 189 190 // constructor 191 Exception(const char* format,...) 192 : fError(errno), 193 fDescription() 194 { 195 va_list args; 196 va_start(args, format); 197 SetTo(errno, format, args); 198 va_end(args); 199 } 200 201 // constructor 202 Exception(int error) 203 : fError(error), 204 fDescription() 205 { 206 } 207 208 // constructor 209 Exception(int error, const char* format,...) 210 : fError(error), 211 fDescription() 212 { 213 va_list args; 214 va_start(args, format); 215 SetTo(error, format, args); 216 va_end(args); 217 } 218 219 // copy constructor 220 Exception(const Exception& exception) 221 : fError(exception.fError), 222 fDescription(exception.fDescription) 223 { 224 } 225 226 // destructor 227 ~Exception() 228 { 229 } 230 231 // SetTo 232 void SetTo(int error, const char* format, va_list arg) 233 { 234 char buffer[2048]; 235 vsprintf(buffer, format, arg); 236 fError = error; 237 fDescription = buffer; 238 } 239 240 // Error 241 int Error() const 242 { 243 return fError; 244 } 245 246 // Description 247 const string& Description() const 248 { 249 return fDescription; 250 } 251 252 private: 253 int fError; 254 string fDescription; 255 }; 256 257 258 // #pragma mark - ELFObject 259 260 261 struct SectionInfo { 262 uint32_t type; 263 uint32_t offset; 264 uint32_t size; 265 const char* name; 266 }; 267 268 269 class ELFObject { 270 public: 271 ELFObject() 272 : fFD(-1), 273 fSectionHeaderStrings(NULL), 274 fSectionHeaderStringsLength(0) 275 { 276 } 277 278 ~ELFObject() 279 { 280 Unset(); 281 } 282 283 void Unset() 284 { 285 if (fFD >= 0) { 286 close(fFD); 287 fFD = -1; 288 } 289 290 delete[] fSectionHeaderStrings; 291 fSectionHeaderStrings = NULL; 292 } 293 294 void SetTo(const char* fileName) 295 { 296 Unset(); 297 298 // open the file 299 fFD = open(fileName, O_RDWR); 300 if (fFD < 0) 301 throw Exception("Failed to open \"%s\"", fileName); 302 303 // get the file size 304 fFileSize = FileSize(); 305 if (fFileSize < 0) 306 throw Exception("Failed to get the file size."); 307 308 // read ELF header 309 Elf32_Ehdr fileHeader; 310 Read(0, &fileHeader, sizeof(Elf32_Ehdr), "Failed to read ELF header."); 311 312 // check data encoding (endianess) 313 switch (fileHeader.e_ident[EI_DATA]) { 314 case ELFDATA2LSB: 315 fHostEndianess = (htonl(1) != 1); 316 break; 317 case ELFDATA2MSB: 318 fHostEndianess = (htonl(1) == 1); 319 break; 320 default: 321 case ELFDATANONE: 322 throw Exception(EIO, "Unsupported ELF data encoding."); 323 break; 324 } 325 326 // get the header values 327 fELFHeaderSize = GetUInt16(fileHeader.e_ehsize); 328 fSectionHeaderTableOffset = GetUInt32(fileHeader.e_shoff); 329 fSectionHeaderSize = GetUInt16(fileHeader.e_shentsize); 330 fSectionHeaderCount = GetUInt16(fileHeader.e_shnum); 331 bool hasSectionHeaderTable = (fSectionHeaderTableOffset != 0); 332 333 // check the sanity of the header values 334 // ELF header size 335 if (fELFHeaderSize < sizeof(Elf32_Ehdr) || fELFHeaderSize > kMaxELFHeaderSize) { 336 throw Exception(EIO, 337 "Invalid ELF header: invalid ELF header size: %lu.", 338 fELFHeaderSize); 339 } 340 341 // section header table offset and entry count/size 342 uint32_t sectionHeaderTableSize = 0; 343 if (hasSectionHeaderTable) { 344 if (fSectionHeaderTableOffset < fELFHeaderSize 345 || fSectionHeaderTableOffset > fFileSize) { 346 throw Exception(EIO, "Invalid ELF header: invalid section " 347 "header table offset: %lu.", 348 fSectionHeaderTableOffset); 349 } 350 sectionHeaderTableSize = fSectionHeaderSize * fSectionHeaderCount; 351 if (fSectionHeaderSize < sizeof(Elf32_Shdr) 352 || fSectionHeaderTableOffset + sectionHeaderTableSize 353 > fFileSize) { 354 throw Exception(EIO, "Invalid ELF header: section header " 355 "table exceeds file: %lu.", 356 fSectionHeaderTableOffset 357 + sectionHeaderTableSize); 358 } 359 360 361 // load section header string section 362 uint16_t sectionHeaderStringSectionIndex 363 = GetUInt16(fileHeader.e_shstrndx); 364 if (sectionHeaderStringSectionIndex != SHN_UNDEF) { 365 if (sectionHeaderStringSectionIndex >= fSectionHeaderCount) { 366 throw Exception(EIO, "Invalid ELF header: invalid section " 367 "header string section index: %lu.", 368 sectionHeaderStringSectionIndex); 369 } 370 371 // get the section info 372 SectionInfo info; 373 if (_ReadSectionHeader(sectionHeaderStringSectionIndex, 374 info)) { 375 fSectionHeaderStrings = new char[info.size + 1]; 376 Read(info.offset, fSectionHeaderStrings, info.size, 377 "Failed to read section header string section."); 378 fSectionHeaderStringsLength = info.size; 379 // null-terminate to be on the safe side 380 fSectionHeaderStrings[info.size] = '\0'; 381 } 382 } 383 } 384 } 385 386 bool FindSectionByName(const char* name, SectionInfo& foundInfo) 387 { 388 // can't find the section by name without section names 389 if (!fSectionHeaderStrings) 390 return false; 391 392 // iterate through the section headers 393 for (int32_t i = 0; i < (int32_t)fSectionHeaderCount; i++) { 394 SectionInfo info; 395 if (_ReadSectionHeader(i, info)) { 396 //printf("section %3d: offset: %7d, size: %7d, name: %s\n", i, info.offset, info.size, info.name); 397 if (strcmp(info.name, name) == 0) { 398 foundInfo = info; 399 return true; 400 } 401 } 402 } 403 404 return false; 405 } 406 407 void Read(off_t position, void* buffer, size_t size, 408 const char *errorMessage = NULL) 409 { 410 if (lseek(fFD, position, SEEK_SET) < 0) 411 throw Exception(errorMessage); 412 413 ssize_t bytesRead = read(fFD, buffer, size); 414 if (bytesRead < 0) 415 throw Exception(errorMessage); 416 417 if ((size_t)bytesRead != size) { 418 if (errorMessage) { 419 throw Exception("%s Read too few bytes (%d/%d).", 420 errorMessage, (int)bytesRead, (int)size); 421 } else { 422 throw Exception("Read too few bytes (%ld/%lu).", 423 (int)bytesRead, (int)size); 424 } 425 } 426 } 427 428 void Write(off_t position, const void* buffer, size_t size, 429 const char *errorMessage = NULL) 430 { 431 if (lseek(fFD, position, SEEK_SET) < 0) 432 throw Exception(errorMessage); 433 434 ssize_t bytesWritten = write(fFD, buffer, size); 435 if (bytesWritten < 0) 436 throw Exception(errorMessage); 437 438 if ((size_t)bytesWritten != size) { 439 if (errorMessage) { 440 throw Exception("%s Wrote too few bytes (%d/%d).", 441 errorMessage, (int)bytesWritten, (int)size); 442 } else { 443 throw Exception("Wrote too few bytes (%ld/%lu).", 444 (int)bytesWritten, (int)size); 445 } 446 } 447 } 448 449 off_t FileSize() 450 { 451 off_t currentPos = lseek(fFD, 0, SEEK_END); 452 if (currentPos < 0) 453 return -1; 454 455 return lseek(fFD, currentPos, SEEK_SET); 456 } 457 458 // GetInt16 459 int16_t GetInt16(int16_t value) 460 { 461 return (fHostEndianess ? value : _SwapUInt16(value)); 462 } 463 464 // GetUInt16 465 uint16_t GetUInt16(uint16_t value) 466 { 467 return (fHostEndianess ? value : _SwapUInt16(value)); 468 } 469 470 // GetInt32 471 int32_t GetInt32(int32_t value) 472 { 473 return (fHostEndianess ? value : _SwapUInt32(value)); 474 } 475 476 // GetUInt32 477 uint32_t GetUInt32(uint32_t value) 478 { 479 return (fHostEndianess ? value : _SwapUInt32(value)); 480 } 481 482 private: 483 bool _ReadSectionHeader(int index, SectionInfo& info) 484 { 485 uint32_t shOffset = fSectionHeaderTableOffset 486 + index * fSectionHeaderSize; 487 Elf32_Shdr sectionHeader; 488 Read(shOffset, §ionHeader, sizeof(Elf32_Shdr), 489 "Failed to read ELF section header."); 490 491 // get the header values 492 uint32_t type = GetUInt32(sectionHeader.sh_type); 493 uint32_t offset = GetUInt32(sectionHeader.sh_offset); 494 uint32_t size = GetUInt32(sectionHeader.sh_size); 495 uint32_t nameIndex = GetUInt32(sectionHeader.sh_name); 496 497 // check the values 498 // SHT_NULL marks the header unused, 499 if (type == SHT_NULL) 500 return false; 501 502 // SHT_NOBITS sections take no space in the file 503 if (type != SHT_NOBITS) { 504 if (offset < fELFHeaderSize || offset > fFileSize) { 505 throw Exception(EIO, "Invalid ELF section header: " 506 "invalid section offset: %lu.", offset); 507 } 508 uint32_t sectionEnd = offset + size; 509 if (sectionEnd > fFileSize) { 510 throw Exception(EIO, "Invalid ELF section header: " 511 "section exceeds file: %lu.", sectionEnd); 512 } 513 } 514 515 // get name, if we have a string section 516 if (fSectionHeaderStrings) { 517 if (nameIndex >= (uint32_t)fSectionHeaderStringsLength) { 518 throw Exception(EIO, "Invalid ELF section header: " 519 "invalid name index: %lu.", nameIndex); 520 } 521 info.name = fSectionHeaderStrings + nameIndex; 522 } else { 523 info.name = ""; 524 } 525 526 info.type = type; 527 info.offset = offset; 528 info.size = size; 529 530 531 return true; 532 } 533 534 // _SwapUInt16 535 static inline uint16_t _SwapUInt16(uint16_t value) 536 { 537 return ((value & 0xff) << 8) | (value >> 8); 538 } 539 540 // _SwapUInt32 541 static inline uint32_t _SwapUInt32(uint32_t value) 542 { 543 return ((uint32_t)_SwapUInt16(value & 0xffff) << 16) 544 | _SwapUInt16(uint16_t(value >> 16)); 545 } 546 547 private: 548 int fFD; 549 bool fHostEndianess; 550 off_t fFileSize; 551 uint32_t fELFHeaderSize; 552 uint32_t fSectionHeaderTableOffset; 553 uint32_t fSectionHeaderSize; 554 uint32_t fSectionHeaderCount; 555 char* fSectionHeaderStrings; 556 int fSectionHeaderStringsLength; 557 }; 558 559 560 // main 561 int 562 main(int argc, const char* const* argv) 563 { 564 sArgc = argc; 565 sArgv = argv; 566 567 if (argc < 3) 568 print_usage_and_exit(true); 569 570 // parameters 571 const char* fileName = argv[1]; 572 const char* revisionString = argv[2]; 573 uint32_t revision = atol(revisionString); 574 575 try { 576 ELFObject elfObject; 577 elfObject.SetTo(fileName); 578 579 // find haiku revision section 580 SectionInfo info; 581 if (!elfObject.FindSectionByName("_haiku_revision", info)) { 582 fprintf(stderr, "haiku revision section not found\n"); 583 exit(1); 584 } 585 586 // convert revision number to object endianess and write to section 587 uint32_t revisionBuffer = elfObject.GetUInt32(revision); 588 elfObject.Write(info.offset, &revisionBuffer, sizeof(revisionBuffer), 589 "Failed to write revision."); 590 591 } catch (Exception exception) { 592 if (exception.Description() == "") { 593 fprintf(stderr, "%s\n", strerror(exception.Error())); 594 } else { 595 fprintf(stderr, "%s: %s\n", exception.Description().c_str(), 596 strerror(exception.Error())); 597 } 598 exit(1); 599 } 600 601 return 0; 602 } 603