Subject: Re: more on non-executable mappings vs. emulations
To: Christos Zoulas <christos@zoulas.com>
From: Chuck Silvers <chuq@chuq.com>
List: tech-kern
Date: 07/13/2004 23:12:17
--5vNYLRcllDrimb99
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

On Mon, Jul 12, 2004 at 12:59:18PM -0400, Christos Zoulas wrote:
> | the ppc ELF tool should be trivial, I'll write it this evening.

there's a source file attached that implements this.
I was thinking we could make a pkg for it and have the suse73_base pkg
have a BUILD_DEPENDS on it, then we can fix the broken ldconfig binary
as we install it.


> | as for the sparc a.out issue, it occurred to me on my morning walk that
> | it may be possible to have ld.so mprotect() the relevant parts of itself
> | before it needs to execute the incorrectly mapped instructions.
> | I'll look into this tonight also.

this does work, the attached diff adds some extra instructions at the start
of the sparc ld.so that fix up the mapping permissions.

-Chuck

--5vNYLRcllDrimb99
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="fixelfprot.c"

/*
 * Fix up the permission bits for the program load section containing the
 * .got section of a PPC ELF binary.
 */

#include <stdio.h>
#include <err.h>
#define ELFSIZE 32
#include <sys/exec_elf.h>
#include <sys/endian.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>

void xlseek(int, off_t, int);
void xread(int, void *, size_t);
void xwrite(int, void *, size_t);
void *xmalloc(size_t);

void
xlseek(int fd, off_t off, int whence)
{
	int rv;

	rv = lseek(fd, off, whence);
	if (rv < 0) {
		err(1, "lseek");
	}
}

void
xread(int fd, void *buf, size_t size)
{
	int rv;

	rv = read(fd, buf, size);
	if (rv < 0) {
		err(1, "read");
	}
	if (rv != size) {
		errx(1, "short read %d vs. %d", rv, size);
	}
}

void
xwrite(int fd, void *buf, size_t size)
{
	int rv;

	rv = write(fd, buf, size);
	if (rv < 0) {
		err(1, "write");
	}
	if (rv != size) {
		errx(1, "short write %d vs. %d", rv, size);
	}
}


void *
xmalloc(size_t size)
{
	void *buf;

	buf = malloc(size);
	if (buf == NULL) {
		err(1, "malloc");
	}
	return buf;
}

int
main(int argc, char **argv)
{
	Elf32_Ehdr eh;
	Elf32_Phdr *ph;
	Elf32_Shdr *sh;
	Elf32_Addr gotaddr;
	size_t phsize, shsize, strsize;
	char *strbuf;
	int fd, i, idx;

	/*
	 * Check arguments and open the file.
	 */

	if (argc != 2) {
		errx(1, "usage: %s <file>", getprogname());
	}
	fd = open(argv[1], O_RDWR);
	if (fd < 0) {
		err(1, "open %s", argv[1]);
	}

	/*
	 * Read and validate the ELF header.
	 */

	xread(fd, &eh, sizeof (eh));
	if (memcmp(eh.e_ident, ELFMAG, SELFMAG) != 0 ||
	    eh.e_ident[EI_CLASS] != ELFCLASS) {
		errx(1, "not an ELF file");
	}
	if (be16toh(eh.e_machine) != EM_PPC) {
		errx(1, "ELF file is wrong architecture");
	}
	if (be16toh(eh.e_type) != ET_EXEC) {
		errx(1, "ELF file is not an executable");
	}
	if (be16toh(eh.e_shnum) > 512 || be16toh(eh.e_phnum) > 128) {
		errx(1, "ELF file has too many sections");
	}
	if (be16toh(eh.e_shstrndx) >= be16toh(eh.e_shnum)) {
		errx(1, "string table index out of range");
	}

	/*
	 * Read the program headers, section headers and string table.
	 */

	phsize = be16toh(eh.e_phnum) * sizeof(Elf_Phdr);
	ph = xmalloc(phsize);
	xlseek(fd, (off_t)be32toh(eh.e_phoff), SEEK_SET);
	xread(fd, ph, phsize);

	shsize = be16toh(eh.e_shnum) * sizeof(Elf_Shdr);
	sh = xmalloc(shsize);
	xlseek(fd, (off_t)be32toh(eh.e_shoff), SEEK_SET);
	xread(fd, sh, shsize);

	idx = be16toh(eh.e_shstrndx);
	strsize = be32toh(sh[idx].sh_size);
	strbuf = xmalloc(strsize);
	xlseek(fd, (off_t)be32toh(sh[idx].sh_offset), SEEK_SET);
	xread(fd, strbuf, strsize);

	/*
	 * Find the .got section.
	 */

	gotaddr = 0;
	for (i = 0; i < be16toh(eh.e_shnum); i++) {
		if (strcmp(&strbuf[be32toh(sh[i].sh_name)], ".got") == 0) {
			gotaddr = be32toh(sh[i].sh_addr);
			break;
		}
	}
	if (gotaddr == 0) {
		errx(1, "couldn't find the .got section");
	}

	/*
	 * Find the program header load section containing the .got section.
	 */

	idx = -1;
	for (i = 0; i < be16toh(eh.e_phnum); i++) {
		if (be32toh(ph[i].p_type) == PT_LOAD &&
		    gotaddr >= be32toh(ph[i].p_vaddr) &&
		    gotaddr < be32toh(ph[i].p_vaddr) + be32toh(ph[i].p_memsz)) {
			idx = i;
		}
	}
	if (idx == -1) {
		errx(1, "couldn't find program header for .got");
	}
	if (be32toh(ph[idx].p_flags) & PF_X) {
		errx(1, "permission bits already include execute");
	}

	/*
	 * Add execute permission and write back the entry.
	 */

	ph[idx].p_flags |= be32toh(PF_X);
	xlseek(fd, (off_t)be32toh(eh.e_phoff), SEEK_SET);
	xwrite(fd, ph, phsize);

	errx(0, "execute permission added");
	exit(0);
}

--5vNYLRcllDrimb99
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="diff.sparcaout"

Index: src/libexec/ld.aout_so/arch/sparc/mdprologue.S
===================================================================
RCS file: /cvsroot/src/libexec/ld.aout_so/arch/sparc/mdprologue.S,v
retrieving revision 1.7
diff -u -p -r1.7 mdprologue.S
--- src/libexec/ld.aout_so/arch/sparc/mdprologue.S	5 Sep 1998 13:08:39 -0000	1.7
+++ src/libexec/ld.aout_so/arch/sparc/mdprologue.S	13 Jul 2004 17:05:12 -0000
@@ -48,6 +48,22 @@
 _rtld_entry:
 !#PROLOGUE# 0
 	save	%sp,-96,%sp
+	
+	! use mprotect() to make the data segment executable.
+	! this is needed because the PLT is there.  doh!
+
+	call	1f
+	mov	0x4a, %g1
+1:
+	sub	%o7, 4 + 32, %o3
+	ld	[%o3 + 4], %o0
+	add	%o0, %o3, %o0
+	ld	[%o3 + 8], %o1
+	mov	7, %o2
+	ta	0
+
+	! now we can continue as before.
+
 L.1B:
 	call	L.2B
 	sethi	%hi((__GLOBAL_OFFSET_TABLE_-(L.1B-.))),%l7

--5vNYLRcllDrimb99--