#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ldr.h>
#include <a.out.h>
#undef FREAD
#undef FWRITE
#include <ldfcn.h>
#ifndef BEGINNING
#define BEGINNING SEEK_SET
#endif
#ifndef FSEEK
#define FSEEK(ldptr,o,p) fseek(IOPTR(ldptr),(p==BEGINNING)?(OFFSET(ldptr) +o):o,p)
#endif
#ifndef FREAD
#define FREAD(p,s,n,ldptr) fread(p,s,n,IOPTR(ldptr))
#endif
#undef RTLD_LAZY
#define RTLD_LAZY 1 /* lazy function call binding */
#undef RTLD_NOW
#define RTLD_NOW 2 /* immediate function call binding */
#undef RTLD_GLOBAL
#define RTLD_GLOBAL 0x100 /* allow symbols to be global */
struct
dl_info {
void
(*init) (
void
);
void
(*fini) (
void
);
};
void
*dlopen(
const
char
*path,
int
mode);
void
*dlsym(
void
*handle,
const
char
*symbol);
const
char
*dlerror(
void
);
int
dlclose(
void
*handle);
typedef
struct
{
char
*name;
void
*addr;
} Export, *ExportPtr;
typedef
struct
{
void
(*init) (
void
);
void
(*term) (
void
);
} Cdtor, *CdtorPtr;
typedef
void
(*GccCDtorPtr) (
void
);
typedef
struct
Module {
struct
Module *next;
char
*name;
int
refCnt;
void
*entry;
struct
dl_info *info;
CdtorPtr cdtors;
GccCDtorPtr gcc_ctor;
GccCDtorPtr gcc_dtor;
int
nExports;
ExportPtr exports;
} Module, *ModulePtr;
static
ModulePtr modList;
static
char
errbuf[BUFSIZ];
static
int
errvalid;
extern
char
*strdup(
const
char
*);
static
void
caterr(
char
*);
static
int
readExports(ModulePtr);
static
void
terminate(
void
);
static
void
*findMain(
void
);
void
*dlopen(
const
char
*path,
int
mode)
{
register
ModulePtr mp;
static
void
*mainModule;
if
(!mainModule) {
if
((mainModule = findMain()) == NULL)
return
NULL;
atexit
(terminate);
}
for
(mp = modList; mp; mp = mp->next)
if
(
strcmp
(mp->name, path) == 0) {
mp->refCnt++;
return
mp;
}
if
((mp = (ModulePtr)
calloc
(1,
sizeof
(*mp))) == NULL) {
errvalid++;
strcpy
(errbuf,
"calloc: "
);
strcat
(errbuf,
strerror
(
errno
));
return
NULL;
}
if
((mp->name = strdup(path)) == NULL) {
errvalid++;
strcpy
(errbuf,
"strdup: "
);
strcat
(errbuf,
strerror
(
errno
));
free
(mp);
return
NULL;
}
if
((mp->entry = (
void
*) load((
char
*) path, L_NOAUTODEFER, NULL)) == NULL) {
free
(mp->name);
free
(mp);
errvalid++;
strcpy
(errbuf,
"dlopen: "
);
strcat
(errbuf, path);
strcat
(errbuf,
": "
);
if
(
errno
== ENOEXEC) {
char
*tmp[BUFSIZ /
sizeof
(
char
*)];
if
(loadquery(L_GETMESSAGES, tmp,
sizeof
(tmp)) == -1)
strcpy
(errbuf,
strerror
(
errno
));
else
{
char
**p;
for
(p = tmp; *p; p++)
caterr(*p);
}
}
else
strcat
(errbuf,
strerror
(
errno
));
return
NULL;
}
mp->refCnt = 1;
mp->next = modList;
modList = mp;
if
(loadbind(0, mainModule, mp->entry) == -1) {
dlclose(mp);
errvalid++;
strcpy
(errbuf,
"loadbind: "
);
strcat
(errbuf,
strerror
(
errno
));
return
NULL;
}
if
(mode & RTLD_GLOBAL) {
register
ModulePtr mp1;
for
(mp1 = mp->next; mp1; mp1 = mp1->next)
if
(loadbind(0, mp1->entry, mp->entry) == -1) {
dlclose(mp);
errvalid++;
strcpy
(errbuf,
"loadbind: "
);
strcat
(errbuf,
strerror
(
errno
));
return
NULL;
}
}
if
(readExports(mp) == -1) {
dlclose(mp);
return
NULL;
}
if
(mp->info = (
struct
dl_info *) dlsym(mp,
"dl_info"
)) {
if
(mp->info->init)
(*mp->info->init) ();
}
else
errvalid = 0;
if
(mp->cdtors = (CdtorPtr) dlsym(mp,
"__cdtors"
)) {
CdtorPtr cp = mp->cdtors;
while
(cp->init || cp->term) {
if
(cp->init && cp->init != (
void
(*)(
void
)) 0xffffffff)
(*cp->init) ();
cp++;
}
}
else
if
(mp->gcc_ctor = (GccCDtorPtr) dlsym(mp,
"_GLOBAL__DI"
)) {
(*mp->gcc_ctor) ();
mp->gcc_dtor = (GccCDtorPtr) dlsym(mp,
"_GLOBAL__DD"
);
}
else
errvalid = 0;
return
mp;
}
static
void
caterr(
char
*s)
{
register
char
*p = s;
while
(*p >=
'0'
&& *p <=
'9'
)
p++;
switch
(
atoi
(s)) {
case
L_ERROR_TOOMANY:
strcat
(errbuf,
"to many errors"
);
break
;
case
L_ERROR_NOLIB:
strcat
(errbuf,
"can't load library"
);
strcat
(errbuf, p);
break
;
case
L_ERROR_UNDEF:
strcat
(errbuf,
"can't find symbol"
);
strcat
(errbuf, p);
break
;
case
L_ERROR_RLDBAD:
strcat
(errbuf,
"bad RLD"
);
strcat
(errbuf, p);
break
;
case
L_ERROR_FORMAT:
strcat
(errbuf,
"bad exec format in"
);
strcat
(errbuf, p);
break
;
case
L_ERROR_ERRNO:
strcat
(errbuf,
strerror
(
atoi
(++p)));
break
;
default
:
strcat
(errbuf, s);
break
;
}
}
void
*dlsym(
void
*handle,
const
char
*symbol)
{
register
ModulePtr mp = (ModulePtr) handle;
register
ExportPtr ep;
register
int
i;
for
(ep = mp->exports, i = mp->nExports; i; i--, ep++)
if
(
strcmp
(ep->name, symbol) == 0)
return
ep->addr;
errvalid++;
strcpy
(errbuf,
"dlsym: undefined symbol "
);
strcat
(errbuf, symbol);
return
NULL;
}
const
char
*dlerror(
void
)
{
if
(errvalid) {
errvalid = 0;
return
errbuf;
}
return
NULL;
}
int
dlclose(
void
*handle)
{
register
ModulePtr mp = (ModulePtr) handle;
int
result;
register
ModulePtr mp1;
if
(--mp->refCnt > 0)
return
0;
if
(mp->info && mp->info->fini)
(*mp->info->fini) ();
if
(mp->cdtors) {
CdtorPtr cp = mp->cdtors;
while
(cp->init || cp->term) {
if
(cp->term && cp->init != (
void
(*)(
void
)) 0xffffffff)
(*cp->term) ();
cp++;
}
}
else
if
(mp->gcc_dtor) {
(*mp->gcc_dtor) ();
}
result = unload(mp->entry);
if
(result == -1) {
errvalid++;
strcpy
(errbuf,
strerror
(
errno
));
}
if
(mp->exports) {
register
ExportPtr ep;
register
int
i;
for
(ep = mp->exports, i = mp->nExports; i; i--, ep++)
if
(ep->name)
free
(ep->name);
free
(mp->exports);
}
if
(mp == modList)
modList = mp->next;
else
{
for
(mp1 = modList; mp1; mp1 = mp1->next)
if
(mp1->next == mp) {
mp1->next = mp->next;
break
;
}
}
free
(mp->name);
free
(mp);
return
result;
}
static
void
terminate(
void
)
{
while
(modList)
dlclose(modList);
}
static
int
readExports(ModulePtr mp)
{
LDFILE *ldp = NULL;
SCNHDR sh, shdata;
LDHDR *lhp;
char
*ldbuf;
LDSYM *ls;
int
i;
ExportPtr ep;
struct
ld_info *lp;
char
*buf;
int
size = 4 * 1024;
void
*dataorg;
if
((buf =
malloc
(size)) == NULL) {
errvalid++;
strcpy
(errbuf,
"readExports: "
);
strcat
(errbuf,
strerror
(
errno
));
return
-1;
}
while
((i = loadquery(L_GETINFO, buf, size)) == -1 &&
errno
== ENOMEM) {
free
(buf);
size += 4 * 1024;
if
((buf =
malloc
(size)) == NULL) {
errvalid++;
strcpy
(errbuf,
"readExports: "
);
strcat
(errbuf,
strerror
(
errno
));
return
-1;
}
}
if
(i == -1) {
errvalid++;
strcpy
(errbuf,
"readExports: "
);
strcat
(errbuf,
strerror
(
errno
));
free
(buf);
return
-1;
}
lp = (
struct
ld_info *) buf;
while
(lp) {
if
((unsigned
long
) mp->entry >= (unsigned
long
) lp->ldinfo_dataorg &&
(unsigned
long
) mp->entry < (unsigned
long
) lp->ldinfo_dataorg +
lp->ldinfo_datasize) {
dataorg = lp->ldinfo_dataorg;
ldp = ldopen(lp->ldinfo_filename, ldp);
break
;
}
if
(lp->ldinfo_next == 0)
lp = NULL;
else
lp = (
struct
ld_info *) ((
char
*) lp + lp->ldinfo_next);
}
free
(buf);
if
(!ldp) {
errvalid++;
strcpy
(errbuf,
"readExports: "
);
strcat
(errbuf,
strerror
(
errno
));
return
-1;
}
if
(TYPE(ldp) != U802TOCMAGIC) {
errvalid++;
strcpy
(errbuf,
"readExports: bad magic"
);
while
(ldclose(ldp) == FAILURE);
return
-1;
}
if
(ldnshread(ldp, _DATA, &shdata) != SUCCESS) {
errvalid++;
strcpy
(errbuf,
"readExports: cannot read data section header"
);
while
(ldclose(ldp) == FAILURE);
return
-1;
}
if
(ldnshread(ldp, _LOADER, &sh) != SUCCESS) {
errvalid++;
strcpy
(errbuf,
"readExports: cannot read loader section header"
);
while
(ldclose(ldp) == FAILURE);
return
-1;
}
if
((ldbuf = (
char
*)
malloc
(sh.s_size)) == NULL) {
errvalid++;
strcpy
(errbuf,
"readExports: "
);
strcat
(errbuf,
strerror
(
errno
));
while
(ldclose(ldp) == FAILURE);
return
-1;
}
if
(FSEEK(ldp, sh.s_scnptr, BEGINNING) != OKFSEEK) {
errvalid++;
strcpy
(errbuf,
"readExports: cannot seek to loader section"
);
free
(ldbuf);
while
(ldclose(ldp) == FAILURE);
return
-1;
}
if
(FREAD(ldbuf, sh.s_size, 1, ldp) != 1) {
errvalid++;
strcpy
(errbuf,
"readExports: cannot read loader section"
);
free
(ldbuf);
while
(ldclose(ldp) == FAILURE);
return
-1;
}
lhp = (LDHDR *) ldbuf;
ls = (LDSYM *) (ldbuf + LDHDRSZ);
for
(i = lhp->l_nsyms; i; i--, ls++) {
if
(!LDR_EXPORT(*ls))
continue
;
mp->nExports++;
}
if
((mp->exports = (ExportPtr)
calloc
(mp->nExports,
sizeof
(*mp->exports))) == NULL) {
errvalid++;
strcpy
(errbuf,
"readExports: "
);
strcat
(errbuf,
strerror
(
errno
));
free
(ldbuf);
while
(ldclose(ldp) == FAILURE);
return
-1;
}
ep = mp->exports;
ls = (LDSYM *) (ldbuf + LDHDRSZ);
for
(i = lhp->l_nsyms; i; i--, ls++) {
char
*symname;
char
tmpsym[SYMNMLEN + 1];
if
(!LDR_EXPORT(*ls))
continue
;
if
(ls->l_zeroes == 0)
symname = ls->l_offset + lhp->l_stoff + ldbuf;
else
{
strncpy
(tmpsym, ls->l_name, SYMNMLEN);
tmpsym[SYMNMLEN] =
'\0'
;
symname = tmpsym;
}
ep->name = strdup(symname);
ep->addr = (
void
*) ((unsigned
long
) dataorg +
ls->l_value - shdata.s_vaddr);
ep++;
}
free
(ldbuf);
while
(ldclose(ldp) == FAILURE);
return
0;
}
static
void
*findMain(
void
)
{
struct
ld_info *lp;
char
*buf;
int
size = 4 * 1024;
int
i;
void
*ret;
if
((buf =
malloc
(size)) == NULL) {
errvalid++;
strcpy
(errbuf,
"findMain: "
);
strcat
(errbuf,
strerror
(
errno
));
return
NULL;
}
while
((i = loadquery(L_GETINFO, buf, size)) == -1 &&
errno
== ENOMEM) {
free
(buf);
size += 4 * 1024;
if
((buf =
malloc
(size)) == NULL) {
errvalid++;
strcpy
(errbuf,
"findMain: "
);
strcat
(errbuf,
strerror
(
errno
));
return
NULL;
}
}
if
(i == -1) {
errvalid++;
strcpy
(errbuf,
"findMain: "
);
strcat
(errbuf,
strerror
(
errno
));
free
(buf);
return
NULL;
}
lp = (
struct
ld_info *) buf;
ret = lp->ldinfo_dataorg;
free
(buf);
return
ret;
}