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