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