summaryrefslogtreecommitdiffstats
path: root/src/libr-ro.c
blob: c3de28d6f3fc4ef155af5b873002f54267d123d0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
/*
 *
 *  Copyright (c) 2009 Erich Hoover
 *  Copyright (c) 2008-2009 Martin Rosenau
 *
 *  libr read-only Backend - Read resources from ELF binaries
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * To provide feedback, report bugs, or otherwise contact me:
 * ehoover at mines dot edu
 *
 */

/* Include compile-time parameters */
#include "config.h"

#include "libr.h"
#include "libr-internal.h"

/* malloc/free */
#include <stdlib.h>

/* For memory byte-wise compare */
#include <string.h>

/* For endian conversion */
#include "cvtendian.h"

#define RETURN_UNSUPPORTED RETURN(LIBR_ERROR_UNSUPPORTED, "The read-only backend does not support this operation");

#ifndef DOXYGEN_SHOULD_SKIP_THIS

typedef struct {
	unsigned char magic[4];
	eClass byte_size;
	eEncoding endian;
	unsigned char version;
	unsigned char padding[9];
} ElfPreHeader;

#endif /* DOXYGEN_SHOULD_SKIP_THIS */

#define ELF_HALF(b)  sizeof(uint16_t)
#define ELF_WORD(b)  sizeof(uint32_t)
#define ELF_XWORD(b) ((b == ELFCLASS32) ? sizeof(uint32_t) : sizeof(uint64_t))
#define ELF_ADDR(b)  ELF_XWORD(b)
#define ELF_OFF(b)   ELF_XWORD(b)

/* ELF Header Offsets */
#define HDROFF_TYPE(b)      sizeof(ElfPreHeader)             /* ElfXX_Half  e_type; */
#define HDROFF_MACHINE(b)   HDROFF_TYPE(b)+ELF_HALF(b)       /* ElfXX_Half  e_machine; */
#define HDROFF_VERSION(b)   HDROFF_MACHINE(b)+ELF_HALF(b)    /* ElfXX_Word  e_version; */
#define HDROFF_ENTRY(b)     HDROFF_VERSION(b)+ELF_WORD(b)    /* ElfXX_Addr  e_entry; */
#define HDROFF_PHOFF(b)     HDROFF_ENTRY(b)+ELF_ADDR(b)      /* ElfXX_Off   e_phoff; */
#define HDROFF_SHOFF(b)     HDROFF_PHOFF(b)+ELF_OFF(b)       /* ElfXX_Off   e_shoff; */
#define HDROFF_FLAGS(b)     HDROFF_SHOFF(b)+ELF_OFF(b)       /* ElfXX_Word  e_flags; */
#define HDROFF_EHSIZE(b)    HDROFF_FLAGS(b)+ELF_WORD(b)      /* ElfXX_Half  e_ehsize; */
#define HDROFF_PHENTSIZE(b) HDROFF_EHSIZE(b)+ELF_HALF(b)     /* ElfXX_Half  e_phentsize; */
#define HDROFF_PHNUM(b)     HDROFF_PHENTSIZE(b)+ELF_HALF(b)  /* ElfXX_Half  e_phnum; */
#define HDROFF_SHENTSIZE(b) HDROFF_PHNUM(b)+ELF_HALF(b)      /* ElfXX_Half  e_shentsize; */
#define HDROFF_SHNUM(b)     HDROFF_SHENTSIZE(b)+ELF_HALF(b)  /* ElfXX_Half  e_shnum; */
#define HDROFF_SHSTRNDX(b)  HDROFF_SHNUM(b)+ELF_HALF(b)      /* ElfXX_Half  e_shstrndx; */

/* ELF Section Offsets */
#define SECOFF_NAME(b)      0                                /* ElfXX_Word  sh_name; */
#define SECOFF_TYPE(b)      SECOFF_NAME(b)+ELF_WORD(b)       /* ElfXX_Word  sh_type; */
#define SECOFF_FLAGS(b)     SECOFF_TYPE(b)+ELF_WORD(b)       /* ElfXX_XWord sh_flags; */
#define SECOFF_ADDR(b)      SECOFF_FLAGS(b)+ELF_XWORD(b)     /* ElfXX_Addr  sh_addr; */
#define SECOFF_OFFSET(b)    SECOFF_ADDR(b)+ELF_ADDR(b)       /* ElfXX_Off   sh_offset; */
#define SECOFF_SIZE(b)      SECOFF_OFFSET(b)+ELF_OFF(b)      /* ElfXX_XWord sh_size; */
#define SECOFF_LINK(b)      SECOFF_SIZE(b)+ELF_XWORD(b)      /* ElfXX_Word  sh_link; */
#define SECOFF_INFO(b)      SECOFF_LINK(b)+ELF_WORD(b)       /* ElfXX_Word  sh_info; */
#define SECOFF_ADDRALIGN    SECOFF_INFO(b)+ELF_WORD(b)       /* ElfXX_XWord sh_addralign; */
#define SECOFF_ENTSIZE      SECOFF_ADDRALIGN(b)+ELF_XWORD(b) /* ElfXX_XWord sh_entsize; */

