Skip to content
elf.c 13.3 KiB
Newer Older
fgilham's avatar
fgilham committed
 This code was written by Fred Gilham, and has been placed in the public
 domain.  It is provided "as-is" and without warranty of any kind.
fgilham's avatar
fgilham committed
 Modified to add elf write code for writing out executables using
 (save-lisp).  FMG 27-Aug-2003

 Above changes put into main CVS branch. 05-Jul-2007.

 $Id: elf.c,v 1.32 2010/12/23 03:20:27 rtoy Exp $
cshapiro's avatar
cshapiro committed
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>

#include "os.h"
fgilham's avatar
fgilham committed
#include "core.h"
#include "internals.h"
fgilham's avatar
fgilham committed
#include "elf.h"
fgilham's avatar
fgilham committed
static char elf_magic_string[] = {ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3};
fgilham's avatar
fgilham committed
/* Elf data structures. */
static Elf_Ehdr eh;
fgilham's avatar
fgilham committed
static Elf_Shdr sh;
fgilham's avatar
fgilham committed
/* Names of the Lisp image ELF sections. These names must be the same as
   the corresponding names found in the linker script.  */
fgilham's avatar
fgilham committed
static char *section_names[] = {"CORDYN", "CORSTA", "CORRO"};
/*
 * Starting address of the three ELF sections/spaces.  These must be
 * in the same order as section_names above!
 *
 * XXX: Should we combine these into a structure?  Should we do this
 * for all architectures?
 */
