xref: /linux/scripts/elf-parse.c (revision 36492b7141b9abc967e92c991af32c670351dc16)
1 #include <sys/types.h>
2 #include <sys/mman.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <stdbool.h>
8 #include <string.h>
9 #include <unistd.h>
10 #include <errno.h>
11 
12 #include "elf-parse.h"
13 
14 struct elf_funcs elf_parser;
15 
16 /*
17  * Get the whole file as a programming convenience in order to avoid
18  * malloc+lseek+read+free of many pieces.  If successful, then mmap
19  * avoids copying unused pieces; else just read the whole file.
20  * Open for both read and write.
21  */
map_file(char const * fname,size_t * size)22 static void *map_file(char const *fname, size_t *size)
23 {
24 	int fd;
25 	struct stat sb;
26 	void *addr = NULL;
27 
28 	fd = open(fname, O_RDWR);
29 	if (fd < 0) {
30 		perror(fname);
31 		return NULL;
32 	}
33 	if (fstat(fd, &sb) < 0) {
34 		perror(fname);
35 		goto out;
36 	}
37 	if (!S_ISREG(sb.st_mode)) {
38 		fprintf(stderr, "not a regular file: %s\n", fname);
39 		goto out;
40 	}
41 
42 	addr = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
43 	if (addr == MAP_FAILED) {
44 		fprintf(stderr, "Could not mmap file: %s\n", fname);
45 		goto out;
46 	}
47 
48 	*size = sb.st_size;
49 
50 out:
51 	close(fd);
52 	return addr;
53 }
54 
elf_parse(const char * fname,void * addr,uint32_t types)55 static int elf_parse(const char *fname, void *addr, uint32_t types)
56 {
57 	Elf_Ehdr *ehdr = addr;
58 	uint16_t type;
59 
60 	switch (ehdr->e32.e_ident[EI_DATA]) {
61 	case ELFDATA2LSB:
62 		elf_parser.r	= rle;
63 		elf_parser.r2	= r2le;
64 		elf_parser.r8	= r8le;
65 		elf_parser.w	= wle;
66 		elf_parser.w8	= w8le;
67 		break;
68 	case ELFDATA2MSB:
69 		elf_parser.r	= rbe;
70 		elf_parser.r2	= r2be;
71 		elf_parser.r8	= r8be;
72 		elf_parser.w	= wbe;
73 		elf_parser.w8	= w8be;
74 		break;
75 	default:
76 		fprintf(stderr, "unrecognized ELF data encoding %d: %s\n",
77 			ehdr->e32.e_ident[EI_DATA], fname);
78 		return -1;
79 	}
80 
81 	if (memcmp(ELFMAG, ehdr->e32.e_ident, SELFMAG) != 0 ||
82 	    ehdr->e32.e_ident[EI_VERSION] != EV_CURRENT) {
83 		fprintf(stderr, "unrecognized ELF file %s\n", fname);
84 		return -1;
85 	}
86 
87 	type = elf_parser.r2(&ehdr->e32.e_type);
88 	if (!((1 << type) & types)) {
89 		fprintf(stderr, "Invalid ELF type file %s\n", fname);
90 		return -1;
91 	}
92 
93 	switch (ehdr->e32.e_ident[EI_CLASS]) {
94 	case ELFCLASS32: {
95 		elf_parser.ehdr_shoff		= ehdr32_shoff;
96 		elf_parser.ehdr_shentsize	= ehdr32_shentsize;
97 		elf_parser.ehdr_shstrndx	= ehdr32_shstrndx;
98 		elf_parser.ehdr_shnum		= ehdr32_shnum;
99 		elf_parser.shdr_addr		= shdr32_addr;
100 		elf_parser.shdr_offset		= shdr32_offset;
101 		elf_parser.shdr_link		= shdr32_link;
102 		elf_parser.shdr_size		= shdr32_size;
103 		elf_parser.shdr_name		= shdr32_name;
104 		elf_parser.shdr_type		= shdr32_type;
105 		elf_parser.shdr_entsize		= shdr32_entsize;
106 		elf_parser.sym_type		= sym32_type;
107 		elf_parser.sym_name		= sym32_name;
108 		elf_parser.sym_value		= sym32_value;
109 		elf_parser.sym_shndx		= sym32_shndx;
110 		elf_parser.rela_offset		= rela32_offset;
111 		elf_parser.rela_info		= rela32_info;
112 		elf_parser.rela_addend		= rela32_addend;
113 		elf_parser.rela_write_addend	= rela32_write_addend;
114 
115 		if (elf_parser.r2(&ehdr->e32.e_ehsize) != sizeof(Elf32_Ehdr) ||
116 		    elf_parser.r2(&ehdr->e32.e_shentsize) != sizeof(Elf32_Shdr)) {
117 			fprintf(stderr,
118 				"unrecognized ET_EXEC/ET_DYN file: %s\n", fname);
119 			return -1;
120 		}
121 
122 		}
123 		break;
124 	case ELFCLASS64: {
125 		elf_parser.ehdr_shoff		= ehdr64_shoff;
126 		elf_parser.ehdr_shentsize		= ehdr64_shentsize;
127 		elf_parser.ehdr_shstrndx		= ehdr64_shstrndx;
128 		elf_parser.ehdr_shnum		= ehdr64_shnum;
129 		elf_parser.shdr_addr		= shdr64_addr;
130 		elf_parser.shdr_offset		= shdr64_offset;
131 		elf_parser.shdr_link		= shdr64_link;
132 		elf_parser.shdr_size		= shdr64_size;
133 		elf_parser.shdr_name		= shdr64_name;
134 		elf_parser.shdr_type		= shdr64_type;
135 		elf_parser.shdr_entsize		= shdr64_entsize;
136 		elf_parser.sym_type		= sym64_type;
137 		elf_parser.sym_name		= sym64_name;
138 		elf_parser.sym_value		= sym64_value;
139 		elf_parser.sym_shndx		= sym64_shndx;
140 		elf_parser.rela_offset		= rela64_offset;
141 		elf_parser.rela_info		= rela64_info;
142 		elf_parser.rela_addend		= rela64_addend;
143 		elf_parser.rela_write_addend	= rela64_write_addend;
144 
145 		if (elf_parser.r2(&ehdr->e64.e_ehsize) != sizeof(Elf64_Ehdr) ||
146 		    elf_parser.r2(&ehdr->e64.e_shentsize) != sizeof(Elf64_Shdr)) {
147 			fprintf(stderr,
148 				"unrecognized ET_EXEC/ET_DYN file: %s\n",
149 				fname);
150 			return -1;
151 		}
152 
153 		}
154 		break;
155 	default:
156 		fprintf(stderr, "unrecognized ELF class %d %s\n",
157 			ehdr->e32.e_ident[EI_CLASS], fname);
158 		return -1;
159 	}
160 	return 0;
161 }
162 
elf_map_machine(void * addr)163 int elf_map_machine(void *addr)
164 {
165 	Elf_Ehdr *ehdr = addr;
166 
167 	return elf_parser.r2(&ehdr->e32.e_machine);
168 }
169 
elf_map_long_size(void * addr)170 int elf_map_long_size(void *addr)
171 {
172 	Elf_Ehdr *ehdr = addr;
173 
174 	return ehdr->e32.e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
175 }
176 
elf_map(char const * fname,size_t * size,uint32_t types)177 void *elf_map(char const *fname, size_t *size, uint32_t types)
178 {
179 	void *addr;
180 	int ret;
181 
182 	addr = map_file(fname, size);
183 	if (!addr)
184 		return NULL;
185 
186 	ret = elf_parse(fname, addr, types);
187 	if (ret < 0) {
188 		elf_unmap(addr, *size);
189 		return NULL;
190 	}
191 
192 	return addr;
193 }
194 
elf_unmap(void * addr,size_t size)195 void elf_unmap(void *addr, size_t size)
196 {
197 	munmap(addr, size);
198 }
199