xref: /haiku/src/tools/set_haiku_revision.cpp (revision c1eed5c7f3295d134760f471bc28c09ce6337726)
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, &sectionHeader, 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