1219820Sjeff/* $NetBSD: exec_elf32.c,v 1.15 2022/07/08 17:47:47 andvar Exp $ */
2219820Sjeff
3219820Sjeff/*
4219820Sjeff * Copyright (c) 1997, 1998 Christopher G. Demetriou
5219820Sjeff * All rights reserved.
6219820Sjeff *
7219820Sjeff * Redistribution and use in source and binary forms, with or without
8219820Sjeff * modification, are permitted provided that the following conditions
9219820Sjeff * are met:
10219820Sjeff * 1. Redistributions of source code must retain the above copyright
11219820Sjeff *    notice, this list of conditions and the following disclaimer.
12219820Sjeff * 2. Redistributions in binary form must reproduce the above copyright
13219820Sjeff *    notice, this list of conditions and the following disclaimer in the
14219820Sjeff *    documentation and/or other materials provided with the distribution.
15219820Sjeff * 3. All advertising materials mentioning features or use of this software
16219820Sjeff *    must display the following acknowledgement:
17219820Sjeff *          This product includes software developed for the
18219820Sjeff *          NetBSD Project.  See http://www.NetBSD.org/ for
19219820Sjeff *          information about NetBSD.
20219820Sjeff * 4. The name of the author may not be used to endorse or promote products
21219820Sjeff *    derived from this software without specific prior written permission.
22219820Sjeff *
23219820Sjeff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24219820Sjeff * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25219820Sjeff * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26219820Sjeff * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27219820Sjeff * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28219820Sjeff * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29219820Sjeff * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30219820Sjeff * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31219820Sjeff * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32219820Sjeff * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33219820Sjeff *
34219820Sjeff * <<Id: LICENSE,v 1.2 2000/06/14 15:57:33 cgd Exp>>
35219820Sjeff */
36219820Sjeff
37219820Sjeff#include <sys/cdefs.h>
38219820Sjeff#ifndef lint
39219820Sjeff__RCSID("$NetBSD: exec_elf32.c,v 1.15 2022/07/08 17:47:47 andvar Exp $");
40219820Sjeff#endif
41219820Sjeff
42219820Sjeff#ifndef ELFSIZE
43219820Sjeff#define ELFSIZE         32
44219820Sjeff#endif
45219820Sjeff
46219820Sjeff#include <sys/types.h>
47219820Sjeff#include <sys/stat.h>
48219820Sjeff
49219820Sjeff#include <errno.h>
50219820Sjeff#include <stdio.h>
51219820Sjeff#include <stdlib.h>
52219820Sjeff#include <string.h>
53219820Sjeff#include <unistd.h>
54219820Sjeff
55219820Sjeff#include "extern.h"
56219820Sjeff
57219820Sjeff#if (defined(NLIST_ELF32) && (ELFSIZE == 32)) || \
58219820Sjeff    (defined(NLIST_ELF64) && (ELFSIZE == 64))
59219820Sjeff
60219820Sjeff#include <sys/exec_elf.h>
61219820Sjeff
62219820Sjeffstruct listelem {
63219820Sjeff	struct listelem *next;
64219820Sjeff	void *mem;
65219820Sjeff	off_t file;
66219820Sjeff	size_t size;
67219820Sjeff};
68219820Sjeff
69219820Sjeffstatic ssize_t
70219820Sjeffxreadatoff(int fd, void *buf, off_t off, size_t size, const char *fn)
71219820Sjeff{
72219820Sjeff	ssize_t rv;
73219820Sjeff
74219820Sjeff	if (lseek(fd, off, SEEK_SET) != off) {
75219820Sjeff		perror(fn);
76219820Sjeff		return -1;
77219820Sjeff	}
78219820Sjeff	if ((size_t)(rv = read(fd, buf, size)) != size) {
79219820Sjeff		fprintf(stderr, "%s: read error: %s\n", fn,
80219820Sjeff		    rv == -1 ? strerror(errno) : "short read");
81219820Sjeff		return -1;
82219820Sjeff	}
83219820Sjeff	return size;
84219820Sjeff}
85219820Sjeff
86219820Sjeffstatic ssize_t
87219820Sjeffxwriteatoff(int fd, void *buf, off_t off, size_t size, const char *fn)
88219820Sjeff{
89219820Sjeff	ssize_t rv;
90219820Sjeff
91219820Sjeff	if (lseek(fd, off, SEEK_SET) != off) {
92219820Sjeff		perror(fn);
93219820Sjeff		return -1;
94219820Sjeff	}
95219820Sjeff	if ((size_t)(rv = write(fd, buf, size)) != size) {
96219820Sjeff		fprintf(stderr, "%s: write error: %s\n", fn,
97219820Sjeff		    rv == -1 ? strerror(errno) : "short write");
98219820Sjeff		return -1;
99219820Sjeff	}
100219820Sjeff	return size;
101219820Sjeff}
102219820Sjeff
103219820Sjeffstatic void *
104219820Sjeffxmalloc(size_t size, const char *fn, const char *use)
105219820Sjeff{
106219820Sjeff	void *rv;
107219820Sjeff
108219820Sjeff	rv = malloc(size);
109219820Sjeff	if (rv == NULL)
110219820Sjeff		fprintf(stderr, "%s: out of memory (allocating for %s)\n",
111219820Sjeff		    fn, use);
112219820Sjeff	return (rv);
113219820Sjeff}
114219820Sjeff
115219820Sjeffstatic void *
116219820Sjeffxrealloc(void *ptr, size_t size, const char *fn, const char *use)
117219820Sjeff{
118219820Sjeff	void *rv;
119219820Sjeff
120219820Sjeff	rv = realloc(ptr, size);
121219820Sjeff	if (rv == NULL) {
122219820Sjeff		free(ptr);
123219820Sjeff		fprintf(stderr, "%s: out of memory (reallocating for %s)\n",
124219820Sjeff		    fn, use);
125219820Sjeff	}
126219820Sjeff	return (rv);
127219820Sjeff}
128219820Sjeff
129219820Sjeffint
130219820SjeffELFNAMEEND(check)(int fd, const char *fn)
131219820Sjeff{
132219820Sjeff	Elf_Ehdr eh;
133219820Sjeff	struct stat sb;
134219820Sjeff
135219820Sjeff	/*
136219820Sjeff	 * Check the header to make sure it's an ELF file (of the
137219820Sjeff	 * appropriate size).
138219820Sjeff	 */
139219820Sjeff	if (fstat(fd, &sb) == -1)
140219820Sjeff		return 0;
141219820Sjeff	if (sb.st_size < (off_t)(sizeof eh))
142219820Sjeff		return 0;
143219820Sjeff	if (read(fd, &eh, sizeof eh) != sizeof eh)
144219820Sjeff		return 0;
145219820Sjeff
146219820Sjeff	if (memcmp(eh.e_ident, ELFMAG, SELFMAG) != 0 ||
147219820Sjeff	    eh.e_ident[EI_CLASS] != ELFCLASS)
148219820Sjeff                return 0;
149219820Sjeff
150219820Sjeff        switch (eh.e_machine) {
151219820Sjeff        ELFDEFNNAME(MACHDEP_ID_CASES)
152219820Sjeff
153219820Sjeff        default:
154219820Sjeff                return 0;
155219820Sjeff        }
156219820Sjeff
157219820Sjeff	return 1;
158219820Sjeff}
159219820Sjeff
160219820Sjeff/*
161219820Sjeff * This function 'hides' (some of) ELF executable file's symbols.
162219820Sjeff * It hides them by renaming them to "_$$hide$$ <filename> <symbolname>".
163219820Sjeff * Symbols in the global keep list, or which are marked as being undefined,
164219820Sjeff * are left alone.
165219820Sjeff *
166219820Sjeff * An old version of this code shuffled various tables around, turning
167219820Sjeff * global symbols to be hidden into local symbols.  That lost on the
168219820Sjeff * mips, because CALL16 relocs must reference global symbols, and, if
169219820Sjeff * those symbols were being hidden, they were no longer global.
170219820Sjeff *
171219820Sjeff * The new renaming behaviour doesn't take global symbols out of the
172219820Sjeff * namespace.  However, it's ... unlikely that there will ever be
173219820Sjeff * any collisions in practice because of the new method.
174219820Sjeff */
175219820Sjeffint
176219820SjeffELFNAMEEND(hide)(int fd, const char *fn)
177219820Sjeff{
178219820Sjeff	Elf_Ehdr ehdr;
179219820Sjeff	Elf_Shdr *shdrp = NULL;
180219820Sjeff	int symtabsnum, strtabsnum;
181219820Sjeff	Elf_Sym *symtabp = NULL;
182219820Sjeff	char *strtabp = NULL, *nstrtabp = NULL;
183219820Sjeff	Elf_Word j, nsyms;
184219820Sjeff	Elf_Off stroff, maxoff;
185219820Sjeff	const char *weirdreason;
186219820Sjeff	ssize_t shdrsize;
187219820Sjeff	size_t nstrtab_size, nstrtab_nextoff, fn_size;
188219820Sjeff	int rv, i, weird;
189219820Sjeff
190219820Sjeff	rv = 0;
191219820Sjeff	if (xreadatoff(fd, &ehdr, 0, sizeof ehdr, fn) != sizeof ehdr)
192219820Sjeff		goto bad;
193219820Sjeff
194219820Sjeff	shdrsize = ehdr.e_shnum * ehdr.e_shentsize;
195219820Sjeff	if ((shdrp = xmalloc(shdrsize, fn, "section header table")) == NULL)
196219820Sjeff		goto bad;
197219820Sjeff	if (xreadatoff(fd, shdrp, ehdr.e_shoff, shdrsize, fn) != shdrsize)
198219820Sjeff		goto bad;
199219820Sjeff
200219820Sjeff	symtabsnum = strtabsnum = -1;
201219820Sjeff	maxoff = stroff = 0;
202219820Sjeff	weird = 0;
203219820Sjeff	weirdreason = "???";
204219820Sjeff	for (i = 0; i < ehdr.e_shnum; i++) {
205219820Sjeff		if (shdrp[i].sh_offset > maxoff) {
206219820Sjeff			maxoff = shdrp[i].sh_offset;
207219820Sjeff		}
208219820Sjeff		switch (shdrp[i].sh_type) {
209219820Sjeff		case SHT_SYMTAB:
210219820Sjeff			if (!weird && symtabsnum != -1) {
211219820Sjeff				weird = 1;
212219820Sjeff				weirdreason = "multiple symbol tables";
213219820Sjeff			}
214219820Sjeff			symtabsnum = i;
215219820Sjeff			strtabsnum = shdrp[i].sh_link;
216219820Sjeff			stroff = shdrp[strtabsnum].sh_offset;
217219820Sjeff			if (!weird && strtabsnum != (ehdr.e_shnum - 1)) {
218219820Sjeff				weird = 1;
219219820Sjeff				weirdreason = "string table not last section";
220219820Sjeff			}
221219820Sjeff			break;
222219820Sjeff		}
223219820Sjeff	}
224219820Sjeff	if (symtabsnum == -1)
225219820Sjeff		goto out;
226219820Sjeff	if (!weird && strtabsnum == -1) {
227219820Sjeff		weird = 1;
228219820Sjeff		weirdreason = "no string table found";
229219820Sjeff	}
230219820Sjeff	if (!weird && stroff != maxoff) {
231219820Sjeff		weird = 1;
232219820Sjeff		weirdreason = "string table section not last in file";
233219820Sjeff	}
234219820Sjeff	if (weird) {
235219820Sjeff		fprintf(stderr, "%s: weird executable (%s); unsupported\n", fn,
236219820Sjeff		    weirdreason);
237219820Sjeff		goto bad;
238219820Sjeff	}
239219820Sjeff
240219820Sjeff	/*
241219820Sjeff	 * load up everything we need
242219820Sjeff	 */
243219820Sjeff
244219820Sjeff	/* symbol table */
245219820Sjeff	if ((symtabp = xmalloc(shdrp[symtabsnum].sh_size, fn, "symbol table"))
246219820Sjeff	    == NULL)
247219820Sjeff		goto bad;
248219820Sjeff	if ((size_t)xreadatoff(fd, symtabp, shdrp[symtabsnum].sh_offset,
249219820Sjeff	    shdrp[symtabsnum].sh_size, fn) != shdrp[symtabsnum].sh_size)
250219820Sjeff		goto bad;
251219820Sjeff
252219820Sjeff	/* string table */
253219820Sjeff	if ((strtabp = xmalloc(shdrp[strtabsnum].sh_size, fn, "string table"))
254219820Sjeff	    == NULL)
255219820Sjeff		goto bad;
256219820Sjeff	if ((size_t)xreadatoff(fd, strtabp, shdrp[strtabsnum].sh_offset,
257219820Sjeff	    shdrp[strtabsnum].sh_size, fn) != shdrp[strtabsnum].sh_size)
258219820Sjeff		goto bad;
259219820Sjeff
260219820Sjeff	nsyms = shdrp[symtabsnum].sh_size / shdrp[symtabsnum].sh_entsize;
261219820Sjeff
262219820Sjeff	nstrtab_size = 256;
263219820Sjeff	nstrtabp = xmalloc(nstrtab_size, fn, "new string table");
264219820Sjeff	if (nstrtabp == NULL)
265219820Sjeff		goto bad;
266219820Sjeff	nstrtab_nextoff = 0;
267219820Sjeff
268219820Sjeff	fn_size = strlen(fn);
269219820Sjeff
270219820Sjeff	for (j = 0; j < nsyms; j++) {
271219820Sjeff		Elf_Sym *sp = &symtabp[j];
272219820Sjeff		const char *symname = strtabp + sp->st_name;
273219820Sjeff		size_t newent_len;
274219820Sjeff
275219820Sjeff		/*
276219820Sjeff		 * make sure there's size for the next entry, even if it's
277219820Sjeff		 * as large as it can be.
278219820Sjeff		 *
279219820Sjeff		 * "_$$hide$$ <filename> <symname><NUL>" ->
280219820Sjeff		 *    9 + 3 + sizes of fn and sym name
281219820Sjeff		 */
282219820Sjeff		while ((nstrtab_size - nstrtab_nextoff) <
283219820Sjeff		    strlen(symname) + fn_size + 12) {
284219820Sjeff			nstrtab_size *= 2;
285219820Sjeff			nstrtabp = xrealloc(nstrtabp, nstrtab_size, fn,
286219820Sjeff			    "new string table");
287219820Sjeff			if (nstrtabp == NULL)
288219820Sjeff				goto bad;
289219820Sjeff		}
290219820Sjeff
291219820Sjeff		sp->st_name = nstrtab_nextoff;
292219820Sjeff
293219820Sjeff		/* if it's a keeper or is undefined, don't rename it. */
294219820Sjeff		if (in_keep_list(symname) ||
295219820Sjeff		    sp->st_shndx == SHN_UNDEF) {
296219820Sjeff			newent_len = sprintf(nstrtabp + nstrtab_nextoff,
297219820Sjeff			    "%s", symname) + 1;
298219820Sjeff		} else {
299219820Sjeff			newent_len = sprintf(nstrtabp + nstrtab_nextoff,
300219820Sjeff			    "_$$hide$$ %s %s", fn, symname) + 1;
301219820Sjeff		}
302219820Sjeff		nstrtab_nextoff += newent_len;
303219820Sjeff	}
304219820Sjeff	shdrp[strtabsnum].sh_size = nstrtab_nextoff;
305219820Sjeff
306219820Sjeff	/*
307219820Sjeff	 * write new tables to the file
308219820Sjeff	 */
309219820Sjeff	if (xwriteatoff(fd, shdrp, ehdr.e_shoff, shdrsize, fn) != shdrsize)
310219820Sjeff		goto bad;
311219820Sjeff	if ((size_t)xwriteatoff(fd, symtabp, shdrp[symtabsnum].sh_offset,
312219820Sjeff	    shdrp[symtabsnum].sh_size, fn) != shdrp[symtabsnum].sh_size)
313219820Sjeff		goto bad;
314219820Sjeff	if ((size_t)xwriteatoff(fd, nstrtabp, shdrp[strtabsnum].sh_offset,
315219820Sjeff	    shdrp[strtabsnum].sh_size, fn) != shdrp[strtabsnum].sh_size)
316219820Sjeff		goto bad;
317219820Sjeff
318219820Sjeffout:
319219820Sjeff	if (shdrp != NULL)
320219820Sjeff		free(shdrp);
321219820Sjeff	if (symtabp != NULL)
322219820Sjeff		free(symtabp);
323219820Sjeff	if (strtabp != NULL)
324219820Sjeff		free(strtabp);
325219820Sjeff	if (nstrtabp != NULL)
326219820Sjeff		free(nstrtabp);
327219820Sjeff	return (rv);
328219820Sjeff
329219820Sjeffbad:
330219820Sjeff	rv = 1;
331219820Sjeff	goto out;
332219820Sjeff}
333219820Sjeff
334219820Sjeff#endif /* include this size of ELF */
335219820Sjeff