Index: /trunk/kLdr/kLdr.h
===================================================================
--- /trunk/kLdr/kLdr.h	(revision 2881)
+++ /trunk/kLdr/kLdr.h	(revision 2882)
@@ -1081,8 +1081,16 @@
 /** Bad iterdata2 (EXEPACK2) data. */
 #define KLDR_ERR_LX_BAD_ITERDATA2                           (KLDR_ERR_LX_BASE + 6)
+/** Bad bundle data. */
+#define KLDR_ERR_LX_BAD_BUNDLE                              (KLDR_ERR_LX_BASE + 7)
+/** No soname. */
+#define KLDR_ERR_LX_NO_SONAME                               (KLDR_ERR_LX_BASE + 8)
+/** Bad soname. */
+#define KLDR_ERR_LX_BAD_SONAME                              (KLDR_ERR_LX_BASE + 9)
+/** Bad forwarder entry. */
+#define KLDR_ERR_LX_BAD_FORWARDER                           (KLDR_ERR_LX_BASE + 10)
 /** @} */
 
 /** End of the valid kLdr status codes. */
-#define KLDR_ERR_END                                        (KLDR_ERR_LX_BASE + 7)
+#define KLDR_ERR_END                                        (KLDR_ERR_LX_BASE + 11)
 
 /** @} */
Index: /trunk/kLdr/kLdrModLX.c
===================================================================
--- /trunk/kLdr/kLdrModLX.c	(revision 2881)
+++ /trunk/kLdr/kLdrModLX.c	(revision 2882)
@@ -117,4 +117,8 @@
                                  PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser);
 static int kldrModLXDoCreate(PKLDRRDR pRdr, off_t offNewHdr, PKLDRMODLX *ppModLX);
+static const uint8_t *kldrModLXDoNameTableLookupByOrdinal(const uint8_t *pbNameTable, int32_t cbNameTable, uint32_t iOrdinal);
+static int kldrModLXDoNameLookup(PKLDRMODLX pModLX, const char *pszSymbol, uint32_t *piSymbol);
+static const uint8_t *kldrModLXDoNameTableLookupByName(const uint8_t *pbNameTable, int32_t cbNameTable,
+                                                       char *pchSymbol, size_t cchSymbol);
 static int kldrModLXDoLoadBits(PKLDRMODLX pModLX, void *pvBits);
 static int kldrModLXDoIterDataUnpacking(uint8_t *pbDst, const uint8_t *pbSrc, int cbSrc);
@@ -123,4 +127,8 @@
 static int kldrModLXDoProtect(PKLDRMODLX pModLX, void *pvBits, unsigned fUnprotectOrProtect);
 static int kldrModLXDoCallDLL(PKLDRMODLX pModLX, unsigned uOp, uintptr_t uHandle);
+static int32_t kldrModLXDoGetOrdinalByName(PKLDRMODLX pModLX, const char *pszName);
+static int kldrModLXDoForwarderQuery(PKLDRMODLX pModLX, const struct e32_entry *pEntry,
+                                     PFNKLDRMODGETIMPORT pfnGetForwarder, void *pvUser, PKLDRADDR puValue, uint32_t *pfKind);
+static int kldrModLXDoLoadFixupSection(PKLDRMODLX pModLX);
 static int32_t kldrModLXDoCall(uintptr_t uEntrypoint, uintptr_t uHandle, uint32_t uOp, void *pvReserved);
 
