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