fixup.c revision 1.1
1/* $NetBSD: fixup.c,v 1.1 2026/01/09 22:54:27 jmcneill Exp $ */
2
3/*-
4 * Copyright (c) 2026 Jared McNeill <jmcneill@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.1 2026/01/09 22:54:27 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
42static bool _rtld_fixup_init;
43static uint32_t _rtld_ppc_pvr;
44static int _rtld_ncpus;
45
46union 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
64static inline uint32_t
65_rtld_ppc_mfpvr(void)
66{
67	uint32_t pvr;
68
69	asm volatile ("mfpvr %0" : "=r"(pvr));
70
71	return pvr;
72}
73
74int
75_rtld_map_segment_fixup(Elf_Phdr *phdr, caddr_t data_addr, size_t data_size,
76    int data_prot)
77{
78	uint32_t *start, *where, *end;
79	union instr previ;
80
81	if (!_rtld_fixup_init) {
82		ssize_t i;
83		size_t j;
84
85		_rtld_ppc_pvr = _rtld_ppc_mfpvr();
86		_rtld_fixup_init = true;
87		j = sizeof(_rtld_ncpus);
88		i = _rtld_sysctl("hw.ncpu", &_rtld_ncpus, &j);
89		if (i != CTLTYPE_INT) {
90			_rtld_ncpus = 1;
91		}
92	}
93	if (!IBMESPRESSO_P(_rtld_ppc_pvr) && _rtld_ncpus == 1) {
94		return 0;
95	}
96	if ((phdr->p_flags & PF_X) == 0) {
97		return 0;
98	}
99
100	start = (uint32_t *)data_addr;
101	end = start + data_size / sizeof(*where);
102	previ.i_int = 0;
103
104	dbg(("fixup (espresso) from %p to %p\n", start, end));
105
106	if ((data_prot & PROT_WRITE) == 0 &&
107	    mprotect(start, data_size, data_prot | PROT_WRITE) == -1) {
108		_rtld_error("Cannot write-enable segment: %s",
109		    xstrerror(errno));
110		return -1;
111	}
112
113	for (where = start; where < end; where++) {
114		union instr i = *(union instr *)where;
115
116		if (i.i_x.i_opcd == OPC_integer_31 &&
117		    i.i_x.i_xo == OPC31_STWCX &&
118		    i.i_x.i_rc == 1) {
119
120			if (previ.i_x.i_opcd == OPC_integer_31 &&
121			    previ.i_x.i_xo == OPC31_DCBST &&
122			    previ.i_x.i_rs == 0 &&
123			    previ.i_x.i_ra == i.i_x.i_ra &&
124			    previ.i_x.i_rb == i.i_x.i_rb) {
125				dbg(("skip instruction at %p (not required)",
126				    where));
127				goto next_opcode;
128			}
129
130			dbg(("fixup instruction at %p: 0x%x", where, i.i_int));
131
132			i.i_x.i_rc = 0;
133
134			*where = i.i_int;
135			__syncicache(where, 4);
136		}
137
138next_opcode:
139		previ = i;
140	}
141
142	if ((data_prot & PROT_WRITE) == 0 &&
143	    mprotect(start, data_size, data_prot) == -1) {
144		_rtld_error("Cannot write-protect segment: %s",
145		    xstrerror(errno));
146		return -1;
147	}
148
149	return 0;
150}
151