posting DPY

faustus at ucbcad.UUCP faustus at ucbcad.UUCP
Fri Apr 12 07:06:37 AEST 1985


Here is a simple package for doing dynamic loading of object files into
running processes. I haven't used it that much, so I would appreciate
any bug reports.

	Wayne
-----
# The rest of this file is a shell script which will extract:
# Makefile dload.c foo.c foo2.c test.c
echo x - Makefile
cat >Makefile <<'!Funky!Stuff!'
#
# Makefile for a.out
#

CFLAGS =	-g

CFILES =	dload.c test.c

OFILES =	dload.o test.o

HFILES =	

a.out:          $(OFILES)
		$(CC) $(CFLAGS) $(OFILES)  -o a.out

ctags:
		ctags $(CFILES) $(HFILES)

!Funky!Stuff!
echo x - dload.c
cat >dload.c <<'!Funky!Stuff!'


/* RCS Info: $Revision: $ on $Date: $
 *           $Source: $
 * Copyright (c) 1985 Wayne A. Christopher, U. C. Berkeley CAD Group
 *
 * These routines are used for dynamically loading routines into a running
 * program, using the -A option to ld. It is simpler and shorter than DynLoad.
 * Usage is as follows:
 * dl_init(argv0) finds the executable file for the current program, and must
 *	be called before any files are loaded. This means that the executable
 *	must be readable and cannot be stripped. The argument to dl_init should
 *	be the program name in argv[0] -- if it isn't a full path name
 *	then dl_init will look in the directories in PATH for it.
 * dl_load(lib, filename, nlist) loads in the file and fills in the slots in
 *	nlist, an array of struct nlist (see nlist(3) and a.out(5)). If
 *	the lib argument is non-NULL then the file is first extracted from
 *	the named library.
 * Some things that this package does not do -- it doesn't search for symbols
 * loaded -- when it fills in the nlist structures, it's up to
 * you from then on to keep track of what's what.
 */

#include <stdio.h>
#include <a.out.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>

#define BLOCKALIGN 1024	/* For VAX - should be enough for other machines. */

static char execfile[512] = "";	/* The path name to the current program. */

/* Set execfile to the path name of the current program. (Hope you don't
 * get hit by rdists in the meantime... Return value 1 is things went
 * ok, 0 if there was an error.
 */

dl_init(pname)
	char *pname;
{
	char *path, *try, buf[512], pbuf[512], *s, *t, *getenv();
	struct stat stbuf;

	if (pname == NULL) {
		fprintf(stderr, "dl_init error: NULL executable name.\n");
		return (0);
	}
	if (index(pname, '/') == NULL) {
		path = getenv("PATH");
		if (path == NULL) {
			fprintf(stderr, 
				"dl_init error: PATH not in environment.\n");
			return (0);
		}
		t = strcpy(buf, path);
		for (;;) {
			s = index(t, ':');
			if (s)
				*s = '\0';
			strcpy(pbuf, t);
			strcat(pbuf, "/");
			strcat(pbuf, pname);
			if (access(pbuf, R_OK | X_OK) == 0) {
				stat(pbuf, &stbuf);
				if (stbuf.st_mode & S_IFREG) {
					strcpy(execfile, pbuf);
					break;
				}
			}
			if (s)
				t = s + 1;
			else
				break;
		}
	} else {
		if (access(pname, R_OK | X_OK) == 0) {
			stat(pname, &stbuf);
			if (stbuf.st_mode & S_IFREG) {
				strcpy(execfile, pname);
			}
		}
	}
	if (execfile[0] == '\0') {
		fprintf(stderr, "dl_init error: can't access executable %s.\n",
			pname);
		return (0);
	}
	return (1);
}

/* Load in a file and set the proper values in the namelist. Return 0 if
 * there were problems loading the file, but not if some nlist values
 * didn't get filled. (Set them to NULL if the file was loaded but the
 * symbol wasn't found). Note that if the lib argument is given, it
 * must be a full path name, because we chdir to /tmp before extracting
 * the file. There might be synch problems here if two people do this
 * at exactly the same time... File file does get moved quickly, but
 * we should do file locking...
 */

dl_load(lib, filename, nl)
	char *lib, *filename;
	struct nlist nl[];
{
	char path[512], buf[512], *sbrk();
	extern char *environ;
	static char *loadfile = "/tmp/DlXXXXXX";
	long status, top, offset;
	int lfd;
	struct exec header;
	union u {
		char *u_cp;
		long u_i;
	} u;

	if (execfile[0] == '\0') {
		fprintf(stderr, "dl_load error: not initialized.\n");
		return (0);
	}

	/* Get the file. If it is in a library then extract it and
	 * stick it in /tmp.
	 */
	
