xref: /openbsd-src/usr.sbin/rdsetroot/rdsetroot.c (revision a17b2361ce87dfbf3d5e3df72c4d77d0df1d35eb)
1 /* $OpenBSD: rdsetroot.c,v 1.5 2023/04/24 14:06:01 krw Exp $ */
2 
3 /*
4  * Copyright (c) 2019 Sunil Nimmagadda <sunil@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/mman.h>
20 #include <sys/stat.h>
21 
22 #include <err.h>
23 #include <fcntl.h>
24 #include <gelf.h>
25 #include <libelf.h>
26 #include <stdio.h>
27 #include <stdint.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 
32 int find_rd_root_image(uint64_t *, uint64_t *, off_t *, size_t *);
33 int symbol_get_u64(const char *, uint64_t *);
34 __dead void usage(void);
35 
36 Elf	*e;
37 Elf_Scn	*symtab;
38 size_t	 nsymb, strtabndx, strtabsz;
39 
40 int
main(int argc,char ** argv)41 main(int argc, char **argv)
42 {
43 	GElf_Shdr	 shdr;
44 	Elf_Scn		*scn;
45 	char		*dataseg, *kernel = NULL, *fs = NULL, *name;
46 	off_t		 mmap_off, rd_root_size_val;
47 	size_t		 shstrndx, mmap_size;
48 	uint64_t	 rd_root_size_off, rd_root_image_off;
49 	uint32_t	*ip;
50 	int		 ch, debug = 0, fsfd, kfd, n, sflag = 0, xflag = 0;
51 
52 	while ((ch = getopt(argc, argv, "dsx")) != -1) {
53 		switch (ch) {
54 		case 'd':
55 			debug = 1;
56 			break;
57 		case 's':
58 			sflag = 1;
59 			break;
60 		case 'x':
61 			xflag = 1;
62 			break;
63 		default:
64 			usage();
65 		}
66 	}
67 	argc -= optind;
68 	argv += optind;
69 
70 	if (sflag && (debug || xflag || argc > 1))
71 		usage();
72 
73 	if (argc == 1)
74 		kernel = argv[0];
75 	else if (argc == 2) {
76 		kernel = argv[0];
77 		fs = argv[1];
78 	} else
79 		usage();
80 
81 	if ((kfd = open(kernel, xflag ? O_RDONLY : O_RDWR)) < 0)
82 		err(1, "%s", kernel);
83 
84 	if (fs) {
85 		if (xflag)
86 			fsfd = open(fs, O_RDWR | O_CREAT | O_TRUNC, 0644);
87 		else
88 			fsfd = open(fs, O_RDONLY);
89 	} else {
90 		if (xflag)
91 			fsfd = dup(STDOUT_FILENO);
92 		else
93 			fsfd = dup(STDIN_FILENO);
94 	}
95 	if (fsfd < 0)
96 		err(1, "%s", fs);
97 
98 	if (pledge("stdio", NULL) == -1)
99 		err(1, "pledge");
100 
101 	if (elf_version(EV_CURRENT) == EV_NONE)
102 		errx(1, "elf_version: %s", elf_errmsg(-1));
103 
104 	if ((e = elf_begin(kfd, xflag ? ELF_C_READ : ELF_C_RDWR, NULL)) == NULL)
105 		errx(1, "elf_begin: %s", elf_errmsg(-1));
106 
107 	if (elf_kind(e) != ELF_K_ELF)
108 		errx(1, "%s: not an elf", kernel);
109 
110 	if (gelf_getclass(e) == ELFCLASSNONE)
111 		errx(1, "%s: invalid elf, not 32 or 64 bit", kernel);
112 
113 	/* Retrieve index of section name string table. */
114 	if (elf_getshdrstrndx(e, &shstrndx) != 0)
115 		errx(1, "elf_getshdrstrndx: %s", elf_errmsg(-1));
116 
117 	/* Find symbol table, string table. */
118 	scn = symtab = NULL;
119 	while ((scn = elf_nextscn(e, scn)) != NULL) {
120 		if (gelf_getshdr(scn, &shdr) != &shdr)
121 			errx(1, "elf_getshdr: %s", elf_errmsg(-1));
122 
123 		if ((name = elf_strptr(e, shstrndx, shdr.sh_name)) == NULL)
124 			errx(1, "elf_strptr: %s", elf_errmsg(-1));
125 
126 		if (strcmp(name, ELF_SYMTAB) == 0 &&
127 		    shdr.sh_type == SHT_SYMTAB && shdr.sh_entsize != 0) {
128 			symtab = scn;
129 			nsymb = shdr.sh_size / shdr.sh_entsize;
130 		}
131 
132 		if (strcmp(name, ELF_STRTAB) == 0 &&
133 		    shdr.sh_type == SHT_STRTAB) {
134 			strtabndx = elf_ndxscn(scn);
135 			strtabsz = shdr.sh_size;
136 		}
137 	}
138 
139 	if (symtab == NULL)
140 		errx(1, "symbol table not found");
141 
142 	if (strtabndx == 0)
143 		errx(1, "string table not found");
144 
145 	if (find_rd_root_image(&rd_root_size_off, &rd_root_image_off,
146 	    &mmap_off, &mmap_size) != 0)
147 		errx(1, "can't locate space for rd_root_image!");
148 
149 	if (debug) {
150 		fprintf(stderr, "rd_root_size_off: 0x%llx\n", rd_root_size_off);
151 		fprintf(stderr, "rd_root_image_off: 0x%llx\n",
152 		    rd_root_image_off);
153 	}
154 
155 	/*
156 	 * Map in the whole data segment.
157 	 * The file offset needs to be page aligned.
158 	 */
159 	dataseg = mmap(NULL, mmap_size,
160 	    xflag ? PROT_READ : PROT_READ | PROT_WRITE,
161 	    MAP_SHARED, kfd, mmap_off);
162 	if (dataseg == MAP_FAILED)
163 		err(1, "%s: cannot map data seg", kernel);
164 
165 	/*
166 	 * Find value in the location: rd_root_size
167 	 */
168 	ip = (uint32_t *) (dataseg + rd_root_size_off);
169 	rd_root_size_val = *ip;
170 	if (sflag) {
171 		fprintf(stdout, "%llu\n", (unsigned long long)rd_root_size_val);
172 		goto done;
173 	}
174 
175 	if (debug) {
176 		fprintf(stderr, "rd_root_size  val: 0x%llx (%lld blocks)\n",
177 		    (unsigned long long)rd_root_size_val,
178 		    (unsigned long long)rd_root_size_val >> 9);
179 		fprintf(stderr, "copying root image...\n");
180 	}
181 
182 	if (xflag) {
183 		n = write(fsfd, dataseg + rd_root_image_off,
184 		    (size_t)rd_root_size_val);
185 		if (n != rd_root_size_val)
186 			err(1, "write");
187 	} else {
188 		struct stat sstat;
189 
190 		if (fstat(fsfd, &sstat) == -1)
191 			err(1, "fstat");
192 		if (S_ISREG(sstat.st_mode) &&
193 		    sstat.st_size > rd_root_size_val) {
194 			fprintf(stderr, "ramdisk too small 0x%llx 0x%llx\n",
195 			    (unsigned long long)sstat.st_size,
196 			    (unsigned long long)rd_root_size_val);
197 			exit(1);
198 		}
199 		n = read(fsfd, dataseg + rd_root_image_off,
200 		    (size_t)rd_root_size_val);
201 		if (n < 0)
202 			err(1, "read");
203 
204 		msync(dataseg, mmap_size, 0);
205 	}
206 
207 	if (debug)
208 		fprintf(stderr, "...copied %d bytes\n", n);
209 
210  done:
211 	elf_end(e);
212 	return 0;
213 }
214 
215 int
find_rd_root_image(uint64_t * rd_root_size_off,uint64_t * rd_root_image_off,off_t * pmmap_off,size_t * pmmap_size)216 find_rd_root_image(uint64_t *rd_root_size_off, uint64_t *rd_root_image_off,
217     off_t *pmmap_off, size_t *pmmap_size)
218 {
219 	GElf_Phdr	phdr;
220 	size_t		i, phdrnum;
221 	unsigned long	kernel_start, kernel_size;
222 	uint64_t	adiff, rd_root_size, rd_root_image, size_off, image_off;
223 	int		error = 1;
224 
225 	if (symbol_get_u64("rd_root_size", &rd_root_size) != 0)
226 		errx(1, "no rd_root_image symbols?");
227 
228 	if (symbol_get_u64("rd_root_image", &rd_root_image) != 0)
229 		errx(1, "no rd_root_image symbols?");
230 
231 	/* Retrieve number of program headers. */
232 	if (elf_getphdrnum(e, &phdrnum) != 0)
233 		errx(1, "elf_getphdrnum: %s", elf_errmsg(-1));
234 
235 	/* Locate the data segment. */
236 	for (i = 0; i < phdrnum; i++) {
237 		if (gelf_getphdr(e, i, &phdr) != &phdr)
238 			errx(1, "gelf_getphdr: %s", elf_errmsg(-1));
239 
240 		if (phdr.p_type != PT_LOAD)
241 			continue;
242 
243 		kernel_start = phdr.p_paddr;
244 		kernel_size = phdr.p_filesz;
245 		adiff = phdr.p_vaddr - phdr.p_paddr;
246 
247 		size_off = rd_root_size - kernel_start;
248 		image_off = rd_root_image - kernel_start;
249 		if (size_off < adiff || image_off < adiff)
250 			continue;
251 
252 		size_off -= adiff;
253 		image_off -= adiff;
254 		if (image_off >= kernel_size)
255 			continue;
256 		if (size_off >= kernel_size)
257 			errx(1, "rd_root_size not in data segment");
258 
259 		*pmmap_off = phdr.p_offset;
260 		*pmmap_size = kernel_size;
261 		*rd_root_size_off = size_off;
262 		*rd_root_image_off = image_off;
263 		error = 0;
264 		break;
265 	}
266 
267 	return error;
268 }
269 
270 int
symbol_get_u64(const char * symbol,uint64_t * result)271 symbol_get_u64(const char *symbol, uint64_t *result)
272 {
273 	GElf_Sym	 sym;
274 	Elf_Data	*data;
275 	const char	*name;
276 	size_t		 i;
277 	int		 error = 1;
278 
279 	data = NULL;
280 	while ((data = elf_rawdata(symtab, data)) != NULL) {
281 		for (i = 0; i < nsymb; i++) {
282 			if (gelf_getsym(data, i, &sym) != &sym)
283 				continue;
284 
285 			if (sym.st_name >= strtabsz)
286 				break;
287 
288 			if ((name = elf_strptr(e, strtabndx,
289 			    sym.st_name)) == NULL)
290 				continue;
291 
292 			if (strcmp(name, symbol) == 0) {
293 				if (result)
294 					*result = sym.st_value;
295 				error = 0;
296 				break;
297 			}
298 		}
299 	}
300 
301 	return error;
302 }
303 
304 __dead void
usage(void)305 usage(void)
306 {
307 	extern char *__progname;
308 
309 	fprintf(stderr, "usage: %s -s kernel\n", __progname);
310 	fprintf(stderr, "       %s [-dx] kernel [disk.fs]\n", __progname);
311 	exit(1);
312 }
313