Newer
Older
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.
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.14 2007/08/14 15:57:47 rtoy Exp $
fgilham
committed
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "os.h"
#include "globals.h"
static char elf_magic_string[] = {ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3};
/* Names of the Lisp image ELF sections. These names must be the same as
the corresponding names found in the linker script. */
static char *section_names[] = {"CORDYN", "CORSTA", "CORRO"};
#ifdef SOLARIS
#include "sparc-validate.h"
/*
* 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[] =
{
DYNAMIC_0_SPACE_START,
STATIC_SPACE_START,
READ_ONLY_SPACE_START
};
#endif
/* Note: write errors are not fatal. */
static int
ewrite(int fd, const void *buf, size_t nbytes, const char *func)
{
if (write(fd, buf, nbytes) < nbytes) {
perror(func);
return -1; /* Simple way to indicate error. */
}
return 0;
Read errors are fatal, because these reads have to succeed for lisp to
get off the ground.
static void
eread(int d, void *buf, size_t nbytes, const char *func)
{
if (res == -1) {
perror(func);
exit(-1);
}
if (res < nbytes) {
fprintf(stderr, "Short read in %s!\n", func);
exit(-1);
}
}
static void
elseek(int d, off_t o, const char *func)
{
if (lseek(d, o, SEEK_SET) == -1) {
perror(func);
exit(-1);
}
}
static int
create_elf_file (const char *dir, int id)
{
/* Note: the space id will be either 1, 2 or 3. Subtract one to index
the name array. */
snprintf(outfilename, MAXPATHLEN, "%s/%s.o", dir, section_names[id - 1]);
out = open(outfilename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if(!out) {
perror("write_elf_object: can't open file");
fprintf(stderr, "%s\n", outfilename);
}
}
static int
write_elf_header(int fd)
{
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;
#ifdef SOLARIS
eh.e_ident[EI_DATA] = ELFDATA2MSB;
#else
#ifdef SOLARIS
eh.e_ident[EI_OSABI] = ELFOSABI_SOLARIS;
#else
#ifdef SOLARIS
eh.e_type = ET_REL; /* ???? */
#else
#endif
#ifdef 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
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__);
}
static int
write_zero_section_header(int fd)
{
/* 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__);
}
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)
{
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__);
}
static int
write_string_section_header(int fd)
{
/* 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__);
}
static int
write_string_section(int fd)
{
char *buffer = malloc(1 + strlen(string_table_name) + 1 +
strlen(object_name) + 1);
int ret;
write(0, "Out of memory in write_string_section()\n", 40);
ret = -1;
*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,
}
static int
write_object_section(int fd, long length, os_vm_address_t real_addr)
{
return ewrite(fd, (void *)real_addr, length, __func__);
}
int
write_elf_object(const char *dir, int id, os_vm_address_t start, os_vm_address_t end)
{
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));
if(id < 1 || id > 3) {
fprintf(stderr, "Invalid space id in %s: %d\n", __func__, id);
fprintf(stderr, "Executable not built.\n");
ret = -1;
}
/* Make id be 0-based to match array. */
id--;
object_name = section_names[id];
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;
}
void
elf_cleanup(const char *dirname)
{
char filename[MAXPATHLEN];
int i;
/* Get rid of lisp space files. */
for(i = 0; i < 3; i++) {
/* Delete core space .o files. */
sprintf(filename, "%s/%s.o", dirname, section_names[i]);
unlink(filename);
}
}
int
elf_run_linker(long init_func_address, char *file)
{
lispobj libstring = SymbolValue(CMUCL_LIB); /* Get library: */
struct vector *vec = (struct vector *)PTR(libstring);
char *paths = strdup((char *)vec->data);
char command[MAXPATHLEN + 1];
char command_line[MAXPATHLEN + MAXPATHLEN + 10];
char *strptr;
struct stat st;
int ret;
strptr = strtok(paths, ":");
while(strptr != NULL) {
sprintf(command, "%s/%s", strptr, LINKER_SCRIPT);
if(stat(command, &st) == 0) {
free(paths);
printf("\t[%s: linking %s... \n", command, file);
fflush(stdout);
sprintf(command_line, "%s %s 0x%lx %s", command, C_COMPILER,
init_func_address, file);
ret = system(command_line);
if(ret == -1) {
perror("Can't run link script");
} else {
printf("\tdone]\n");
fflush(stdout);
}
return ret;
"Can't find %s script in CMUCL library directory list.\n", LINKER_SCRIPT);
}
/* Read the ELF header from a file descriptor and stuff it into a
eread(fd, ehp, sizeof(Elf_Ehdr), __func__);
if (strncmp(ehp->e_ident, elf_magic_string, 4)) {
fprintf(stderr,
"Bad ELF magic number --- not an elf file. Exiting in %s.\n",
NOTE! We need to do this without using malloc because the memory layout
is not set until some time after this is done.
*/
void
map_core_sections(char *exec_name)
{
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;
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__);
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__);
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)) {
#ifdef SOLARIS
/*
* On Solaris, the section header sets the addr
* field to 0 because the linker script says the
* sections are NOTE sections. Hence, we need to
* look up the section addresses ourselves.
*/
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]);
exit(-1);
}
switch(j) {
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;
}
sections_remaining--;
/* Found a core section, don't check the other core section names. */
break;
if (sections_remaining != 0) {
fprintf(stderr, "Couldn't map all core sections! Exiting!\n");
exit(-1);
}