@@ -355,6 +363,16 @@
     /*
      * Get the soname from the resident name table.
-     */
-    /** @todo */
+     * Very convenient that it's the 0 ordinal, because then we get a
+     * free string terminator.
+     * (The table entry consists of a pascal string followed by a 16-bit ordinal.)
+     */
+    pMod->pszName = kldrModLXDoNameTableLookupByOrdinal(pModLX->pbResNameTab,
+                                                        pModLX->pbLoaderSectionLast - pModLX->pbResNameTab + 1,
+                                                        0);
+    if (!pMod->pszName)
+        return KLDR_ERR_LX_NO_SONAME;
+    pMod->cchName = *(const uint8_t *)pMod->pszName++;
+    if (pMod->cchName != kLdrHlpStrLen(pMod->pszName))
+        return KLDR_ERR_LX_BAD_SONAME;
 
     /*
@@ -484,9 +502,4 @@
         pModLX->pbFixupSection = NULL;
     }
-    if (pMod->pszName)
-    {
-        kldrHlpFree((void *)pMod->pszName);
-        pMod->pszName = NULL;
-    }
     pMod->u32Magic = 0;
     pMod->pOps = NULL;
@@ -516,9 +529,349 @@
                                 PKLDRADDR puValue, uint32_t *pfKind)
 {
-    PKLDRMODLX                      pModLX = (PKLDRMODLX)pMod->pvData;
-    //int                             rc;
-
-    kldrModLXResolveBaseAddress(pModLX, &BaseAddress);
-
+    PKLDRMODLX                  pModLX = (PKLDRMODLX)pMod->pvData;
+    uint32_t                    iOrdinal;
+    int                         rc;
+    const struct b32_bundle     *pBundle;
+
+
+    /*
+     * Give up at once if there is no entry table.
+     */
+    if (!pModLX->Hdr.e32_enttab)
+        return KLDR_ERR_SYMBOL_NOT_FOUND;
+
+    /*
+     * Translate the symbol name into an ordinal.
+     */
+    if (pszSymbol)
+    {
+        rc = kldrModLXDoNameLookup(pModLX, pszSymbol, &iSymbol);
+        if (rc)
+            return rc;
+    }
+
+    /*
+     * Iterate the entry table.
+     * (The entry table is made up of bundles of similar exports.)
+     */
+    iOrdinal = 0;
+    pBundle = (const struct b32_bundle *)pModLX->pbEntryTab;
+    while (!pBundle->b32_cnt && iOrdinal <= iSymbol)
+    {
+        static const size_t s_cbEntry[] = { 0, 3, 5, 5, 7 };
+
+        /*
+         * Check for a hit first.
+         */
+        iOrdinal += pBundle->b32_cnt;
+        if (iSymbol < iOrdinal)
+        {
+            uint32_t offObject;
+            const struct e32_entry *pEntry = (const struct e32_entry *)((uintptr_t)(pBundle + 1)
+                                                                        + iSymbol - (iOrdinal - pBundle->b32_cnt));
+
+            /*
+             * Calculate the return address.
+             */
+            kldrModLXResolveBaseAddress(pModLX, &BaseAddress);
+            switch (pBundle->b32_type)
+            {
+                /* empty bundles are place holders unused ordinal ranges. */
+                case EMPTY:
+                    return KLDR_ERR_SYMBOL_NOT_FOUND;
+
+                /* e32_flags + a 16-bit offset. */
+                case ENTRY16:
+                    offObject = pEntry->e32_variant.e32_offset.offset16;
+                    if (pfKind)
+                        *pfKind = KLDRSYMKIND_16BIT | KLDRSYMKIND_NO_TYPE;
+                    break;
+
+                /* e32_flags + a 16-bit offset + a 16-bit callgate selector. */
+                case GATE16:
+                    offObject = pEntry->e32_variant.e32_callgate.offset;
+                    if (pfKind)
+                        *pfKind = KLDRSYMKIND_16BIT | KLDRSYMKIND_CODE;
+                    break;
+
+                /* e32_flags + a 32-bit offset. */
+                case ENTRY32:
+                    offObject = pEntry->e32_variant.e32_offset.offset32;
+                    if (pfKind)
+                        *pfKind = KLDRSYMKIND_32BIT;
+                    break;
+
+                /* e32_flags + 16-bit import module ordinal + a 32-bit procname or ordinal. */
+                case ENTRYFWD:
+                    return kldrModLXDoForwarderQuery(pModLX, pEntry, pfnGetForwarder, pvUser, puValue, pfKind);
+
+                default:
+                    /* anyone actually using TYPEINFO will end up here. */
+                    KLDRMODLX_ASSERT(!"Bad bundle type");
+                    break;
+            }
+
+            /*
+             * Validate the object number and calc the return address.
+             */
+            if (    pBundle->b32_obj <= 0
+                ||  pBundle->b32_obj > pMod->cSegments)
+                return KLDR_ERR_LX_BAD_BUNDLE;
+            if (puValue)
+                *puValue = BaseAddress
+                         + offObject
+                         + pMod->aSegments[pBundle->b32_obj - 1].RVA;
+            return 0;
+        }
+
+        /*
+         * Skip the bundle.
+         */
+        if (pBundle->b32_type > ENTRYFWD)
+        {
+            KLDRMODLX_ASSERT(!"Bad type"); /** @todo figure out TYPEINFO. */
+            return KLDR_ERR_LX_BAD_BUNDLE;
+        }
+        if (pBundle->b32_type == 0)
+            pBundle = (const struct b32_bundle *)((const uint8_t *)pBundle + 2);
+        else
+            pBundle = (const struct b32_bundle *)((const uint8_t *)pBundle + s_cbEntry[pBundle->b32_type] * pBundle->b32_cnt);
+    }
+
+    return KLDR_ERR_SYMBOL_NOT_FOUND;
+}
+
+
+/**
+ * Do name lookup.
+ *
+ * @returns See kLdrModQuerySymbol.
+ * @param   pModLX      The module to lookup the symbol in.
+ * @param   pszSymbol   The symbol to lookup.
+ * @param   piSymbol    Where to store the symbol ordinal.
+ */
+static int kldrModLXDoNameLookup(PKLDRMODLX pModLX, const char *pszSymbol, uint32_t *piSymbol)
+{
+
+    /*
+     * First do a hash table lookup.
+     */
+
+
+
+    /*
+
+    */
+
+    return -1;
+}
+
+
+/**
+ * Hash a symbol using the algorithm from sdbm.
+ *
+ * The following was is the documenation of the orignal sdbm functions:
+ *
+ * This algorithm was created for sdbm (a public-domain reimplementation of
+ * ndbm) database library. it was found to do well in scrambling bits,
+ * causing better distribution of the keys and fewer splits. it also happens
+ * to be a good general hashing function with good distribution. the actual
+ * function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
+ * is the faster version used in gawk. [there is even a faster, duff-device
+ * version] the magic constant 65599 was picked out of thin air while
+ * experimenting with different constants, and turns out to be a prime.
+ * this is one of the algorithms used in berkeley db (see sleepycat) and
+ * elsewhere.
+ */
+static uint32_t kldrModLXDoHash(const char *pchSymbol, uint8_t cchSymbol)
+{
+    uint32_t hash = 0;
+    int ch;
+
+    while (     cchSymbol-- > 0
+           &&   (ch = *(unsigned const char *)pchSymbol++))
+        hash = ch + (hash << 6) + (hash << 16) - hash;
+
+    return hash;
+}
+
+
+/**
+ * Lookup a name table entry by name.
+ *
+ * @returns Pointer to the name table entry if found.
+ * @returns NULL if not found.
+ * @param   pbNameTable     Pointer to the name table that should be searched.
+ * @param   cbNameTable     The size of the name table.
+ * @param   pchSymbol       The name of the symbol we're looking for.
+ * @param   cchSymbol       The length of the symbol name.
+ */
+static const uint8_t *kldrModLXDoNameTableLookupByName(const uint8_t *pbNameTable, int32_t cbNameTable,
+                                                       char *pchSymbol, size_t cchSymbol)
+{
+    /*
+     * Determin the namelength up front so we can skip anything which doesn't matches the length.
+     */
+    uint8_t         cbSymbol8Bit = (uint8_t)cchSymbol;
+    if (cbSymbol8Bit != cchSymbol)
+        return NULL; /* too long. */
+
+    /*
+     * Walk the name table.
+     */
+    while (*pbNameTable != 0 && cbNameTable > 0)
+    {
+        const uint8_t   cbName = *pbNameTable;
+
+        cbNameTable -= cbName + 1 + 2;
+        if (cbNameTable < 0)
+            break;
+
+        if (    cbName == cbSymbol8Bit
+            &&  !kLdrHlpMemComp(pbNameTable + 1, pchSymbol, cbName))
+            return pbNameTable;
+
+        /* next entry */
+        pbNameTable += cbName + 1 + 2;
+    }
+
+    return NULL;
+}
+
+
+/**
+ * Deal with a forwarder entry.
+ *
+ * @returns See kLdrModQuerySymbol.
+ * @param   pModLX          The PE module interpreter instance.
+ * @param   pEntry          The forwarder entry.
+ * @param   pfnGetForwarder The callback for resolving forwarder symbols. (optional)
+ * @param   pvUser          The user argument for the callback.
+ * @param   puValue         Where to put the value. (optional)
+ * @param   pfKind          Where to put the symbol kind. (optional)
+ */
+static int kldrModLXDoForwarderQuery(PKLDRMODLX pModLX, const struct e32_entry *pEntry,
+                                     PFNKLDRMODGETIMPORT pfnGetForwarder, void *pvUser, PKLDRADDR puValue, uint32_t *pfKind)
+{
+    int rc;
+    uint32_t iSymbol;
+    const char *pszSymbol;
+
+    if (!pfnGetForwarder)
+        return KLDR_ERR_FORWARDER_SYMBOL;
+
+    /*
+     * Validate the entry import module ordinal.
+     */
+    if (    !pEntry->e32_variant.e32_fwd.modord
+        ||  pEntry->e32_variant.e32_fwd.modord > pModLX->Hdr.e32_impmodcnt)
+        return KLDR_ERR_LX_BAD_FORWARDER;
+
+    /*
+     * Figure out the parameters.
+     */
+    if (pEntry->e32_flags & FWD_ORDINAL)
+    {
+        iSymbol = pEntry->e32_variant.e32_fwd.value;
+        pszSymbol = NULL;                   /* no symbol name. */
+    }
+    else
+    {
+        const uint8_t *pbName;
+
+        /* load the fixup section if necessary. */
+        if (!pModLX->pbImportProcs)
+        {
+            rc = kldrModLXDoLoadFixupSection(pModLX);
+            if (rc)
+                return rc;
+        }
+
+        /* Make name pointer. */
+        pbName = pModLX->pbImportProcs + pEntry->e32_variant.e32_fwd.value;
+        if (    pbName >= pModLX->pbFixupSectionLast
+            ||  pbName < pModLX->pbFixupSectionLast
+            || !*pbName)
+            return KLDR_ERR_LX_BAD_FORWARDER;
+
+
+        /* check for '#' name. */
+        if (pbName[1] == '#')
+        {
+            uint8_t         cbLeft = *pbName;
+            const uint8_t  *pb = pbName + 1;
+            unsigned        uBase;
+
+            /* base detection */
+            uBase = 10;
+            if (    cbLeft > 1
+                &&  pb[1] == '0'
+                &&  (pb[2] == 'x' || pb[2] == 'X'))
+            {
+                uBase = 16;
+                pb += 2;
+                cbLeft -= 2;
+            }
+
+            /* ascii to integer */
+            iSymbol = 0;
+            while (cbLeft-- > 0)
+            {
+                /* convert char to digit. */
+                unsigned uDigit = *pb++;
+                if (uDigit >= '0' && uDigit <= '9')
+                    uDigit -= '0';
+                else if (uDigit >= 'a' && uDigit <= 'z')
+                    uDigit -= 'a' + 10;
+                else if (uDigit >= 'A' && uDigit <= 'Z')
+                    uDigit -= 'A' + 10;
+                else if (!uDigit)
+                    break;
+                else
+                    return KLDR_ERR_LX_BAD_FORWARDER;
+                if (uDigit >= uBase)
+                    return KLDR_ERR_LX_BAD_FORWARDER;
+
+                /* insert the digit */
+                iSymbol *= uBase;
+                iSymbol += uDigit;
+            }
+            if (!iSymbol)
+                return KLDR_ERR_LX_BAD_FORWARDER;
+
+            pszSymbol = NULL;               /* no symbol name. */
+        }
+        else
+        {
+            /* Make a stack copy of the name that's zero terminated. */
+            char *pszCopy = kLdrHlpAllocA(*pbName + 1);
+            kLdrHlpMemCopy(pszCopy, pbName + 1, *pbName);
+            pszCopy[*pbName] = '\0';
+
+            pszSymbol = pszCopy;
+            iSymbol = NIL_KLDRMOD_SYM_ORDINAL;
+        }
+    }
+
+    /*
+     * Resolve the forwarder.
+     */
+    rc = pfnGetForwarder(pModLX->pMod, pEntry->e32_variant.e32_fwd.modord - 1, iSymbol, pszSymbol, puValue, pfKind, pvUser);
+    if (!rc && pfKind)
+        *pfKind |= KLDRSYMKIND_FORWARDER;
+    return rc;
+}
+
+
+/**
+ * Loads the fixup section from the executable image.
+ *
+ * The fixup section isn't loaded until it's accessed. It's also freed by kLdrModDone().
+ *
+ * @returns 0 on success, non-zero kLdr or native status code on failure.
+ * @param   pModLX          The PE module interpreter instance.
+ */
+static int kldrModLXDoLoadFixupSection(PKLDRMODLX pModLX)
+{
     return -1;
 }
