diff options
| author | Richard M. Stallman | 1992-10-06 04:54:56 +0000 |
|---|---|---|
| committer | Richard M. Stallman | 1992-10-06 04:54:56 +0000 |
| commit | 5743648e5f3f0e79d3918f024d5160cfd4b6b157 (patch) | |
| tree | be16a6d34bd41dc1b2bbcea3f5dbd0d7e45b91b3 /src | |
| parent | 172a9c1f72c78b7b4d1b4fc5e9afa0fc79d1f413 (diff) | |
| download | emacs-5743648e5f3f0e79d3918f024d5160cfd4b6b157.tar.gz emacs-5743648e5f3f0e79d3918f024d5160cfd4b6b157.zip | |
entered into RCS
Diffstat (limited to 'src')
| -rw-r--r-- | src/unexnext.c | 433 |
1 files changed, 433 insertions, 0 deletions
diff --git a/src/unexnext.c b/src/unexnext.c new file mode 100644 index 00000000000..08fe46489a3 --- /dev/null +++ b/src/unexnext.c | |||
| @@ -0,0 +1,433 @@ | |||
| 1 | /* Dump Emacs in macho format. | ||
| 2 | Copyright (C) 1990 Free Software Foundation, Inc. | ||
| 3 | Written by Bradley Taylor (btaylor@next.com). | ||
| 4 | |||
| 5 | This file is part of GNU Emacs. | ||
| 6 | |||
| 7 | GNU Emacs is free software; you can redistribute it and/or modify | ||
| 8 | it under the terms of the GNU General Public License as published by | ||
| 9 | the Free Software Foundation; either version 1, or (at your option) | ||
| 10 | any later version. | ||
| 11 | |||
| 12 | GNU Emacs is distributed in the hope that it will be useful, | ||
| 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | GNU General Public License for more details. | ||
| 16 | |||
| 17 | You should have received a copy of the GNU General Public License | ||
| 18 | along with GNU Emacs; see the file COPYING. If not, write to | ||
| 19 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ | ||
| 20 | |||
| 21 | |||
| 22 | #undef __STRICT_BSD__ | ||
| 23 | |||
| 24 | #include <stdio.h> | ||
| 25 | #include <stdlib.h> | ||
| 26 | #include <stdarg.h> | ||
| 27 | #include <mach.h> | ||
| 28 | #include <sys/loader.h> | ||
| 29 | #include <sys/file.h> | ||
| 30 | #include <sys/stat.h> | ||
| 31 | #include <libc.h> | ||
| 32 | |||
| 33 | |||
| 34 | extern struct section *getsectbyname(char *, char *); | ||
| 35 | |||
| 36 | /* | ||
| 37 | * Kludge: we don't expect any program data beyond VM_HIGHDATA | ||
| 38 | * What is really needed is a way to find out from malloc() which | ||
| 39 | * pages it vm_allocated and write only those out into the data segment. | ||
| 40 | * | ||
| 41 | * This kludge may break when we stop using fixed virtual address | ||
| 42 | * shared libraries. Actually, emacs will probably continue working, but be | ||
| 43 | * much larger on disk than it needs to be (because non-malloced data will | ||
| 44 | * be in the file). | ||
| 45 | */ | ||
| 46 | static const unsigned VM_HIGHDATA = 0x2000000; | ||
| 47 | |||
| 48 | typedef struct region_t { | ||
| 49 | vm_address_t address; | ||
| 50 | vm_size_t size; | ||
| 51 | vm_prot_t protection; | ||
| 52 | vm_prot_t max_protection; | ||
| 53 | vm_inherit_t inheritance; | ||
| 54 | boolean_t shared; | ||
| 55 | port_t object_name; | ||
| 56 | vm_offset_t offset; | ||
| 57 | } region_t; | ||
| 58 | |||
| 59 | |||
| 60 | static void | ||
| 61 | grow( | ||
| 62 | struct load_command ***the_commands, | ||
| 63 | unsigned *the_commands_len | ||
| 64 | ) | ||
| 65 | { | ||
| 66 | if (*the_commands == NULL) { | ||
| 67 | *the_commands_len = 1; | ||
| 68 | *the_commands = malloc(sizeof(*the_commands)); | ||
| 69 | } else { | ||
| 70 | (*the_commands_len)++; | ||
| 71 | *the_commands = realloc(*the_commands, | ||
| 72 | (*the_commands_len * | ||
| 73 | sizeof(**the_commands))); | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | |||
| 78 | static void | ||
| 79 | save_command( | ||
| 80 | struct load_command *command, | ||
| 81 | struct load_command ***the_commands, | ||
| 82 | unsigned *the_commands_len | ||
| 83 | ) | ||
| 84 | { | ||
| 85 | struct load_command **tmp; | ||
| 86 | |||
| 87 | grow(the_commands, the_commands_len); | ||
| 88 | tmp = &(*the_commands)[*the_commands_len - 1]; | ||
| 89 | *tmp = malloc(command->cmdsize); | ||
| 90 | bcopy(command, *tmp, command->cmdsize); | ||
| 91 | } | ||
| 92 | |||
| 93 | static void | ||
| 94 | fatal_unexec(char *format, ...) | ||
| 95 | { | ||
| 96 | va_list ap; | ||
| 97 | |||
| 98 | va_start(ap, format); | ||
| 99 | fprintf(stderr, "unexec: "); | ||
| 100 | vfprintf(stderr, format, ap); | ||
| 101 | fprintf(stderr, "\n"); | ||
| 102 | va_end(ap); | ||
| 103 | } | ||
| 104 | |||
| 105 | static int | ||
| 106 | read_macho( | ||
| 107 | int fd, | ||
| 108 | struct mach_header *the_header, | ||
| 109 | struct load_command ***the_commands, | ||
| 110 | unsigned *the_commands_len | ||
| 111 | ) | ||
| 112 | { | ||
| 113 | struct load_command command; | ||
| 114 | struct load_command *buf; | ||
| 115 | int i; | ||
| 116 | int size; | ||
| 117 | |||
| 118 | if (read(fd, the_header, sizeof(*the_header)) != sizeof(*the_header)) { | ||
| 119 | fatal_unexec("cannot read macho header"); | ||
| 120 | return (0); | ||
| 121 | } | ||
| 122 | for (i = 0; i < the_header->ncmds; i++) { | ||
| 123 | if (read(fd, &command, sizeof(struct load_command)) != | ||
| 124 | sizeof(struct load_command)) { | ||
| 125 | fatal_unexec("cannot read macho load command header"); | ||
| 126 | return (0); | ||
| 127 | } | ||
| 128 | size = command.cmdsize - sizeof(struct load_command); | ||
| 129 | if (size < 0) { | ||
| 130 | fatal_unexec("bogus load command size"); | ||
| 131 | return (0); | ||
| 132 | } | ||
| 133 | buf = malloc(command.cmdsize); | ||
| 134 | buf->cmd = command.cmd; | ||
| 135 | buf->cmdsize = command.cmdsize; | ||
| 136 | if (read(fd, ((char *)buf + | ||
| 137 | sizeof(struct load_command)), | ||
| 138 | size) != size) { | ||
| 139 | fatal_unexec("cannot read load command data"); | ||
| 140 | return (0); | ||
| 141 | } | ||
| 142 | save_command(buf, the_commands, the_commands_len); | ||
| 143 | } | ||
| 144 | return (1); | ||
| 145 | } | ||
| 146 | |||
| 147 | static int | ||
| 148 | filldatagap( | ||
| 149 | vm_address_t start_address, | ||
| 150 | vm_size_t *size, | ||
| 151 | vm_address_t end_address | ||
| 152 | ) | ||
| 153 | { | ||
| 154 | vm_address_t address; | ||
| 155 | vm_size_t gapsize; | ||
| 156 | |||
| 157 | address = (start_address + *size); | ||
| 158 | gapsize = end_address - address; | ||
| 159 | *size += gapsize; | ||
| 160 | if (vm_allocate(task_self(), &address, gapsize, | ||
| 161 | FALSE) != KERN_SUCCESS) { | ||
| 162 | fatal_unexec("cannot vm_allocate"); | ||
| 163 | return (0); | ||
| 164 | } | ||
| 165 | return (1); | ||
| 166 | } | ||
| 167 | |||
| 168 | static int | ||
| 169 | get_data_region( | ||
| 170 | vm_address_t *address, | ||
| 171 | vm_size_t *size | ||
| 172 | ) | ||
| 173 | { | ||
| 174 | region_t region; | ||
| 175 | kern_return_t ret; | ||
| 176 | struct section *sect; | ||
| 177 | |||
| 178 | sect = getsectbyname(SEG_DATA, SECT_DATA); | ||
| 179 | region.address = 0; | ||
| 180 | *address = 0; | ||
| 181 | for (;;) { | ||
| 182 | ret = vm_region(task_self(), | ||
| 183 | ®ion.address, | ||
| 184 | ®ion.size, | ||
| 185 | ®ion.protection, | ||
| 186 | ®ion.max_protection, | ||
| 187 | ®ion.inheritance, | ||
| 188 | ®ion.shared, | ||
| 189 | ®ion.object_name, | ||
| 190 | ®ion.offset); | ||
| 191 | if (ret != KERN_SUCCESS || region.address >= VM_HIGHDATA) { | ||
| 192 | break; | ||
| 193 | } | ||
| 194 | if (*address != 0) { | ||
| 195 | if (region.address > *address + *size) { | ||
| 196 | if (!filldatagap(*address, size, | ||
| 197 | region.address)) { | ||
| 198 | return (0); | ||
| 199 | } | ||
| 200 | } | ||
| 201 | *size += region.size; | ||
| 202 | } else { | ||
| 203 | if (region.address == sect->addr) { | ||
| 204 | *address = region.address; | ||
| 205 | *size = region.size; | ||
| 206 | } | ||
| 207 | } | ||
| 208 | region.address += region.size; | ||
| 209 | } | ||
| 210 | return (1); | ||
| 211 | } | ||
| 212 | |||
| 213 | static char * | ||
| 214 | my_malloc( | ||
| 215 | vm_size_t size | ||
| 216 | ) | ||
| 217 | { | ||
| 218 | vm_address_t address; | ||
| 219 | |||
| 220 | if (vm_allocate(task_self(), &address, size, TRUE) != KERN_SUCCESS) { | ||
| 221 | return (NULL); | ||
| 222 | } | ||
| 223 | return ((char *)address); | ||
| 224 | } | ||
| 225 | |||
| 226 | static void | ||
| 227 | my_free( | ||
| 228 | char *buf, | ||
| 229 | vm_size_t size | ||
| 230 | ) | ||
| 231 | { | ||
| 232 | vm_deallocate(task_self(), (vm_address_t)buf, size); | ||
| 233 | } | ||
| 234 | |||
| 235 | static int | ||
| 236 | unexec_doit( | ||
| 237 | int infd, | ||
| 238 | int outfd | ||
| 239 | ) | ||
| 240 | { | ||
| 241 | int i; | ||
| 242 | struct load_command **the_commands = NULL; | ||
| 243 | unsigned the_commands_len; | ||
| 244 | struct mach_header the_header; | ||
| 245 | int fgrowth; | ||
| 246 | int fdatastart; | ||
| 247 | int fdatasize; | ||
| 248 | int size; | ||
| 249 | struct stat st; | ||
| 250 | char *buf; | ||
| 251 | vm_address_t data_address; | ||
| 252 | vm_size_t data_size; | ||
| 253 | |||
| 254 | struct segment_command *segment; | ||
| 255 | |||
| 256 | if (!read_macho(infd, &the_header, &the_commands, &the_commands_len)) { | ||
| 257 | return (0); | ||
| 258 | } | ||
| 259 | |||
| 260 | |||
| 261 | { | ||
| 262 | extern int malloc_cookie; | ||
| 263 | malloc_cookie = malloc_freezedry(); | ||
| 264 | } | ||
| 265 | if (!get_data_region(&data_address, &data_size)) { | ||
| 266 | return (0); | ||
| 267 | } | ||
| 268 | |||
| 269 | |||
| 270 | /* | ||
| 271 | * DO NOT USE MALLOC IN THIS SECTION | ||
| 272 | */ | ||
| 273 | { | ||
| 274 | /* | ||
| 275 | * Fix offsets | ||
| 276 | */ | ||
| 277 | for (i = 0; i < the_commands_len; i++) { | ||
| 278 | switch (the_commands[i]->cmd) { | ||
| 279 | case LC_SEGMENT: | ||
| 280 | segment = ((struct segment_command *) | ||
| 281 | the_commands[i]); | ||
| 282 | if (strcmp(segment->segname, SEG_DATA) == 0) { | ||
| 283 | fdatastart = segment->fileoff; | ||
| 284 | fdatasize = segment->filesize; | ||
| 285 | fgrowth = (data_size - | ||
| 286 | segment->filesize); | ||
| 287 | segment->vmsize = data_size; | ||
| 288 | segment->filesize = data_size; | ||
| 289 | } | ||
| 290 | break; | ||
| 291 | case LC_SYMTAB: | ||
| 292 | ((struct symtab_command *) | ||
| 293 | the_commands[i])->symoff += fgrowth; | ||
| 294 | ((struct symtab_command *) | ||
| 295 | the_commands[i])->stroff += fgrowth; | ||
| 296 | break; | ||
| 297 | case LC_SYMSEG: | ||
| 298 | ((struct symseg_command *) | ||
| 299 | the_commands[i])->offset += fgrowth; | ||
| 300 | break; | ||
| 301 | default: | ||
| 302 | break; | ||
| 303 | } | ||
| 304 | } | ||
| 305 | |||
| 306 | /* | ||
| 307 | * Write header | ||
| 308 | */ | ||
| 309 | if (write(outfd, &the_header, | ||
| 310 | sizeof(the_header)) != sizeof(the_header)) { | ||
| 311 | fatal_unexec("cannot write output file"); | ||
| 312 | return (0); | ||
| 313 | } | ||
| 314 | |||
| 315 | /* | ||
| 316 | * Write commands | ||
| 317 | */ | ||
| 318 | for (i = 0; i < the_commands_len; i++) { | ||
| 319 | if (write(outfd, the_commands[i], | ||
| 320 | the_commands[i]->cmdsize) != | ||
| 321 | the_commands[i]->cmdsize) { | ||
| 322 | fatal_unexec("cannot write output file"); | ||
| 323 | return (0); | ||
| 324 | } | ||
| 325 | } | ||
| 326 | |||
| 327 | /* | ||
| 328 | * Write original text | ||
| 329 | */ | ||
| 330 | if (lseek(infd, the_header.sizeofcmds + sizeof(the_header), | ||
| 331 | L_SET) < 0) { | ||
| 332 | fatal_unexec("cannot seek input file"); | ||
| 333 | return (0); | ||
| 334 | } | ||
| 335 | size = fdatastart - (sizeof(the_header) + | ||
| 336 | the_header.sizeofcmds); | ||
| 337 | buf = my_malloc(size); | ||
| 338 | if (read(infd, buf, size) != size) { | ||
| 339 | my_free(buf, size); | ||
| 340 | fatal_unexec("cannot read input file"); | ||
| 341 | } | ||
| 342 | if (write(outfd, buf, size) != size) { | ||
| 343 | my_free(buf, size); | ||
| 344 | fatal_unexec("cannot write output file"); | ||
| 345 | return (0); | ||
| 346 | } | ||
| 347 | my_free(buf, size); | ||
| 348 | |||
| 349 | |||
| 350 | /* | ||
| 351 | * Write new data | ||
| 352 | */ | ||
| 353 | if (write(outfd, (char *)data_address, | ||
| 354 | data_size) != data_size) { | ||
| 355 | fatal_unexec("cannot write output file"); | ||
| 356 | return (0); | ||
| 357 | } | ||
| 358 | |||
| 359 | } | ||
| 360 | |||
| 361 | /* | ||
| 362 | * OKAY TO USE MALLOC NOW | ||
| 363 | */ | ||
| 364 | |||
| 365 | /* | ||
| 366 | * Write rest of file | ||
| 367 | */ | ||
| 368 | fstat(infd, &st); | ||
| 369 | if (lseek(infd, fdatasize, L_INCR) < 0) { | ||
| 370 | fatal_unexec("cannot seek input file"); | ||
| 371 | return (0); | ||
| 372 | } | ||
| 373 | size = st.st_size - lseek(infd, 0, L_INCR); | ||
| 374 | |||
| 375 | buf = malloc(size); | ||
| 376 | if (read(infd, buf, size) != size) { | ||
| 377 | free(buf); | ||
| 378 | fatal_unexec("cannot read input file"); | ||
| 379 | return (0); | ||
| 380 | } | ||
| 381 | if (write(outfd, buf, size) != size) { | ||
| 382 | free(buf); | ||
| 383 | fatal_unexec("cannot write output file"); | ||
| 384 | return (0); | ||
| 385 | } | ||
| 386 | free(buf); | ||
| 387 | return (1); | ||
| 388 | } | ||
| 389 | |||
| 390 | void | ||
| 391 | unexec( | ||
| 392 | char *outfile, | ||
| 393 | char *infile | ||
| 394 | ) | ||
| 395 | { | ||
| 396 | int infd; | ||
| 397 | int outfd; | ||
| 398 | char tmpbuf[L_tmpnam]; | ||
| 399 | char *tmpfile; | ||
| 400 | |||
| 401 | infd = open(infile, O_RDONLY, 0); | ||
| 402 | if (infd < 0) { | ||
| 403 | fatal_unexec("cannot open input file `%s'", infile); | ||
| 404 | exit(1); | ||
| 405 | } | ||
| 406 | |||
| 407 | tmpnam(tmpbuf); | ||
| 408 | tmpfile = rindex(tmpbuf, '/'); | ||
| 409 | if (tmpfile == NULL) { | ||
| 410 | tmpfile = tmpbuf; | ||
| 411 | } else { | ||
| 412 | tmpfile++; | ||
| 413 | } | ||
| 414 | outfd = open(tmpfile, O_WRONLY|O_TRUNC|O_CREAT, 0755); | ||
| 415 | if (outfd < 0) { | ||
| 416 | close(infd); | ||
| 417 | fatal_unexec("cannot open tmp file `%s'", tmpfile); | ||
| 418 | exit(1); | ||
| 419 | } | ||
| 420 | if (!unexec_doit(infd, outfd)) { | ||
| 421 | close(infd); | ||
| 422 | close(outfd); | ||
| 423 | unlink(tmpfile); | ||
| 424 | exit(1); | ||
| 425 | } | ||
| 426 | close(infd); | ||
| 427 | close(outfd); | ||
| 428 | if (rename(tmpfile, outfile) < 0) { | ||
| 429 | unlink(tmpfile); | ||
| 430 | fatal_unexec("cannot rename `%s' to `%s'", tmpfile, outfile); | ||
| 431 | exit(1); | ||
| 432 | } | ||
| 433 | } | ||