1 /* $NetBSD: exec_elf32.c,v 1.12 2000/06/14 17:26:00 cgd Exp $ */
2 
3 /*
4  * Copyright (c) 1997, 1998 Christopher G. Demetriou
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *          This product includes software developed for the
18  *          NetBSD Project.  See http://www.netbsd.org/ for
19  *          information about NetBSD.
20  * 4. The name of the author may not be used to endorse or promote products
21  *    derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  * <<Id: LICENSE,v 1.2 2000/06/14 15:57:33 cgd Exp>>
35  */
36 
37 #include <sys/cdefs.h>
38 #ifndef lint
39 __RCSID("$NetBSD: exec_elf32.c,v 1.12 2000/06/14 17:26:00 cgd Exp $");
40 #endif
41 
42 #ifndef ELFSIZE
43 #define ELFSIZE         32
44 #endif
45 
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 
49 #include <errno.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
54 
55 #include "extern.h"
56 
57 #if (defined(NLIST_ELF32) && (ELFSIZE == 32)) || \
58     (defined(NLIST_ELF64) && (ELFSIZE == 64))
59 
60 #include <sys/exec_elf.h>
61 
62 struct listelem {
63 	struct listelem *next;
64 	void *mem;
65 	off_t file;
66 	size_t size;
67 };
68 
69 static ssize_t
70 xreadatoff(int fd, void *buf, off_t off, size_t size, const char *fn)
71 {
72 	ssize_t rv;
73 
74 	if (lseek(fd, off, SEEK_SET) != off) {
75 		perror(fn);
76 		return -1;
77 	}
78 	if ((rv = read(fd, buf, size)) != size) {
79 		fprintf(stderr, "%s: read error: %s\n", fn,
80 		    rv == -1 ? strerror(errno) : "short read");
81 		return -1;
82 	}
83 	return size;
84 }
85 
86 static ssize_t
87 xwriteatoff(int fd, void *buf, off_t off, size_t size, const char *fn)
88 {
89 	ssize_t rv;
90 
91 	if (lseek(fd, off, SEEK_SET) != off) {
92 		perror(fn);
93 		return -1;
94 	}
95 	if ((rv = write(fd, buf, size)) != size) {
96 		fprintf(stderr, "%s: write error: %s\n", fn,
97 		    rv == -1 ? strerror(errno) : "short write");
98 		return -1;
99 	}
100 	return size;
101 }
102 
103 static void *
104 xmalloc(size_t size, const char *fn, const char *use)
105 {
106 	void *rv;
107 
108 	rv = malloc(size);
109 	if (rv == NULL)
110 		fprintf(stderr, "%s: out of memory (allocating for %s)\n",
111 		    fn, use);
112 	return (rv);
113 }
114 
115 static void *
116 xrealloc(void *ptr, size_t size, const char *fn, const char *use)
117 {
118 	void *rv;
119 
120 	rv = realloc(ptr, size);
121 	if (rv == NULL) {
122 		free(ptr);
123 		fprintf(stderr, "%s: out of memory (reallocating for %s)\n",
124 		    fn, use);
125 	}
126 	return (rv);
127 }
128 
129 int
130 ELFNAMEEND(check)(int fd, const char *fn)
131 {
132 	Elf_Ehdr eh;
133 	struct stat sb;
134 
135 	/*
136 	 * Check the header to maek sure it's an ELF file (of the
137 	 * appropriate size).
138 	 */
139 	if (fstat(fd, &sb) == -1)
140 		return 0;
141 	if (sb.st_size < sizeof eh)
142 		return 0;
143 	if (read(fd, &eh, sizeof eh) != sizeof eh)
144 		return 0;
145 
146 	if (memcmp(eh.e_ident, ELFMAG, SELFMAG) != 0 ||
147 	    eh.e_ident[EI_CLASS] != ELFCLASS)
148                 return 0;
149 
150         switch (eh.e_machine) {
151         ELFDEFNNAME(MACHDEP_ID_CASES)
152 
153         default:
154                 return 0;
155         }
156 
157 	return 1;
158 }
159 
160 /*
161  * This function 'hides' (some of) ELF executable file's symbols.
162  * It hides them by renaming them to "_$$hide$$ <filename> <symbolname>".
163  * Symbols in the global keep list, or which are marked as being undefined,
164  * are left alone.
165  *
166  * An old version of this code shuffled various tables around, turning
167  * global symbols to be hidden into local symbols.  That lost on the
168  * mips, because CALL16 relocs must reference global symbols, and, if
169  * those symbols were being hidden, they were no longer global.
170  *
171  * The new renaming behaviour doesn't take global symbols out of the
172  * namespace.  However, it's ... unlikely that there will ever be
173  * any collisions in practice because of the new method.
174  */
175 int
176 ELFNAMEEND(hide)(int fd, const char *fn)
177 {
178 	Elf_Ehdr ehdr;
179 	Elf_Shdr *shdrp = NULL;
180 	int symtabsnum, strtabsnum;
181 	Elf_Sym *symtabp = NULL;
182 	char *strtabp = NULL, *nstrtabp = NULL;
183 	Elf_Word nsyms;
184 	Elf_Off stroff, maxoff;
185 	const char *weirdreason;
186 	ssize_t shdrsize;
187 	size_t nstrtab_size, nstrtab_nextoff, fn_size;
188 	int rv, i, weird;
189 
190 	rv = 0;
191 	if (xreadatoff(fd, &ehdr, 0, sizeof ehdr, fn) != sizeof ehdr)
192 		goto bad;
193 
194 	shdrsize = ehdr.e_shnum * ehdr.e_shentsize;
195 	if ((shdrp = xmalloc(shdrsize, fn, "section header table")) == NULL)
196 		goto bad;
197 	if (xreadatoff(fd, shdrp, ehdr.e_shoff, shdrsize, fn) != shdrsize)
198 		goto bad;
199 
200 	symtabsnum = strtabsnum = -1;
201 	maxoff = stroff = 0;
202 	weird = 0;
203 	weirdreason = "???";
204 	for (i = 0; i < ehdr.e_shnum; i++) {
205 		if (shdrp[i].sh_offset > maxoff) {
206 			maxoff = shdrp[i].sh_offset;
207 		}
208 		switch (shdrp[i].sh_type) {
209 		case SHT_SYMTAB:
210 			if (!weird && symtabsnum != -1) {
211 				weird = 1;
212 				weirdreason = "multiple symbol tables";
213 			}
214 			symtabsnum = i;
215 			strtabsnum = shdrp[i].sh_link;
216 			stroff = shdrp[strtabsnum].sh_offset;
217 			if (!weird && strtabsnum != (ehdr.e_shnum - 1)) {
218 				weird = 1;
219 				weirdreason = "string table not last section";
220 			}
221 			break;
222 		}
223 	}
224 	if (symtabsnum == -1)
225 		goto out;
226 	if (!weird && strtabsnum == -1) {
227 		weird = 1;
228 		weirdreason = "no string table found";
229 	}
230 	if (!weird && stroff != maxoff) {
231 		weird = 1;
232 		weirdreason = "string table section not last in file";
233 	}
234 	if (weird) {
235 		fprintf(stderr, "%s: weird executable (%s); unsupported\n", fn,
236 		    weirdreason);
237 		goto bad;
238 	}
239 
240 	/*
241 	 * load up everything we need
242 	 */
243 
244 	/* symbol table */
245 	if ((symtabp = xmalloc(shdrp[symtabsnum].sh_size, fn, "symbol table"))
246 	    == NULL)
247 		goto bad;
248 	if (xreadatoff(fd, symtabp, shdrp[symtabsnum].sh_offset,
249 	    shdrp[symtabsnum].sh_size, fn) != shdrp[symtabsnum].sh_size)
250 		goto bad;
251 
252 	/* string table */
253 	if ((strtabp = xmalloc(shdrp[strtabsnum].sh_size, fn, "string table"))
254 	    == NULL)
255 		goto bad;
256 	if (xreadatoff(fd, strtabp, shdrp[strtabsnum].sh_offset,
257 	    shdrp[strtabsnum].sh_size, fn) != shdrp[strtabsnum].sh_size)
258 		goto bad;
259 
260 	nsyms = shdrp[symtabsnum].sh_size / shdrp[symtabsnum].sh_entsize;
261 
262 	nstrtab_size = 256;
263 	nstrtabp = xmalloc(nstrtab_size, fn, "new string table");
264 	if (nstrtabp == NULL)
265 		goto bad;
266 	nstrtab_nextoff = 0;
267 
268 	fn_size = strlen(fn);
269 
270 	for (i = 0; i < nsyms; i++) {
271 		Elf_Sym *sp = &symtabp[i];
272 		const char *symname = strtabp + sp->st_name;
273 		size_t newent_len;
274 
275 		/*
276 		 * make sure there's size for the next entry, even if it's
277 		 * as large as it can be.
278 		 *
279 		 * "_$$hide$$ <filename> <symname><NUL>" ->
280 		 *    9 + 3 + sizes of fn and sym name
281 		 */
282 		while ((nstrtab_size - nstrtab_nextoff) <
283 		    strlen(symname) + fn_size + 12) {
284 			nstrtab_size *= 2;
285 			nstrtabp = xrealloc(nstrtabp, nstrtab_size, fn,
286 			    "new string table");
287 			if (nstrtabp == NULL)
288 				goto bad;
289 		}
290 
291 		sp->st_name = nstrtab_nextoff;
292 
293 		/* if it's a keeper or is undefined, don't rename it. */
294 		if (in_keep_list(symname) ||
295 		    sp->st_shndx == SHN_UNDEF) {
296 			newent_len = sprintf(nstrtabp + nstrtab_nextoff,
297 			    "%s", symname) + 1;
298 		} else {
299 			newent_len = sprintf(nstrtabp + nstrtab_nextoff,
300 			    "_$$hide$$ %s %s", fn, symname) + 1;
301 		}
302 		nstrtab_nextoff += newent_len;
303 	}
304 	shdrp[strtabsnum].sh_size = nstrtab_nextoff;
305 
306 	/*
307 	 * write new tables to the file
308 	 */
309 	if (xwriteatoff(fd, shdrp, ehdr.e_shoff, shdrsize, fn) != shdrsize)
310 		goto bad;
311 	if (xwriteatoff(fd, symtabp, shdrp[symtabsnum].sh_offset,
312 	    shdrp[symtabsnum].sh_size, fn) != shdrp[symtabsnum].sh_size)
313 		goto bad;
314 	if (xwriteatoff(fd, nstrtabp, shdrp[strtabsnum].sh_offset,
315 	    shdrp[strtabsnum].sh_size, fn) != shdrp[strtabsnum].sh_size)
316 		goto bad;
317 
318 out:
319 	if (shdrp != NULL)
320 		free(shdrp);
321 	if (symtabp != NULL)
322 		free(symtabp);
323 	if (strtabp != NULL)
324 		free(strtabp);
325 	if (nstrtabp != NULL)
326 		free(nstrtabp);
327 	return (rv);
328 
329 bad:
330 	rv = 1;
331 	goto out;
332 }
333 
334 #endif /* include this size of ELF */
335