unexelf.c 20.8 KB
Newer Older
Paul Eggert's avatar
Paul Eggert committed
1
/* Copyright (C) 1985-1988, 1990, 1992, 1999-2020 Free Software
2
   Foundation, Inc.
Jim Blandy's avatar
Jim Blandy committed
3

4
This file is part of GNU Emacs.
5

6
GNU Emacs is free software: you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8 9
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.
10

11 12 13 14 15 16
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
17
along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
Jim Blandy's avatar
Jim Blandy committed
18

19
/*
Jim Blandy's avatar
Jim Blandy committed
20 21 22 23 24 25 26 27 28
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */


/*
 * unexec.c - Convert a running program into an a.out file.
 *
 * Author:	Spencer W. Thomas
29 30
 *		Computer Science Dept.
 *		University of Utah
Jim Blandy's avatar
Jim Blandy committed
31 32 33 34
 * Date:	Tue Mar  2 1982
 * Modified heavily since then.
 *
 * Synopsis:
35
 *	unexec (const char *new_name, const char *old_name);
Jim Blandy's avatar
Jim Blandy committed
36 37 38
 *
 * Takes a snapshot of the program and makes an a.out format file in the
 * file named by the string argument new_name.
39 40
 * If old_name is non-NULL, the symbol table will be taken from the given file.
 * On some machines, an existing old_name file is required.
Jim Blandy's avatar
Jim Blandy committed
41 42
 *
 */
43

44 45 46
/* We do not use mmap because that fails with NFS.
   Instead we read the whole file, modify it, and write it out.  */

Dave Love's avatar
Dave Love committed
47
#include <config.h>
48 49
#include "unexec.h"
#include "lisp.h"
Dave Love's avatar
Dave Love committed
50

51 52 53 54
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <memory.h>
55
#include <stdint.h>
Jim Blandy's avatar
Jim Blandy committed
56 57
#include <stdio.h>
#include <sys/stat.h>
58
#include <sys/types.h>
Jim Blandy's avatar
Jim Blandy committed
59
#include <unistd.h>
60

Paul Eggert's avatar
Paul Eggert committed
61 62 63 64 65
#ifdef __QNX__
# include <sys/elf.h>
#elif !defined __NetBSD__ && !defined __OpenBSD__
# include <elf.h>
#endif
Jim Blandy's avatar
Jim Blandy committed
66
#include <sys/mman.h>
Dan Nicolaescu's avatar
Dan Nicolaescu committed
67
#if defined (_SYSTYPE_SYSV)
68 69
#include <sys/elf_mips.h>
#include <sym.h>
Dan Nicolaescu's avatar
Dan Nicolaescu committed
70
#endif /* _SYSTYPE_SYSV */
71

72 73 74 75 76 77 78 79 80 81 82 83
#ifndef MAP_ANON
#ifdef MAP_ANONYMOUS
#define MAP_ANON MAP_ANONYMOUS
#else
#define MAP_ANON 0
#endif
#endif

#ifndef MAP_FAILED
#define MAP_FAILED ((void *) -1)
#endif

84
#if defined (__alpha__) && !defined (__NetBSD__) && !defined (__OpenBSD__)
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
/* Declare COFF debugging symbol table.  This used to be in
   /usr/include/sym.h, but this file is no longer included in Red Hat
   5.0 and presumably in any other glibc 2.x based distribution.  */
typedef struct {
	short magic;
	short vstamp;
	int ilineMax;
	int idnMax;
	int ipdMax;
	int isymMax;
	int ioptMax;
	int iauxMax;
	int issMax;
	int issExtMax;
	int ifdMax;
	int crfd;
	int iextMax;
	long cbLine;
	long cbLineOffset;
	long cbDnOffset;
	long cbPdOffset;
	long cbSymOffset;
	long cbOptOffset;
	long cbAuxOffset;
	long cbSsOffset;
	long cbSsExtOffset;
	long cbFdOffset;
	long cbRfdOffset;
	long cbExtOffset;
Dave Love's avatar
Dave Love committed
114
} HDRR, *pHDRR;
Juanma Barranquero's avatar
Juanma Barranquero committed
115
#define cbHDRR sizeof (HDRR)
116
#define hdrNil ((pHDRR)0)
117 118
#endif

119 120 121 122
#ifdef __NetBSD__
/*
 * NetBSD does not have normal-looking user-land ELF support.
 */
