/* Dynamically-loadable-modules API Copyright (C) 2000 Andrew Zabolotny Partly based on work by Charles Sandmann and DJ Delorie. Usage of this library is not restricted in any way. The full license text can be found in the file dxe.txt. */ #include #include #include #include #include #include #include #include #include #include #include #include /* Exported symbols table entry */ typedef struct { unsigned long offset; char name [1]; /* expanded as needed */ } __attribute__((packed)) exp_table_entry; /* Unresolved symbols table entry */ typedef struct { unsigned short n_rel_relocs; unsigned short n_abs_relocs; char name [1]; /* expanded as needed */ } __attribute__((packed)) unres_table_entry; /* This is the private dxe_h structure */ typedef struct __dxe_handle { struct __dxe_handle *next; /* Pointer to next module in chain */ char *fname; /* Full module pathname */ int mode; /* Module open mode */ int inuse; /* In-use counter */ int n_exp_syms; /* Number of entries in export table */ exp_table_entry **exp_table; /* Exported symbols table */ char *header; /* The resident portion of header */ char *section; /* code+data+bss section */ } dxe_handle, *dxe_h; #define __DXE2_INTERNAL_API__ #include /* Last-resort symbol resolver */ static void *(*_symresolver) (const char *symname) = NULL; /* Last-error unresolved symbol count */ static int err_unresolved_count = 0; /* The list of symbol tables */ static dxe_symbol_table **symtabs = NULL; /* Number of symbol table & max space allocated for symbol tables */ static int nsymtab = 0, nmaxsymtab = 0; /* The chained list of linked modules */ static dxe_h dxe_chain = NULL; static void _dxefree (dxe_h dxe) { free (dxe->fname); free (dxe->section); free (dxe->header); free (dxe->exp_table); free (dxe); } dxe_h dlopen (const char *filename, int mode) { dxe2_header dxehdr; /* The header of DXE module */ dxe_h cur,dxe; /* The module handle */ int i, j, fh; /* Miscelaneous variables */ size_t hdrsize; /* DXE header size plus tables */ char *scan; /* Work variable */ char realfn [FILENAME_MAX + 1]; /* Real module filename */ char tempfn [FILENAME_MAX + 1]; /* Temporary filename */ unsigned limit; /* The minimal CS limit */ err_unresolved_count = 0; errno = 0; // Find the dynamic module along the LD_LIBRARY_PATH if (access (filename, R_OK) != 0) { char *nextscan; size_t fnl = strlen (filename) + 1; for (scan = getenv ("LD_LIBRARY_PATH"); scan && *scan; scan = nextscan + strspn (nextscan, "; \t")) { char *name; nextscan = strchr (scan, ';'); if (!nextscan) nextscan = strchr (scan, 0); if (nextscan - scan > FILENAME_MAX - 12) continue; memcpy (tempfn, scan, nextscan - scan); name = tempfn + (nextscan - scan); if (name [-1] != '/' && name [-1] != '\\') *name++ = '/'; memcpy (name, filename, fnl); if (access (tempfn, R_OK) == 0) { filename = tempfn; goto found; } } errno = ENOENT; return NULL; } found: _fixpath (filename, realfn); /* First of all, look through the loaded modules list */ for (cur = dxe_chain; cur; cur = cur->next) if (!strcmp (realfn, cur->fname)) { cur->inuse++; return cur; } fh = open (filename, O_RDONLY | O_BINARY); if (fh < 0) return NULL; if (read (fh, &dxehdr, sizeof (dxehdr)) < sizeof (dxehdr)) { close (fh); return NULL; } if (dxehdr.magic != DXE2_MAGIC) { errno = ENOEXEC; close (fh); return NULL; } /* O.k, allocate the module handle structure */ dxe = malloc (sizeof (dxe_handle)); dxe->fname = strdup (realfn); dxe->inuse = 1; dxe->mode = mode; dxe->n_exp_syms = dxehdr.n_exp_syms; dxe->exp_table = malloc (dxehdr.n_exp_syms * sizeof (void *)); /* Read the entire DXE header and the data section */ hdrsize = dxehdr.sec_f_offset - sizeof (dxehdr); dxe->section = malloc (dxehdr.sec_size); dxe->header = malloc (hdrsize); if ((lseek (fh, dxehdr.sym_f_offset, SEEK_SET) != dxehdr.sym_f_offset) || (read (fh, dxe->header, hdrsize) != hdrsize) || (lseek (fh, dxehdr.sec_f_offset, SEEK_SET) != dxehdr.sec_f_offset) || (read (fh, dxe->section, dxehdr.sec_f_size) != dxehdr.sec_f_size)) { _dxefree (dxe); close (fh); return NULL; } /* We don't need the file anymore */ close (fh); /* Fill the unfilled portion of code+data+bss segment with zeros */ memset (dxe->section + dxehdr.sec_f_size, 0, dxehdr.sec_size - dxehdr.sec_f_size); /* Check if CS limit is not less than the last byte of the module */ limit = (unsigned)(dxe->section + dxehdr.sec_size); if ((__dpmi_get_segment_limit (_my_cs ()) < limit) || (__dpmi_get_segment_limit (_my_ds ()) < limit)) { /* Kludge warning: I would set selector limits to `limit' but under OS/2 (at least) it doesn't allow setting selector limit above some value, but allows setting it to 4Gb. */ _crt0_startup_flags |= _CRT0_FLAG_NEARPTR; __dpmi_set_segment_limit (_my_cs (), -1); __dpmi_set_segment_limit (_my_ds (), -1); __dpmi_set_segment_limit (__djgpp_ds_alias, -1); } /* Count the size of resident header portion */ scan = dxe->header; for (i = 0; i < dxehdr.n_exp_syms; i++) scan = strchr (((exp_table_entry *)scan)->name, 0) + 1; /* Remember the size of resident portion of header */ hdrsize = scan - dxe->header; /* Allright, now we're ready to resolve all unresolved symbols */ err_unresolved_count = dxehdr.n_unres_syms; for (i = 0; i < dxehdr.n_unres_syms; i++) { unres_table_entry *ute = (unres_table_entry *)scan; long offset = (long)dlsym (RTLD_DEFAULT, ute->name); if (offset) { /* Resolve all the references to this symbol */ long *relocs = (long *)(strchr (ute->name, 0) + 1); for (j = 0; j < ute->n_rel_relocs; j++, relocs++) { char *fixaddr = dxe->section + *relocs; *(long *)fixaddr = offset - (long)fixaddr - sizeof (long); } for (j = 0; j < ute->n_abs_relocs; j++, relocs++) *(long *)(dxe->section + *relocs) += offset; err_unresolved_count--; } scan = strchr (ute->name, 0) + 1 + sizeof (long) * (ute->n_rel_relocs + ute->n_abs_relocs); } /* Are there any unresolved symbols? */ if (err_unresolved_count) { _dxefree (dxe); return NULL; } /* Apply relocations */ for (i = 0; i < dxehdr.n_relocs; i++) *(long *)(dxe->section + ((long *)scan)[i]) += (long)dxe->section; /* And now discard the transient portion of header */ dxe->header = realloc (dxe->header, hdrsize); /* Parse the header again and fill the exported names table */ scan = dxe->header; for (i = 0; i < dxehdr.n_exp_syms; i++) { dxe->exp_table [i] = (exp_table_entry *)scan; scan = strchr (dxe->exp_table [i]->name, 0) + 1; } /* Call all the constructors in dynamic module */ { void (**ctors) () = dlsym (dxe, "djgpp_first_ctor"); if (ctors) { void (**ctors_end) () = dlsym (dxe, "djgpp_last_ctor"); if (ctors_end) for (; ctors < ctors_end; ctors++) ctors [0] (); } } /* Put the dxe module in loaded modules chain */ dxe->next = dxe_chain; dxe_chain = dxe; return dxe; } int dlclose (dxe_h dxe) { if (!dxe) return -1; if (--dxe->inuse) return 0; /* Call all the destructors in dynamic module */ { void (**dtors) () = dlsym (dxe, "djgpp_first_dtor"); if (dtors) { void (**dtors_end) () = dlsym (dxe, "djgpp_last_dtor"); if (dtors_end) for (; dtors < dtors_end; dtors++) dtors [0] (); } } /* Remove the module from the list of loaded DXE modules */ { dxe_h *cur; for (cur = &dxe_chain; *cur; cur = &(*cur)->next) if (*cur == dxe) { *cur = dxe->next; break; } } _dxefree (dxe); return 0; } void *dlsym (dxe_h dxe, const char *symname) { int i, j; if (dxe == RTLD_DEFAULT) { void *sym = 0; dxe_h cur; for (i = 0; (i < nsymtab) && !sym; i++) { dxe_symbol_table *table = symtabs [i]; for (j = 0; table [j].name; j++) if (!strcmp (symname, table [j].name)) { sym = table [j].offset; break; } } for (cur = dxe_chain; cur; cur = cur->next) if (cur->mode & RTLD_GLOBAL) for (i = 0; i < cur->n_exp_syms; i++) if (!strcmp (symname, cur->exp_table [i]->name)) { sym = cur->section + cur->exp_table [i]->offset; goto modscan_done; } modscan_done: if (!sym && _symresolver) sym = _symresolver (symname); return sym; } else for (i = 0; i < dxe->n_exp_syms; i++) if (!strcmp (dxe->exp_table [i]->name, symname)) return dxe->section + dxe->exp_table [i]->offset; return NULL; } char *dlerror () { int oerrno = errno; errno = 0; if (err_unresolved_count) { static char buff [30]; itoa (err_unresolved_count, buff, 10); strcat (buff, " unresolved symbols"); err_unresolved_count = 0; return buff; } if (!oerrno) return "no error"; return strerror (oerrno); } void dlsetres (void *(*res) (const char *)) { _symresolver = res; } int dlregsym (dxe_symbol_table *symtab) { int rc; if (nsymtab >= nmaxsymtab) { nmaxsymtab += 8; symtabs = realloc (symtabs, nmaxsymtab * sizeof (dxe_symbol_table *)); } rc = nsymtab; symtabs [nsymtab++] = symtab; return rc; } int dlunregsym (dxe_symbol_table *symtab) { int i; for (i = 0; i < nsymtab; i++) if (symtabs [i] == symtab) { memcpy (&symtabs [i], &symtabs [i + 1], (--nsymtab - i) * sizeof (dxe_symbol_table *)); return i; } return -1; }