Skip to content
Snippets Groups Projects
Commit dc8b7cc2 authored by rtoy's avatar rtoy
Browse files

First cut at executable images on Darwin/x86. The resulting

executable appears to work!

bootfiles/20a/boot-2010-07-1.lisp:
o Add :executable for darwin/x86.

code/bsd-os.lisp:
o Make :elf and :mach-o runtime features too.
o Register :executable of :executable is defined.

lisp/Config.x86_darwin:
o Need mach-o.c
o Add exec-final.c

lisp/lisp.c:
o Don't include elf.h if we're on Darwin since Darwin uses Mach-O, not
  ELF.
o For Darwin, we get the initial function address from
  initial_function_addr, not from &initial_function_addr, like for
  Linux.

lisp/save.c
o Don't include libgen.h and elf.h on Darwin.

tools/linker-x86.sh:
o Update to support Darwin.

tools/make-main-dist.sh:
o Add support for executables on Darwin.

lisp/mach-o.c:
o Initial support for writing Mach-O files for the Lisp spaces.
o Initial support for reading a Mach-O executable to find and map the
  Lisp spaces.
parent a5cd2c4f
No related branches found
No related tags found
No related merge requests found
...@@ -34,5 +34,5 @@ ...@@ -34,5 +34,5 @@
) )
;; Executable feature works on sparc! ;; Executable feature works on sparc!
#+sparc #+(or sparc (and x86 darwin))
(pushnew :executable *features*) (pushnew :executable *features*)
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
;;; Carnegie Mellon University, and has been placed in the public domain. ;;; Carnegie Mellon University, and has been placed in the public domain.
;;; ;;;
(ext:file-comment (ext:file-comment
"$Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/code/bsd-os.lisp,v 1.16 2010/04/20 17:57:43 rtoy Exp $") "$Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/code/bsd-os.lisp,v 1.17 2010/07/30 22:51:58 rtoy Rel $")
;;; ;;;
;;; ********************************************************************** ;;; **********************************************************************
;;; ;;;
...@@ -35,11 +35,11 @@ ...@@ -35,11 +35,11 @@
#-(or freebsd NetBSD OpenBSD Darwin) :bsd) #-(or freebsd NetBSD OpenBSD Darwin) :bsd)
#+elf #+elf
(register-lisp-feature :elf) (register-lisp-runtime-feature :elf)
#+mach-o #+mach-o
(register-lisp-feature :mach-o) (register-lisp-runtime-feature :mach-o)
#+freebsd #+executable
(register-lisp-runtime-feature :executable) (register-lisp-runtime-feature :executable)
(setq *software-type* #+OpenBSD "OpenBSD" (setq *software-type* #+OpenBSD "OpenBSD"
......
...@@ -12,7 +12,10 @@ ASFLAGS += -g3 $(MIN_VER) ...@@ -12,7 +12,10 @@ ASFLAGS += -g3 $(MIN_VER)
UNDEFSYMPATTERN = -Xlinker -u -Xlinker & UNDEFSYMPATTERN = -Xlinker -u -Xlinker &
OS_SRC += Darwin-os.c OS_SRC += Darwin-os.c mach-o.c
OS_LINK_FLAGS = $(MIN_VER) OS_LINK_FLAGS = $(MIN_VER)
OS_LIBS = OS_LIBS =
EXEC_FINAL_OBJ = exec-final.o
/* /*
* main() entry point for a stand alone lisp image. * main() entry point for a stand alone lisp image.
* *
* $Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/lisp/lisp.c,v 1.72 2010/07/30 20:26:11 rtoy Exp $ * $Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/lisp/lisp.c,v 1.73 2010/07/30 22:51:58 rtoy Exp $
* *
*/ */
...@@ -32,9 +32,11 @@ ...@@ -32,9 +32,11 @@
#include "core.h" #include "core.h"
#include "save.h" #include "save.h"
#include "lispregs.h" #include "lispregs.h"
#if defined FEATURE_EXECUTABLE #if defined(FEATURE_EXECUTABLE)
#if !defined(DARWIN)
#include "elf.h" #include "elf.h"
#endif #endif
#endif
/* SIGINT handler that invokes the monitor. */ /* SIGINT handler that invokes the monitor. */
...@@ -437,8 +439,8 @@ main(int argc, const char *argv[], const char *envp[]) ...@@ -437,8 +439,8 @@ main(int argc, const char *argv[], const char *envp[])
lispobj initial_function = 0; lispobj initial_function = 0;
if (builtin_image_flag != 0) { if (builtin_image_flag != 0) {
#if defined(i386) && defined(__linux__) #if defined(i386) && (defined(__linux__) || defined(DARWIN))
initial_function = initial_function_addr; initial_function = (lispobj) initial_function_addr;
#else #else
initial_function = (lispobj) & initial_function_addr; initial_function = (lispobj) & initial_function_addr;
#endif #endif
......
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "os.h"
#include "core.h"
#include "internals.h"
#include "globals.h"
#include "validate.h"
#include "mach-o/loader.h"
#define LINKER_SCRIPT "linker-x86.sh"
#define C_COMPILER "cc"
typedef struct mach_header MachO_hdr;
/* Elf data structures. */
static MachO_hdr eh;
/* 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"};
/* 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)
{
int res = read(d, buf, nbytes);
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, int whence, const char *func)
{
if (lseek(d, o, whence) == -1) {
perror(func);
exit(-1);
}
}
static int
create_elf_file (const char *dir, int id)
{
char outfilename[FILENAME_MAX + 1];
int out;
/* 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]);
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);
}
return out;
}
static int
write_elf_header(int fd)
{
extern MachO_hdr eh;
/* Ident array. */
eh.magic = MH_MAGIC;
/* Support any kind x86. (Should we be more specific?) */
eh.cputype = CPU_TYPE_I386;
eh.cpusubtype = CPU_SUBTYPE_I386_ALL;
eh.filetype = MH_OBJECT;
/* We only have 1 load command in our object */
eh.ncmds = 1;
/* Size of 1 segment command plus size of 1 section */
eh.sizeofcmds = sizeof(struct segment_command) + sizeof(struct section);
eh.flags = MH_NOUNDEFS | MH_NOMULTIDEFS;
return ewrite(fd, &eh, sizeof(MachO_hdr), __func__);
}
static int
write_load_command(int fd, char* name, int length, os_vm_address_t start)
{
struct segment_command lc;
lc.cmd = LC_SEGMENT;
/* Size is 1 segment command + 1 section command */
lc.cmdsize = sizeof(lc) + sizeof(struct section);
strncpy(lc.segname, name, sizeof(lc.segname));
lc.vmaddr = start;
lc.vmsize = length;
/* Offset where the data is. It's the header, the segment
* command, and one section */
lc.fileoff = lc.cmdsize + sizeof(struct mach_header);
lc.filesize = length;
lc.maxprot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
lc.initprot = lc.maxprot;
lc.nsects = 1;
lc.flags = 0;
return ewrite(fd, &lc, sizeof(lc), __func__);
}
static int
write_section(int fd, int length, os_vm_address_t start, char* object_name)
{
struct section sc;
strncpy(sc.sectname, object_name, sizeof(sc.sectname));
strncpy(sc.segname, object_name, sizeof(sc.segname));
sc.addr = start;
sc.size = length;
/* Offset of the data. We have one header, one segment and one
* section */
sc.offset = sizeof(struct mach_header) + sizeof(struct segment_command) +
sizeof(struct section);
sc.align = 12; /* Align on 2^12 = 4096 boundary */
sc.reloff = 0;
sc.nreloc = 0;
sc.flags = 0;
sc.reserved1 = 0;
sc.reserved2 = 0;
return ewrite(fd, &sc, sizeof(sc), __func__);
}
static int
write_section_data(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--;
if ((write_elf_header(out) == -1)
|| (write_load_command(out, section_names[id], length, start) == -1)
|| (write_section(out, length, start, section_names[id]) == -1)
|| (write_section_data(out, length, start) == -1)) {
fprintf(stderr, "Executable not built.\n");
ret = -1;
}
close(out);
return ret;
}
void
elf_cleanup(const char *dirname)
{
char filename[FILENAME_MAX + 1];
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;
char command[FILENAME_MAX + 1];
char command_line[FILENAME_MAX + FILENAME_MAX + 10];
char *strptr;
struct stat st;
int ret;
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
strptr = strtok(paths, ":");
if (debug_lisp_search) {
printf("Searching for linker.sh script\n");
}
while(strptr != NULL) {
sprintf(command, "%s/%s", strptr, LINKER_SCRIPT);
if (debug_lisp_search) {
printf(" %s\n", command);
}
if (stat(command, &st) == 0) {
extern int main();
free(paths);
printf("\t[%s: linking %s... \n", command, file);
fflush(stdout);
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);
ret = system(command_line);
if(ret == -1) {
perror("Can't run link script");
} else {
printf("\tdone]\n");
fflush(stdout);
}
return ret;
}
strptr = strtok(NULL, ":");
}
fprintf(stderr,
"Can't find %s script in CMUCL library directory list.\n", LINKER_SCRIPT);
free(paths);
return -1;
}
/* Read the ELF header from a file descriptor and stuff it into a
structure. Make sure it is really an elf header etc. */
static void
read_elf_header(int fd, MachO_hdr *ehp)
{
eread(fd, ehp, sizeof(MachO_hdr), __func__);
if (ehp->magic != MH_MAGIC) {
fprintf(stderr,
"Bad ELF magic number --- not an elf file. Exiting in %s.\n",
__func__);
exit(-1);
}
}
/*
Map the built-in lisp core sections.
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(const char *exec_name)
{
int exec_fd;
int sections_remaining = 3;
int i, j;
extern int image_dynamic_space_size;
extern int image_static_space_size;
extern int 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);
for (i = 0; i < eh.ncmds && sections_remaining > 0; i++) {
struct load_command lc;
struct segment_command sc;
/* Read the load command and see if its segname matches one of
* our section names. If it does, save the file offset for
* later so we can map the data */
eread(exec_fd, &lc, sizeof(lc), __func__);
fprintf(stderr, "Load %d: cmd = %d, cmdsize = %d\n", i, lc.cmd, lc.cmdsize);
if (lc.cmd == LC_SEGMENT) {
/* Read the segment command and save the file offset */
fprintf(stderr, "Reading next %d bytes for SEGMENT\n", sizeof(sc) - sizeof(lc));
eread(exec_fd, &sc.segname, sizeof(sc) - sizeof(lc), __func__);
fprintf(stderr, "LC_SEGMENT: name = %s\n", sc.segname);
for (j = 0; j < 3; ++j) {
if (strncmp(sc.segname, section_names[j], sizeof(sc.segname)) == 0) {
/* Found a core segment. Map it! */
fprintf(stderr, "Matched!\n");
fprintf(stderr, " Fileoff = %ld\n", sc.fileoff);
fprintf(stderr, " vmaddr = 0x%lx\n", sc.vmaddr);
fprintf(stderr, " vmsize = 0x%lx\n", sc.vmsize);
if ((os_vm_address_t) os_map(exec_fd, sc.fileoff,
(os_vm_address_t) sc.vmaddr,
sc.vmsize)
== (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 */
image_dynamic_space_size = sc.vmsize;
break;
case 1:
/* Static space */
image_static_space_size = sc.vmsize;
break;
case 2:
/* Read only */
image_read_only_space_size = sc.vmsize;
break;
default:
/* Shouldn't happen! */
abort();
}
--sections_remaining;
break;
}
}
fprintf(stderr, "Reading %d remainder bytes left in command\n",
lc.cmdsize - sizeof(sc));
elseek(exec_fd, lc.cmdsize - sizeof(sc), SEEK_CUR, __func__);
} else {
/* Seek to the next command */
fprintf(stderr, "Seeking by %d bytes\n", lc.cmdsize - sizeof(lc));
elseek(exec_fd, lc.cmdsize - sizeof(lc), SEEK_CUR, __func__);
}
}
close(exec_fd);
if (sections_remaining != 0) {
fprintf(stderr, "Couldn't map all core sections! Exiting!\n");
exit(-1);
}
}
/* /*
$Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/lisp/save.c,v 1.25 2010/07/30 20:26:11 rtoy Exp $ $Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/lisp/save.c,v 1.26 2010/07/30 22:51:58 rtoy Exp $
This code was written as part of the CMU Common Lisp project at This code was written as part of the CMU Common Lisp project at
Carnegie Mellon University, and has been placed in the public domain. Carnegie Mellon University, and has been placed in the public domain.
...@@ -28,9 +28,11 @@ ...@@ -28,9 +28,11 @@
#endif #endif
#ifdef FEATURE_EXECUTABLE #ifdef FEATURE_EXECUTABLE
#if !defined(DARWIN)
#include <libgen.h> #include <libgen.h>
#include "elf.h" #include "elf.h"
#endif #endif
#endif
extern int version; extern int version;
......
#!/bin/sh #!/bin/sh
# $Id: linker-x86.sh,v 1.2 2010/07/30 20:26:12 rtoy Exp $ # $Id: linker-x86.sh,v 1.3 2010/07/30 22:51:58 rtoy Exp $
# This file written by Raymond Toy as part of CMU Common Lisp and is # This file written by Raymond Toy as part of CMU Common Lisp and is
# placed in the public domain. # placed in the public domain.
...@@ -19,23 +19,53 @@ fi ...@@ -19,23 +19,53 @@ fi
CCOMPILER=$1 CCOMPILER=$1
IFADDR=$2 IFADDR=$2
EXEC=$3 EXEC=$3
RO_ADDR="-Wl,--section-start=CORRO=$4"
STATIC_ADDR="-Wl,--section-start=CORSTA=$5"
DYN_ADDR="-Wl,--section-start=CORDYN=$6"
OUTDIR=`dirname $EXEC` OUTDIR=`dirname $EXEC`
OUTNAME=`basename $EXEC` OUTNAME=`basename $EXEC`
CMUCLLIB=`dirname $0` CMUCLLIB=`dirname $0`
#OPT_IFADDR="-Wl,--defsym -Wl,initial_function_addr=$IFADDR" # Name of file where we write the actual initial function address.
OPT_IFADDR="ifaddr.c" OPT_IFADDR="cmu-ifaddr-$$.c"
OPT_ARCHIVE="-Wl,--whole-archive -Wl,$CMUCLLIB/lisp.a -Wl,--no-whole-archive" # Names of the core sections from Lisp.
OPT_CORE="CORRO.o CORSTA.o CORDYN.o" OPT_CORE="CORRO.o CORSTA.o CORDYN.o"
case `uname` in
Linux*)
# How to specify the starting address for each of the sections
RO_ADDR="-Wl,--section-start=CORRO=$4"
STATIC_ADDR="-Wl,--section-start=CORSTA=$5"
DYN_ADDR="-Wl,--section-start=CORDYN=$6"
#OPT_IFADDR="-Wl,--defsym -Wl,initial_function_addr=$IFADDR"
# Specify how to link the entire lisp.a library
OPT_ARCHIVE="-Wl,--whole-archive -Wl,$CMUCLLIB/lisp.a -Wl,--no-whole-archive"
# See Config.x86_linux
OS_LIBS=-ldl
;;
Darwin*)
# How to specify the starting address for each of the sections
RO_ADDR="-segaddr CORRO $4"
STATIC_ADDR="-segaddr CORSTA $5"
DYN_ADDR="-segaddr CORDYN $6"
# Specify how to link the entire lisp.a library
OPT_ARCHIVE="-all_load $CMUCLLIB/lisp.a"
# Extra stuff. For some reason one __LINKEDIT segment is mapped
# just past the dynamic space. This messes things up, so we move it
# to another address. This seems to be free, at least on 10.5.
OPT_EXTRA="-segaddr __LINKEDIT 0x99000000"
# See Config.x86_darwin
OS_LIBS=
;;
esac
trap 'rm -f $OUTDIR/$OPT_IFADDR' 0 trap 'rm -f $OUTDIR/$OPT_IFADDR' 0
(cd $OUTDIR (cd $OUTDIR
echo "long initial_function_addr = $IFADDR;" > $OPT_IFADDR echo "long initial_function_addr = $IFADDR;" > $OPT_IFADDR
$CCOMPILER -m32 -o $OUTNAME -rdynamic $OPT_IFADDR $OPT_ARCHIVE $OPT_CORE $RO_ADDR $STATIC_ADDR $DYN_ADDR -ldl -lm) $CCOMPILER -m32 -o $OUTNAME -rdynamic $OPT_IFADDR $OPT_ARCHIVE $OPT_CORE $RO_ADDR $STATIC_ADDR $DYN_ADDR $OPT_EXTRA $OS_LIBS -lm)
...@@ -51,19 +51,24 @@ case $ARCH in ...@@ -51,19 +51,24 @@ case $ARCH in
esac esac
case $OS in case $OS in
FreeBSD*) FreeBSD*)
EXECUTABLE=true EXECUTABLE=true
SCRIPT=FreeBSD SCRIPT=FreeBSD
;; ;;
linux*) linux*)
EXECUTABLE=true EXECUTABLE=true
SCRIPT=Linux SCRIPT=Linux
;; ;;
solaris*) solaris*)
EXECUTABLE=true EXECUTABLE=true
SCRIPT=SunOS SCRIPT=SunOS
;; ;;
*) EXECUTABLE="" ;; darwin*)
EXECUTABLE=true
;;
*)
EXECUTABLE=""
;;
esac esac
# Frob PATH to use /usr/ucb/install for Solaris # Frob PATH to use /usr/ucb/install for Solaris
...@@ -93,6 +98,7 @@ then ...@@ -93,6 +98,7 @@ then
install ${GROUP} ${OWNER} -m 0755 $TARGET/lisp/lisp.a $DESTDIR/lib/cmucl/lib/ install ${GROUP} ${OWNER} -m 0755 $TARGET/lisp/lisp.a $DESTDIR/lib/cmucl/lib/
install ${GROUP} ${OWNER} -m 0755 src/tools/linker.sh $DESTDIR/lib/cmucl/lib/ install ${GROUP} ${OWNER} -m 0755 src/tools/linker.sh $DESTDIR/lib/cmucl/lib/
install ${GROUP} ${OWNER} -m 0755 src/tools/linker-x86.sh $DESTDIR/lib/cmucl/lib/ install ${GROUP} ${OWNER} -m 0755 src/tools/linker-x86.sh $DESTDIR/lib/cmucl/lib/
install ${GROUP} ${OWNER} -m 0755 src/tools/linker-darwin.sh $DESTDIR/lib/cmucl/lib/
install ${GROUP} ${OWNER} -m 0755 src/tools/$SCRIPT-cmucl-linker-script $DESTDIR/lib/cmucl/lib/ install ${GROUP} ${OWNER} -m 0755 src/tools/$SCRIPT-cmucl-linker-script $DESTDIR/lib/cmucl/lib/
fi fi
for corefile in $TARGET/lisp/$CORE for corefile in $TARGET/lisp/$CORE
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment