Example #1
0
/* Copy a LC_SEGMENT load command for the __DATA segment in the input
   file to the output file.  We assume that only one such segment load
   command exists in the input file and it contains the sections
   __data, __bss, __common, __la_symbol_ptr, __nl_symbol_ptr, and
   __dyld.  The first three of these should be dumped from memory and
   the rest should be copied from the input file.  Note that the
   sections __bss and __common contain no data in the input file
   because their flag fields have the value S_ZEROFILL.  Dumping these
   from memory makes it necessary to adjust file offset fields in
   subsequently dumped load commands.  Then, create new __DATA segment
   load commands for regions on the region list other than the one
   corresponding to the __DATA segment in the input file.  */
static void
copy_data_segment (struct load_command *lc)
{
  struct segment_command *scp = (struct segment_command *) lc;
  struct section *sectp;
  int j;
  unsigned long header_offset, old_file_offset;

  /* The new filesize of the segment is set to its vmsize because data
     blocks for segments must start at region boundaries.  Note that
     this may leave unused locations at the end of the segment data
     block because the total of the sizes of all sections in the
     segment is generally smaller than vmsize.  */
  scp->filesize = scp->vmsize;

  printf ("Writing segment %-16.16s @ %#8lx (%#8lx/%#8lx @ %#10lx)\n",
	  scp->segname, curr_file_offset, (long)(scp->filesize),
	  (long)(scp->vmsize), (long) (scp->vmaddr));

  /* Offsets in the output file for writing the next section structure
     and segment data block, respectively.  */
  header_offset = curr_header_offset + sizeof (struct segment_command);

  sectp = (struct section *) (scp + 1);
  for (j = 0; j < scp->nsects; j++)
    {
      old_file_offset = sectp->offset;
      sectp->offset = sectp->addr - scp->vmaddr + curr_file_offset;
      /* The __data section is dumped from memory.  The __bss and
	 __common sections are also dumped from memory but their flag
	 fields require changing (from S_ZEROFILL to S_REGULAR).  The
	 other three kinds of sections are just copied from the input
	 file.  */
      if (strncmp (sectp->sectname, SECT_DATA, 16) == 0)
	{
	  extern char my_edata[];
	  unsigned long my_size;

	  /* The __data section is basically dumped from memory.  But
	     initialized data in statically linked libraries are
	     copied from the input file.  In particular,
	     add_image_hook.names and add_image_hook.pointers stored
	     by libarclite_macosx.a, are restored so that they will be
	     reinitialized when the dumped binary is executed.  */
	  my_size = (unsigned long)my_edata - sectp->addr;
	  if (!(sectp->addr <= (unsigned long)my_edata
		&& my_size <= sectp->size))
	    unexec_error ("my_edata is not in section %s", SECT_DATA);
	  if (!unexec_write (sectp->offset, (void *) sectp->addr, my_size))
	    unexec_error ("cannot write section %s", SECT_DATA);
	  if (!unexec_copy (sectp->offset + my_size, old_file_offset + my_size,
			    sectp->size - my_size))
	    unexec_error ("cannot copy section %s", SECT_DATA);
	  if (!unexec_write (header_offset, sectp, sizeof (struct section)))
	    unexec_error ("cannot write section %s's header", SECT_DATA);
	}
      else if (strncmp (sectp->sectname, SECT_COMMON, 16) == 0)
	{
	  sectp->flags = S_REGULAR;
	  if (!unexec_write (sectp->offset, (void *) sectp->addr, sectp->size))
	    unexec_error ("cannot write section %.16s", sectp->sectname);
	  if (!unexec_write (header_offset, sectp, sizeof (struct section)))
	    unexec_error ("cannot write section %.16s's header", sectp->sectname);
	}
      else if (strncmp (sectp->sectname, SECT_BSS, 16) == 0)
	{
	  extern char *my_endbss_static;
	  unsigned long my_size;

	  sectp->flags = S_REGULAR;

	  /* Clear uninitialized local variables in statically linked
	     libraries.  In particular, function pointers stored by
	     libSystemStub.a, which is introduced in Mac OS X 10.4 for
	     binary compatibility with respect to long double, are
	     cleared so that they will be reinitialized when the
	     dumped binary is executed on other versions of OS.  */
	  my_size = (unsigned long)my_endbss_static - sectp->addr;
	  if (!(sectp->addr <= (unsigned long)my_endbss_static
		&& my_size <= sectp->size))
	    unexec_error ("my_endbss_static is not in section %.16s",
			  sectp->sectname);
	  if (!unexec_write (sectp->offset, (void *) sectp->addr, my_size))
	    unexec_error ("cannot write section %.16s", sectp->sectname);
	  if (!unexec_write_zero (sectp->offset + my_size,
				  sectp->size - my_size))
	    unexec_error ("cannot write section %.16s", sectp->sectname);
	  if (!unexec_write (header_offset, sectp, sizeof (struct section)))
	    unexec_error ("cannot write section %.16s's header", sectp->sectname);
	}
      else if (strncmp (sectp->sectname, "__la_symbol_ptr", 16) == 0
	       || strncmp (sectp->sectname, "__nl_symbol_ptr", 16) == 0
	       || strncmp (sectp->sectname, "__got", 16) == 0
	       || strncmp (sectp->sectname, "__la_sym_ptr2", 16) == 0
	       || strncmp (sectp->sectname, "__dyld", 16) == 0
	       || strncmp (sectp->sectname, "__const", 16) == 0
	       || strncmp (sectp->sectname, "__cfstring", 16) == 0
	       || strncmp (sectp->sectname, "__gcc_except_tab", 16) == 0
	       || strncmp (sectp->sectname, "__program_vars", 16) == 0
	       || strncmp (sectp->sectname, "__mod_init_func", 16) == 0
	       || strncmp (sectp->sectname, "__mod_term_func", 16) == 0
	       || strncmp (sectp->sectname, "__objc_", 7) == 0)
	{
	  if (!unexec_copy (sectp->offset, old_file_offset, sectp->size))
	    unexec_error ("cannot copy section %.16s", sectp->sectname);
	  if (!unexec_write (header_offset, sectp, sizeof (struct section)))
	    unexec_error ("cannot write section %.16s's header", sectp->sectname);
	}
      else
	unexec_error ("unrecognized section %.16s in __DATA segment",
		      sectp->sectname);

      printf ("        section %-16.16s at %#8lx - %#8lx (sz: %#8lx)\n",
	      sectp->sectname, (long) (sectp->offset),
	      (long) (sectp->offset + sectp->size), (long) (sectp->size));

      header_offset += sizeof (struct section);
      sectp++;
    }

  curr_file_offset += ROUNDUP_TO_PAGE_BOUNDARY (scp->filesize);

  if (!unexec_write (curr_header_offset, scp, sizeof (struct segment_command)))
    unexec_error ("cannot write header of __DATA segment");
  curr_header_offset += lc->cmdsize;

  /* Create new __DATA segment load commands for regions on the region
     list that do not corresponding to any segment load commands in
     the input file.
  */
  for (j = 0; j < num_unexec_regions; j++)
    {
      struct segment_command sc;

      sc.cmd = LC_SEGMENT;
      sc.cmdsize = sizeof (struct segment_command);
      strncpy (sc.segname, SEG_DATA, 16);
      sc.vmaddr = unexec_regions[j].range.address;
      sc.vmsize = unexec_regions[j].range.size;
      sc.fileoff = curr_file_offset;
      sc.filesize = unexec_regions[j].filesize;
      sc.maxprot = VM_PROT_READ | VM_PROT_WRITE;
      sc.initprot = VM_PROT_READ | VM_PROT_WRITE;
      sc.nsects = 0;
      sc.flags = 0;

      printf ("Writing segment %-16.16s @ %#8lx (%#8lx/%#8lx @ %#10lx)\n",
	      sc.segname, (long) (sc.fileoff), (long) (sc.filesize),
	      (long) (sc.vmsize), (long) (sc.vmaddr));

      if (!unexec_write (sc.fileoff, (void *) sc.vmaddr, sc.filesize))
	unexec_error ("cannot write new __DATA segment");
      curr_file_offset += ROUNDUP_TO_PAGE_BOUNDARY (sc.filesize);

      if (!unexec_write (curr_header_offset, &sc, sc.cmdsize))
	unexec_error ("cannot write new __DATA segment's header");
      curr_header_offset += sc.cmdsize;
      mh.ncmds++;
    }
}
Example #2
0
/* Copy a LC_DYSYMTAB load command from the input file to the output
   file, adjusting the file offset fields.  */
