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