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