xref: /haiku/src/tools/set_haiku_revision.cpp (revision 77d95e1d47ef811b7c30cf79d7f6be1c3f08575a)
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
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
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
232 	Exception()
233 		: fError(errno),
234 		  fDescription()
235 	{
236 	}
237 
238 	// constructor
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
250 	Exception(int error)
251 		: fError(error),
252 		  fDescription()
253 	{
254 	}
255 
256 	// constructor
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
268 	Exception(const Exception& exception)
269 		: fError(exception.fError),
270 		  fDescription(exception.fDescription)
271 	{
272 	}
273 
274 	// destructor
275 	~Exception()
276 	{
277 	}
278 
279 	// SetTo
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
289 	int Error() const
290 	{
291 		return fError;
292 	}
293 
294 	// Description
295 	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:
319 	ELFObject()
320 		: fFD(-1),
321 		  fSectionHeaderStrings(NULL),
322 		  fSectionHeaderStringsLength(0)
323 	{
324 	}
325 
326 	~ELFObject()
327 	{
328 		Unset();
329 	}
330 
331 	void Unset()
332 	{
333 		if (fFD >= 0) {
334 			close(fFD);
335 			fFD = -1;
336 		}
337 
338 		delete[] fSectionHeaderStrings;
339 		fSectionHeaderStrings = NULL;
340 	}
341 
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 
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 
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 (%d/%d).",
410 					errorMessage, (int)bytesRead, (int)size);
411 			} else {
412 				throw Exception("Read too few bytes (%ld/%lu).",
413 					(int)bytesRead, (int)size);
414 			}
415 		}
416 	}
417 
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 (%d/%d).",
431 					errorMessage, (int)bytesWritten, (int)size);
432 			} else {
433 				throw Exception("Wrote too few bytes (%ld/%lu).",
434 					(int)bytesWritten, (int)size);
435 			}
436 		}
437 	}
438 
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 		// read ELF header
456 		EhdrType fileHeader;
457 		Read(0, &fileHeader, sizeof(EhdrType), "Failed to read ELF header.");
458 
459 		// check data encoding (endianess)
460 		switch (fileHeader.e_ident[EI_DATA]) {
461 			case ELFDATA2LSB:
462 				fHostEndianess = (htonl(1) != 1);
463 				break;
464 			case ELFDATA2MSB:
465 				fHostEndianess = (htonl(1) == 1);
466 				break;
467 			default:
468 			case ELFDATANONE:
469 				throw Exception(EIO, "Unsupported ELF data encoding.");
470 				break;
471 		}
472 
473 		// get the header values
474 		fELFHeaderSize	= GetValue(fileHeader.e_ehsize);
475 		fSectionHeaderTableOffset = GetValue(fileHeader.e_shoff);
476 		fSectionHeaderSize	= GetValue(fileHeader.e_shentsize);
477 		fSectionHeaderCount = GetValue(fileHeader.e_shnum);
478 		bool hasSectionHeaderTable = (fSectionHeaderTableOffset != 0);
479 
480 		// check the sanity of the header values
481 		// ELF header size
482 		if (fELFHeaderSize < sizeof(EhdrType)) {
483 			throw Exception(EIO,
484 				"Invalid ELF header: invalid ELF header size: %lu.",
485 				fELFHeaderSize);
486 		}
487 
488 		// section header table offset and entry count/size
489 		if (hasSectionHeaderTable) {
490 			if (fSectionHeaderTableOffset < (off_t)fELFHeaderSize
491 				|| fSectionHeaderTableOffset > fFileSize) {
492 				throw Exception(EIO, "Invalid ELF header: invalid section "
493 								"header table offset: %llu.",
494 								fSectionHeaderTableOffset);
495 			}
496 			size_t sectionHeaderTableSize
497 				= fSectionHeaderSize * fSectionHeaderCount;
498 			if (fSectionHeaderSize < (off_t)sizeof(ShdrType)
499 				|| fSectionHeaderTableOffset + (off_t)sectionHeaderTableSize
500 					> fFileSize) {
501 				throw Exception(EIO, "Invalid ELF header: section header "
502 								"table exceeds file: %llu.",
503 								fSectionHeaderTableOffset
504 									+ sectionHeaderTableSize);
505 			}
506 
507 
508 			// load section header string section
509 			uint16_t sectionHeaderStringSectionIndex
510 				= GetValue(fileHeader.e_shstrndx);
511 			if (sectionHeaderStringSectionIndex != SHN_UNDEF) {
512 				if (sectionHeaderStringSectionIndex >= fSectionHeaderCount) {
513 					throw Exception(EIO, "Invalid ELF header: invalid section "
514 									"header string section index: %u.",
515 									sectionHeaderStringSectionIndex);
516 				}
517 
518 				// get the section info
519 				SectionInfo info;
520 				if (_ReadSectionHeader<ShdrType>(sectionHeaderStringSectionIndex,
521 						info)) {
522 					fSectionHeaderStrings = new char[info.size + 1];
523 					Read(info.offset, fSectionHeaderStrings, info.size,
524 						"Failed to read section header string section.");
525 					fSectionHeaderStringsLength = info.size;
526 					// null-terminate to be on the safe side
527 					fSectionHeaderStrings[info.size] = '\0';
528 				}
529 			}
530 		}
531 	}
532 
533 	template<typename ShdrType>
534 	bool _ReadSectionHeader(int index, SectionInfo& info)
535 	{
536 		off_t shOffset = fSectionHeaderTableOffset
537 			+ index * fSectionHeaderSize;
538 		ShdrType sectionHeader;
539 		Read(shOffset, &sectionHeader, sizeof(ShdrType),
540 			"Failed to read ELF section header.");
541 
542 		// get the header values
543 		uint32_t type		= GetValue(sectionHeader.sh_type);
544 		off_t offset		= GetValue(sectionHeader.sh_offset);
545 		size_t size			= GetValue(sectionHeader.sh_size);
546 		uint32_t nameIndex	= GetValue(sectionHeader.sh_name);
547 
548 		// check the values
549 		// SHT_NULL marks the header unused,
550 		if (type == SHT_NULL)
551 			return false;
552 
553 		// SHT_NOBITS sections take no space in the file
554 		if (type != SHT_NOBITS) {
555 			if (offset < (off_t)fELFHeaderSize || offset > fFileSize) {
556 				throw Exception(EIO, "Invalid ELF section header: "
557 								"invalid section offset: %llu.", offset);
558 			}
559 			off_t sectionEnd = offset + size;
560 			if (sectionEnd > fFileSize) {
561 				throw Exception(EIO, "Invalid ELF section header: "
562 								"section exceeds file: %llu.", sectionEnd);
563 			}
564 		}
565 
566 		// get name, if we have a string section
567 		if (fSectionHeaderStrings) {
568 			if (nameIndex >= (uint32_t)fSectionHeaderStringsLength) {
569 				throw Exception(EIO, "Invalid ELF section header: "
570 								"invalid name index: %lu.", nameIndex);
571 			}
572 			info.name = fSectionHeaderStrings + nameIndex;
573 		} else {
574 			info.name = "";
575 		}
576 
577 		info.type = type;
578 		info.offset = offset;
579 		info.size = size;
580 
581 
582 		return true;
583 	}
584 
585 	// _SwapUInt16
586 	static inline uint16_t _SwapUInt16(uint16_t value)
587 	{
588 		return ((value & 0xff) << 8) | (value >> 8);
589 	}
590 
591 	// _SwapUInt32
592 	static inline uint32_t _SwapUInt32(uint32_t value)
593 	{
594 		return ((uint32_t)_SwapUInt16(value & 0xffff) << 16)
595 			| _SwapUInt16(uint16_t(value >> 16));
596 	}
597 
598 	// _SwapUInt64
599 	static inline uint64_t _SwapUInt64(uint64_t value)
600 	{
601 		return ((uint64_t)_SwapUInt32(value & 0xffffffff) << 32)
602 			| _SwapUInt32(uint32_t(value >> 32));
603 	}
604 
605 private:
606 	int			fFD;
607 	uint8_t		fELFClass;
608 	bool		fHostEndianess;
609 	off_t		fFileSize;
610 	size_t		fELFHeaderSize;
611 	off_t		fSectionHeaderTableOffset;
612 	size_t		fSectionHeaderSize;
613 	size_t		fSectionHeaderCount;
614 	char*		fSectionHeaderStrings;
615 	uint32_t	fSectionHeaderStringsLength;
616 };
617 
618 
619 template<>
620 int16_t ELFObject::GetValue(int16_t& value)
621 {
622 	return (fHostEndianess ? value : _SwapUInt16(value));
623 }
624 
625 
626 template<>
627 uint16_t ELFObject::GetValue(uint16_t& value)
628 {
629 	return (fHostEndianess ? value : _SwapUInt16(value));
630 }
631 
632 
633 template<>
634 int32_t ELFObject::GetValue(int32_t& value)
635 {
636 	return (fHostEndianess ? value : _SwapUInt32(value));
637 }
638 
639 
640 template<>
641 uint32_t ELFObject::GetValue(uint32_t& value)
642 {
643 	return (fHostEndianess ? value : _SwapUInt32(value));
644 }
645 
646 
647 template<>
648 int64_t ELFObject::GetValue(int64_t& value)
649 {
650 	return (fHostEndianess ? value : _SwapUInt64(value));
651 }
652 
653 
654 template<>
655 uint64_t ELFObject::GetValue(uint64_t& value)
656 {
657 	return (fHostEndianess ? value : _SwapUInt64(value));
658 }
659 
660 
661 // main
662 int
663 main(int argc, const char* const* argv)
664 {
665 	sArgc = argc;
666 	sArgv = argv;
667 
668 	if (argc < 3)
669 		print_usage_and_exit(true);
670 
671 	// parameters
672 	const char* fileName = argv[1];
673 	const char* revisionString = argv[2];
674 
675 	try {
676 		ELFObject elfObject;
677 		elfObject.SetTo(fileName);
678 
679 		// find haiku revision section
680 		SectionInfo info;
681 		if (!elfObject.FindSectionByName("_haiku_revision", info)) {
682 			fprintf(stderr, "haiku revision section not found\n");
683 			exit(1);
684 		}
685 
686 		// write revision string to section
687 		elfObject.Write(info.offset, revisionString,
688 			min((size_t)SYSTEM_REVISION_LENGTH, strlen(revisionString) + 1),
689 			"Failed to write revision.");
690 
691 	} catch (Exception exception) {
692 		if (exception.Description() == "") {
693 			fprintf(stderr, "%s\n", strerror(exception.Error()));
694 		} else {
695 			fprintf(stderr, "%s: %s\n", exception.Description().c_str(),
696 				strerror(exception.Error()));
697 		}
698 		exit(1);
699 	}
700 
701 	return 0;
702 }
703