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