static os_vm_address_t section_addr[] =
{
  (os_vm_address_t) DYNAMIC_0_SPACE_START,
  (os_vm_address_t) STATIC_SPACE_START,
  (os_vm_address_t) READ_ONLY_SPACE_START
fgilham's avatar
fgilham committed
/* Note: write errors are not fatal. */
static int
ewrite(int fd, const void *buf, size_t nbytes, const char *func)
{
fgilham's avatar
fgilham committed
    if (write(fd, buf, nbytes) < nbytes) {
	perror(func);
	return -1;	/* Simple way to indicate error. */
    }
    return 0;
fgilham's avatar
fgilham committed
}

/*
fgilham's avatar
fgilham committed
  Read errors are fatal, because these reads have to succeed for lisp to
  get off the ground.
fgilham's avatar
fgilham committed
 */
static void
eread(int d, void *buf, size_t nbytes, const char *func)
{
fgilham's avatar
fgilham committed
    int res = read(d, buf, nbytes);
fgilham's avatar
fgilham committed
    if (res == -1) {
	perror(func);
	exit(-1);
    }
fgilham's avatar
fgilham committed
    if (res < nbytes) {
	fprintf(stderr, "Short read in %s!\n", func);
	exit(-1);
    }
}

static void
elseek(int d, off_t o, const char *func)
{
fgilham's avatar
fgilham committed
    if (lseek(d, o, SEEK_SET) == -1) {
	perror(func);
	exit(-1);
    }
fgilham's avatar
fgilham committed
}


static int
create_elf_file (const char *dir, int id)
{
    char outfilename[FILENAME_MAX + 1];
fgilham's avatar
fgilham committed
    int out;
fgilham's avatar
fgilham committed

fgilham's avatar
fgilham committed
    /* Note: the space id will be either 1, 2 or 3.  Subtract one to index
       the name array. */
    snprintf(outfilename, FILENAME_MAX, "%s/%s.o", dir, section_names[id - 1]);
fgilham's avatar
fgilham committed
    out = open(outfilename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
fgilham's avatar
fgilham committed

fgilham's avatar
fgilham committed
    if(!out) {
	perror("write_elf_object: can't open file");
	fprintf(stderr, "%s\n", outfilename);
    }
fgilham's avatar
fgilham committed

fgilham's avatar
fgilham committed
    return out;
fgilham's avatar
fgilham committed
}


static int
write_elf_header(int fd)
{
fgilham's avatar
fgilham committed
    extern Elf_Ehdr eh;

    /* Ident array. */
    eh.e_ident[EI_MAG0]		= ELFMAG0;
    eh.e_ident[EI_MAG1]		= ELFMAG1;
    eh.e_ident[EI_MAG2]		= ELFMAG2;
    eh.e_ident[EI_MAG3]		= ELFMAG3;

    eh.e_ident[EI_CLASS]	= ELFCLASS32;
#if defined(sparc) && defined(SOLARIS)
    eh.e_ident[EI_DATA]		= ELFDATA2MSB;
#else
fgilham's avatar
fgilham committed
    eh.e_ident[EI_DATA]		= ELFDATA2LSB;
fgilham's avatar
fgilham committed
    eh.e_ident[EI_VERSION]	= EV_CURRENT;
#ifdef SOLARIS
    eh.e_ident[EI_OSABI]	= ELFOSABI_SOLARIS;
#else
fgilham's avatar
fgilham committed
    eh.e_ident[EI_OSABI]	= ELFOSABI_FREEBSD;
#ifdef SOLARIS
    eh.e_type		= ET_REL;	/* ???? */
#else
fgilham's avatar
fgilham committed
    eh.e_type		= ET_NONE;	/* ???? */
#if defined(sparc) && defined(SOLARIS)
    /*
     * We only support 32-bit code right now, and our binaries are
     * v8plus binaries.
     */
    eh.e_machine	= EM_SPARC32PLUS;
#else
    eh.e_machine	= EM_386;
#endif
fgilham's avatar
fgilham committed
    eh.e_version	= EV_CURRENT;
    eh.e_entry		= 0;
    eh.e_phoff		= 0;
    eh.e_shoff		= sizeof(Elf_Ehdr);
    eh.e_flags		= 0;
    eh.e_ehsize		= sizeof(Elf_Ehdr);
    eh.e_phentsize	= 0;
    eh.e_phnum		= 0;
    eh.e_shentsize	= sizeof(Elf_Shdr);
    eh.e_shnum		= 3;		/* Number of lisp spaces. */
    eh.e_shstrndx	= 2;

    return ewrite(fd, &eh, sizeof(Elf_Ehdr), __func__);
fgilham's avatar
fgilham committed
}


static int
write_zero_section_header(int fd)
{
fgilham's avatar
fgilham committed
    /* Section index 0. */
    sh.sh_name		= 0;
    sh.sh_type		= SHT_NULL;
    sh.sh_flags		= 0;
    sh.sh_addr		= 0;
    sh.sh_offset	= 0;
    sh.sh_size		= 0;
    sh.sh_link		= SHN_UNDEF;
    sh.sh_info		= 0;
    sh.sh_addralign	= 0;
    sh.sh_entsize	= 0;

    return ewrite(fd, &sh, eh.e_shentsize, __func__);
fgilham's avatar
fgilham committed
}


static char *string_table_name = ".shstrtab";
static char *object_name;

static int
write_object_section_header(int fd, long length, os_vm_address_t addr)
{
fgilham's avatar
fgilham committed
    extern Elf_Shdr sh;
    Elf_Word flags = SHF_ALLOC | SHF_EXECINSTR | SHF_WRITE;

    sh.sh_name		= strlen(string_table_name) + 2;
    sh.sh_type		= SHT_PROGBITS;
    sh.sh_flags		= flags;
    sh.sh_addr		= (Elf_Addr)addr;

    /* The object section offset is figured out as follows: elf-header +
       three section headers + string table.  The string table is the
       initial null byte followed by the name strings for the string
       table section and the object section. */
    sh.sh_offset	= sizeof(Elf_Ehdr) + 3 * sizeof(Elf_Shdr) + 1 +
	(strlen(string_table_name) + 1) + (strlen(object_name) + 1);
    sh.sh_size		= length;
    sh.sh_link		= SHN_UNDEF;
    sh.sh_info		= 0;
    sh.sh_addralign	= os_vm_page_size;	/* Must be page aligned. */
    sh.sh_entsize	= 0;

    return ewrite(fd, &sh, eh.e_shentsize, __func__);
fgilham's avatar
fgilham committed
}


static int
write_string_section_header(int fd)
{
fgilham's avatar
fgilham committed
    /* Section index 0. */

    extern Elf_Shdr sh;

    sh.sh_name		= 1;
    sh.sh_type		= SHT_STRTAB;
    sh.sh_flags		= 0;
    sh.sh_addr		= 0;
    sh.sh_offset	= sizeof(Elf_Ehdr) + 3 * sizeof(Elf_Shdr);
    /* The size of this section is the lengths of the strings plus the
       nulls. */
    sh.sh_size		= strlen(string_table_name) + strlen(object_name) + 3;
    sh.sh_link		= SHN_UNDEF;
    sh.sh_info		= 0;
    sh.sh_addralign	= 0;
    sh.sh_entsize	= 0;

    return ewrite(fd, &sh, eh.e_shentsize, __func__);
fgilham's avatar
fgilham committed
}


static int
write_string_section(int fd)
{
fgilham's avatar
fgilham committed
    char *buffer = malloc(1 + strlen(string_table_name) + 1 +
			  strlen(object_name) + 1);
    int ret;
fgilham's avatar
fgilham committed

fgilham's avatar
fgilham committed
    if(buffer == NULL) {
	perror("Out of memory in write_string_section()");
fgilham's avatar
fgilham committed
	ret = -1;
fgilham's avatar
fgilham committed

fgilham's avatar
fgilham committed
    } else {
fgilham's avatar
fgilham committed

fgilham's avatar
fgilham committed
	*buffer = '\0';
	memcpy(buffer + 1, string_table_name, strlen(string_table_name) + 1);
	memcpy(buffer + 1 + strlen(string_table_name) + 1,
	       object_name,
	       strlen(object_name) + 1);
	ret = ewrite(fd, buffer,
		     1 + strlen(string_table_name) + 1 + strlen(object_name) + 1,
		     __func__);
fgilham's avatar
fgilham committed
	free(buffer);
    }
fgilham's avatar
fgilham committed

fgilham's avatar
fgilham committed
    return ret;
fgilham's avatar
fgilham committed
}


static int
write_object_section(int fd, long length, os_vm_address_t real_addr)
{
    return ewrite(fd, (void *)real_addr, length, __func__);
write_space_object(const char *dir, int id, os_vm_address_t start, os_vm_address_t end)
fgilham's avatar
fgilham committed
{
fgilham's avatar
fgilham committed
    int out = create_elf_file(dir, id);
    int ret = 0;
    /* The length should be a multiple of the page size. */
    size_t length = end - start + (os_vm_page_size -
				   ((end - start) % os_vm_page_size));
    static char *names[] = { "Dynamic", "Static", "Read-Only" };
fgilham's avatar
fgilham committed

    if(id < 1 || id > 3) {
	fprintf(stderr, "Invalid space id in %s: %d\n", __func__, id);
fgilham's avatar
fgilham committed
	fprintf(stderr, "Executable not built.\n");
	ret = -1;
    }

    /* Make id be 0-based to match array. */
    id--;

    object_name = section_names[id];

    printf("\t %s: %d bytes...\n", names[id], (end - start));
    fflush(stdout);

fgilham's avatar
fgilham committed
    if ((write_elf_header(out) == -1) ||
	(write_zero_section_header(out) == -1) ||
	(write_object_section_header(out, length, start) == -1) ||
	(write_string_section_header(out) == -1) ||
	(write_string_section(out) == -1) ||
	(write_object_section(out, length, start) == -1)) {
	fprintf(stderr, "Executable not built.\n");
	ret = -1;
    }

    close(out);
    return ret;
fgilham's avatar
fgilham committed
}

int
obj_run_linker(long init_func_address, char *file)
fgilham's avatar
fgilham committed
{
fgilham's avatar
fgilham committed
    lispobj libstring = SymbolValue(CMUCL_LIB);     /* Get library: */
    struct vector *vec = (struct vector *)PTR(libstring);
rtoy's avatar
rtoy committed
    char *paths;
    char command[FILENAME_MAX + 1];
    char command_line[FILENAME_MAX + FILENAME_MAX + 10];
fgilham's avatar
fgilham committed
    char *strptr;
    struct stat st;
    int ret;
rtoy's avatar
rtoy committed
    extern int debug_lisp_search;
#ifndef UNICODE
    paths = strdup((char *)vec->data);
#else
    /*
     * What should we do here with 16-bit characters?  For now we just
     * take the low 8-bits.
     */
    paths = malloc(vec->length);
    {
        int k;
        unsigned short *data;
        data = (unsigned short*) vec->data;
        
        for (k = 0; k < vec->length; ++k) {
            paths[k] = data[k] & 0xff;
        }
    }
#endif
fgilham's avatar
fgilham committed
    strptr = strtok(paths, ":");

rtoy's avatar
rtoy committed
    if (debug_lisp_search) {
        printf("Searching for linker.sh script\n");
    }
rtoy's avatar
rtoy committed
    while(strptr != NULL) {
        
fgilham's avatar
fgilham committed
	sprintf(command, "%s/%s", strptr, LINKER_SCRIPT);

rtoy's avatar
rtoy committed
        if (debug_lisp_search) {
            printf("  %s\n", command);
        }
        
	if (stat(command, &st) == 0) {
fgilham's avatar
fgilham committed
	    free(paths);
	    printf("\t[%s: linking %s... \n", command, file);
	    fflush(stdout);
#if defined(__linux__) || defined(__FreeBSD__) || defined(SOLARIS) || defined(__NetBSD__)
            sprintf(command_line, "%s %s 0x%lx '%s' 0x%lx 0x%lx 0x%lx", command,
                    C_COMPILER, init_func_address, file,
                    (unsigned long) READ_ONLY_SPACE_START,
                    (unsigned long) STATIC_SPACE_START,
                    (unsigned long) DYNAMIC_0_SPACE_START);
#else
rtoy's avatar
rtoy committed
	    sprintf(command_line, "%s %s 0x%lx 0x%lx %s", command, C_COMPILER,
                    init_func_address, (unsigned long) &main, file);
fgilham's avatar
fgilham committed
	    ret = system(command_line);
	    if(ret == -1) {
		perror("Can't run link script");
	    } else {
		printf("\tdone]\n");
		fflush(stdout);
	    }
	    return ret;
fgilham's avatar
fgilham committed
	}
fgilham's avatar
fgilham committed
	strptr = strtok(NULL, ":");
    }
fgilham's avatar
fgilham committed

fgilham's avatar
fgilham committed
    fprintf(stderr,
	    "Can't find %s script in CMUCL library directory list.\n", LINKER_SCRIPT);
fgilham's avatar
fgilham committed
    free(paths);
    return -1;
}


/* Read the ELF header from a file descriptor and stuff it into a
fgilham's avatar
fgilham committed
	 structure.	 Make sure it is really an elf header etc. */
static void
fgilham's avatar
fgilham committed
read_elf_header(int fd, Elf_Ehdr *ehp)
    eread(fd, ehp, sizeof(Elf_Ehdr), __func__);
cshapiro's avatar
cshapiro committed
    if (strncmp((const char *) ehp->e_ident, elf_magic_string, 4)) {
fgilham's avatar
fgilham committed
	fprintf(stderr,
		"Bad ELF magic number --- not an elf file.	Exiting in %s.\n",
fgilham's avatar
fgilham committed
	exit(-1);
    }
fgilham's avatar
fgilham committed
read_section_header_entry(int fd, Elf_Shdr *shp)
    eread(fd, shp, eh.e_shentsize, __func__);
fgilham's avatar
fgilham committed
  Map the built-in lisp core sections.
fgilham's avatar
fgilham committed
  NOTE!  We need to do this without using malloc because the memory layout
  is not set until some time after this is done.
map_core_sections(const char *exec_name)
fgilham's avatar
fgilham committed
    int exec_fd;
    Elf_Shdr sh;		/* A section header entry. */
    Elf_Shdr strsecent;
    char nambuf[10];
    int soff;
    int strsecoff;		/* File offset to string table section. */
    int sections_remaining = 3;
    int i, j;
    extern int
	image_dynamic_space_size,
	image_static_space_size,
	image_read_only_space_size;

    if (!(exec_fd = open(exec_name, O_RDONLY))) {
	perror("Can't open executable!");
	exit(-1);
    }

    read_elf_header(exec_fd, &eh);

    /* Find the section name string section.	Save its file offset. */
    soff = eh.e_shoff + eh.e_shstrndx * eh.e_shentsize;
    elseek(exec_fd, soff, __func__);
fgilham's avatar
fgilham committed
    read_section_header_entry(exec_fd, &strsecent);
    strsecoff = strsecent.sh_offset;

    for (i = 0; i < eh.e_shnum && sections_remaining > 0; i++) {

	/* Read an entry from the section header table. */
	elseek(exec_fd, eh.e_shoff + i * eh.e_shentsize, __func__);
fgilham's avatar
fgilham committed
	read_section_header_entry(exec_fd, &sh);

	/* Read the name from the string section. */
	elseek(exec_fd, strsecoff + sh.sh_name, __func__);
	eread(exec_fd, nambuf, 6, __func__);
fgilham's avatar
fgilham committed

	if (sh.sh_type == SHT_PROGBITS) {
	    /* See if this section is one of the lisp core sections. */
	    for (j = 0; j < 3; j++) {
		if (!strncmp(nambuf, section_names[j], 6)) {
		    os_vm_address_t addr;
rtoy's avatar
rtoy committed
		    /*
rtoy's avatar
rtoy committed
                     * Found a core section. Map it!
rtoy's avatar
rtoy committed
                     *
rtoy's avatar
rtoy committed
                     * Although the segment may contain the correct
                     * address for the start of the segment, we don't
                     * care.  We infer the address from the segment
                     * name.  (The names better be unique!!!!)  This
                     * approach allows for a possibly simpler linking
                     * operation because we don't have to figure out
                     * how to get the linker to give segments the
                     * correct address.
rtoy's avatar
rtoy committed
		     */
		    addr = section_addr[j];
		    if ((os_vm_address_t) os_map(exec_fd, sh.sh_offset,
						 addr, sh.sh_size)
			== (os_vm_address_t) -1) {
			fprintf(stderr, "%s: Can't map section %s\n", __func__, section_names[j]);
fgilham's avatar
fgilham committed
			exit(-1);
		    }
		    switch(j) {
rtoy's avatar
rtoy committed
                      case 0: /* Dynamic space. */
                          /* Dynamic space variables are set in lisp.c. */
                          image_dynamic_space_size = sh.sh_size;
                          break;
                      case 1: /* Static space. */
                          image_static_space_size = sh.sh_size;
                          break;
                      case 2: /* Read-only space. */
                          image_read_only_space_size = sh.sh_size;
                          break;
                      default:
                          /* Should never get here. */
                          abort();
                          break;
fgilham's avatar
fgilham committed
		    }

		    sections_remaining--;
		    /* Found a core section, don't check the other core section names. */
		    break;
fgilham's avatar
fgilham committed
    close(exec_fd);
fgilham's avatar
fgilham committed
    if (sections_remaining != 0) {
	fprintf(stderr, "Couldn't map all core sections!	Exiting!\n");
	exit(-1);
    }