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