/*
 * Safely read a parameter from the ELF binary
 */
static int read_param(FILE *handle, void *result, size_t bytes, eClass endian)
{
	if(fread(result, 1, bytes, handle) != bytes)
		return 0;
	if(ferror(handle))
		return 0;
	if(endian != HOST_ENDIAN && !ConvertEndian(result, bytes))
		return 0;
	return 1;
}

/*
 * The read-only backend requires no initialization
 */
void initialize_backend(void)
{
	if(sizeof(ElfPreHeader) != 16)
		fprintf(stderr, "WARNING: Your compiler did not properly pack important structures!\n");
}

/*
 * The read-only backend cannot write an output file
 */
void write_output(libr_file *file_handle) {}

/*
 * The read-only backend cannot add sections
 */
libr_intstatus add_section(libr_file *file_handle, char *resource_name, libr_section **retscn)
{
	RETURN_UNSUPPORTED;
}

/*
 * Return the name of a section
 */
char *section_name(libr_file *file_handle, libr_section *scn)
{
	if(scn == NULL)
		return NULL;
	return scn->name;
}

/*
 * Return the pointer to the actual data in the section
 */
void *data_pointer(libr_section *scn, libr_data *data)
{
	return (void *) data;
}

/*
 * Return the size of the data in the section
 */
size_t data_size(libr_section *scn, libr_data *data)
{
	return scn->size;
}

/*
 * Find the resource stored in the ELF binary
 */
libr_intstatus find_section(libr_file *file_handle, char *section, libr_section **retscn)
{
	char *test_name;
	int i;
	
	for(i=0; i<file_handle->total_sections; i++)
	{
		test_name = section_name(file_handle, &(file_handle->secdata[i]));
		if(test_name != NULL && strcmp(test_name, section) == 0)
			break;
	}
	if(i >= file_handle->total_sections)
		RETURN(LIBR_ERROR_NOSECTION, "ELF resource section not found");
	
	/* Found the resource, hurray! */
	*retscn = &(file_handle->secdata[i]);
	RETURN_OK;
}

/*
 * Read the section from the ELF binary
 */
libr_data *get_data(libr_file *file_handle, libr_section *scn)
{
	FILE *handle = file_handle->handle;
	libr_data *data = NULL;
	size_t n;
	
	fseek(handle, scn->data_offset, SEEK_SET);
	data = (libr_data *) malloc(scn->size);
	n = fread(data, 1, scn->size, handle);
	if(n == 0)
		goto failed; /* Empty section? */
	if(ferror(handle))
		goto failed;
	
	/* Succeeded in reading the data */
	return data;
failed:
	free(data);
	return NULL;
}

/*
 * UNSUPORTED BY BACKEND: Create a new data section
 */
libr_data *new_data(libr_file *file_handle, libr_section *scn)
{
	return NULL;
}

/*
 * Find the next section given a section pointer
 */
libr_section *next_section(libr_file *file_handle, libr_section *scn)
{
	int total_sections = file_handle->total_sections;
	libr_section *test_scn = NULL;
	int i;
	
	if(total_sections == 0)
		return NULL;
	/* Requesting the first section */
	if(scn == NULL)
	{
		i = 0;
		/* Do not return an empty section */
		while(test_scn == NULL || test_scn->size == 0)
		{
			if(i > total_sections)
				return NULL;
			test_scn = &(file_handle->secdata[i++]);
		}
		return test_scn;
	}
	/* Return the next section given a section pointer */
	for(i=0; i<total_sections; i++)
	{
		test_scn = &(file_handle->secdata[i]);
		
		if(test_scn == scn && (i+1) < total_sections)
		{
			libr_section *next_scn = &(file_handle->secdata[i+1]);
			
			/* Returning empty sections is pointless */
			if(next_scn->size != 0)
				return next_scn;
		}
	}
	return NULL;
}

/*
 * UNSUPORTED BY BACKEND: Remove a section
 */
libr_intstatus remove_section(libr_file *file_handle, libr_section *scn)
{
	RETURN_UNSUPPORTED;
}

/*
 * UNSUPORTED BY BACKEND: Set the data for a section
 */