123
# if defined __alpha__ || defined __sparc_v9__ || defined _LP64
124 125 126 127 128 129
#  define ELFSIZE	64
# else
#  define ELFSIZE	32
# endif
# include <sys/exec_elf.h>

130 131
# ifndef PT_LOAD
#  define PT_LOAD	Elf_pt_load
132 133 134
#  if 0						/* was in pkgsrc patches for 20.7 */
#   define SHT_PROGBITS Elf_sht_progbits
#  endif
135 136 137 138 139 140 141 142 143 144
#  define SHT_SYMTAB	Elf_sht_symtab
#  define SHT_DYNSYM	Elf_sht_dynsym
#  define SHT_NULL	Elf_sht_null
#  define SHT_NOBITS	Elf_sht_nobits
#  define SHT_REL	Elf_sht_rel
#  define SHT_RELA	Elf_sht_rela

#  define SHN_UNDEF	Elf_eshn_undefined
#  define SHN_ABS	Elf_eshn_absolute
#  define SHN_COMMON	Elf_eshn_common
145
# endif /* !PT_LOAD */
146 147 148 149 150

# ifdef __alpha__
#  include <sys/exec_ecoff.h>
#  define HDRR		struct ecoff_symhdr
#  define pHDRR		HDRR *
151
# endif /* __alpha__ */
152

153
#ifdef __mips__			/* was in pkgsrc patches for 20.7 */
154 155 156
# define SHT_MIPS_DEBUG	DT_MIPS_FLAGS
# define HDRR		struct Elf_Shdr
#endif /* __mips__ */
157
#endif /* __NetBSD__ */
158

159 160 161 162
#ifdef __OpenBSD__
# include <sys/exec_elf.h>
#endif

163 164 165 166 167
#if __GNU_LIBRARY__ - 0 >= 6
# include <link.h>	/* get ElfW etc */
#endif

#ifndef ElfW
Paul Eggert's avatar
Paul Eggert committed
168
# define ElfBitsW(bits, type) Elf##bits##_##type
Paul Eggert's avatar
Paul Eggert committed
169 170 171 172 173 174
# ifndef ELFSIZE
#  ifdef _LP64
#   define ELFSIZE 64
#  else
#   define ELFSIZE 32
#  endif
Dave Love's avatar
Dave Love committed
175 176 177 178
# endif
  /* This macro expands `bits' before invoking ElfBitsW.  */
# define ElfExpandBitsW(bits, type) ElfBitsW (bits, type)
# define ElfW(type) ElfExpandBitsW (ELFSIZE, type)
Jim Blandy's avatar
Jim Blandy committed
179 180
#endif

181
/* The code often converts ElfW (Half) values like e_shentsize to ptrdiff_t;
182 183 184
   check that this doesn't lose information.  */
#include <intprops.h>
#include <verify.h>
185 186 187
verify ((! TYPE_SIGNED (ElfW (Half))
	 || PTRDIFF_MIN <= TYPE_MINIMUM (ElfW (Half)))
	&& TYPE_MAXIMUM (ElfW (Half)) <= PTRDIFF_MAX);
188 189

#ifdef UNEXELF_DEBUG
Paul Eggert's avatar
Paul Eggert committed
190 191
# define DEBUG_LOG(expr) fprintf (stderr, #expr " 0x%"PRIxMAX"\n", \
				  (uintmax_t) (expr))
192 193
#endif

Jim Blandy's avatar
Jim Blandy committed
194 195 196 197
/* Get the address of a particular section or program header entry,
 * accounting for the size of the entries.
 */

198
static void *
199
entry_address (void *section_h, ptrdiff_t idx, ptrdiff_t entsize)
200 201
{
  char *h = section_h;
202
  return h + idx * entsize;
203 204
}

Jim Blandy's avatar
Jim Blandy committed
205
#define OLD_SECTION_H(n) \
206
  (*(ElfW (Shdr) *) entry_address (old_section_h, n, old_file_h->e_shentsize))
Jim Blandy's avatar
Jim Blandy committed
207
#define NEW_SECTION_H(n) \
208
  (*(ElfW (Shdr) *) entry_address (new_section_h, n, new_file_h->e_shentsize))
209 210
#define OLD_PROGRAM_H(n) \
  (*(ElfW (Phdr) *) entry_address (old_program_h, n, old_file_h->e_phentsize))
