aboutsummaryrefslogtreecommitdiff
path: root/user/lib/ld-weenix/ldstart.c
diff options
context:
space:
mode:
Diffstat (limited to 'user/lib/ld-weenix/ldstart.c')
-rw-r--r--user/lib/ld-weenix/ldstart.c573
1 files changed, 573 insertions, 0 deletions
diff --git a/user/lib/ld-weenix/ldstart.c b/user/lib/ld-weenix/ldstart.c
new file mode 100644
index 0000000..85fe4ec
--- /dev/null
+++ b/user/lib/ld-weenix/ldstart.c
@@ -0,0 +1,573 @@
+/*
+ * File: ldstart.c
+ * Date: 14 March 1998
+ * Acct: David Powell (dep)
+ * Desc: A run time linker
+ *
+ *
+ * Acct: Rob Manchester (rmanches)
+ * Desc: Added x86 Elf Compatability
+ *
+ *
+ * Acct: Sandy Harvie (charvie)
+ * Date: 27 March 2019
+ * Desc: Modified for x86-64
+ */
+
+#include "fcntl.h"
+#include "stdio.h"
+#include "stdlib.h"
+#include "string.h"
+#include "sys/mman.h"
+#include "sys/types.h"
+#include "unistd.h"
+
+#include "elf.h"
+
+#include "ldalloc.h"
+#include "ldnames.h"
+#include "ldutil.h"
+
+#ifndef DEFAULT_RUNPATH
+#define DEFAULT_RUNPATH "/lib:/usr/lib"
+#endif
+
+extern int _ldbindnow(module_t *curmod);
+
+static const char *default_runpath = DEFAULT_RUNPATH;
+
+static const char *err_cantfind =
+ "ld.so.1: panic - unable to find library \"%s\"\n";
+static const char *err_noentry = "ld.so.1: panic - no entry point\n";
+static const char *err_mapping =
+ "ld.so.1: panic - failure to map section of length 0x%x at 0x%x\n";
+static const char *err_zeromap = "ld.so.1: panic - failure to map /dev/zero\n";
+
+static module_t *_ldfirst;
+static module_t **_ldlast;
+
+static size_t pagesize;
+static char **env;
+ldenv_t _ldenv;
+
+#define trunc_page(x) ((x) & ~(pagesize - 1))
+#define round_page(x) (((x) + pagesize - 1) & ~(pagesize - 1))
+
+static const char *_ldgetenv(const char *var)
+{
+ char **e = env;
+ while (*e)
+ {
+ const char *p = *e;
+ while (*p == *var)
+ p++, var++;
+ if (*p == '=' && *var == 0)
+ {
+ return p++;
+ }
+ e++;
+ }
+ return 0;
+}
+
+static void _ldenv_init(char **environ)
+{
+ env = environ;
+ if (_ldgetenv("LD_BIND_NOW"))
+ {
+ _ldenv.ld_bind_now = 1;
+ }
+ if (_ldgetenv("LD_DEBUG"))
+ {
+ _ldenv.ld_debug = 1;
+ }
+ _ldenv.ld_preload = _ldgetenv("LD_PRELOAD");
+ _ldenv.ld_library_path = _ldgetenv("LD_LIBRARY_PATH");
+}
+
+static module_t *_ldlinkobj(module_t *info, const char *baseaddr,
+ Elf64_Dyn *dyn)
+{
+ Elf64_Dyn *curdyn;
+ module_t **curmod;
+ char *name;
+
+ /* uint64_t d_needed = 0; */
+ uint64_t d_rpath = 0;
+ uint64_t d_relocsz = 0;
+ uint64_t d_relocent = 0;
+
+ uint64_t d_plttype = 0;
+ uint64_t d_pltsize = 0;
+
+ /* create an info structure if we weren't passed one */
+ if (!info)
+ {
+ info = (module_t *)_ldalloc(sizeof(*info));
+ memset(info, 0, sizeof(*info));
+ _ldfirst = info->first = info;
+ curmod = &(info->next);
+ }
+ else
+ {
+ curmod = _ldlast;
+ }
+
+ info->base = (unsigned long)baseaddr;
+
+ for (curdyn = dyn; curdyn->d_tag != DT_NULL; curdyn++)
+ {
+ switch (curdyn->d_tag)
+ {
+ case DT_HASH:
+ info->hash = (void *)(info->base + curdyn->d_un.d_ptr);
+ break;
+ case DT_SYMTAB:
+ info->dynsym = (void *)(info->base + curdyn->d_un.d_ptr);
+ break;
+ case DT_STRTAB:
+ info->dynstr = (char *)(info->base + curdyn->d_un.d_ptr);
+ break;
+ case DT_JMPREL:
+ info->pltreloc = (void *)(info->base + curdyn->d_un.d_ptr);
+ break;
+ case DT_RELA:
+ case DT_REL:
+ info->reloc = (void *)(info->base + curdyn->d_un.d_ptr);
+ break;
+ case DT_INIT:
+ info->init = (ldfunc_t)(info->base + curdyn->d_un.d_ptr);
+ break;
+ case DT_FINI:
+ info->fini = (ldfunc_t)(info->base + curdyn->d_un.d_ptr);
+ break;
+ case DT_NEEDED:
+ /* d_needed = curdyn->d_un.d_val; */
+ break;
+ case DT_RPATH:
+ d_rpath = curdyn->d_un.d_val;
+ break;
+ case DT_PLTGOT:
+ info->pltgot = (void *)(info->base + curdyn->d_un.d_ptr);
+ break;
+ case DT_PLTRELSZ:
+ d_pltsize = curdyn->d_un.d_val;
+ break;
+ case DT_PLTREL:
+ d_plttype = curdyn->d_un.d_val;
+ break;
+ case DT_BIND_NOW:
+ _ldenv.ld_bind_now = 1;
+ break;
+ case DT_RELENT:
+ case DT_RELAENT:
+ d_relocent = curdyn->d_un.d_val;
+ break;
+ case DT_RELSZ:
+ case DT_RELASZ:
+ d_relocsz = curdyn->d_un.d_val;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (info->reloc)
+ {
+ info->nreloc = d_relocsz / d_relocent;
+ }
+
+ if (info->pltreloc)
+ {
+ /* bytes per element */
+ size_t bpe =
+ d_plttype == DT_REL ? sizeof(Elf64_Rel) : sizeof(Elf64_Rela);
+ info->npltreloc = d_pltsize / bpe;
+ }
+
+ /* Set up plt */
+ if (info->pltgot)
+ {
+ _ldpltgot_init(info);
+ }
+
+ /* create modules for dependencies */
+ for (curdyn = dyn; curdyn->d_tag != DT_NULL; curdyn++)
+ {
+ if (curdyn->d_tag == DT_NEEDED)
+ {
+ name = info->dynstr + curdyn->d_un.d_val;
+ if (_ldchkname(name))
+ break;
+ _ldaddname(name);
+ *curmod = (module_t *)_ldalloc(sizeof(module_t));
+ (**curmod).name = name;
+ _ldaddname((**curmod).name);
+ if (d_rpath)
+ {
+ (**curmod).runpath = info->dynstr + d_rpath;
+ }
+ else
+ {
+ (**curmod).runpath = NULL;
+ }
+ (**curmod).next = NULL;
+ (**curmod).first = _ldfirst;
+ curmod = &((**curmod).next);
+ }
+ }
+ _ldlast = curmod;
+
+ return info;
+}
+
+/* Given a filename and a colon-delimited path, this function attempts
+ * to open the named file using each element of the path as a prefix
+ * for the file. The result of the first successful open is returned,
+ * otherwise -1 is returned */
+
+int _ldtryopen(const char *filename, const char *path)
+{
+ char buffer[2048]; /* shouldn't be overflown */
+ const char *pos, *oldpos;
+ size_t len, flen;
+ int fd;
+
+ if (!path || !*path)
+ return -1;
+
+ flen = strlen(filename) + 1;
+
+ oldpos = pos = path;
+
+ /* ADDED: try w/ no prefix first */
+ strncpy(buffer, filename, flen);
+ fd = open(buffer, O_RDONLY, 0);
+ if (fd >= 0)
+ {
+ return fd;
+ }
+ /* END ADDED */
+
+ while (*pos)
+ {
+ while (*pos && *pos != ':')
+ pos++;
+
+ len = pos - oldpos;
+ strncpy(buffer, oldpos, len + 1);
+ buffer[len] = '/';
+ strncpy(buffer + len + 1, filename, flen);
+
+ fd = open(buffer, O_RDONLY, 0);
+ if (fd >= 0)
+ {
+ return fd;
+ }
+
+ oldpos = ++pos;
+ }
+
+ return -1;
+}
+
+/* This function maps the specified section of a shared library. It
+ * also handles the special cases involving the bss and other
+ * anonymously mapped areas. */
+
+/*
+ * ----------------------- top
+ * | | (+phdr->p_memsz)
+ * | anonymous mapping |
+ * | (bss) |
+ * | |
+ * ----------------------- mid2
+ * ----------------------- ztop
+ * | zeroed out file |
+ * ----------------------- zbegin (+phdr->p_filesz)
+ * | | mid1
+ * | mapped file |
+ * | |
+ * | |
+ * ----------------------- bottom (+0)
+ */
+
+void _ldmapsect(int fd, unsigned long baseaddr, Elf64_Phdr *phdr, int textrel)
+{
+ uintptr_t vmaddr = ((uintptr_t)phdr->p_vaddr) + baseaddr;
+ uintptr_t offset = phdr->p_offset;
+ uintptr_t memsz = phdr->p_memsz;
+ uintptr_t filsz = phdr->p_filesz;
+
+ uintptr_t map_addr = trunc_page(vmaddr);
+ uintptr_t file_addr = trunc_page(offset);
+ uintptr_t map_len;
+ uintptr_t copy_len;
+ int perms = 0;
+
+ if (phdr->p_flags & PF_R)
+ perms |= PROT_READ;
+ if (phdr->p_flags & PF_W)
+ perms |= PROT_WRITE;
+ if (phdr->p_flags & PF_X)
+ perms |= PROT_EXEC;
+
+ /* Check if read-only sections will need relocation */
+ if (textrel)
+ perms |= PROT_WRITE;
+
+ if (memsz > filsz)
+ {
+ map_len = trunc_page(offset + filsz) - file_addr;
+ }
+ else
+ {
+ map_len = round_page(offset + filsz) - file_addr;
+ }
+
+ if (map_len != 0)
+ {
+ if (mmap((char *)map_addr, map_len, perms,
+ ((perms & PROT_WRITE) ? MAP_PRIVATE : MAP_SHARED) | MAP_FIXED,
+ fd, file_addr) == MAP_FAILED)
+ {
+ printf(err_mapping, map_len, map_addr);
+ exit(1);
+ }
+ }
+
+ if (memsz == filsz)
+ {
+ return;
+ }
+
+ file_addr = trunc_page(offset + filsz);
+ copy_len = (offset + filsz) - file_addr;
+ map_addr = trunc_page(vmaddr + filsz);
+ map_len = round_page(vmaddr + memsz) - map_addr;
+
+ if (map_len != 0)
+ {
+ void *addr;
+ int zfd = _ldzero();
+ addr = mmap((char *)map_addr, map_len, perms, MAP_PRIVATE | MAP_FIXED,
+ zfd, 0);
+ if (addr == MAP_FAILED)
+ {
+ printf("%s", err_zeromap);
+ exit(1);
+ }
+ close(zfd);
+
+ if (copy_len != 0)
+ {
+ lseek(fd, file_addr, SEEK_SET);
+ read(fd, addr, copy_len);
+ }
+ }
+}
+
+/* This function finds and maps the shared object associated with the
+ * specified module. When it is done, it calls _ldlinkobj to perform
+ * additional operations pertaining to the object's dependencies as
+ * well as the managment of the object at runtime */
+
+void _ldloadobj(module_t *module)
+{
+ unsigned long bottom, top, size;
+ Elf64_Ehdr *hdr;
+ Elf64_Phdr *phdr;
+ Elf64_Dyn *dyn = 0;
+ char *loc;
+ int fd;
+
+ /* attempt to open library */
+ fd = _ldtryopen(module->name, _ldenv.ld_library_path);
+ if (fd == -1)
+ fd = _ldtryopen(module->name, module->runpath);
+ if (fd == -1)
+ fd = _ldtryopen(module->name, default_runpath);
+ if (fd == -1)
+ {
+ printf(err_cantfind, module->name);
+ exit(1);
+ }
+
+ /* compute image size */
+ hdr = (Elf64_Ehdr *)mmap(0, pagesize, PROT_READ | PROT_EXEC, MAP_SHARED, fd,
+ 0);
+ phdr = (Elf64_Phdr *)(hdr->e_phoff + (unsigned long)hdr);
+
+ bottom = (unsigned long)-1;
+ top = 0;
+ for (size_t i = 0; i < hdr->e_phnum; i++)
+ {
+ if (phdr[i].p_type == PT_LOAD)
+ {
+ if (phdr[i].p_vaddr < bottom)
+ bottom = phdr[i].p_vaddr;
+ if (phdr[i].p_vaddr + phdr[i].p_memsz > top)
+ top = phdr[i].p_vaddr + phdr[i].p_memsz;
+ }
+ }
+
+ bottom = trunc_page(bottom);
+ top = round_page(top);
+ size = top - bottom;
+
+ loc = (char *)mmap(NULL, size, PROT_NONE, MAP_SHARED, fd, 0);
+ munmap(loc, size);
+
+ /* Figure out whether or not things marked readonly need to
+ * be writeable (find DT_TEXTREL). This is kind of a mess,
+ * as we have to do this before we've mapped in the dynamic
+ * section (need to read from file directly). */
+ off_t dynoff = 0;
+ Elf64_Dyn curdyn;
+ int textrel = 0;
+ for (size_t i = 0; i < hdr->e_phnum; i++)
+ {
+ if (phdr[i].p_type == PT_DYNAMIC)
+ {
+ dynoff = phdr[i].p_offset;
+ break;
+ }
+ }
+ lseek(fd, dynoff, SEEK_SET);
+ do
+ {
+ if ((ssize_t)sizeof(curdyn) > read(fd, &curdyn, sizeof(curdyn)))
+ exit(1);
+
+ if (curdyn.d_tag == DT_TEXTREL)
+ {
+ textrel = 1;
+ break;
+ }
+ } while (curdyn.d_tag != DT_NULL);
+
+ for (size_t i = 0; i < hdr->e_phnum; i++)
+ {
+ if (phdr[i].p_type == PT_LOAD)
+ _ldmapsect(fd, (unsigned long)loc - bottom, phdr + i, textrel);
+ else if (phdr[i].p_type == PT_DYNAMIC)
+ dyn = (Elf64_Dyn *)(loc + phdr[i].p_vaddr);
+ }
+ munmap(hdr, pagesize);
+ close(fd);
+
+ /* set up additional module information */
+ _ldlinkobj(module, loc - bottom, dyn);
+}
+
+void _ldcleanup(int status)
+{
+ module_t *curmod;
+
+ /* Call .fini functions */ /* XXX: fix ordering */
+ curmod = _ldfirst->next;
+ while (curmod)
+ {
+ if (curmod->fini)
+ curmod->fini();
+ curmod = curmod->next;
+ }
+
+ exit(status);
+}
+
+/* This is function is about as close to a 'mainline' as you will find
+ * in the linker loader. We initiate the process of
+ * evaluating and loading the dependencies of the executable. Next we
+ * relocate all the loaded modules, followed by calling the _init
+ * function of all the dependencies. Lastly, we return the entry point
+ * to the calling function (the bootstrap code), which runs the now
+ * linked process
+ */
+
+ldinit_t _ldstart(char **environ, auxv_t *auxv)
+{
+ uint64_t abuf[10];
+ module_t *curmod;
+ Elf64_Phdr *phdr;
+
+ /* Populate the auxv array */
+ memset(abuf, 0, 10 * sizeof(unsigned long));
+ for (size_t i = 0; auxv[i].a_type != AT_NULL; i++)
+ {
+ if (auxv[i].a_type < 10)
+ {
+ abuf[auxv[i].a_type] = (uint64_t)auxv[i].a_un.a_val;
+ }
+ }
+
+ pagesize = abuf[AT_PAGESZ];
+
+ /* Set up memory pool */
+ _ldainit(pagesize, 1);
+
+ _ldenv_init(environ);
+ /* Load the executable and all of it's dependencies */
+ phdr = (Elf64_Phdr *)abuf[AT_PHDR];
+
+ for (size_t i = 0; i < abuf[AT_PHNUM]; i++)
+ {
+ if (phdr[i].p_type == PT_DYNAMIC)
+ {
+ _ldlinkobj(NULL, (char *)0, (Elf64_Dyn *)phdr[i].p_vaddr);
+ break;
+ }
+ }
+
+ curmod = _ldfirst->next;
+ while (curmod)
+ {
+ _ldloadobj(curmod);
+ curmod = curmod->next;
+ }
+
+ /* Perform all necessary relocations */
+ /* We relocate the current module (executable) last, as it is the only one
+ * that will contain R_386_COPY entries, and we need to make sure the things
+ * being copied are correctly relocated (they are probably R_386_RELATIVE)
+ * prior to copying */
+ curmod = _ldfirst->next; /* Assume at least one module... */
+ while (curmod)
+ {
+ _ldrelocobj(curmod);
+ curmod = curmod->next;
+ }
+ _ldrelocobj(_ldfirst);
+
+ curmod = _ldfirst;
+ while (curmod)
+ {
+ _ldrelocplt(curmod);
+ curmod = curmod->next;
+ }
+
+ if (_ldenv.ld_bind_now)
+ {
+ curmod = _ldfirst;
+ while (curmod)
+ {
+ _ldbindnow(curmod);
+ curmod = curmod->next;
+ }
+ }
+
+ /* Call .init functions */ /* XXX: fix ordering */
+ curmod = _ldfirst->next;
+ while (curmod)
+ {
+ if (curmod->init)
+ {
+ curmod->init();
+ }
+ curmod = curmod->next;
+ }
+
+ /* Jump to the linkee's entry point */
+ _ldverify(!abuf[AT_ENTRY], err_noentry);
+ return (ldinit_t)abuf[AT_ENTRY];
+}