libr_intstatus set_data(libr_file *file_handle, libr_section *scn, libr_data *data, off_t offset, char *buffer, size_t size)
{
	RETURN_UNSUPPORTED;
}

/*
 * Open a handle to the ELF binary (provided that read-only access is requested)
 */
libr_intstatus open_handles(libr_file *file_handle, char *filename, libr_access_t access)
{
	const char elf_magic[] = {'\x7F','E','L','F'};
	uint16_t total_sections, sh_size, strings_sec;
	ElfPreHeader file_info;
	libr_section *secdata;
	FILE *handle = NULL;
	uint64_t sh_offset;
	unsigned long i;
	
	if(access == LIBR_READ_WRITE)
		RETURN_UNSUPPORTED;
	handle = fopen(filename, "rb");
	if(!handle)
		RETURN(LIBR_ERROR_OPENFAILED, "Failed to open input file");
	if(fread(&file_info, 1, sizeof(ElfPreHeader), handle) != sizeof(ElfPreHeader))
		RETURN(LIBR_ERROR_WRONGFORMAT, "Failed to read pre-header bytes from input file");
	if(memcmp(file_info.magic, elf_magic, sizeof(elf_magic)) != 0)
		RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format: not an ELF binary");

	/* Confirm processor (byte size) and packing (endian) */
	if(!enum_valid(file_info.byte_size, ELFCLASS))
		RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format: invalid byte size");
	if(!enum_valid(file_info.endian, ELFDATA))
		RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format: invalid endian type");
	
	/* Get the file offset to the Section Header tables */
	fseek(handle, HDROFF_SHOFF(file_info.byte_size), SEEK_SET);
	if(!read_param(handle, &sh_offset, ELF_OFF(file_info.byte_size), file_info.endian))
		RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format: failed to read section header offset");
	/* Get the size of the Section Header tables */
	fseek(handle, HDROFF_SHENTSIZE(file_info.byte_size), SEEK_SET);
	if(!read_param(handle, &sh_size, ELF_HALF(file_info.byte_size), file_info.endian))
		RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format: failed to read section header size");
	/* Get the total number of sections */
	fseek(handle, HDROFF_SHNUM(file_info.byte_size), SEEK_SET);
	if(!read_param(handle, &total_sections, ELF_HALF(file_info.byte_size), file_info.endian))
		RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format: failed to read total number of sections");
	/* Get the ID of the "strings" section */
	fseek(handle, HDROFF_SHSTRNDX(file_info.byte_size), SEEK_SET);
	if(!read_param(handle, &strings_sec, ELF_HALF(file_info.byte_size), file_info.endian))
		RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format: failed to read string section ID");
	if(strings_sec >= total_sections)
		RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format: invalid string section ID");
	secdata = (libr_section *) malloc(sizeof(libr_section)*total_sections);
	
	/* Load section information */
	for(i=0; i<total_sections; i++)
	{
		long sec_start = sh_offset+sh_size*i;
		
		/* Grab the offset in the string table to the name of the section */
		fseek(handle, sec_start+SECOFF_NAME(file_info.byte_size), SEEK_SET);
		if(!read_param(handle, &(secdata[i].name_offset), ELF_WORD(file_info.byte_size), file_info.endian))
			RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format: failed to read section name offset");
		/* Grab the offset to the data for the section */
		fseek(handle, sec_start+SECOFF_OFFSET(file_info.byte_size), SEEK_SET);
		if(!read_param(handle, &(secdata[i].data_offset), ELF_OFF(file_info.byte_size), file_info.endian))
			RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format: failed to read section data offset");
		/* Grab the size of the data for the section */
		fseek(handle, sec_start+SECOFF_SIZE(file_info.byte_size), SEEK_SET);
		if(!read_param(handle, &(secdata[i].size), ELF_XWORD(file_info.byte_size), file_info.endian))
			RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format: failed to read section size");
	}
	/* Locate the name offset within the "strings" section and load the string */
	for(i=0; i<total_sections; i++)
	{
		long stringsec_start = secdata[strings_sec].data_offset;
		size_t n;
		
		fseek(handle, stringsec_start+secdata[i].name_offset, SEEK_SET);
		n = fread(secdata[i].name, 1, ELFSTRING_MAX-1, handle);
		if(ferror(handle))
			RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format: failed to read string");
		secdata[i].name[n] = '\0';
	}
	
	/* Hold onto the important parameters */
	file_handle->secdata = secdata;
	file_handle->total_sections = total_sections;
	file_handle->endian = file_info.endian;
	file_handle->byte_size = file_info.byte_size;
	file_handle->handle = handle;
	file_handle->filename = filename;
	file_handle->access = access;
	RETURN_OK;
}