	if (lib) {
		sprintf(path, "/tmp/%d%s", getpid(), filename);
		if (vfork() == 0) {
			chdir("/tmp");
			execle("/bin/ar", "ar", "x", lib, filename, 0,
				environ);
			fprintf(stderr, "Can't exec /bin/ar\n");
			exit (1);
		} else {
			wait(status);
			if (status != 0) {
				fprintf(stderr, 
					"dl_load error: can't extract %s from",
					filename);
				fprintf(stderr,"%s (exit %ld)\n", lib, status);
				return (0);
			}
			(void) rename(filename, path);
		}
	} else
		strcpy(path, filename);

	/* Now get some space to load the stuff into. Can't use malloc
	 * between now and after the file is loaded, or the break may
	 * change.
	 */
	u.u_cp = sbrk(0);
	top = u.u_i;
	if (top % BLOCKALIGN)
		top = (top / BLOCKALIGN + 1) * BLOCKALIGN;
	if (brk(top) < 0) {
		perror("dl_load error: out of space");
		return (0);
	}

	mktemp(loadfile);
	sprintf(buf, "%x", top);
	if (vfork() == 0) {
		execle("/bin/ld", "ld", "-X", "-A", execfile, "-T", buf,
			"-o", loadfile, path, 0, environ);
		fprintf(stderr, "Can't exec /bin/ld\n");
		exit (1);
	} else {
		wait(&status);
	if (status != 0) {
			fprintf(stderr,"dl_load error: ld -X -A %s -T %d -o %s",
				execfile, top, loadfile);
			fprintf(stderr, " %s failed (exit %d)\n", path, status);
			return (0);
		}
	}

	/* Now figure out how much room we need. */
	if ((lfd = open(loadfile, O_RDONLY)) == -1) {
		fprintf(stderr, "ld_load error ");
		perror(loadfile);
		return (0);
	}
	if ((read(lfd, &header, sizeof (struct exec)) < sizeof (struct exec))
			|| N_BADMAG(header)) {
		fprintf(stderr, "ld_load error: bad exec structure in %s\n",
			loadfile);
		return (0);
	}
	offset = header.a_text + header.a_data + header.a_bss;
	if (offset % BLOCKALIGN)
		offset = (offset / BLOCKALIGN + 1) * BLOCKALIGN;
	if (brk(top + offset) < 0) {
		perror("dl_load error");
		fprintf(stderr, "Can't allocate %d bytes.\n", offset);
		return (0);
	}

	/* Load the stuff into memory. */
	offset = header.a_text + header.a_data;
	lseek(lfd, (long) N_TXTOFF(header), 0);
	if (read(lfd, (char *) top, offset) < offset) {
		fprintf(stderr, 
		    "ld_load error: unexpected EOF while reading %d bytes.\n",
		    offset);
		return (0);
	}

	/* Now, read in the namelist. This is easy... */
	nlist(loadfile, nl);

	/* All done. Get rid of temp files... */
	if (lib)
		unlink(path);
	unlink(loadfile);
	return (1);
}

!Funky!Stuff!
echo x - foo.c
cat >foo.c <<'!Funky!Stuff!'
char *string1 = "string 1 here... ";
char *string2 = "string 2 here... ";

func1() { printf("Here we are in func1...\n"); }
!Funky!Stuff!
echo x - foo2.c
cat >foo2.c <<'!Funky!Stuff!'
int int1 = 1;
int int2 = 2;
func2() { printf("Here we are in func2...\n"); }
!Funky!Stuff!
echo x - test.c
cat >test.c <<'!Funky!Stuff!'
/* Test ndload. */

#include <nlist.h>

struct nlist nl[] = {	{ "_string1" } ,
			{ "_string2" } ,
			{ "_func1" } ,
			{ "_main" } ,
			{ (char *) 0 }
} ;

struct nlist nl2[] = {	{ "_func2" } ,
			{ "_int1" } ,
			{ "_int2" } ,
			{ "_base" } ,
			{ (char *) 0 }
};

main(ac, av)
	char **av;
{
	union u {
		int u_i;
		char **u_cp;
		int (*u_func) ();
		int *u_ip;
	} u;

	dl_init(av[0]);
	dl_load((char *) 0, "foo.o", nl);

	u.u_i = nl[0].n_value;
	printf("string 0 is --- %s \n", *u.u_cp);
	u.u_i = nl[1].n_value;
	printf("string 1 is --- %s \n", *u.u_cp);
	u.u_i = nl[2].n_value;
	(*u.u_func) ();
	u.u_i = nl[3].n_value;
	printf("main: %x...\n", u.u_i);

	dl_load((char *) 0, "foo2.o", nl2);
	u.u_i = nl2[0].n_value;
	(*u.u_func) ();
	u.u_i = nl2[1].n_value;
	printf("int 1 is --- %d \n", *u.u_ip);
	u.u_i = nl2[2].n_value;
	printf("int 2 is --- %d \n", *u.u_ip);
	u.u_i = nl2[3].n_value;
	(*u.u_func) ();

	exit(0);
}

base()
{
	printf("Hey dude...\n");
}
!Funky!Stuff!



More information about the Comp.sources.unix mailing list