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