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