static void
copy_dysymtab (struct load_command *lc, long delta)
{
  struct dysymtab_command *dstp = (struct dysymtab_command *) lc;
  vm_address_t base;

#ifdef _LP64
#if __ppc64__
  {
    int i;

    base = 0;
    for (i = 0; i < nlc; i++)
      if (lca[i]->cmd == LC_SEGMENT)
	{
	  struct segment_command *scp = (struct segment_command *) lca[i];

	  if (scp->vmaddr + scp->vmsize > 0x100000000
	      && (scp->initprot & VM_PROT_WRITE) != 0)
	    {
	      base = data_segment_scp->vmaddr;
	      break;
	    }
	}
  }
#else
  /* First writable segment address.  */
  base = data_segment_scp->vmaddr;
#endif
#else
  /* First segment address in the file (unless MH_SPLIT_SEGS set). */
  base = 0;
#endif

  unrelocate ("local", dstp->locreloff, dstp->nlocrel, base);
  unrelocate ("external", dstp->extreloff, dstp->nextrel, base);

  if (dstp->nextrel > 0) {
    dstp->extreloff += delta;
  }

  if (dstp->nlocrel > 0) {
    dstp->locreloff += delta;
  }

  if (dstp->nindirectsyms > 0)
    dstp->indirectsymoff += delta;

  printf ("Writing LC_DYSYMTAB command\n");

  if (!unexec_write (curr_header_offset, lc, lc->cmdsize))
    unexec_error ("cannot write symtab command to header");

  curr_header_offset += lc->cmdsize;

#if __ppc64__
  /* Check if the relocation base needs to be changed.  */
  if (base == 0)
    {
      vm_address_t newbase = 0;
      int i;

      for (i = 0; i < num_unexec_regions; i++)
	if (unexec_regions[i].range.address + unexec_regions[i].range.size
	    > 0x100000000)
	  {
	    newbase = data_segment_scp->vmaddr;
	    break;
	  }

      if (newbase)
	{
	  rebase_reloc_address (dstp->locreloff, dstp->nlocrel, delta, newbase);
	  rebase_reloc_address (dstp->extreloff, dstp->nextrel, delta, newbase);
	}
    }
#endif
}
Example #3
0
/* Loop through all load commands and dump them.  Then write the Mach
   header.  */
