/* $Id: kLdr.c 2883 2006-11-18 11:21:33Z bird $ */ /** @file * * kLdr - The Dynamic Loader. * * Copyright (c) 2006 knut st. osmundsen * * * This file is part of kLdr. * * kLdr is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * kLdr is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with kLdr; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /** @mainpage kLdr - The Dynamic Loader * * The purpose of kLdr is to provide a generic interface for querying * information about and loading executable image modules. * * kLdr defines the term executable image to include all kinds of files that contains * binary code that can be executed on a CPU - linker objects (OBJs/Os), shared * objects (SOs), dynamic link libraries (DLLs), executables (EXEs), and all kinds * of kernel modules / device drivers (SYSs). * * kLdr provides two types of services: * -# Inspect or/and load individual modules (kLdrMod). * -# Work as a dynamic loader - construct and maintain an address space (kLdrDy). * * The kLdrMod API works on KLDRMOD structures where all the internals are exposed, while * the kLdrDy API works opque KLDRDY structures. KLDRDY are in reality simple wrappers * around KLDRMOD with some extra linking and attributes. * */ /******************************************************************************* * Header Files * *******************************************************************************/ #include "kLdr.h" #include "kLdrHlp.h" #include "kLdrInternal.h" /******************************************************************************* * Global Variables * *******************************************************************************/ /** Flag indicating whether we've initialized the loader or not. * * 0 if not initialized. * -1 if we're initializing or terminating. * 1 if we've successfully initialized it. * -2 if initialization failed. */ static int volatile g_fInitialized; /** * Initializes the loader. * @returns 0 on success, non-zero OS status code on failure. */ int kldrInit(void) { int rc; /* check we're already good. */ if (g_fInitialized == 1) return 0; /* a tiny serialization effort. */ for (;;) { if (g_fInitialized == 1) return 0; if (g_fInitialized == -2) return -1; /** @todo atomic test and set if we care. */ if (g_fInitialized == 0) { g_fInitialized = -1; break; } kldrHlpSleep(1); } /* * Do the initialization. */ rc = kldrHlpHeapInit(); if (!rc) { rc = kldrHlpSemInit(); if (!rc) { rc = kldrDyldInit(); if (!rc) { g_fInitialized = 1; return 0; } kldrHlpSemTerm(); } kldrHlpHeapTerm(); } g_fInitialized = -2; return rc; } /** * Terminates the loader. */ void kldrTerm(void) { /* can't terminate unless it's initialized. */ if (g_fInitialized != 1) return; g_fInitialized = -1; /* * Do the termination. */ kldrHlpSemTerm(); kldrHlpHeapTerm(); /* done */ g_fInitialized = 0; } /** * Compares arch+cpu some code was generated for with a arch+cpu for executing it * to see if it'll work out fine or not. * * @returns 0 if the code is compatible with the cpu. * @returns KLDR_ERR_ARCH_CPU_NOT_COMPATIBLE if the arch+cpu isn't compatible with the code. * @param enmCodeArch The architecture the code was generated for. * @param enmCodeCpu The cpu the code was generated for. * @param enmArch The architecture to run it on. * @param enmCpu The cpu to run it on. */ int kLdrCompareCpus(KLDRARCH enmCodeArch, KLDRCPU enmCodeCpu, KLDRARCH enmArch, KLDRCPU enmCpu) { /* * Compare arch and cpu. */ if (enmCodeArch != enmArch) return KLDR_ERR_ARCH_CPU_NOT_COMPATIBLE; /* exact match is nice. */ if (enmCodeCpu == enmCpu) return 0; switch (enmArch) { case KLDRARCH_X86_16: if (enmCpu < KLDRCPU_FIRST_X86_16 || enmCpu > KLDRCPU_LAST_X86_16) return KLDR_ERR_INVALID_PARAMETER; /* intel? */ if (enmCodeCpu <= KLDRCPU_CORE2_16) { /* also intel? */ if (enmCpu <= KLDRCPU_CORE2_16) return enmCodeCpu <= enmCpu ? 0 : KLDR_ERR_ARCH_CPU_NOT_COMPATIBLE; switch (enmCpu) { case KLDRCPU_K6_16: return enmCodeCpu <= KLDRCPU_I586 ? 0 : KLDR_ERR_ARCH_CPU_NOT_COMPATIBLE; case KLDRCPU_K7_16: case KLDRCPU_K8_16: default: return enmCodeCpu <= KLDRCPU_I686 ? 0 : KLDR_ERR_ARCH_CPU_NOT_COMPATIBLE; } } /* amd */ return enmCpu >= KLDRCPU_K6_16 && enmCpu <= KLDRCPU_K8_16 ? 0 : KLDR_ERR_ARCH_CPU_NOT_COMPATIBLE; case KLDRARCH_X86_32: if (enmCpu < KLDRCPU_FIRST_X86_32 || enmCpu > KLDRCPU_LAST_X86_32) return KLDR_ERR_INVALID_PARAMETER; /* blend? */ if (enmCodeCpu == KLDRCPU_X86_32_BLEND) return 0; /* intel? */ if (enmCodeCpu <= KLDRCPU_CORE2_32) { /* also intel? */ if (enmCpu <= KLDRCPU_CORE2_32) return enmCodeCpu <= enmCpu ? 0 : KLDR_ERR_ARCH_CPU_NOT_COMPATIBLE; switch (enmCpu) { case KLDRCPU_K6: return enmCodeCpu <= KLDRCPU_I586 ? 0 : KLDR_ERR_ARCH_CPU_NOT_COMPATIBLE; case KLDRCPU_K7: case KLDRCPU_K8_32: default: return enmCodeCpu <= KLDRCPU_I686 ? 0 : KLDR_ERR_ARCH_CPU_NOT_COMPATIBLE; } } /* amd */ return enmCpu >= KLDRCPU_K6 && enmCpu <= KLDRCPU_K8_32 ? 0 : KLDR_ERR_ARCH_CPU_NOT_COMPATIBLE; case KLDRARCH_AMD64: if (enmCpu < KLDRCPU_FIRST_AMD64 || enmCpu > KLDRCPU_LAST_AMD64) return KLDR_ERR_INVALID_PARAMETER; /* blend? */ if (enmCodeCpu == KLDRCPU_AMD64_BLEND) return 0; /* this is simple for now. */ return KLDR_ERR_ARCH_CPU_NOT_COMPATIBLE; default: break; } return KLDR_ERR_ARCH_CPU_NOT_COMPATIBLE; } /** * Gets the arch+cpu of the calling cpu. * * @param penmArch Where to store the cpu architecture. * @param penmCpu Where to store the cpu brand/model. */ void kLdrGetArchCpu(PKLDRARCH penmArch, PKLDRCPU penmCpu) { #ifdef __AMD64__ *penmArch = KLDRARCH_AMD64; *penmCpu = KLDRCPU_AMD64_BLEND; /** @todo check it using cpu. */ #elif defined(__X86__) *penmArch = KLDRARCH_X86_32; *penmCpu = KLDRCPU_X86_32_BLEND; /** @todo check it using cpu. */ #else # error "Port me" #endif }