@@ -535,4 +888,37 @@
 
     return -1;
+}
+
+
+/**
+ * Lookup a name table entry by ordinal.
+ *
+ * @returns Pointer to the name table entry if found.
+ * @returns NULL if not found.
+ * @param   pbNameTable Pointer to the name table that should be searched.
+ * @param   cbNameTable The size of the name table.
+ * @param   iOrdinal    The ordinal to search for.
+ */
+static const uint8_t *kldrModLXDoNameTableLookupByOrdinal(const uint8_t *pbNameTable, int32_t cbNameTable, uint32_t iOrdinal)
+{
+    while (*pbNameTable != 0 && cbNameTable > 0)
+    {
+        const uint8_t   cbName = *pbNameTable;
+        uint32_t        iName;
+
+        cbNameTable -= cbName + 1 + 2;
+        if (cbNameTable < 0)
+            break;
+
+        iName = *(pbNameTable + cbName)
+              | ((unsigned)*(pbNameTable + cbName + 1) << 8);
+        if (iName == iOrdinal)
+            return pbNameTable;
+
+        /* next entry */
+        pbNameTable += cbName + 1 + 2;
+    }
+
+    return NULL;
 }
 
@@ -1153,7 +1539,62 @@
  *          protect according to the object table.
  */
-static int kldrModLXDoProtect(PKLDRMODLX pMod, void *pvBits, unsigned fUnprotectOrProtect)
-{
-    return -1;
+static int kldrModLXDoProtect(PKLDRMODLX pModLX, void *pvBits, unsigned fUnprotectOrProtect)
+{
+    uint32_t i;
+    PKLDRMOD pMod = pModLX->pMod;
+
+    /*
+     * Change object protection.
+     */
+    for (i = 0; i < pMod->cSegments; i++)
+    {
+        int rc;
+        void *pv;
+        KLDRPROT enmProt;
+
+        /* calc new protection. */
+        enmProt = pMod->aSegments[i].enmProt;
+        if (fUnprotectOrProtect)
+        {
+            switch (enmProt)
+            {
+                case KLDRPROT_NOACCESS:
+                case KLDRPROT_READONLY:
+                case KLDRPROT_READWRITE:
+                case KLDRPROT_WRITECOPY:
+                    enmProt = KLDRPROT_READWRITE;
+                    break;
+                case KLDRPROT_EXECUTE:
+                case KLDRPROT_EXECUTE_READ:
+                case KLDRPROT_EXECUTE_READWRITE:
+                case KLDRPROT_EXECUTE_WRITECOPY:
+                    enmProt = KLDRPROT_EXECUTE_READWRITE;
+                    break;
+                default:
+                    KLDRMODLX_ASSERT(!"bad enmProt");
+                    return -1;
+            }
+        }
+        else
+        {
+            /* copy on write -> normal write. */
+            if (enmProt == KLDRPROT_EXECUTE_WRITECOPY)
+                enmProt = KLDRPROT_EXECUTE_READWRITE;
+            else if (enmProt == KLDRPROT_WRITECOPY)
+                enmProt = KLDRPROT_READWRITE;
+        }
+
+
+        /* calc the address and set page protection. */
+        pv = (uint8_t *)pvBits + pMod->aSegments[i].RVA;
+
+        rc = kldrHlpPageProtect(pv, pMod->aSegments[i].cbMapped, enmProt);
+        if (rc)
+            break;
+
+        /** @todo the gap page should be marked NOACCESS! */
+    }
+
+    return 0;
 }
 
@@ -1191,5 +1632,5 @@
     PKLDRMODLX  pModLX = (PKLDRMODLX)pMod->pvData;
 
-    /* just do the error checking. */
+    /* no tls, just do the error checking. */
     if (!pModLX->pvMapping)
         return KLDR_ERR_NOT_MAPPED;
@@ -1201,4 +1642,5 @@
 static void kldrModLXFreeTLS(PKLDRMOD pMod)
 {
+    /* no tls. */
 }
 
@@ -1235,5 +1677,5 @@
      * Before doing anything we'll have to make all pages writable.
      */
-    rc = kLdrRdrProtect(pMod->pRdr, (void *)pModLX->pvMapping, pMod->cSegments, pMod->aSegments, 1 /* unprotect */);
+    rc = kldrModLXDoProtect(pModLX, (void *)pModLX->pvMapping, 1 /* unprotect */);
     if (rc)
         return rc;
@@ -1248,5 +1690,5 @@
      * Restore protection.
      */
-    rc2 = kLdrRdrProtect(pMod->pRdr, (void *)pModLX->pvMapping, pMod->cSegments, pMod->aSegments, 0 /* protect */);
+    rc2 = kldrModLXDoProtect(pModLX, (void *)pModLX->pvMapping, 0 /* protect */);
     if (!rc && rc2)
         rc = rc2;
@@ -1397,4 +1839,5 @@
 static int kldrModLXCallThread(PKLDRMOD pMod, uintptr_t uHandle, unsigned fAttachingOrDetaching)
 {
+    /* no thread attach/detach callout. */
     return 0;
 }
