/* $Id: kLdrRdrFile.c 2857 2006-11-05 04:12:13Z bird $ */ /** @file * * kLdr - Module interpreter testcase. * * 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 * */ /******************************************************************************* * Header Files * *******************************************************************************/ #include #include #include #include #include /******************************************************************************* * Defined Constants And Macros * *******************************************************************************/ /** The default base address used in the tests. */ #define MY_BASEADDRESS 0x4200000 /******************************************************************************* * Global Variables * *******************************************************************************/ /** The numbers of errors. */ static int g_cErrors = 0; /** * Report failure. */ static int Failure(const char *pszFormat, ...) { va_list va; g_cErrors++; printf("tstLdrMod: "); va_start(va, pszFormat); vprintf(pszFormat, va); va_end(va); printf("\n"); return 1; } /** * Dump symbols and check that we can query each of them recursivly. */ static int BasicTestsEnumSymCallback(PKLDRMOD pMod, uint32_t iSymbol, const char *pszSymbol, KLDRADDR uValue, uint32_t fKind, void *pvUser) { KLDRADDR uValue2; uint32_t fKind2; int rc; /* dump */ printf("#0x%08x: %016" PRI_KLDRADDR " %#08x", iSymbol, uValue, fKind); if (pszSymbol) printf(" %s", pszSymbol); printf("\n"); /* query by ordinal */ if (iSymbol != NIL_KLDRMOD_SYM_ORDINAL) { rc = kLdrModQuerySymbol(pMod, pvUser, MY_BASEADDRESS, iSymbol, NULL, NULL, NULL, &uValue2, &fKind2); if (rc) return Failure("Couldn't find symbol %#x (%s) by ordinal. rc=%d\n", iSymbol, pszSymbol, rc); if (uValue != uValue2) return Failure("Symbol %#x (%s): Value mismatch %016" PRI_KLDRADDR " != %016" PRI_KLDRADDR " (enum!=query/ord) pvBits=%p\n", iSymbol, pszSymbol, uValue, uValue2, pvUser); if (fKind != fKind2) return Failure("Symbol %#x (%s): Kind mismatch %#x != %#x (enum!=query/ord) pvBits=%p\n", iSymbol, pszSymbol, fKind, fKind2, pvUser); } /* query by name. */ if (pszSymbol) { rc = kLdrModQuerySymbol(pMod, pvUser, MY_BASEADDRESS, NIL_KLDRMOD_SYM_ORDINAL, pszSymbol, NULL, NULL, &uValue2, &fKind2); if (rc) return Failure("Couldn't find symbol %#x (%s) by name. rc=%d\n", iSymbol, pszSymbol, rc); if (uValue != uValue2) return Failure("Symbol %#x (%s): Value mismatch %016" PRI_KLDRADDR " != %016" PRI_KLDRADDR " (enum!=query/name) pvBits=%p\n", iSymbol, pszSymbol, uValue, uValue2, pvUser); if (fKind != fKind2) return Failure("Symbol %#x (%s): Kind mismatch %#x != %#x (enum!=query/name) pvBits=%p\n", iSymbol, pszSymbol, fKind, fKind2, pvUser); } return 0; } /** * Dump debugger information and check it for correctness. */ static int BasicTestEnumDbgInfoCallback(PKLDRMOD pMod, uint32_t iDbgInfo, KLDRDBGINFOTYPE enmType, int16_t iMajorVer, int16_t iMinorVer, off_t offFile, KLDRADDR LinkAddress, KLDRSIZE cb, const char *pszExtFile, void *pvUser) { printf("#0x%08x: enmType=%d %d.%d offFile=0x%" PRI_KLDRADDR " LinkAddress=%" PRI_KLDRADDR " cb=%" PRI_KLDRSIZE " pvUser=%p\n", iDbgInfo, enmType, iMajorVer, iMinorVer, (KLDRADDR)offFile, LinkAddress, cb, pvUser); if (pszExtFile) printf(" pszExtFile=%p '%s'\n", pszExtFile, pszExtFile); if (enmType >= KLDRDBGINFOTYPE_END || enmType <= KLDRDBGINFOTYPE_INVALID) return Failure("Bad enmType"); if (pvUser != NULL) return Failure("pvUser"); return 0; } /** * Performs the basic module loader test on the specified module and image bits. */ static int BasicTestsSub2(PKLDRMOD pMod, void *pvBits) { int32_t cImports; int32_t i; int rc; uint32_t fKind; KLDRADDR Value; KLDRADDR MainEPAddress; KLDRSTACKINFO StackInfo; /* * Get the import modules. */ cImports = kLdrModNumberOfImports(pMod, pvBits); printf("cImports=%d\n", cImports); if (cImports < 0) return Failure("failed to allocate %d bytes for the image", cImports); for (i = 0; i < cImports; i++) { char szImportModule[260]; rc = kLdrModGetImport(pMod, pvBits, i, szImportModule, sizeof(szImportModule)); if (rc) return Failure("failed to get import module name, rc=%d. (%.260s)", rc, szImportModule); printf("import #%d: '%s'\n", i, szImportModule); } /* * Query stack info. */ StackInfo.Address = ~(KLDRADDR)42; StackInfo.LinkAddress = ~(KLDRADDR)42; StackInfo.cbStack = ~(KLDRSIZE)42; StackInfo.cbStackThread = ~(KLDRSIZE)42; rc = kLdrModGetStackInfo(pMod, pvBits, MY_BASEADDRESS, &StackInfo); if (rc) return Failure("kLdrModGetStackInfo failed with rc=%d", rc); printf("Stack: Address=%016" PRI_KLDRADDR " LinkAddress=%016" PRI_KLDRADDR "\n" " cbStack=%016" PRI_KLDRSIZE " cbStackThread=%016" PRI_KLDRSIZE "\n", StackInfo.Address, StackInfo.LinkAddress, StackInfo.cbStack, StackInfo.cbStackThread); if (StackInfo.Address == ~(KLDRADDR)42) return Failure("Bad StackInfo.Address"); if (StackInfo.LinkAddress == ~(KLDRADDR)42) return Failure("Bad StackInfo.LinkAddress"); if (StackInfo.cbStack == ~(KLDRSIZE)42) return Failure("Bad StackInfo.cbStack"); if (StackInfo.cbStackThread == ~(KLDRSIZE)42) return Failure("Bad StackInfo.cbStackThread"); /* * Query entrypoint. */ MainEPAddress = ~(KLDRADDR)42; rc = kLdrModQueryMainEntrypoint(pMod, pvBits, MY_BASEADDRESS, &MainEPAddress); if (rc) return Failure("kLdrModQueryMainEntrypoint failed with rc=%d", rc); printf("Entrypoint: %016" PRI_KLDRADDR "\n", MainEPAddress); if (MainEPAddress == ~(KLDRADDR)42) return Failure("MainEPAddress wasn't set."); /* * Debugger information. */ rc = kLdrModHasDbgInfo(pMod, pvBits); if (!rc) printf("Has Debugger Information\n"); else if (rc == KLDR_ERR_NO_DEBUG_INFO) printf("NO Debugger Information\n"); else return Failure("kLdrModHasDbgInfo failed with rc=%d", rc); rc = kLdrModEnumDbgInfo(pMod, pvBits, BasicTestEnumDbgInfoCallback, NULL); if (rc) return Failure("kLdrModEnumDbgInfo failed with rc=%d", rc); /* * Negative symbol query tests. */ fKind = 0xdeadf00d; Value = 0x0badc0de; rc = kLdrModQuerySymbol(pMod, pvBits, MY_BASEADDRESS, NIL_KLDRMOD_SYM_ORDINAL - 20, NULL, NULL, NULL, &Value, &fKind); if (rc) { if (fKind != 0) return Failure("fKind wasn't cleared on failure."); if (Value != 0) return Failure("Value wasn't cleared on failure."); } fKind = 0xdeadf00d; Value = 0x0badc0de; rc = kLdrModQuerySymbol(pMod, pvBits, MY_BASEADDRESS, NIL_KLDRMOD_SYM_ORDINAL, NULL, NULL, NULL, &Value, &fKind); if (!rc) return Failure("NIL ordinal succeeded!"); if (fKind != 0) return Failure("fKind wasn't cleared on failure."); if (Value != 0) return Failure("Value wasn't cleared on failure."); /* * Enumerate and query all symbols. */ printf("\n" "Symbols:\n"); rc = kLdrModEnumSymbols(pMod, pvBits, MY_BASEADDRESS, 0, BasicTestsEnumSymCallback, pvBits); if (rc) return Failure("kLdrModEnumSymbols failed with rc=%d", rc); /*int kLdrModCanExecuteOn(PKLDRMOD pMod, const void *pvBits, KLDRARCH enmArch, KLDRCPU enmCpu); */ return 0; } /** Dummy import resolver callback. */ static int BasicTestsGetImport(PKLDRMOD pMod, uint32_t iImport, uint32_t iSymbol, const char *pszSymbol, PKLDRADDR puValue, uint32_t *pfKind, void *pvUser) { *puValue = 0xdeadface; *pfKind = KLDRSYMKIND_NO_BIT | KLDRSYMKIND_NO_TYPE; return 0; } /** * Performs the basic module loader test on the specified module */ static int BasicTestsSub(PKLDRMOD pMod) { int rc; uint32_t i; void *pvBits; size_t cbImage; /* * Check/dump the module structure. */ printf("pMod=%p u32Magic=%#x cSegments=%d\n", pMod, pMod->u32Magic, pMod->cSegments); printf("enmType=%d enmFmt=%d enmArch=%d enmCpu=%d enmEndian=%d\n", pMod->enmType, pMod->enmFmt, pMod->enmArch, pMod->enmCpu, pMod->enmEndian); printf("Filename: %s (%d bytes)\n", pMod->pszFilename, pMod->cchFilename); printf(" Name: %s (%d bytes)\n", pMod->pszName, pMod->cchName); printf("\n"); if (pMod->u32Magic != KLDRMOD_MAGIC) return Failure("Bad u32Magic"); if (strlen(pMod->pszFilename) != pMod->cchFilename) return Failure("Bad cchFilename"); if (strlen(pMod->pszName) != pMod->cchName) return Failure("Bad cchName"); if (pMod->enmFmt >= KLDRFMT_END || pMod->enmFmt <= KLDRFMT_INVALID) return Failure("Bad enmFmt"); if (pMod->enmType >= KLDRTYPE_END || pMod->enmType <= KLDRTYPE_INVALID) return Failure("Bad enmType: %d", pMod->enmType); if (pMod->enmArch >= KLDRARCH_END || pMod->enmArch <= KLDRARCH_INVALID) return Failure("Bad enmArch"); if (pMod->enmCpu >= KLDRCPU_END || pMod->enmCpu <= KLDRCPU_INVALID) return Failure("Bad enmCpu"); if (pMod->enmEndian >= KLDRENDIAN_END || pMod->enmEndian <= KLDRENDIAN_INVALID) return Failure("Bad enmEndian"); for (i = 0; i < pMod->cSegments; i++) { printf("seg #%d: pvUser=%p enmProt=%d Name: '%.*s' (%d bytes)\n", i, pMod->aSegments[i].pvUser, pMod->aSegments[i].enmProt, pMod->aSegments[i].cchName, pMod->aSegments[i].pchName, pMod->aSegments[i].cchName); printf("LinkAddress: %016" PRI_KLDRADDR " cb: %016" PRI_KLDRSIZE " Alignment=%08" PRI_KLDRADDR " \n", pMod->aSegments[i].LinkAddress, pMod->aSegments[i].cb, pMod->aSegments[i].Alignment); printf(" RVA: %016" PRI_KLDRADDR " cbMapped: %016" PRI_KLDRSIZE " MapAddress=%p\n", pMod->aSegments[i].RVA, (KLDRSIZE)pMod->aSegments[i].cbMapped, pMod->aSegments[i].MapAddress); printf(" offFile: %016" PRI_KLDRADDR " cbFile: %016" PRI_KLDRSIZE "\n", (KLDRADDR)pMod->aSegments[i].offFile, (KLDRSIZE)pMod->aSegments[i].cbFile); printf("\n"); if (pMod->aSegments[i].pvUser != NULL) return Failure("Bad pvUser"); if (pMod->aSegments[i].enmProt >= KLDRPROT_END || pMod->aSegments[i].enmProt <= KLDRPROT_INVALID) return Failure("Bad enmProt"); if (pMod->aSegments[i].MapAddress != 0) return Failure("Bad MapAddress"); if (pMod->aSegments[i].cbMapped < pMod->aSegments[i].cb) return Failure("Bad cbMapped (1)"); if (pMod->aSegments[i].cbMapped && !pMod->aSegments[i].Alignment) return Failure("Bad cbMapped (2)"); if (pMod->aSegments[i].cbMapped > kLdrModSize(pMod)) return Failure("Bad cbMapped (3)"); if ( pMod->aSegments[i].Alignment && (pMod->aSegments[i].RVA & (pMod->aSegments[i].Alignment - 1))) return Failure("Bad RVA (1)"); if (pMod->aSegments[i].RVA != NIL_KLDRADDR && !pMod->aSegments[i].Alignment) return Failure("Bad RVA (2)"); if ( pMod->aSegments[i].RVA != NIL_KLDRADDR && pMod->aSegments[i].RVA >= kLdrModSize(pMod)) return Failure("Bad RVA (3)"); if ( pMod->aSegments[i].RVA != NIL_KLDRADDR && pMod->aSegments[i].RVA + pMod->aSegments[i].cbMapped > kLdrModSize(pMod)) return Failure("Bad RVA/cbMapped (4)"); if (pMod->aSegments[i].LinkAddress != NIL_KLDRADDR && !pMod->aSegments[i].Alignment) return Failure("Bad LinkAddress"); if ( pMod->aSegments[i].LinkAddress != NIL_KLDRADDR && (pMod->aSegments[i].LinkAddress) & (pMod->aSegments[i].Alignment - 1)) return Failure("Bad LinkAddress alignment"); if (pMod->aSegments[i].offFile != -1 && pMod->aSegments[i].cbFile == -1) return Failure("Bad offFile"); if (pMod->aSegments[i].offFile == -1 && pMod->aSegments[i].cbFile != -1) return Failure("Bad cbFile"); } /* * Get image the size and query the image bits. */ cbImage = (size_t)kLdrModSize(pMod); if (cbImage != kLdrModSize(pMod)) return Failure("aborting test because the image is too huge!\n"); pvBits = malloc((size_t)cbImage); if (!pvBits) return Failure("failed to allocate %d bytes for the image\n", cbImage); rc = kLdrModGetBits(pMod, pvBits, (uintptr_t)pvBits, BasicTestsGetImport, NULL); if (rc) return Failure("failed to allocate %d bytes for the image\n", cbImage); /* * Another cleanup nesting. */ rc = BasicTestsSub2(pMod, pvBits); free(pvBits); return rc; } /** * Tests the mapping related api. */ static int BasicTestsSubMap(PKLDRMOD pMod) { int rc; rc = kLdrModMap(pMod); if (rc) return Failure("kLdrModMap failed, rc=%d\n", rc); return 0; } /** * Performs basic module loader tests on the specified file. */ static int BasicTests(const char *pszFilename) { PKLDRMOD pMod; int rc, rc2; printf("tstLdrMod: Testing '%s'\n", pszFilename); rc = kLdrModOpen(pszFilename, &pMod); if (!rc) { rc = BasicTestsSub(pMod); if (!rc) rc = BasicTestsSubMap(pMod); rc2 = kLdrModClose(pMod); if (rc2) Failure("failed to close '%s', rc=%d\n", pszFilename, rc); if (rc2 && !rc) rc = rc2; } else Failure("Failed to open '%s', rc=%d\n", pszFilename, rc); return rc ? 1 : 0; } int main(int argc, char **argv) { BasicTests(argv[argc-1]); if (!g_cErrors) printf("tstLdrMod: SUCCESS\n"); else printf("tstLdrMod: FAILURE - %d errors\n", g_cErrors); return !!g_cErrors; }