1/* Dump Emacs in macho format. 2 Copyright (C) 1990, 1993, 2001, 2002, 2003, 2004, 3 2005, 2006, 2007 Free Software Foundation, Inc. 4 Written by Bradley Taylor (btaylor@next.com). 5 6This file is part of GNU Emacs. 7 8GNU Emacs is free software; you can redistribute it and/or modify 9it under the terms of the GNU General Public License as published by 10the Free Software Foundation; either version 2, or (at your option) 11any later version. 12 13GNU Emacs is distributed in the hope that it will be useful, 14but WITHOUT ANY WARRANTY; without even the implied warranty of 15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16GNU General Public License for more details. 17 18You should have received a copy of the GNU General Public License 19along with GNU Emacs; see the file COPYING. If not, write to 20the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21Boston, MA 02110-1301, USA. */ 22 23 24#undef __STRICT_BSD__ 25 26#include <stdio.h> 27#include <stdlib.h> 28#include <stdarg.h> 29#include <mach/mach.h> 30#include <mach-o/loader.h> 31#include <mach-o/reloc.h> 32#include <sys/file.h> 33#include <sys/stat.h> 34#include <unistd.h> 35/* Instead of unistd.h, this used to include libc.h. 36 "Nelson H. F. Beebe" <beebe@math.utah.edu> says that doesn't work 37 in system version 3.3. */ 38 39 40int malloc_cookie; 41 42/* 43 * Kludge: we don't expect any program data beyond VM_HIGHDATA 44 * What is really needed is a way to find out from malloc() which 45 * pages it vm_allocated and write only those out into the data segment. 46 * 47 * This kludge may break when we stop using fixed virtual address 48 * shared libraries. Actually, emacs will probably continue working, but be 49 * much larger on disk than it needs to be (because non-malloced data will 50 * be in the file). 51 */ 52static const unsigned VM_HIGHDATA = 0x2000000; 53 54typedef struct region_t { 55 vm_address_t address; 56 vm_size_t size; 57 vm_prot_t protection; 58 vm_prot_t max_protection; 59 vm_inherit_t inheritance; 60 boolean_t shared; 61 port_t object_name; 62 vm_offset_t offset; 63} region_t; 64 65 66static void 67grow( 68 struct load_command ***the_commands, 69 unsigned *the_commands_len 70 ) 71{ 72 if (*the_commands == NULL) { 73 *the_commands_len = 1; 74 *the_commands = malloc(sizeof(*the_commands)); 75 } else { 76 (*the_commands_len)++; 77 *the_commands = realloc(*the_commands, 78 (*the_commands_len * 79 sizeof(**the_commands))); 80 } 81} 82 83 84static void 85save_command( 86 struct load_command *command, 87 struct load_command ***the_commands, 88 unsigned *the_commands_len 89 ) 90{ 91 struct load_command **tmp; 92 93 grow(the_commands, the_commands_len); 94 tmp = &(*the_commands)[*the_commands_len - 1]; 95 *tmp = malloc(command->cmdsize); 96 bcopy(command, *tmp, command->cmdsize); 97} 98 99static void 100fatal_unexec(char *format, ...) 101{ 102 va_list ap; 103 104 va_start(ap, format); 105 fprintf(stderr, "unexec: "); 106 vfprintf(stderr, format, ap); 107 fprintf(stderr, "\n"); 108 va_end(ap); 109} 110 111static int 112read_macho( 113 int fd, 114 struct mach_header *the_header, 115 struct load_command ***the_commands, 116 unsigned *the_commands_len 117 ) 118{ 119 struct load_command command; 120 struct load_command *buf; 121 int i; 122 int size; 123 124 if (read(fd, the_header, sizeof(*the_header)) != sizeof(*the_header)) { 125 fatal_unexec("cannot read macho header"); 126 return (0); 127 } 128 for (i = 0; i < the_header->ncmds; i++) { 129 if (read(fd, &command, sizeof(struct load_command)) != 130 sizeof(struct load_command)) { 131 fatal_unexec("cannot read macho load command header"); 132 return (0); 133 } 134 size = command.cmdsize - sizeof(struct load_command); 135 if (size < 0) { 136 fatal_unexec("bogus load command size"); 137 return (0); 138 } 139 buf = malloc(command.cmdsize); 140 buf->cmd = command.cmd; 141 buf->cmdsize = command.cmdsize; 142 if (read(fd, ((char *)buf + 143 sizeof(struct load_command)), 144 size) != size) { 145 fatal_unexec("cannot read load command data"); 146 return (0); 147 } 148 save_command(buf, the_commands, the_commands_len); 149 } 150 return (1); 151} 152 153static int 154filldatagap( 155 vm_address_t start_address, 156 vm_size_t *size, 157 vm_address_t end_address 158 ) 159{ 160 vm_address_t address; 161 vm_size_t gapsize; 162 163 address = (start_address + *size); 164 gapsize = end_address - address; 165 *size += gapsize; 166 if (vm_allocate(task_self(), &address, gapsize, 167 FALSE) != KERN_SUCCESS) { 168 fatal_unexec("cannot vm_allocate"); 169 return (0); 170 } 171 return (1); 172} 173 174static int 175get_data_region( 176 vm_address_t *address, 177 vm_size_t *size 178 ) 179{ 180 region_t region; 181 kern_return_t ret; 182 struct section *sect; 183 184 sect = (struct section *) getsectbyname(SEG_DATA, SECT_DATA); 185 region.address = 0; 186 *address = 0; 187 for (;;) { 188 ret = vm_region(task_self(), 189 ®ion.address, 190 ®ion.size, 191 ®ion.protection, 192 ®ion.max_protection, 193 ®ion.inheritance, 194 ®ion.shared, 195 ®ion.object_name, 196 ®ion.offset); 197 if (ret != KERN_SUCCESS || region.address >= VM_HIGHDATA) { 198 break; 199 } 200 if (*address != 0) { 201 if (region.address > *address + *size) { 202 if (!filldatagap(*address, size, 203 region.address)) { 204 return (0); 205 } 206 } 207 *size += region.size; 208 } else { 209 if (region.address == sect->addr) { 210 *address = region.address; 211 *size = region.size; 212 } 213 } 214 region.address += region.size; 215 } 216 return (1); 217} 218 219static char * 220my_malloc( 221 vm_size_t size 222 ) 223{ 224 vm_address_t address; 225 226 if (vm_allocate(task_self(), &address, size, TRUE) != KERN_SUCCESS) { 227 return (NULL); 228 } 229 return ((char *)address); 230} 231 232static void 233my_free( 234 char *buf, 235 vm_size_t size 236 ) 237{ 238 vm_deallocate(task_self(), (vm_address_t)buf, size); 239} 240 241static int 242unexec_doit( 243 int infd, 244 int outfd 245 ) 246{ 247 int i; 248 struct load_command **the_commands = NULL; 249 unsigned the_commands_len; 250 struct mach_header the_header; 251 int fgrowth = 0; 252 int fdatastart; 253 int fdatasize; 254 int size; 255 struct stat st; 256 char *buf; 257 vm_address_t data_address; 258 vm_size_t data_size; 259 vm_size_t vmaddr_growth = 0; 260 vm_size_t dataseg_vmaddr, dataseg_vmend; 261 262 struct segment_command *segment; 263 264#ifdef NS_TARGET 265 unsigned long extreloff = 0; 266 unsigned long nextrel = 0; 267 struct dysymtab_command *dysymtab; 268 struct relocation_info reloc_info; 269#endif 270 271 if (!read_macho(infd, &the_header, &the_commands, &the_commands_len)) { 272 return (0); 273 } 274 275 276 malloc_cookie = malloc_freezedry (); 277 if (!get_data_region(&data_address, &data_size)) { 278 return (0); 279 } 280 281 282 /* 283 * DO NOT USE MALLOC IN THIS SECTION 284 */ 285 { 286 /* 287 * Fix offsets 288 */ 289 for (i = 0; i < the_commands_len; i++) { 290 switch (the_commands[i]->cmd) { 291 case LC_SEGMENT: 292 segment = ((struct segment_command *) 293 the_commands[i]); 294 if (strcmp(segment->segname, SEG_DATA) == 0) { 295 fdatastart = segment->fileoff; 296 fdatasize = segment->filesize; 297 fgrowth = (data_size - 298 segment->filesize); 299 segment->vmsize = data_size; 300 segment->filesize = data_size; 301 dataseg_vmaddr = segment->vmaddr; 302 dataseg_vmend = segment->vmaddr + segment->vmsize; 303 vmaddr_growth = segment->vmaddr + segment->vmsize; 304 } else { 305 ((struct segment_command *)the_commands[i])->fileoff += fgrowth; 306 } 307 308 if( strcmp( segment->segname, SEG_LINKEDIT ) == 0 ) { 309 segment->vmaddr = vmaddr_growth; 310 } 311 312 break; 313 case LC_SYMTAB: 314 ((struct symtab_command *) 315 the_commands[i])->symoff += fgrowth; 316 ((struct symtab_command *) 317 the_commands[i])->stroff += fgrowth; 318 break; 319 case LC_SYMSEG: 320 ((struct symseg_command *) 321 the_commands[i])->offset += fgrowth; 322 break; 323#ifdef NS_TARGET 324 case LC_DYSYMTAB: 325 dysymtab = ((struct dysymtab_command *)the_commands[i]); 326 extreloff = dysymtab->extreloff; 327 nextrel = dysymtab->nextrel; 328 dysymtab->indirectsymoff += fgrowth; 329 dysymtab->extreloff += fgrowth; 330 break; 331#endif 332 default: 333 break; 334 } 335 } 336 337 /* 338 * Write header 339 */ 340 if (write(outfd, &the_header, 341 sizeof(the_header)) != sizeof(the_header)) { 342 fatal_unexec("cannot write output file"); 343 return (0); 344 } 345 346 /* 347 * Write commands 348 */ 349 for (i = 0; i < the_commands_len; i++) { 350 if (write(outfd, the_commands[i], 351 the_commands[i]->cmdsize) != 352 the_commands[i]->cmdsize) { 353 fatal_unexec("cannot write output file"); 354 return (0); 355 } 356 } 357 358 /* 359 * Write original text 360 */ 361 if (lseek(infd, the_header.sizeofcmds + sizeof(the_header), 362 L_SET) < 0) { 363 fatal_unexec("cannot seek input file"); 364 return (0); 365 } 366 size = fdatastart - (sizeof(the_header) + 367 the_header.sizeofcmds); 368 buf = my_malloc(size); 369 if (read(infd, buf, size) != size) { 370 my_free(buf, size); 371 fatal_unexec("cannot read input file"); 372 } 373 if (write(outfd, buf, size) != size) { 374 my_free(buf, size); 375 fatal_unexec("cannot write output file"); 376 return (0); 377 } 378 my_free(buf, size); 379 380 381 /* 382 * Write new data 383 */ 384 if (write(outfd, (char *)data_address, 385 data_size) != data_size) { 386 fatal_unexec("cannot write output file"); 387 return (0); 388 } 389 390 } 391 392 /* 393 * OKAY TO USE MALLOC NOW 394 */ 395 396 /* 397 * Write rest of file 398 */ 399 fstat(infd, &st); 400 if (lseek(infd, fdatasize, L_INCR) < 0) { 401 fatal_unexec("cannot seek input file"); 402 return (0); 403 } 404 size = st.st_size - lseek(infd, 0, L_INCR); 405 406 buf = malloc(size); 407 if (read(infd, buf, size) != size) { 408 free(buf); 409 fatal_unexec("cannot read input file"); 410 return (0); 411 } 412 if (write(outfd, buf, size) != size) { 413 free(buf); 414 fatal_unexec("cannot write output file"); 415 return (0); 416 } 417 free(buf); 418 419#ifdef NS_TARGET 420 /* 421 * Fix up relocation entries in the data segment. 422 */ 423 424 if (lseek(infd, extreloff, L_SET) < 0) { 425 fatal_unexec("cannot seek input file"); 426 return (0); 427 } 428 429 for (i = 0; i < nextrel; i++) 430 { 431 long zeroval = 0; 432 433 if (read(infd, &reloc_info, sizeof (reloc_info)) != sizeof (reloc_info)) { 434 fatal_unexec("cannot read input file"); 435 return (0); 436 } 437 if (reloc_info.r_address >= dataseg_vmaddr && reloc_info.r_address < dataseg_vmend) 438 { 439 if (lseek (outfd, fdatastart + reloc_info.r_address - dataseg_vmaddr, L_SET) < 0 ) { 440 fatal_unexec("cannot seek input file"); 441 return (0); 442 } 443 switch (reloc_info.r_length) { 444 case 0: 445 if (write(outfd, &zeroval, 1) != 1) { 446 fatal_unexec("cannot write output file"); 447 return (0); 448 } 449 break; 450 case 1: 451 if (write(outfd, &zeroval, 2) != 2) { 452 fatal_unexec("cannot write output file"); 453 return (0); 454 } 455 break; 456 case 2: 457 if (write(outfd, &zeroval, 4) != 4) { 458 fatal_unexec("cannot write output file"); 459 return (0); 460 } 461 break; 462 } 463 } 464 } 465#endif 466 467 return (1); 468} 469 470void 471unexec( 472 char *outfile, 473 char *infile 474 ) 475{ 476 int infd; 477 int outfd; 478 char tmpbuf[L_tmpnam]; 479 char *tmpfile; 480 481 infd = open(infile, O_RDONLY, 0); 482 if (infd < 0) { 483 fatal_unexec("cannot open input file `%s'", infile); 484 exit(1); 485 } 486 487 tmpnam(tmpbuf); 488 tmpfile = rindex(tmpbuf, '/'); 489 if (tmpfile == NULL) { 490 tmpfile = tmpbuf; 491 } else { 492 tmpfile++; 493 } 494 outfd = open(tmpfile, O_WRONLY|O_TRUNC|O_CREAT, 0755); 495 if (outfd < 0) { 496 close(infd); 497 fatal_unexec("cannot open tmp file `%s'", tmpfile); 498 exit(1); 499 } 500 if (!unexec_doit(infd, outfd)) { 501 close(infd); 502 close(outfd); 503 unlink(tmpfile); 504 exit(1); 505 } 506 close(infd); 507 close(outfd); 508 if (rename(tmpfile, outfile) < 0) { 509 unlink(tmpfile); 510 fatal_unexec("cannot rename `%s' to `%s'", tmpfile, outfile); 511 exit(1); 512 } 513} 514 515/* arch-tag: 9796bdc3-c050-417a-b2f5-4cfd31032634 516 (do not change this comment) */ 517