xref: /openbsd-src/usr.sbin/rdsetroot/rdsetroot.c (revision a17b2361ce87dfbf3d5e3df72c4d77d0df1d35eb)
1*a17b2361Skrw /* $OpenBSD: rdsetroot.c,v 1.5 2023/04/24 14:06:01 krw Exp $ */
27829d1e3Sderaadt 
37829d1e3Sderaadt /*
4f356787bSsunil  * Copyright (c) 2019 Sunil Nimmagadda <sunil@openbsd.org>
57829d1e3Sderaadt  *
6f356787bSsunil  * Permission to use, copy, modify, and distribute this software for any
7f356787bSsunil  * purpose with or without fee is hereby granted, provided that the above
8f356787bSsunil  * copyright notice and this permission notice appear in all copies.
97829d1e3Sderaadt  *
10f356787bSsunil  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11f356787bSsunil  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12f356787bSsunil  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13f356787bSsunil  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14f356787bSsunil  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15f356787bSsunil  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16f356787bSsunil  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
177829d1e3Sderaadt  */
187829d1e3Sderaadt 
197829d1e3Sderaadt #include <sys/mman.h>
207829d1e3Sderaadt #include <sys/stat.h>
217829d1e3Sderaadt 
227829d1e3Sderaadt #include <err.h>
23f356787bSsunil #include <fcntl.h>
24f356787bSsunil #include <gelf.h>
25f356787bSsunil #include <libelf.h>
26f356787bSsunil #include <stdio.h>
27f356787bSsunil #include <stdint.h>
28f356787bSsunil #include <stdlib.h>
29f356787bSsunil #include <string.h>
30f356787bSsunil #include <unistd.h>
317829d1e3Sderaadt 
32f356787bSsunil int find_rd_root_image(uint64_t *, uint64_t *, off_t *, size_t *);
33f356787bSsunil int symbol_get_u64(const char *, uint64_t *);
347829d1e3Sderaadt __dead void usage(void);
357829d1e3Sderaadt 
36f356787bSsunil Elf	*e;
37f356787bSsunil Elf_Scn	*symtab;
38f356787bSsunil size_t	 nsymb, strtabndx, strtabsz;
397829d1e3Sderaadt 
407829d1e3Sderaadt int
main(int argc,char ** argv)41f356787bSsunil main(int argc, char **argv)
427829d1e3Sderaadt {
43f356787bSsunil 	GElf_Shdr	 shdr;
44f356787bSsunil 	Elf_Scn		*scn;
45f356787bSsunil 	char		*dataseg, *kernel = NULL, *fs = NULL, *name;
46f356787bSsunil 	off_t		 mmap_off, rd_root_size_val;
47f356787bSsunil 	size_t		 shstrndx, mmap_size;
48f356787bSsunil 	uint64_t	 rd_root_size_off, rd_root_image_off;
49f356787bSsunil 	uint32_t	*ip;
50*a17b2361Skrw 	int		 ch, debug = 0, fsfd, kfd, n, sflag = 0, xflag = 0;
517829d1e3Sderaadt 
52*a17b2361Skrw 	while ((ch = getopt(argc, argv, "dsx")) != -1) {
537829d1e3Sderaadt 		switch (ch) {
547829d1e3Sderaadt 		case 'd':
557829d1e3Sderaadt 			debug = 1;
567829d1e3Sderaadt 			break;
57*a17b2361Skrw 		case 's':
58*a17b2361Skrw 			sflag = 1;
59*a17b2361Skrw 			break;
607829d1e3Sderaadt 		case 'x':
617829d1e3Sderaadt 			xflag = 1;
627829d1e3Sderaadt 			break;
637829d1e3Sderaadt 		default:
647829d1e3Sderaadt 			usage();
657829d1e3Sderaadt 		}
667829d1e3Sderaadt 	}
677829d1e3Sderaadt 	argc -= optind;
687829d1e3Sderaadt 	argv += optind;
697829d1e3Sderaadt 
70*a17b2361Skrw 	if (sflag && (debug || xflag || argc > 1))
71*a17b2361Skrw 		usage();
72*a17b2361Skrw 
737829d1e3Sderaadt 	if (argc == 1)
747829d1e3Sderaadt 		kernel = argv[0];
757829d1e3Sderaadt 	else if (argc == 2) {
767829d1e3Sderaadt 		kernel = argv[0];
777829d1e3Sderaadt 		fs = argv[1];
787829d1e3Sderaadt 	} else
797829d1e3Sderaadt 		usage();
807829d1e3Sderaadt 
81b7041c07Sderaadt 	if ((kfd = open(kernel, xflag ? O_RDONLY : O_RDWR)) < 0)
827829d1e3Sderaadt 		err(1, "%s", kernel);
837829d1e3Sderaadt 
847829d1e3Sderaadt 	if (fs) {
857829d1e3Sderaadt 		if (xflag)
867829d1e3Sderaadt 			fsfd = open(fs, O_RDWR | O_CREAT | O_TRUNC, 0644);
877829d1e3Sderaadt 		else
88b7041c07Sderaadt 			fsfd = open(fs, O_RDONLY);
897829d1e3Sderaadt 	} else {
907829d1e3Sderaadt 		if (xflag)
917829d1e3Sderaadt 			fsfd = dup(STDOUT_FILENO);
927829d1e3Sderaadt 		else
937829d1e3Sderaadt 			fsfd = dup(STDIN_FILENO);
947829d1e3Sderaadt 	}
957829d1e3Sderaadt 	if (fsfd < 0)
967829d1e3Sderaadt 		err(1, "%s", fs);
977829d1e3Sderaadt 
987829d1e3Sderaadt 	if (pledge("stdio", NULL) == -1)
997829d1e3Sderaadt 		err(1, "pledge");
1007829d1e3Sderaadt 
101f356787bSsunil 	if (elf_version(EV_CURRENT) == EV_NONE)
102f356787bSsunil 		errx(1, "elf_version: %s", elf_errmsg(-1));
1037829d1e3Sderaadt 
104f356787bSsunil 	if ((e = elf_begin(kfd, xflag ? ELF_C_READ : ELF_C_RDWR, NULL)) == NULL)
105f356787bSsunil 		errx(1, "elf_begin: %s", elf_errmsg(-1));
1067829d1e3Sderaadt 
107f356787bSsunil 	if (elf_kind(e) != ELF_K_ELF)
108f356787bSsunil 		errx(1, "%s: not an elf", kernel);
109f356787bSsunil 
110f356787bSsunil 	if (gelf_getclass(e) == ELFCLASSNONE)
111f356787bSsunil 		errx(1, "%s: invalid elf, not 32 or 64 bit", kernel);
112f356787bSsunil 
113f356787bSsunil 	/* Retrieve index of section name string table. */
114f356787bSsunil 	if (elf_getshdrstrndx(e, &shstrndx) != 0)
115f356787bSsunil 		errx(1, "elf_getshdrstrndx: %s", elf_errmsg(-1));
116f356787bSsunil 
117f356787bSsunil 	/* Find symbol table, string table. */
118f356787bSsunil 	scn = symtab = NULL;
119f356787bSsunil 	while ((scn = elf_nextscn(e, scn)) != NULL) {
120f356787bSsunil 		if (gelf_getshdr(scn, &shdr) != &shdr)
121f356787bSsunil 			errx(1, "elf_getshdr: %s", elf_errmsg(-1));
122f356787bSsunil 
123f356787bSsunil 		if ((name = elf_strptr(e, shstrndx, shdr.sh_name)) == NULL)
124f356787bSsunil 			errx(1, "elf_strptr: %s", elf_errmsg(-1));
125f356787bSsunil 
126f356787bSsunil 		if (strcmp(name, ELF_SYMTAB) == 0 &&
127f356787bSsunil 		    shdr.sh_type == SHT_SYMTAB && shdr.sh_entsize != 0) {
128f356787bSsunil 			symtab = scn;
129f356787bSsunil 			nsymb = shdr.sh_size / shdr.sh_entsize;
1307829d1e3Sderaadt 		}
1317829d1e3Sderaadt 
132f356787bSsunil 		if (strcmp(name, ELF_STRTAB) == 0 &&
133f356787bSsunil 		    shdr.sh_type == SHT_STRTAB) {
134f356787bSsunil 			strtabndx = elf_ndxscn(scn);
135f356787bSsunil 			strtabsz = shdr.sh_size;
136f356787bSsunil 		}
137f356787bSsunil 	}
138f356787bSsunil 
139f356787bSsunil 	if (symtab == NULL)
140f356787bSsunil 		errx(1, "symbol table not found");
141f356787bSsunil 
142f356787bSsunil 	if (strtabndx == 0)
143f356787bSsunil 		errx(1, "string table not found");
144f356787bSsunil 
145f356787bSsunil 	if (find_rd_root_image(&rd_root_size_off, &rd_root_image_off,
146f356787bSsunil 	    &mmap_off, &mmap_size) != 0)
147f356787bSsunil 		errx(1, "can't locate space for rd_root_image!");
148f356787bSsunil 
149f356787bSsunil 	if (debug) {
150f356787bSsunil 		fprintf(stderr, "rd_root_size_off: 0x%llx\n", rd_root_size_off);
151f356787bSsunil 		fprintf(stderr, "rd_root_image_off: 0x%llx\n",
152f356787bSsunil 		    rd_root_image_off);
153f356787bSsunil 	}
1547829d1e3Sderaadt 
1557829d1e3Sderaadt 	/*
1567829d1e3Sderaadt 	 * Map in the whole data segment.
1577829d1e3Sderaadt 	 * The file offset needs to be page aligned.
1587829d1e3Sderaadt 	 */
1597829d1e3Sderaadt 	dataseg = mmap(NULL, mmap_size,
1607829d1e3Sderaadt 	    xflag ? PROT_READ : PROT_READ | PROT_WRITE,
1617829d1e3Sderaadt 	    MAP_SHARED, kfd, mmap_off);
1627829d1e3Sderaadt 	if (dataseg == MAP_FAILED)
1637829d1e3Sderaadt 		err(1, "%s: cannot map data seg", kernel);
1647829d1e3Sderaadt 
1657829d1e3Sderaadt 	/*
1667829d1e3Sderaadt 	 * Find value in the location: rd_root_size
1677829d1e3Sderaadt 	 */
168f356787bSsunil 	ip = (uint32_t *) (dataseg + rd_root_size_off);
1697829d1e3Sderaadt 	rd_root_size_val = *ip;
170*a17b2361Skrw 	if (sflag) {
171*a17b2361Skrw 		fprintf(stdout, "%llu\n", (unsigned long long)rd_root_size_val);
172*a17b2361Skrw 		goto done;
173*a17b2361Skrw 	}
174*a17b2361Skrw 
175f356787bSsunil 	if (debug) {
1767829d1e3Sderaadt 		fprintf(stderr, "rd_root_size  val: 0x%llx (%lld blocks)\n",
1777829d1e3Sderaadt 		    (unsigned long long)rd_root_size_val,
1787829d1e3Sderaadt 		    (unsigned long long)rd_root_size_val >> 9);
1797829d1e3Sderaadt 		fprintf(stderr, "copying root image...\n");
180f356787bSsunil 	}
1817829d1e3Sderaadt 
1827829d1e3Sderaadt 	if (xflag) {
1837829d1e3Sderaadt 		n = write(fsfd, dataseg + rd_root_image_off,
1847829d1e3Sderaadt 		    (size_t)rd_root_size_val);
1857829d1e3Sderaadt 		if (n != rd_root_size_val)
1867829d1e3Sderaadt 			err(1, "write");
1877829d1e3Sderaadt 	} else {
1887829d1e3Sderaadt 		struct stat sstat;
1897829d1e3Sderaadt 
1907829d1e3Sderaadt 		if (fstat(fsfd, &sstat) == -1)
1917829d1e3Sderaadt 			err(1, "fstat");
1927829d1e3Sderaadt 		if (S_ISREG(sstat.st_mode) &&
1937829d1e3Sderaadt 		    sstat.st_size > rd_root_size_val) {
1947829d1e3Sderaadt 			fprintf(stderr, "ramdisk too small 0x%llx 0x%llx\n",
1957829d1e3Sderaadt 			    (unsigned long long)sstat.st_size,
1967829d1e3Sderaadt 			    (unsigned long long)rd_root_size_val);
1977829d1e3Sderaadt 			exit(1);
1987829d1e3Sderaadt 		}
1997829d1e3Sderaadt 		n = read(fsfd, dataseg + rd_root_image_off,
2007829d1e3Sderaadt 		    (size_t)rd_root_size_val);
2017829d1e3Sderaadt 		if (n < 0)
2027829d1e3Sderaadt 			err(1, "read");
2037829d1e3Sderaadt 
2047829d1e3Sderaadt 		msync(dataseg, mmap_size, 0);
2057829d1e3Sderaadt 	}
2067829d1e3Sderaadt 
2077829d1e3Sderaadt 	if (debug)
2087829d1e3Sderaadt 		fprintf(stderr, "...copied %d bytes\n", n);
209f356787bSsunil 
210*a17b2361Skrw  done:
211f356787bSsunil 	elf_end(e);
212f356787bSsunil 	return 0;
213f356787bSsunil }
214f356787bSsunil 
215f356787bSsunil 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)216f356787bSsunil find_rd_root_image(uint64_t *rd_root_size_off, uint64_t *rd_root_image_off,
217f356787bSsunil     off_t *pmmap_off, size_t *pmmap_size)
218f356787bSsunil {
219f356787bSsunil 	GElf_Phdr	phdr;
220f356787bSsunil 	size_t		i, phdrnum;
221f356787bSsunil 	unsigned long	kernel_start, kernel_size;
222f356787bSsunil 	uint64_t	adiff, rd_root_size, rd_root_image, size_off, image_off;
223f356787bSsunil 	int		error = 1;
224f356787bSsunil 
225f356787bSsunil 	if (symbol_get_u64("rd_root_size", &rd_root_size) != 0)
226f356787bSsunil 		errx(1, "no rd_root_image symbols?");
227f356787bSsunil 
228f356787bSsunil 	if (symbol_get_u64("rd_root_image", &rd_root_image) != 0)
229f356787bSsunil 		errx(1, "no rd_root_image symbols?");
230f356787bSsunil 
231f356787bSsunil 	/* Retrieve number of program headers. */
232f356787bSsunil 	if (elf_getphdrnum(e, &phdrnum) != 0)
233f356787bSsunil 		errx(1, "elf_getphdrnum: %s", elf_errmsg(-1));
234f356787bSsunil 
235f356787bSsunil 	/* Locate the data segment. */
236f356787bSsunil 	for (i = 0; i < phdrnum; i++) {
237f356787bSsunil 		if (gelf_getphdr(e, i, &phdr) != &phdr)
238f356787bSsunil 			errx(1, "gelf_getphdr: %s", elf_errmsg(-1));
239f356787bSsunil 
240f356787bSsunil 		if (phdr.p_type != PT_LOAD)
241f356787bSsunil 			continue;
242f356787bSsunil 
243f356787bSsunil 		kernel_start = phdr.p_paddr;
244f356787bSsunil 		kernel_size = phdr.p_filesz;
245f356787bSsunil 		adiff = phdr.p_vaddr - phdr.p_paddr;
246f356787bSsunil 
247f356787bSsunil 		size_off = rd_root_size - kernel_start;
248f356787bSsunil 		image_off = rd_root_image - kernel_start;
249f356787bSsunil 		if (size_off < adiff || image_off < adiff)
250f356787bSsunil 			continue;
251f356787bSsunil 
252f356787bSsunil 		size_off -= adiff;
253f356787bSsunil 		image_off -= adiff;
254f356787bSsunil 		if (image_off >= kernel_size)
255f356787bSsunil 			continue;
256f356787bSsunil 		if (size_off >= kernel_size)
257f356787bSsunil 			errx(1, "rd_root_size not in data segment");
258f356787bSsunil 
259f356787bSsunil 		*pmmap_off = phdr.p_offset;
260f356787bSsunil 		*pmmap_size = kernel_size;
261f356787bSsunil 		*rd_root_size_off = size_off;
262f356787bSsunil 		*rd_root_image_off = image_off;
263f356787bSsunil 		error = 0;
264f356787bSsunil 		break;
265f356787bSsunil 	}
266f356787bSsunil 
267f356787bSsunil 	return error;
268f356787bSsunil }
269f356787bSsunil 
270f356787bSsunil int
symbol_get_u64(const char * symbol,uint64_t * result)271f356787bSsunil symbol_get_u64(const char *symbol, uint64_t *result)
272f356787bSsunil {
273f356787bSsunil 	GElf_Sym	 sym;
274f356787bSsunil 	Elf_Data	*data;
275f356787bSsunil 	const char	*name;
276f356787bSsunil 	size_t		 i;
277f356787bSsunil 	int		 error = 1;
278f356787bSsunil 
279f356787bSsunil 	data = NULL;
280f356787bSsunil 	while ((data = elf_rawdata(symtab, data)) != NULL) {
281f356787bSsunil 		for (i = 0; i < nsymb; i++) {
282f356787bSsunil 			if (gelf_getsym(data, i, &sym) != &sym)
283f356787bSsunil 				continue;
284f356787bSsunil 
285f356787bSsunil 			if (sym.st_name >= strtabsz)
286f356787bSsunil 				break;
287f356787bSsunil 
288f356787bSsunil 			if ((name = elf_strptr(e, strtabndx,
289f356787bSsunil 			    sym.st_name)) == NULL)
290f356787bSsunil 				continue;
291f356787bSsunil 
292f356787bSsunil 			if (strcmp(name, symbol) == 0) {
293f356787bSsunil 				if (result)
294f356787bSsunil 					*result = sym.st_value;
295f356787bSsunil 				error = 0;
296f356787bSsunil 				break;
297f356787bSsunil 			}
298f356787bSsunil 		}
299f356787bSsunil 	}
300f356787bSsunil 
301f356787bSsunil 	return error;
3027829d1e3Sderaadt }
3037829d1e3Sderaadt 
3047829d1e3Sderaadt __dead void
usage(void)3057829d1e3Sderaadt usage(void)
3067829d1e3Sderaadt {
3077829d1e3Sderaadt 	extern char *__progname;
3087829d1e3Sderaadt 
309*a17b2361Skrw 	fprintf(stderr, "usage: %s -s kernel\n", __progname);
310*a17b2361Skrw 	fprintf(stderr, "       %s [-dx] kernel [disk.fs]\n", __progname);
3117829d1e3Sderaadt 	exit(1);
3127829d1e3Sderaadt }
313