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