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 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 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 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 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