Jim Blandy's avatar
Jim Blandy committed
211 212 213 214 215 216 217 218

typedef unsigned char byte;

/* ****************************************************************
 * unexec
 *
 * driving logic.
 *
219 220
 * In ELF, this works by replacing the old bss SHT_NOBITS section with
 * a new, larger, SHT_PROGBITS section.
Jim Blandy's avatar
Jim Blandy committed
221 222 223
 *
 */
void
224
unexec (const char *new_name, const char *old_name)
Jim Blandy's avatar
Jim Blandy committed
225
{
226 227
  int new_file, old_file;
  off_t new_file_size;
228

229
  /* Pointers to the base of the image of the two files.  */
Jim Blandy's avatar
Jim Blandy committed
230 231
  caddr_t old_base, new_base;

232 233 234 235 236 237
#if MAP_ANON == 0
  int mmap_fd;
#else
# define mmap_fd -1
#endif

238 239
  /* Pointers to the file, program and section headers for the old and
     new files.  */
Juanma Barranquero's avatar
Juanma Barranquero committed
240 241 242
  ElfW (Ehdr) *old_file_h, *new_file_h;
  ElfW (Phdr) *old_program_h, *new_program_h;
  ElfW (Shdr) *old_section_h, *new_section_h;
Jim Blandy's avatar
Jim Blandy committed
243

244 245
  /* Point to the section name table.  */
  char *old_section_names, *new_section_names;
Jim Blandy's avatar
Jim Blandy committed
246

247
  ElfW (Phdr) *old_bss_seg, *new_bss_seg;
Juanma Barranquero's avatar
Juanma Barranquero committed
248
  ElfW (Addr) old_bss_addr, new_bss_addr;
Paul Eggert's avatar
Paul Eggert committed
249
  ElfW (Word) old_bss_size, bss_size_growth, new_data2_size;
250
  ElfW (Off) old_bss_offset, new_data2_offset;
Jim Blandy's avatar
Jim Blandy committed
251

252 253
  ptrdiff_t n;
  ptrdiff_t old_bss_index;
Jim Blandy's avatar
Jim Blandy committed
254
  struct stat stat_buf;
255
  off_t old_file_size;
Jim Blandy's avatar
Jim Blandy committed
256

257
  /* Open the old file, allocate a buffer of the right size, and read
258
     in the file contents.  */
Jim Blandy's avatar
Jim Blandy committed
259

260
  old_file = emacs_open (old_name, O_RDONLY, 0);
Jim Blandy's avatar
Jim Blandy committed
261 262

  if (old_file < 0)
263
    fatal ("Can't open %s for reading: %s", old_name, strerror (errno));
Jim Blandy's avatar
Jim Blandy committed
264

265 266
  if (fstat (old_file, &stat_buf) != 0)
    fatal ("Can't fstat (%s): %s", old_name, strerror (errno));
Jim Blandy's avatar
Jim Blandy committed
267

268
#if MAP_ANON == 0
269
  mmap_fd = emacs_open ("/dev/zero", O_RDONLY, 0);
270
  if (mmap_fd < 0)
271
    fatal ("Can't open /dev/zero for reading: %s", strerror (errno));
272 273
#endif

274 275 276 277 278
  /* We cannot use malloc here because that may use sbrk.  If it does,
     we'd dump our temporary buffers with Emacs, and we'd have to be
     extra careful to use the correct value of sbrk(0) after
     allocating all buffers in the code below, which we aren't.  */
  old_file_size = stat_buf.st_size;
279 280
  if (! (0 <= old_file_size && old_file_size <= SIZE_MAX))
    fatal ("File size out of range");
281
  old_base = mmap (NULL, old_file_size, PROT_READ | PROT_WRITE,
282 283
		   MAP_ANON | MAP_PRIVATE, mmap_fd, 0);
  if (old_base == MAP_FAILED)
284
    fatal ("Can't allocate buffer for %s: %s", old_name, strerror (errno));
Jim Blandy's avatar
Jim Blandy committed
285

286 287
  if (read (old_file, old_base, old_file_size) != old_file_size)
    fatal ("Didn't read all of %s: %s", old_name, strerror (errno));
288

Jim Blandy's avatar
Jim Blandy committed
289 290
  /* Get pointers to headers & section names */

Juanma Barranquero's avatar
Juanma Barranquero committed
291 292 293
  old_file_h = (ElfW (Ehdr) *) old_base;
  old_program_h = (ElfW (Phdr) *) ((byte *) old_base + old_file_h->e_phoff);
  old_section_h = (ElfW (Shdr) *) ((byte *) old_base + old_file_h->e_shoff);
Jim Blandy's avatar
Jim Blandy committed
294
  old_section_names = (char *) old_base
295
    + OLD_SECTION_H (old_file_h->e_shstrndx).sh_offset;
Jim Blandy's avatar
Jim Blandy committed
296

297 298 299 300
  /* Find the PT_LOAD header covering the highest address.  This
     segment will be where bss sections are located, past p_filesz.  */
  old_bss_seg = 0;
  for (n = old_file_h->e_phnum; --n >= 0; )
301
    {
302 303 304 305 306
      ElfW (Phdr) *seg = &OLD_PROGRAM_H (n);
      if (seg->p_type == PT_LOAD
	  && (old_bss_seg == 0
	      || seg->p_vaddr > old_bss_seg->p_vaddr))
	old_bss_seg = seg;
307
    }
308
  eassume (old_bss_seg);
309 310 311 312 313 314 315

  /* Note that old_bss_addr may be lower than the first bss section
     address, since the section may need aligning.  */
  old_bss_addr = old_bss_seg->p_vaddr + old_bss_seg->p_filesz;
  old_bss_offset = old_bss_seg->p_offset + old_bss_seg->p_filesz;
  old_bss_size = old_bss_seg->p_memsz - old_bss_seg->p_filesz;

316
  /* Find the last bss style section in the bss segment range.  */
317 318
  old_bss_index = -1;
  for (n = old_file_h->e_shnum; --n > 0; )
319
    {
320 321 322 323 324
      ElfW (Shdr) *shdr = &OLD_SECTION_H (n);
      if (shdr->sh_type == SHT_NOBITS
	  && shdr->sh_addr >= old_bss_addr
	  && shdr->sh_addr + shdr->sh_size <= old_bss_addr + old_bss_size
	  && (old_bss_index == -1
325
	      || OLD_SECTION_H (old_bss_index).sh_addr < shdr->sh_addr))
326
	old_bss_index = n;
327 328
    }

329 330 331
  if (old_bss_index == -1)
    fatal ("no bss section found");

Paul Eggert's avatar
Paul Eggert committed
332 333
  void *no_break = (void *) (intptr_t) -1;
  void *new_break = no_break;
Paul Eggert's avatar
Paul Eggert committed
334
#ifdef HAVE_SBRK
335
  new_break = sbrk (0);
Paul Eggert's avatar
Paul Eggert committed
336
#endif
Paul Eggert's avatar
Paul Eggert committed
337 338
  if (new_break == no_break)
    new_break = (byte *) old_bss_addr + old_bss_size;
Juanma Barranquero's avatar
Juanma Barranquero committed
339
  new_bss_addr = (ElfW (Addr)) new_break;
Paul Eggert's avatar
Paul Eggert committed
340 341 342 343 344
  bss_size_growth = new_bss_addr - old_bss_addr;
  new_data2_size = bss_size_growth;
  new_data2_size += alignof (ElfW (Shdr)) - 1;
  new_data2_size -= new_data2_size % alignof (ElfW (Shdr));

345
  new_data2_offset = old_bss_offset;
Jim Blandy's avatar
Jim Blandy committed
346

347
#ifdef UNEXELF_DEBUG
348
  fprintf (stderr, "old_bss_index %td\n", old_bss_index);
349 350 351 352 353 354
  DEBUG_LOG (old_bss_addr);
  DEBUG_LOG (old_bss_size);
  DEBUG_LOG (old_bss_offset);
  DEBUG_LOG (new_bss_addr);
  DEBUG_LOG (new_data2_size);
  DEBUG_LOG (new_data2_offset);
Jim Blandy's avatar
Jim Blandy committed
355 356
#endif

357 358
  if (new_bss_addr < old_bss_addr + old_bss_size)
    fatal (".bss shrank when undumping");
Jim Blandy's avatar
Jim Blandy committed
359

360
  /* Set the output file to the right size.  Allocate a buffer to hold
361
     the image of the new file.  Set pointers to various interesting
362
     objects.  */
Jim Blandy's avatar
Jim Blandy committed
363

364
  new_file = emacs_open (new_name, O_RDWR | O_CREAT, 0777);
Jim Blandy's avatar
Jim Blandy committed
365
  if (new_file < 0)
366
    fatal ("Can't creat (%s): %s", new_name, strerror (errno));
Jim Blandy's avatar
Jim Blandy committed
367

368
  new_file_size = old_file_size + new_data2_size;
Jim Blandy's avatar
Jim Blandy committed
369 370

  if (ftruncate (new_file, new_file_size))
371
    fatal ("Can't ftruncate (%s): %s", new_name, strerror (errno));
Jim Blandy's avatar
Jim Blandy committed
372

373
  new_base = mmap (NULL, new_file_size, PROT_READ | PROT_WRITE,
374 375
		   MAP_ANON | MAP_PRIVATE, mmap_fd, 0);
  if (new_base == MAP_FAILED)
376
    fatal ("Can't allocate buffer for %s: %s", old_name, strerror (errno));
377

Jim Blandy's avatar
Jim Blandy committed
378
  /* Make our new file, program and section headers as copies of the
379
     originals.  */
Jim Blandy's avatar
Jim Blandy committed
380

Alan Modra's avatar
Alan Modra committed
381
  new_file_h = (ElfW (Ehdr) *) new_base;
Jim Blandy's avatar
Jim Blandy committed
382 383
  memcpy (new_file_h, old_file_h, old_file_h->e_ehsize);

384
  /* Fix up file header.  Section header is further away now.  */
Jim Blandy's avatar
Jim Blandy committed
385

386 387
  if (new_file_h->e_shoff >= old_bss_offset)
    new_file_h->e_shoff += new_data2_size;
Alan Modra's avatar
Alan Modra committed
388

389 390
  new_program_h = (ElfW (Phdr) *) ((byte *) new_base + new_file_h->e_phoff);
  new_section_h = (ElfW (Shdr) *) ((byte *) new_base + new_file_h->e_shoff);
Alan Modra's avatar
Alan Modra committed
391 392 393

  memcpy (new_program_h, old_program_h,
	  old_file_h->e_phnum * old_file_h->e_phentsize);
394 395
  memcpy (new_section_h, old_section_h,
	  old_file_h->e_shnum * old_file_h->e_shentsize);
Alan Modra's avatar
Alan Modra committed
396

397 398
#ifdef UNEXELF_DEBUG
  DEBUG_LOG (old_file_h->e_shoff);
399
  fprintf (stderr, "Old section count %td\n", (ptrdiff_t) old_file_h->e_shnum);
400
  DEBUG_LOG (new_file_h->e_shoff);
401
  fprintf (stderr, "New section count %td\n", (ptrdiff_t) new_file_h->e_shnum);
Jim Blandy's avatar
Jim Blandy committed
402 403
#endif

404 405
  /* Fix up program header.  Extend the writable data segment so
     that the bss area is covered too.  */
Jim Blandy's avatar
Jim Blandy committed
406

407 408 409
  new_bss_seg = new_program_h + (old_bss_seg - old_program_h);
  new_bss_seg->p_filesz = new_bss_addr - new_bss_seg->p_vaddr;
  new_bss_seg->p_memsz = new_bss_seg->p_filesz;
Jim Blandy's avatar
Jim Blandy committed
410

411
  /* Copy over what we have in memory now for the bss area. */
Paul Eggert's avatar
Paul Eggert committed
412 413
  memcpy (new_base + new_data2_offset, (caddr_t) old_bss_addr,
	  bss_size_growth);
Jim Blandy's avatar
Jim Blandy committed
414

415 416
  /* Walk through all section headers, copying data and updating.  */
  for (n = 1; n < old_file_h->e_shnum; n++)
Jim Blandy's avatar
Jim Blandy committed
417 418
    {
      caddr_t src;
Alan Modra's avatar
Alan Modra committed
419
      ElfW (Shdr) *old_shdr = &OLD_SECTION_H (n);
420
      ElfW (Shdr) *new_shdr = &NEW_SECTION_H (n);
Dave Love's avatar
Dave Love committed
421

422 423 424 425
      if (new_shdr->sh_type == SHT_NOBITS
	  && new_shdr->sh_addr >= old_bss_addr
	  && (new_shdr->sh_addr + new_shdr->sh_size
	      <= old_bss_addr + old_bss_size))
426
	{
427 428 429
	  /* This section now has file backing.  */
	  new_shdr->sh_type = SHT_PROGBITS;

430 431 432 433 434 435 436 437 438 439 440 441 442 443
	  /* SHT_NOBITS sections do not need a valid sh_offset, so it
	     might be incorrect.  Write the correct value.  */
	  new_shdr->sh_offset = (new_shdr->sh_addr - new_bss_seg->p_vaddr
				 + new_bss_seg->p_offset);

	  /* If this is was a SHT_NOBITS .plt section, then it is
	     probably a PowerPC PLT.  If it is PowerPC64 ELFv1 then
	     glibc ld.so doesn't initialize the toc pointer word.  A
	     non-zero toc pointer word can defeat Power7 thread safety
	     during lazy update of a PLT entry.  This only matters if
	     emacs becomes multi-threaded.  */
	  if (strcmp (old_section_names + new_shdr->sh_name, ".plt") == 0)
	    memset (new_shdr->sh_offset + new_base, 0, new_shdr->sh_size);

444 445 446 447
	  /* Extend the size of the last bss section to cover dumped
	     data.  */
	  if (n == old_bss_index)
	    new_shdr->sh_size = new_bss_addr - new_shdr->sh_addr;
Dan Nicolaescu's avatar
Dan Nicolaescu committed
448

449 450 451
	  /* We have already copied this section from the current
	     process.  */
	  continue;
452 453
	}

454 455 456 457
      /* Any section that was originally placed after the .bss
	 section should now be offset by NEW_DATA2_SIZE.  */
      if (new_shdr->sh_offset >= old_bss_offset)
	new_shdr->sh_offset += new_data2_size;
Dave Love's avatar
Dave Love committed
458

459
      /* Now, start to copy the content of sections.  */
Alan Modra's avatar
Alan Modra committed
460 461
      if (new_shdr->sh_type == SHT_NULL
	  || new_shdr->sh_type == SHT_NOBITS)
Jim Blandy's avatar
Jim Blandy committed
462
	continue;
463

464 465
      /* Some sections are copied from the current process instead of
	 the old file.  */
Alan Modra's avatar
Alan Modra committed
466 467 468 469 470
      if (!strcmp (old_section_names + new_shdr->sh_name, ".data")
	  || !strcmp (old_section_names + new_shdr->sh_name, ".sdata")
	  || !strcmp (old_section_names + new_shdr->sh_name, ".lit4")
	  || !strcmp (old_section_names + new_shdr->sh_name, ".lit8")
	  || !strcmp (old_section_names + new_shdr->sh_name, ".sdata1")
471
	  || !strcmp (old_section_names + new_shdr->sh_name, ".data1"))
Alan Modra's avatar
Alan Modra committed
472
	src = (caddr_t) old_shdr->sh_addr;
Jim Blandy's avatar
Jim Blandy committed
473
      else
Alan Modra's avatar
Alan Modra committed
474
	src = old_base + old_shdr->sh_offset;
475

Alan Modra's avatar
Alan Modra committed
476
      memcpy (new_shdr->sh_offset + new_base, src, new_shdr->sh_size);
477

478 479 480 481 482 483 484 485
#if (defined __alpha__ && !defined __OpenBSD__) || defined _SYSTYPE_SYSV
      /* Update Alpha and MIPS COFF debug symbol table.  */
      if (strcmp (old_section_names + new_shdr->sh_name, ".mdebug") == 0
	  && new_shdr->sh_offset - old_shdr->sh_offset != 0
#if defined _SYSTYPE_SYSV
	  && new_shdr->sh_type == SHT_MIPS_DEBUG
#endif
	  )
486
	{
487
	  ptrdiff_t diff = new_shdr->sh_offset - old_shdr->sh_offset;
Alan Modra's avatar
Alan Modra committed
488
	  HDRR *phdr = (HDRR *) (new_shdr->sh_offset + new_base);
489

490 491 492 493 494 495 496 497 498 499 500
	  phdr->cbLineOffset += diff;
	  phdr->cbDnOffset += diff;
	  phdr->cbPdOffset += diff;
	  phdr->cbSymOffset += diff;
	  phdr->cbOptOffset += diff;
	  phdr->cbAuxOffset += diff;
	  phdr->cbSsOffset += diff;
	  phdr->cbSsExtOffset += diff;
	  phdr->cbFdOffset += diff;
	  phdr->cbRfdOffset += diff;
	  phdr->cbExtOffset += diff;
501
	}
502
#endif /* __alpha__ || _SYSTYPE_SYSV */
Jim Blandy's avatar
Jim Blandy committed
503 504
    }

505
  /* Update the symbol values of _edata and _end.  */
506
  for (n = new_file_h->e_shnum; 0 < --n; )
507 508
    {
      byte *symnames;
Juanma Barranquero's avatar
Juanma Barranquero committed
509
      ElfW (Sym) *symp, *symendp;
Alan Modra's avatar
Alan Modra committed
510
      ElfW (Shdr) *sym_shdr = &NEW_SECTION_H (n);
511

Alan Modra's avatar
Alan Modra committed
512 513
      if (sym_shdr->sh_type != SHT_DYNSYM
	  && sym_shdr->sh_type != SHT_SYMTAB)
514 515
	continue;

Karl Heuer's avatar
Karl Heuer committed
516
      symnames = ((byte *) new_base
Alan Modra's avatar
Alan Modra committed
517 518 519
		  + NEW_SECTION_H (sym_shdr->sh_link).sh_offset);
      symp = (ElfW (Sym) *) (sym_shdr->sh_offset + new_base);
      symendp = (ElfW (Sym) *) ((byte *) symp + sym_shdr->sh_size);
520 521

      for (; symp < symendp; symp ++)
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
	{
	  if (strcmp ((char *) (symnames + symp->st_name), "_end") == 0
	      || strcmp ((char *) (symnames + symp->st_name), "end") == 0
	      || strcmp ((char *) (symnames + symp->st_name), "_edata") == 0
	      || strcmp ((char *) (symnames + symp->st_name), "edata") == 0)
	    memcpy (&symp->st_value, &new_bss_addr, sizeof (new_bss_addr));

	  /* Strictly speaking, #ifdef below is not necessary.  But we
	     keep it to indicate that this kind of change may also be
	     necessary for other unexecs to support GNUstep.  */
#ifdef NS_IMPL_GNUSTEP
	  /* ObjC runtime modifies the values of some data structures
	     such as classes and selectors in the .data section after
	     loading.  As the dump process copies the .data section
	     from the current process, that causes problems when the
	     modified classes are reinitialized in the dumped
	     executable.  We copy such data from the old file, not
	     from the current process.  */
	  if (strncmp ((char *) (symnames + symp->st_name),
		       "_OBJC_", sizeof ("_OBJC_") - 1) == 0)
	    {
Alan Modra's avatar
Alan Modra committed
543
	      ElfW (Shdr) *new_shdr = &NEW_SECTION_H (symp->st_shndx);
544
	      if (new_shdr->sh_type != SHT_NOBITS)
545
		{
546
		  ElfW (Shdr) *old_shdr = &OLD_SECTION_H (symp->st_shndx);
547 548 549 550 551 552 553 554 555 556 557
		  ptrdiff_t reladdr = symp->st_value - new_shdr->sh_addr;
		  ptrdiff_t newoff = reladdr + new_shdr->sh_offset;

		  if (old_shdr->sh_type == SHT_NOBITS)
		    memset (new_base + newoff, 0, symp->st_size);
		  else
		    {
		      ptrdiff_t oldoff = reladdr + old_shdr->sh_offset;
		      memcpy (new_base + newoff, old_base + oldoff,
			      symp->st_size);
		    }
558
		}
559 560 561
	    }
#endif
	}
562 563
    }

564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582
  /* Modify the names of sections we changed from SHT_NOBITS to
     SHT_PROGBITS.  This is really just cosmetic, but some tools that
     (wrongly) operate on section names rather than types might be
     confused by a SHT_PROGBITS .bss section.  */
  new_section_names = ((char *) new_base
		       + NEW_SECTION_H (new_file_h->e_shstrndx).sh_offset);
  for (n = new_file_h->e_shnum; 0 < --n; )
    {
      ElfW (Shdr) *old_shdr = &OLD_SECTION_H (n);
      ElfW (Shdr) *new_shdr = &NEW_SECTION_H (n);

      /* Replace the leading '.' with ','.  When .shstrtab is string
	 merged this will rename both .bss and .rela.bss to ,bss and
	 .rela,bss.  */
      if (old_shdr->sh_type == SHT_NOBITS
	  && new_shdr->sh_type == SHT_PROGBITS)
	*(new_section_names + new_shdr->sh_name) = ',';
    }

583
  /* This loop seeks out relocation sections for the data section, so
584 585 586 587 588 589 590 591 592 593 594
     that it can undo relocations performed by the runtime loader.

     The following approach does not work on x86 platforms that use
     the GNU Gold linker, which can generate .rel.dyn relocation
     sections containing R_386_32 entries that the following code does
     not grok.  Emacs works around this problem by avoiding C
     constructs that generate such entries, which is horrible hack.

     FIXME: Presumably more problems like this will crop up as linkers
     get fancier.  We really need to stop assuming that Emacs can grok
     arbitrary linker output.  See Bug#27248.  */
595
  for (n = new_file_h->e_shnum; 0 < --n; )
596
    {
Alan Modra's avatar
Alan Modra committed
597 598
      ElfW (Shdr) *rel_shdr = &NEW_SECTION_H (n);
      ElfW (Shdr) *shdr;
599

Alan Modra's avatar
Alan Modra committed
600
      switch (rel_shdr->sh_type)
601 602 603 604 605 606 607 608
	{
	default:
	  break;
	case SHT_REL:
	case SHT_RELA:
	  /* This code handles two different size structs, but there should
	     be no harm in that provided that r_offset is always the first
	     member.  */
Alan Modra's avatar
Alan Modra committed
609 610 611 612 613 614 615
	  shdr = &NEW_SECTION_H (rel_shdr->sh_info);
	  if (!strcmp (old_section_names + shdr->sh_name, ".data")
	      || !strcmp (old_section_names + shdr->sh_name, ".sdata")
	      || !strcmp (old_section_names + shdr->sh_name, ".lit4")
	      || !strcmp (old_section_names + shdr->sh_name, ".lit8")
	      || !strcmp (old_section_names + shdr->sh_name, ".sdata1")
	      || !strcmp (old_section_names + shdr->sh_name, ".data1"))
616
	    {
Alan Modra's avatar
Alan Modra committed
617 618 619 620 621
	      ElfW (Addr) offset = shdr->sh_addr - shdr->sh_offset;
	      caddr_t reloc = old_base + rel_shdr->sh_offset, end;
	      for (end = reloc + rel_shdr->sh_size;
		   reloc < end;
		   reloc += rel_shdr->sh_entsize)
622
		{
Juanma Barranquero's avatar
Juanma Barranquero committed
623
		  ElfW (Addr) addr = ((ElfW (Rel) *) reloc)->r_offset - offset;
Alan Modra's avatar
Alan Modra committed
624
		  /* Ignore R_*_NONE relocs.  */
Juanma Barranquero's avatar
Juanma Barranquero committed
625
		  if (((ElfW (Rel) *) reloc)->r_offset == 0)
626
		    continue;
Alan Modra's avatar
Alan Modra committed
627 628 629 630 631
		  /* Assume reloc applies to a word.
		     ??? This is not always true, eg. TLS module/index
		     pair in .got which occupies two words.  */
		  memcpy (new_base + addr, old_base + addr,
			  sizeof (ElfW (Addr)));
632 633 634 635
		}
	    }
	  break;
	}
636 637
    }

638
  /* Write out new_file, and free the buffers.  */
639 640

  if (write (new_file, new_base, new_file_size) != new_file_size)
641 642
    fatal ("Didn't write %lu bytes to %s: %s",
	   (unsigned long) new_file_size, new_name, strerror (errno));
643 644
  munmap (old_base, old_file_size);
  munmap (new_base, new_file_size);
645

646
  /* Close the files and make the new file executable.  */
Jim Blandy's avatar
Jim Blandy committed
647

648
#if MAP_ANON == 0
649
  emacs_close (mmap_fd);
650 651
#endif

652
  if (emacs_close (old_file) != 0)
653
    fatal ("Can't close (%s): %s", old_name, strerror (errno));
Jim Blandy's avatar
Jim Blandy committed
654

655
  if (emacs_close (new_file) != 0)
656
    fatal ("Can't close (%s): %s", new_name, strerror (errno));
Jim Blandy's avatar
Jim Blandy committed
657
}