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