Goto sanos source index

//
// ldr.c
//
// Kernel module loader
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 
// 1. Redistributions of source code must retain the above copyright 
//    notice, this list of conditions and the following disclaimer.  
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.  
// 3. Neither the name of the project nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission. 
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
// SUCH DAMAGE.
// 

#include <os/krnl.h>

struct moddb kmods;
struct mutex ldr_lock;

static void *load_image(char *filename) {
  return load_image_file(filename, 0);
}

static int unload_image(hmodule_t hmod, size_t size) {
  free_module_mem(hmod, PAGES(size));
  return 0;
}

void *load_image_file(char *filename, int userspace) {
  struct file *f;
  char *buffer;
  char *imgbase;
  struct dos_header *doshdr;
  struct image_header *imghdr;
  int i;
  unsigned int bytes;

  //kprintf("ldr: loading module %s\n", filename);

  // Allocate header buffer
  buffer = kmalloc(PAGESIZE);
  if (!buffer) return NULL;

  // Open file
  if (open(filename, O_RDONLY | O_BINARY, 0, &f) < 0) {
    kfree(buffer);
    return NULL;
  }

  // Read headers
  if ((bytes = read(f, buffer, PAGESIZE)) < 0) {
    close(f);
    destroy(f);
    kfree(buffer);
    return NULL;
  }
  doshdr = (struct dos_header *) buffer;
  imghdr = (struct image_header *) (buffer + doshdr->e_lfanew);

  // Check PE file signature
  if (doshdr->e_lfanew > bytes || imghdr->signature != IMAGE_PE_SIGNATURE) panic("invalid PE signature");

  // Check alignment
  //if (imghdr->optional.file_alignment != PAGESIZE || imghdr->optional.section_alignment != PAGESIZE) panic("image not page aligned");

  // Allocate memory for module
  if (userspace) {
    // User module
    imgbase = (char *) vmalloc((void *) (imghdr->optional.image_base), imghdr->optional.size_of_image, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE, 'UMOD', NULL);
    if (imgbase == NULL) {
      // Try to load image at any available address 
      imgbase = (char *) vmalloc(NULL, imghdr->optional.size_of_image, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE, 'UMOD', NULL);
    }
  } else {
    // Kernel module
    imgbase = (char *) alloc_module_mem(PAGES(imghdr->optional.size_of_image));
    if (imgbase) memset(imgbase, 0, PAGES(imghdr->optional.size_of_image) * PAGESIZE);
  }

  if (imgbase == NULL) {
    close(f);
    destroy(f);
    kfree(buffer);
    return NULL;
  }

  // Copy header to image
  memcpy(imgbase, buffer, PAGESIZE);

  // Read sections
  for (i = 0; i < imghdr->header.number_of_sections; i++) {
    if (imghdr->sections[i].pointer_to_raw_data != 0) {
      lseek(f, imghdr->sections[i].pointer_to_raw_data, SEEK_SET);
      if (read(f, RVA(imgbase, imghdr->sections[i].virtual_address), imghdr->sections[i].size_of_raw_data) < 0) {
        if (userspace) {
          vmfree(imgbase, imghdr->optional.size_of_image, MEM_RELEASE);
        } else {
          free_module_mem(imgbase, imghdr->optional.size_of_image);
        }

        close(f);
        destroy(f);
        kfree(buffer);
        return NULL;
      }
    }
  }

  //kprintf("image %s loaded at %p (%d KB)\n", filename, imgbase, imghdr->optional.size_of_image / 1024);

  // Close file
  close(f);
  destroy(f);
  kfree(buffer);

  return imgbase;
}

static void logldr(char *msg) {
  kprintf(KERN_WARNING "ldr: %s\n", msg);
}

void *resolve(hmodule_t hmod, char *procname) {
  void *addr;

  wait_for_object(&ldr_lock, INFINITE);
  addr = get_proc_address(hmod, procname);
  release_mutex(&ldr_lock);
  return addr;
}

hmodule_t getmodule(char *name) {
  hmodule_t hmod;

  wait_for_object(&ldr_lock, INFINITE);
  hmod = get_module_handle(&kmods, name);
  release_mutex(&ldr_lock);
  return hmod;
}

int getmodpath(hmodule_t hmod, char *buffer, int size) {
  int rc;

  wait_for_object(&ldr_lock, INFINITE);
  rc = get_module_filename(&kmods, hmod, buffer, size);
  release_mutex(&ldr_lock);
  return rc;
}

hmodule_t load(char *name, int flags) {
  hmodule_t hmod;

  wait_for_object(&ldr_lock, INFINITE);
  hmod = load_module(&kmods, name, flags);
  release_mutex(&ldr_lock);
  return hmod;
}

int unload(hmodule_t hmod) {
  int rc;

  wait_for_object(&ldr_lock, INFINITE);
  rc = unload_module(&kmods, hmod);
  release_mutex(&ldr_lock);
  return rc;
}

void *getentrypoint(hmodule_t hmod) {
  return get_entrypoint(hmod);
}

static int dump_mods(struct proc_file *pf, struct moddb *moddb) {
  struct module *mod = moddb->modules;

  pprintf(pf, "handle   module           refs entry      size   text   data    bss\n");
  pprintf(pf, "-------- ---------------- ---- --------  -----  -----  -----  -----\n");

  while (1) {
    struct image_header *imghdr = get_image_header(mod->hmod);

    pprintf(pf, "%08X %-16s %4d %08X %5dK %5dK %5dK %5dK\n", 
            mod->hmod, mod->name, mod->refcnt, 
            get_entrypoint(mod->hmod),
            imghdr->optional.size_of_image / 1024,
            imghdr->optional.size_of_code / 1024,
            imghdr->optional.size_of_initialized_data / 1024,
            imghdr->optional.size_of_uninitialized_data / 1024
            );

    mod = mod->next;
    if (mod == moddb->modules) break;
  }

  return 0;
}

static int kmods_proc(struct proc_file *pf, void *arg) {
  return dump_mods(pf, &kmods);
}

static int umods_proc(struct proc_file *pf, void *arg) {
  if (!page_mapped((void *) PEB_ADDRESS)) return -EFAULT;
  if (!((struct peb *) PEB_ADDRESS)->usermods) return -EFAULT;

  return dump_mods(pf, ((struct peb *) PEB_ADDRESS)->usermods);
}

void init_kernel_modules() {
  struct module *krnlmod;

  init_mutex(&ldr_lock, 0);

  kmods.read_magic = NULL;
  kmods.load_image = load_image;
  kmods.unload_image = unload_image;
  kmods.protect_region = NULL;
  kmods.log = logldr;
  kmods.notify_load = dbg_notify_load_module;
  kmods.notify_unload = dbg_notify_unload_module;

  init_module_database(&kmods, "krnl.dll", (hmodule_t) OSBASE, get_property(krnlcfg, "kernel", "libpath", "/boot"), find_section(krnlcfg, "modaliases"), 0);

  register_proc_inode("kmods", kmods_proc, NULL);
  register_proc_inode("umods", umods_proc, NULL);

  krnlmod = kmods.modules;
  set_pageframe_tag(krnlmod->hmod, get_image_header(krnlmod->hmod)->optional.size_of_image, 'KMOD');
}