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