Home | History | Annotate | Line # | Download | only in powerpc
      1 /* $NetBSD: fixup.c,v 1.3 2026/01/18 19:19:09 jmcneill Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2026 Jared McNeill <jmcneill (at) invisible.ca>
      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  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 #ifndef lint
     31 __RCSID("$NetBSD: fixup.c,v 1.3 2026/01/18 19:19:09 jmcneill Exp $");
     32 #endif
     33 
     34 #include <sys/types.h>
     35 #include <sys/mman.h>
     36 #include <sys/sysctl.h>
     37 #include <machine/cpu.h>
     38 #include <errno.h>
     39 #include "debug.h"
     40 #include "rtld.h"
     41 
     42 static bool _rtld_fixup_init;
     43 static uint32_t _rtld_ppc_pvr;
     44 static int _rtld_ncpus;
     45 
     46 union instr {
     47 	u_int	i_int;
     48 	struct {
     49 		u_int	i_opcd:6;
     50 		u_int	i_rs:5;
     51 		u_int	i_ra:5;
     52 		u_int	i_rb:5;
     53 		u_int	i_xo:10;
     54 		u_int	i_rc:1;
     55 	} i_x;
     56 };
     57 
     58 #define OPC_integer_31		0x1f
     59 #define OPC31_DCBST		0x036
     60 #define OPC31_STWCX		0x096
     61 
     62 #define IBMESPRESSO_P(_pvr)	(((_pvr) >> 16) == 0x7001)
     63 
     64 static int
     65 _rtld_espresso_fixup_range(caddr_t data_addr, size_t data_size, int data_prot)
     66 {
     67 	uint32_t *start, *end, *where;
     68 	union instr previ;
     69 
     70 	start = (uint32_t *)data_addr;
     71 	end = start + data_size / sizeof(*where);
     72 	previ.i_int = 0;
     73 
     74 	if ((data_prot & PROT_WRITE) == 0 &&
     75 	    mprotect(start, data_size, data_prot | PROT_WRITE) == -1) {
     76 		_rtld_error("Cannot write-enable segment: %s",
     77 		    xstrerror(errno));
     78 		return -1;
     79 	}
     80 
     81 	for (where = start; where < end; where++) {
     82 		union instr i = *(union instr *)where;
     83 
     84 		if (i.i_x.i_opcd == OPC_integer_31 &&
     85 		    i.i_x.i_xo == OPC31_STWCX &&
     86 		    i.i_x.i_rc == 1) {
     87 
     88 			if (previ.i_x.i_opcd == OPC_integer_31 &&
     89 			    previ.i_x.i_xo == OPC31_DCBST &&
     90 			    previ.i_x.i_rs == 0 &&
     91 			    previ.i_x.i_ra == i.i_x.i_ra &&
     92 			    previ.i_x.i_rb == i.i_x.i_rb) {
     93 				dbg(("skip instruction at %p (not required)",
     94 				    where));
     95 				goto next_opcode;
     96 			}
     97 
     98 			dbg(("fixup instruction at %p: 0x%x", where, i.i_int));
     99 
    100 			i.i_x.i_rc = 0;
    101 
    102 			*where = i.i_int;
    103 			__syncicache(where, 4);
    104 		}
    105 
    106 next_opcode:
    107 		previ = i;
    108 	}
    109 
    110 	if ((data_prot & PROT_WRITE) == 0 &&
    111 	    mprotect(start, data_size, data_prot) == -1) {
    112 		_rtld_error("Cannot write-protect segment: %s",
    113 		    xstrerror(errno));
    114 		return -1;
    115 	}
    116 
    117 	return 0;
    118 }
    119 
    120 static int
    121 _rtld_espresso_fixup(const char *path, int fd, Elf_Ehdr *ehdr, Elf_Phdr *phdr,
    122     caddr_t data_addr, size_t data_size, int data_prot)
    123 {
    124 	Elf_Shdr *shdr;
    125 	size_t shdr_size;
    126 	int i;
    127 
    128 	if (_rtld_ncpus == 1 || ehdr->e_shnum == 0 ||
    129 	    (phdr->p_flags & PF_X) == 0) {
    130 		return 0;
    131 	}
    132 
    133 	shdr_size = (size_t)ehdr->e_shentsize * ehdr->e_shnum;
    134 	shdr = mmap(NULL, shdr_size, PROT_READ, MAP_FILE | MAP_SHARED, fd,
    135 	    ehdr->e_shoff);
    136 	if (shdr == MAP_FAILED) {
    137 		_rtld_error("%s: mmap of shdr failed: %s", path,
    138 		    xstrerror(errno));
    139 		return -1;
    140 	}
    141 
    142 	for (i = 0; i < ehdr->e_shnum; i++) {
    143 		Elf_Addr start = shdr[i].sh_addr;
    144 		Elf_Addr end = shdr[i].sh_addr + shdr[i].sh_size - 1;
    145 
    146 		if (shdr[i].sh_type != SHT_PROGBITS) {
    147 			continue;
    148 		}
    149 		if ((shdr[i].sh_flags & SHF_EXECINSTR) == 0) {
    150 			continue;
    151 		}
    152 
    153 		if (start >= phdr->p_vaddr &&
    154 		    end < phdr->p_vaddr + phdr->p_filesz) {
    155 			dbg(("%s: fixup (espresso) from %p to %p", path,
    156 			    (void *)start, (void *)end));
    157 
    158 			if (_rtld_espresso_fixup_range(
    159 			    data_addr + (start - phdr->p_vaddr),
    160 			    shdr[i].sh_size, data_prot) != 0) {
    161 				_rtld_error("%s: fixup failed", path);
    162 				munmap(shdr, shdr_size);
    163 				return -1;
    164 			}
    165 		}
    166 	}
    167 
    168 	munmap(shdr, shdr_size);
    169 
    170 	return 0;
    171 }
    172 
    173 int
    174 _rtld_map_segment_fixup(const char *path, int fd, Elf_Ehdr *ehdr,
    175     Elf_Phdr *phdr, caddr_t data_addr, size_t data_size, int data_prot)
    176 {
    177 	if (!_rtld_fixup_init) {
    178 		ssize_t i;
    179 		size_t j;
    180 
    181 		j = sizeof(_rtld_ppc_pvr);
    182 		i = _rtld_sysctl("machdep.pvr", &_rtld_ppc_pvr, &j);
    183 		if (i != CTLTYPE_INT) {
    184 			_rtld_ppc_pvr = 0;
    185 		}
    186 		j = sizeof(_rtld_ncpus);
    187 		i = _rtld_sysctl("hw.ncpu", &_rtld_ncpus, &j);
    188 		if (i != CTLTYPE_INT) {
    189 			_rtld_ncpus = 1;
    190 		}
    191 
    192 		_rtld_fixup_init = true;
    193 	}
    194 
    195 	if (IBMESPRESSO_P(_rtld_ppc_pvr)) {
    196 		return _rtld_espresso_fixup(path, fd, ehdr, phdr, data_addr,
    197 		    data_size, data_prot);
    198 	}
    199 
    200 	return 0;
    201 }
    202