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
print_usage(bool error)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
print_usage_and_exit(bool error)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
Exception()232 Exception()
233 : fError(errno),
234 fDescription()
235 {
236 }
237
238 // constructor
Exception(const char * format,...)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
Exception(int error)250 Exception(int error)
251 : fError(error),
252 fDescription()
253 {
254 }
255
256 // constructor
Exception(int error,const char * format,...)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
Exception(const Exception & exception)268 Exception(const Exception& exception)
269 : fError(exception.fError),
270 fDescription(exception.fDescription)
271 {
272 }
273
274 // destructor
~Exception()275 ~Exception()
276 {
277 }
278
279 // SetTo
SetTo(int error,const char * format,va_list arg)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
Error() const289 int Error() const
290 {
291 return fError;
292 }
293
294 // Description
Description() const295 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:
ELFObject()319 ELFObject()
320 : fFD(-1),
321 fSectionHeaderStrings(NULL),
322 fSectionHeaderStringsLength(0)
323 {
324 }
325
~ELFObject()326 ~ELFObject()
327 {
328 Unset();
329 }
330
Unset()331 void Unset()
332 {
333 if (fFD >= 0) {
334 close(fFD);
335 fFD = -1;
336 }
337
338 delete[] fSectionHeaderStrings;
339 fSectionHeaderStrings = NULL;
340 }
341
SetTo(const char * fileName)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
FindSectionByName(const char * name,SectionInfo & foundInfo)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
Read(off_t position,void * buffer,size_t size,const char * errorMessage=NULL)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 (%zd/%zu).",
410 errorMessage, bytesRead, size);
411 } else {
412 throw Exception("Read too few bytes (%zd/%zu).",
413 bytesRead, size);
414 }
415 }
416 }
417
Write(off_t position,const void * buffer,size_t size,const char * errorMessage=NULL)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 (%zd/%zu).",
431 errorMessage, bytesWritten, size);
432 } else {
433 throw Exception("Wrote too few bytes (%zd/%zu).",
434 bytesWritten, size);
435 }
436 }
437 }
438
FileSize()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
_SwapUInt16(uint16_t value)459 static inline uint16_t _SwapUInt16(uint16_t value)
460 {
461 return ((value & 0xff) << 8) | (value >> 8);
462 }
463
464 // _SwapUInt32
_SwapUInt32(uint32_t value)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
_SwapUInt64(uint64_t value)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<>
GetValue(int16_t & value)493 int16_t ELFObject::GetValue(int16_t& value)
494 {
495 return (fHostEndianess ? value : _SwapUInt16(value));
496 }
497
498
499 template<>
GetValue(uint16_t & value)500 uint16_t ELFObject::GetValue(uint16_t& value)
501 {
502 return (fHostEndianess ? value : _SwapUInt16(value));
503 }
504
505
506 template<>
GetValue(int32_t & value)507 int32_t ELFObject::GetValue(int32_t& value)
508 {
509 return (fHostEndianess ? value : _SwapUInt32(value));
510 }
511
512
513 template<>
GetValue(uint32_t & value)514 uint32_t ELFObject::GetValue(uint32_t& value)
515 {
516 return (fHostEndianess ? value : _SwapUInt32(value));
517 }
518
519
520 template<>
GetValue(int64_t & value)521 int64_t ELFObject::GetValue(int64_t& value)
522 {
523 return (fHostEndianess ? value : _SwapUInt64(value));
524 }
525
526
527 template<>
GetValue(uint64_t & value)528 uint64_t ELFObject::GetValue(uint64_t& value)
529 {
530 return (fHostEndianess ? value : _SwapUInt64(value));
531 }
532
533
534 template<typename EhdrType, typename ShdrType>
_ParseELFHeader()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>
_ReadSectionHeader(int index,SectionInfo & info)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: %" PRIu32 ".", 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
main(int argc,const char * const * argv)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