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