#include <config.h>
#include <unexec.h>
-extern void fatal (const char *msgid, ...);
+extern _Noreturn void fatal (const char *, ...) ATTRIBUTE_FORMAT_PRINTF (1, 2);
-#include <sys/types.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <memory.h>
+#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/stat.h>
-#include <memory.h>
-#include <errno.h>
+#include <sys/types.h>
#include <unistd.h>
-#include <fcntl.h>
+
#if !defined (__NetBSD__) && !defined (__OpenBSD__)
#include <elf.h>
#endif /* not __NetBSD__ and not __OpenBSD__ */
# define ElfW(type) ElfExpandBitsW (ELFSIZE, type)
#endif
+/* The code often converts ElfW (Half) values like e_shentsize to int;
+ check that this doesn't lose information. */
+#include <intprops.h>
+#include <verify.h>
+verify ((! TYPE_SIGNED (ElfW (Half)) || INT_MIN <= TYPE_MINIMUM (ElfW (Half)))
+ && TYPE_MAXIMUM (ElfW (Half)) <= INT_MAX);
+
+#ifdef UNEXELF_DEBUG
+# define DEBUG_LOG(expr) fprintf (stderr, #expr " 0x%jx\n", (uintmax_t) (expr))
+#endif
+
/* Get the address of a particular section or program header entry,
* accounting for the size of the entries.
*/
Apr 23, 1996
*/
+static void *
+entry_address (void *section_h, int idx, int num, int entsize)
+{
+ char *h = section_h;
+ ptrdiff_t n = idx;
+ return h + entsize * n;
+}
+
#define OLD_SECTION_H(n) \
- (*(ElfW (Shdr) *) ((byte *) old_section_h + old_file_h->e_shentsize * (n)))
+ (*(ElfW (Shdr) *) entry_address (old_section_h, n, old_file_h->e_shnum, \
+ old_file_h->e_shentsize))
#define NEW_SECTION_H(n) \
- (*(ElfW (Shdr) *) ((byte *) new_section_h + new_file_h->e_shentsize * (n)))
+ (*(ElfW (Shdr) *) entry_address (new_section_h, n, new_file_h->e_shnum, \
+ new_file_h->e_shentsize))
#define NEW_PROGRAM_H(n) \
- (*(ElfW (Phdr) *) ((byte *) new_program_h + new_file_h->e_phentsize * (n)))
+ (*(ElfW (Phdr) *) entry_address (new_program_h, n, new_file_h->e_phnum, \
+ new_file_h->e_phentsize))
-#define PATCH_INDEX(n) \
- do { \
- if ((int) (n) >= old_bss_index) \
- (n)++; } while (0)
+#define PATCH_INDEX(n) ((n) += old_bss_index <= (n))
typedef unsigned char byte;
/* Round X up to a multiple of Y. */
static ElfW (Addr)
round_up (ElfW (Addr) x, ElfW (Addr) y)
{
- int rem = x % y;
+ ElfW (Addr) rem = x % y;
if (rem == 0)
return x;
return x - rem + y;
about the file we are looking in.
If we don't find the section NAME, that is a fatal error
- if NOERROR is 0; we return -1 if NOERROR is nonzero. */
+ if NOERROR is false; return -1 if NOERROR is true. */
static int
find_section (const char *name, const char *section_names, const char *file_name,
- ElfW (Ehdr) *old_file_h, ElfW (Shdr) *old_section_h, int noerror)
+ ElfW (Ehdr) *old_file_h, ElfW (Shdr) *old_section_h,
+ bool noerror)
{
int idx;
for (idx = 1; idx < old_file_h->e_shnum; idx++)
{
-#ifdef DEBUG
- fprintf (stderr, "Looking for %s - found %s\n", name,
- section_names + OLD_SECTION_H (idx).sh_name);
+ char const *found_name = section_names + OLD_SECTION_H (idx).sh_name;
+#ifdef UNEXELF_DEBUG
+ fprintf (stderr, "Looking for %s - found %s\n", name, found_name);
#endif
- if (!strcmp (section_names + OLD_SECTION_H (idx).sh_name,
- name))
- break;
- }
- if (idx == old_file_h->e_shnum)
- {
- if (noerror)
- return -1;
- else
- fatal ("Can't find %s in %s.\n", name, file_name);
+ if (strcmp (name, found_name) == 0)
+ return idx;
}
- return idx;
+ if (! noerror)
+ fatal ("Can't find %s in %s", name, file_name);
+ return -1;
}
/* ****************************************************************
void
unexec (const char *new_name, const char *old_name)
{
- int new_file, old_file, new_file_size;
-
-#if defined (emacs) || !defined (DEBUG)
+ int new_file, old_file;
+ off_t new_file_size;
void *new_break;
-#endif
/* Pointers to the base of the image of the two files. */
caddr_t old_base, new_base;
int old_mdebug_index;
#endif
struct stat stat_buf;
- int old_file_size;
+ off_t old_file_size;
/* Open the old file, allocate a buffer of the right size, and read
in the file contents. */
old_file = open (old_name, O_RDONLY);
if (old_file < 0)
- fatal ("Can't open %s for reading: errno %d\n", old_name, errno);
+ fatal ("Can't open %s for reading: %s", old_name, strerror (errno));
- if (fstat (old_file, &stat_buf) == -1)
- fatal ("Can't fstat (%s): errno %d\n", old_name, errno);
+ if (fstat (old_file, &stat_buf) != 0)
+ fatal ("Can't fstat (%s): %s", old_name, strerror (errno));
#if MAP_ANON == 0
mmap_fd = open ("/dev/zero", O_RDONLY);
if (mmap_fd < 0)
- fatal ("Can't open /dev/zero for reading: errno %d\n", errno, 0);
+ fatal ("Can't open /dev/zero for reading: %s", strerror (errno));
#endif
/* We cannot use malloc here because that may use sbrk. If it does,
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;
+ if (! (0 <= old_file_size && old_file_size <= SIZE_MAX))
+ fatal ("File size out of range");
old_base = mmap (NULL, old_file_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE, mmap_fd, 0);
if (old_base == MAP_FAILED)
- fatal ("Can't allocate buffer for %s\n", old_name, 0);
+ fatal ("Can't allocate buffer for %s: %s", old_name, strerror (errno));
- if (read (old_file, old_base, stat_buf.st_size) != stat_buf.st_size)
- fatal ("Didn't read all of %s: errno %d\n", old_name, errno);
+ if (read (old_file, old_base, old_file_size) != old_file_size)
+ fatal ("Didn't read all of %s: %s", old_name, strerror (errno));
/* Get pointers to headers & section names */
old_data_index = find_section (".data", old_section_names,
old_name, old_file_h, old_section_h, 0);
-#if defined (emacs) || !defined (DEBUG)
new_break = sbrk (0);
new_bss_addr = (ElfW (Addr)) new_break;
-#else
- new_bss_addr = old_bss_addr + old_bss_size + 0x1234;
-#endif
new_data2_addr = old_bss_addr;
new_data2_size = new_bss_addr - old_bss_addr;
new_data2_offset = OLD_SECTION_H (old_data_index).sh_offset
section) was unaligned. */
new_data2_incr = new_data2_size + (new_data2_offset - old_bss_offset);
-#ifdef DEBUG
+#ifdef UNEXELF_DEBUG
fprintf (stderr, "old_bss_index %d\n", old_bss_index);
- fprintf (stderr, "old_bss_addr %x\n", old_bss_addr);
- fprintf (stderr, "old_bss_size %x\n", old_bss_size);
- fprintf (stderr, "old_bss_offset %x\n", old_bss_offset);
- fprintf (stderr, "new_bss_addr %x\n", new_bss_addr);
- fprintf (stderr, "new_data2_addr %x\n", new_data2_addr);
- fprintf (stderr, "new_data2_size %x\n", new_data2_size);
- fprintf (stderr, "new_data2_offset %x\n", new_data2_offset);
- fprintf (stderr, "new_data2_incr %x\n", new_data2_incr);
+ DEBUG_LOG (old_bss_addr);
+ DEBUG_LOG (old_bss_size);
+ DEBUG_LOG (old_bss_offset);
+ DEBUG_LOG (new_bss_addr);
+ DEBUG_LOG (new_data2_addr);
+ DEBUG_LOG (new_data2_size);
+ DEBUG_LOG (new_data2_offset);
+ DEBUG_LOG (new_data2_incr);
#endif
- if ((uintptr_t) new_bss_addr < (uintptr_t) old_bss_addr + old_bss_size)
- fatal (".bss shrank when undumping???\n", 0, 0);
+ if (new_bss_addr < old_bss_addr + old_bss_size)
+ fatal (".bss shrank when undumping");
/* Set the output file to the right size. Allocate a buffer to hold
the image of the new file. Set pointers to various interesting
- objects. stat_buf still has old_file data. */
+ objects. */
new_file = open (new_name, O_RDWR | O_CREAT, 0666);
if (new_file < 0)
- fatal ("Can't creat (%s): errno %d\n", new_name, errno);
+ fatal ("Can't creat (%s): %s", new_name, strerror (errno));
- new_file_size = stat_buf.st_size + old_file_h->e_shentsize + new_data2_incr;
+ new_file_size = old_file_size + old_file_h->e_shentsize + new_data2_incr;
if (ftruncate (new_file, new_file_size))
- fatal ("Can't ftruncate (%s): errno %d\n", new_name, errno);
+ fatal ("Can't ftruncate (%s): %s", new_name, strerror (errno));
new_base = mmap (NULL, new_file_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE, mmap_fd, 0);
if (new_base == MAP_FAILED)
- fatal ("Can't allocate buffer for %s\n", old_name, 0);
+ fatal ("Can't allocate buffer for %s: %s", old_name, strerror (errno));
new_file_h = (ElfW (Ehdr) *) new_base;
new_program_h = (ElfW (Phdr) *) ((byte *) new_base + old_file_h->e_phoff);
new_file_h->e_shoff += new_data2_incr;
new_file_h->e_shnum += 1;
-#ifdef DEBUG
- fprintf (stderr, "Old section offset %x\n", old_file_h->e_shoff);
+#ifdef UNEXELF_DEBUG
+ DEBUG_LOG (old_file_h->e_shoff);
fprintf (stderr, "Old section count %d\n", old_file_h->e_shnum);
- fprintf (stderr, "New section offset %x\n", new_file_h->e_shoff);
+ DEBUG_LOG (new_file_h->e_shoff);
fprintf (stderr, "New section count %d\n", new_file_h->e_shnum);
#endif
to adjust the offset and address of any segment that is above
data2, just in case we decide to allow this later. */
- for (n = new_file_h->e_phnum - 1; n >= 0; n--)
+ for (n = new_file_h->e_phnum; --n >= 0; )
{
/* Compute maximum of all requirements for alignment of section. */
ElfW (Word) alignment = (NEW_PROGRAM_H (n)).p_align;
> (old_sbss_index == -1
? old_bss_addr
: round_up (old_bss_addr, alignment)))
- fatal ("Program segment above .bss in %s\n", old_name, 0);
+ fatal ("Program segment above .bss in %s", old_name);
if (NEW_PROGRAM_H (n).p_type == PT_LOAD
&& (round_up ((NEW_PROGRAM_H (n)).p_vaddr
break;
}
if (n < 0)
- fatal ("Couldn't find segment next to .bss in %s\n", old_name, 0);
+ fatal ("Couldn't find segment next to .bss in %s", old_name);
/* Make sure that the size includes any padding before the old .bss
section. */
NEW_PROGRAM_H (n).p_memsz = NEW_PROGRAM_H (n).p_filesz;
#if 0 /* Maybe allow section after data2 - does this ever happen? */
- for (n = new_file_h->e_phnum - 1; n >= 0; n--)
+ for (n = new_file_h->e_phnum; --n >= 0; )
{
if (NEW_PROGRAM_H (n).p_vaddr
&& NEW_PROGRAM_H (n).p_vaddr >= new_data2_addr)
/* Walk through all section headers, insert the new data2 section right
before the new bss section. */
- for (n = 1, nn = 1; n < (int) old_file_h->e_shnum; n++, nn++)
+ for (n = 1, nn = 1; n < old_file_h->e_shnum; n++, nn++)
{
caddr_t src;
/* If it is (s)bss section, insert the new data2 section before it. */
}
/* Update the symbol values of _edata and _end. */
- for (n = new_file_h->e_shnum - 1; n; n--)
+ for (n = new_file_h->e_shnum; 0 < --n; )
{
byte *symnames;
ElfW (Sym) *symp, *symendp;
/* This loop seeks out relocation sections for the data section, so
that it can undo relocations performed by the runtime linker. */
- for (n = new_file_h->e_shnum - 1; n; n--)
+ for (n = new_file_h->e_shnum; 0 < --n; )
{
ElfW (Shdr) section = NEW_SECTION_H (n);
/* Write out new_file, and free the buffers. */
if (write (new_file, new_base, new_file_size) != new_file_size)
- fatal ("Didn't write %d bytes to %s: errno %d\n",
- new_file_size, new_name, errno);
+ fatal ("Didn't write %lu bytes to %s: %s",
+ (unsigned long) new_file_size, new_name, strerror (errno));
munmap (old_base, old_file_size);
munmap (new_base, new_file_size);
close (mmap_fd);
#endif
- if (close (old_file))
- fatal ("Can't close (%s): errno %d\n", old_name, errno);
+ if (close (old_file) != 0)
+ fatal ("Can't close (%s): %s", old_name, strerror (errno));
- if (close (new_file))
- fatal ("Can't close (%s): errno %d\n", new_name, errno);
+ if (close (new_file) != 0)
+ fatal ("Can't close (%s): %s", new_name, strerror (errno));
- if (stat (new_name, &stat_buf) == -1)
- fatal ("Can't stat (%s): errno %d\n", new_name, errno);
+ if (stat (new_name, &stat_buf) != 0)
+ fatal ("Can't stat (%s): %s", new_name, strerror (errno));
n = umask (777);
umask (n);
stat_buf.st_mode |= 0111 & ~n;
- if (chmod (new_name, stat_buf.st_mode) == -1)
- fatal ("Can't chmod (%s): errno %d\n", new_name, errno);
+ if (chmod (new_name, stat_buf.st_mode) != 0)
+ fatal ("Can't chmod (%s): %s", new_name, strerror (errno));
}