static void
dump_it (void)
{
  int i;
  long linkedit_delta = 0;

  printf ("--- Load Commands written to Output File ---\n");

  for (i = 0; i < nlc; i++)
    switch (lca[i]->cmd)
      {
      case LC_SEGMENT:
	{
	  struct segment_command *scp = (struct segment_command *) lca[i];
	  if (strncmp (scp->segname, SEG_DATA, 16) == 0)
	    {
	      /* save data segment file offset and segment_command for
		 unrelocate */
	      if (data_segment_old_fileoff)
		unexec_error ("cannot handle multiple DATA segments"
			      " in input file");
	      data_segment_old_fileoff = scp->fileoff;
	      data_segment_scp = scp;

	      copy_data_segment (lca[i]);
	    }
	  else if (strncmp (scp->segname, EMACS_READ_ONLY_SEGMENT, 16) == 0)
	    {
	      copy_emacs_read_only_segment (lca[i]);
	    }
	  else
	    {
	      if (strncmp (scp->segname, SEG_LINKEDIT, 16) == 0)
		{
		  if (linkedit_delta)
		    unexec_error ("cannot handle multiple LINKEDIT segments"
				  " in input file");
		  linkedit_delta = curr_file_offset - scp->fileoff;
		}

	      copy_segment (lca[i]);
	    }
	}
	break;
      case LC_SYMTAB:
	copy_symtab (lca[i], linkedit_delta);
	break;
      case LC_DYSYMTAB:
	copy_dysymtab (lca[i], linkedit_delta);
	break;
      case LC_TWOLEVEL_HINTS:
	copy_twolevelhints (lca[i], linkedit_delta);
	break;
#ifdef LC_DYLD_INFO
      case LC_DYLD_INFO:
      case LC_DYLD_INFO_ONLY:
	copy_dyld_info (lca[i], linkedit_delta);
	break;
#endif
#ifdef LC_FUNCTION_STARTS
      case LC_FUNCTION_STARTS:
#ifdef LC_DATA_IN_CODE
      case LC_DATA_IN_CODE:
#endif
#ifdef LC_DYLIB_CODE_SIGN_DRS
      case LC_DYLIB_CODE_SIGN_DRS:
#endif
	copy_linkedit_data (lca[i], linkedit_delta);
	break;
#endif
      default:
	copy_other (lca[i]);
	break;
      }

  if (curr_header_offset > text_seg_lowest_offset)
    unexec_error ("not enough room for load commands for new __DATA segments");

  printf ("%ld unused bytes follow Mach-O header\n",
	  text_seg_lowest_offset - curr_header_offset);

  mh.sizeofcmds = curr_header_offset - sizeof (struct mach_header);
  if (!unexec_write (0, &mh, sizeof (struct mach_header)))
    unexec_error ("cannot write final header contents");
}
Example #4
0
int
unexec (char *new_name, char *old_name,
        unsigned int emacs_edata, unsigned int dummy1, unsigned int dummy2)
{
  /* /dld.sl data */
  struct dynamic *ld = 0;
  /* old and new state */
  int old_fd;
  int new_fd;
  struct exec old_hdr;
  struct exec new_hdr;
  struct stat old_buf;
  /* some process specific "constants" */
  unsigned long n_pagsiz;
  caddr_t dynamic_beg;
  caddr_t current_break = (caddr_t) sbrk (0);

  /* dynamically linked image? -- if so, find dld.sl structures */
  if (dynamic_addr)
    {
      ld = (struct dynamic *) dynamic_addr;
#ifdef DEBUG
      printf ("dl_text = %#x\n", ld->text);
      printf ("dl_data = %#x\n", ld->data);
      printf ("dl_bss = %#x\n", ld->bss);
      printf ("dl_end = %#x\n", ld->end);
      printf ("dl_dmodule = %#x\n", ld->dmodule);
      printf ("dl_dlt = %#x\n", ld->dlt);
      printf ("dl_plt = %#x\n", ld->plt);
#endif
    }

  /* open the old and new files, figuring out how big the old one is
     so that we can map it in */
  old_fd = unexec_open (old_name, O_RDONLY, 0);
  new_fd = unexec_open (new_name, O_RDWR | O_CREAT | O_TRUNC, 0666);

  /* setup the header and the statbuf for old_fd */
  unexec_read (old_fd, 0, (char *) &old_hdr, sizeof (old_hdr));
  unexec_fstat (old_fd, &old_buf);

  /* set up some important constants */
  n_pagsiz = EXEC_PAGESIZE;

  /* setup beginning of data to copy from executable */
  if (ld)
      dynamic_beg = ld->dmodule;
  else
      dynamic_beg = (caddr_t)EXEC_ALIGN (old_hdr.a_text) + old_hdr.a_data;

  /* set up the new exec */
  new_hdr = old_hdr;
  new_hdr.a_text = MASK_DOWN (emacs_edata, n_pagsiz);
  new_hdr.a_data = MASK_UP (current_break, n_pagsiz)
      - EXEC_ALIGN(new_hdr.a_text);
  new_hdr.a_bss  = 0;

#ifdef DEBUG
  printf ("old text %#x\n", old_hdr.a_text);
  printf ("new text %#x\n", new_hdr.a_text);
  printf ("old data %#x\n", old_hdr.a_data);
  printf ("new data %#x\n", new_hdr.a_data);
  printf ("old bss %#x\n", old_hdr.a_bss);
  printf ("new bss %#x\n", new_hdr.a_bss);
#endif

  /* set up this variable, in case we want to reset "the break" 
     when restarting */
  sbrk_of_0_at_unexec = ((unsigned long) MASK_UP (current_break, n_pagsiz));
     
  /* Write out the first approximation to the new file. The sizes of
     each section will be correct, but there will be a number of 
     corrections that will need to be made. */
  {
    long old_datoff = DATA_OFFSET (old_hdr);
    long new_datoff = DATA_OFFSET (new_hdr);
    long old_dataddr = EXEC_ALIGN (old_hdr.a_text);
    long new_dataddr = EXEC_ALIGN (new_hdr.a_text);
    long new_mcaloff = MODCAL_OFFSET (new_hdr);
    long old_mcaloff = MODCAL_OFFSET (old_hdr);
    long newtext_size = new_hdr.a_text - old_dataddr;
    long newdata1_size = (unsigned long)dynamic_beg - new_dataddr;
    long dyn_size = (EXEC_ALIGN (old_hdr.a_text) + old_hdr.a_data)
        - (unsigned long)dynamic_beg;
    long newdata2_size = (unsigned long)current_break
        - ((unsigned long)dynamic_beg + dyn_size);
    long pad_size = 
      MASK_UP (current_break, n_pagsiz) - ((unsigned long) current_break);

#ifdef DEBUG
    printf ("current break is %#lx\n", current_break);

    printf ("old_dataddr = %#lx, dynamic_beg = %#lx\n",
            old_dataddr, dynamic_beg);
#endif

    /*
     * First, write the text segment with new header -- copy
     * everything until the start of the data segment from the old
     * file
     */
#ifdef DEBUG
    printf ("copying %#lx bytes of text from 0\n", old_datoff);
#endif
    unexec_copy (new_fd, old_fd, 0, 0, old_datoff);
    /* pad out the text segment */
#ifdef DEBUG
    printf ( "text pad size is %#x\n", old_dataddr - old_hdr.a_text);
#endif
    unexec_pad (new_fd, old_dataddr - old_hdr.a_text);

    /*
     * Update debug header spoo
     */
    if (new_hdr.a_extension > 0)
    {
	new_hdr.a_extension += LESYM_OFFSET(new_hdr) - LESYM_OFFSET(old_hdr);
    }

    /*
     * go back and write the new header.
     */
    unexec_write (new_fd, 0, (char *) &new_hdr, sizeof (new_hdr));

    
    /*
     * Copy the part of the data segment which becomes text from the
     * running image.
     */
#ifdef DEBUG
    printf ("copying %#lx bytes of new text from %#lx to position %#lx\n",
            newtext_size, old_dataddr, TEXT_OFFSET(new_hdr) + old_dataddr);
#endif
    unexec_write (new_fd, TEXT_OFFSET(new_hdr) + old_dataddr,
                  (caddr_t)old_dataddr, newtext_size);

#ifdef DEBUG
    printf ("new DATA_OFFSET is %#lx\n", new_datoff);
#endif

    /*
     * Copy the part of the old data segment which will be data
     * in the new executable (before the dynamic stuff)
     * from the running image.
     */
#ifdef DEBUG
    printf ("copying %#lx bytes of data from %#lx to position %#lx\n",
            newdata1_size, new_dataddr, new_datoff);
#endif
    unexec_write (new_fd, new_datoff, (caddr_t)new_dataddr, newdata1_size);

    /* copy the dynamic part of the data segment from the old executable */
    if (dyn_size)
      {
#ifdef DEBUG
        printf ("copying %#lx bytes of dyn data from executable"
                " at address %#lx to position %#lx\n", 
                dyn_size, dynamic_beg, new_datoff + newdata1_size);
#endif
        unexec_copy (new_fd, old_fd, old_datoff + newtext_size + newdata1_size,
                     new_datoff + newdata1_size, dyn_size);
      }

    /* copy remaining data (old bss) from the running image */
#ifdef DEBUG
    printf ("copying %#lx bytes of data from %#lx to position %#lx\n",
            newdata2_size, new_dataddr + newdata1_size + dyn_size,
            new_datoff + newdata1_size + dyn_size);
#endif
    unexec_write (new_fd, new_datoff + newdata1_size + dyn_size,
                  (caddr_t)(new_dataddr + newdata1_size + dyn_size),
                  newdata2_size);

    /* pad out the data segment */
#ifdef DEBUG
    printf ( "pad size is %#x\n", pad_size);
#endif
    unexec_pad (new_fd, pad_size);
    
    /* Finally, copy the rest of the junk from the old file. */
#ifdef DEBUG
    printf ("Copying %#lx bytes of junk from %#lx (old) to %#lx (new)\n",
            old_buf.st_size - old_mcaloff, old_mcaloff, new_mcaloff);
#endif
    unexec_copy (new_fd, old_fd, old_mcaloff, new_mcaloff,
                 old_buf.st_size - old_mcaloff);

    {
	long			curpos, offset;
	struct _debug_header	dhdr;
	int			new_header_delta;

	new_header_delta = LESYM_OFFSET(new_hdr) - LESYM_OFFSET(old_hdr);
	if ((new_header_delta > 0) &&
	    ((offset = EXT_OFFSET(old_hdr)) > 0))
	{
	    curpos = lseek(new_fd, 0, SEEK_CUR);
	    lseek(old_fd, offset, 0);
	    if (read(old_fd, &dhdr, sizeof(dhdr)) == sizeof(dhdr))
	    {
		dhdr.header_offset += new_header_delta;
		dhdr.gntt_offset += new_header_delta;
		dhdr.lntt_offset += new_header_delta;
		dhdr.slt_offset += new_header_delta;
		dhdr.vt_offset += new_header_delta;
		dhdr.xt_offset += new_header_delta;
		lseek(new_fd, EXT_OFFSET(new_hdr), SEEK_SET);
		if (write(new_fd, &dhdr, sizeof(dhdr)) != sizeof(dhdr))
		{
		    unexec_error("Unable to write debug information to \"%s\"\n",
				 1, new_name);
		}
		lseek(new_fd, curpos, SEEK_SET);
	    }
	    else
	    {
		unexec_error("Unable to read debug information from \"%s\"\n",
			     1, old_name);
	    }
	}
    }
  }
  
     
  /* make the output file executable -- then quit */
  unexec_fchmod (new_fd, 0755);
  close (old_fd);
  close (new_fd);
  return 0;
}