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