diff options
Diffstat (limited to 'src/VBox/Runtime/common/ldr')
-rw-r--r-- | src/VBox/Runtime/common/ldr/ldr.cpp | 81 | ||||
-rw-r--r-- | src/VBox/Runtime/common/ldr/ldrELF.cpp | 6 | ||||
-rw-r--r-- | src/VBox/Runtime/common/ldr/ldrELFRelocatable.cpp.h | 837 | ||||
-rw-r--r-- | src/VBox/Runtime/common/ldr/ldrEx.cpp | 42 | ||||
-rw-r--r-- | src/VBox/Runtime/common/ldr/ldrFile.cpp | 5 | ||||
-rw-r--r-- | src/VBox/Runtime/common/ldr/ldrMemory.cpp | 324 | ||||
-rw-r--r-- | src/VBox/Runtime/common/ldr/ldrNative.cpp | 88 | ||||
-rw-r--r-- | src/VBox/Runtime/common/ldr/ldrPE.cpp | 806 | ||||
-rw-r--r-- | src/VBox/Runtime/common/ldr/ldrkStuff.cpp | 151 |
9 files changed, 2140 insertions, 200 deletions
diff --git a/src/VBox/Runtime/common/ldr/ldr.cpp b/src/VBox/Runtime/common/ldr/ldr.cpp index fda1e437..218b4105 100644 --- a/src/VBox/Runtime/common/ldr/ldr.cpp +++ b/src/VBox/Runtime/common/ldr/ldr.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -40,14 +40,6 @@ #include "internal/ldr.h" -/** - * Checks if a library is loadable or not. - * - * This may attempt load and unload the library. - * - * @returns true/false accordingly. - * @param pszFilename Image filename. - */ RTDECL(bool) RTLdrIsLoadable(const char *pszFilename) { /* @@ -65,15 +57,6 @@ RTDECL(bool) RTLdrIsLoadable(const char *pszFilename) RT_EXPORT_SYMBOL(RTLdrIsLoadable); -/** - * Gets the address of a named exported symbol. - * - * @returns iprt status code. - * @param hLdrMod The loader module handle. - * @param pszSymbol Symbol name. - * @param ppvValue Where to store the symbol value. Note that this is restricted to the - * pointer size used on the host! - */ RTDECL(int) RTLdrGetSymbol(RTLDRMOD hLdrMod, const char *pszSymbol, void **ppvValue) { LogFlow(("RTLdrGetSymbol: hLdrMod=%RTldrm pszSymbol=%p:{%s} ppvValue=%p\n", @@ -110,15 +93,53 @@ RTDECL(int) RTLdrGetSymbol(RTLDRMOD hLdrMod, const char *pszSymbol, void **ppvVa RT_EXPORT_SYMBOL(RTLdrGetSymbol); -/** - * Closes a loader module handle. - * - * The handle can be obtained using any of the RTLdrLoad(), RTLdrOpen() - * and RTLdrOpenBits() functions. - * - * @returns iprt status code. - * @param hLdrMod The loader module handle. - */ +RTDECL(PFNRT) RTLdrGetFunction(RTLDRMOD hLdrMod, const char *pszSymbol) +{ + PFNRT pfn; + int rc = RTLdrGetSymbol(hLdrMod, pszSymbol, (void **)&pfn); + if (RT_SUCCESS(rc)) + return pfn; + return NULL; +} +RT_EXPORT_SYMBOL(RTLdrGetFunction); + + +RTDECL(RTLDRFMT) RTLdrGetFormat(RTLDRMOD hLdrMod) +{ + AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), RTLDRFMT_INVALID); + PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod; + return pMod->enmFormat; +} +RT_EXPORT_SYMBOL(RTLdrGetFormat); + + +RTDECL(RTLDRTYPE) RTLdrGetType(RTLDRMOD hLdrMod) +{ + AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), RTLDRTYPE_INVALID); + PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod; + return pMod->enmType; +} +RT_EXPORT_SYMBOL(RTLdrGetType); + + +RTDECL(RTLDRENDIAN) RTLdrGetEndian(RTLDRMOD hLdrMod) +{ + AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), RTLDRENDIAN_INVALID); + PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod; + return pMod->enmEndian; +} +RT_EXPORT_SYMBOL(RTLdrGetEndian); + + +RTDECL(RTLDRARCH) RTLdrGetArch(RTLDRMOD hLdrMod) +{ + AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), RTLDRARCH_INVALID); + PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod; + return pMod->enmArch; +} +RT_EXPORT_SYMBOL(RTLdrGetArch); + + RTDECL(int) RTLdrClose(RTLDRMOD hLdrMod) { LogFlow(("RTLdrClose: hLdrMod=%RTldrm\n", hLdrMod)); @@ -137,6 +158,12 @@ RTDECL(int) RTLdrClose(RTLDRMOD hLdrMod) AssertRC(rc); pMod->eState = LDR_STATE_INVALID; pMod->u32Magic++; + if (pMod->pReader) + { + rc = pMod->pReader->pfnDestroy(pMod->pReader); + AssertRC(rc); + pMod->pReader = NULL; + } RTMemFree(pMod); LogFlow(("RTLdrClose: returns VINF_SUCCESS\n")); diff --git a/src/VBox/Runtime/common/ldr/ldrELF.cpp b/src/VBox/Runtime/common/ldr/ldrELF.cpp index 47b45f61..ce08533b 100644 --- a/src/VBox/Runtime/common/ldr/ldrELF.cpp +++ b/src/VBox/Runtime/common/ldr/ldrELF.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -48,8 +48,10 @@ /******************************************************************************* * Defined Constants And Macros * *******************************************************************************/ -/** Finds an ELF string. */ +/** Finds an ELF symbol table string. */ #define ELF_STR(pHdrs, iStr) ((pHdrs)->pStr + (iStr)) +/** Finds an ELF section header string. */ +#define ELF_SH_STR(pHdrs, iStr) ((pHdrs)->pShStr + (iStr)) diff --git a/src/VBox/Runtime/common/ldr/ldrELFRelocatable.cpp.h b/src/VBox/Runtime/common/ldr/ldrELFRelocatable.cpp.h index 20d11b35..bd7760b4 100644 --- a/src/VBox/Runtime/common/ldr/ldrELFRelocatable.cpp.h +++ b/src/VBox/Runtime/common/ldr/ldrELFRelocatable.cpp.h @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -92,21 +92,25 @@ typedef struct RTLDRMODELF { /** Core module structure. */ RTLDRMODINTERNAL Core; - /** Pointer to the reader instance. */ - PRTLDRREADER pReader; /** Pointer to readonly mapping of the image bits. * This mapping is provided by the pReader. */ const void *pvBits; /** The ELF header. */ Elf_Ehdr Ehdr; - /** Pointer to our copy of the section headers. + /** Pointer to our copy of the section headers with sh_addr as RVAs. * The virtual addresses in this array is the 0 based assignments we've given the image. * Not valid if the image is DONE. */ Elf_Shdr *paShdrs; + /** Unmodified section headers (allocated after paShdrs, so no need to free). + * Not valid if the image is DONE. */ + Elf_Shdr const *paOrgShdrs; /** The size of the loaded image. */ size_t cbImage; + /** The image base address if it's an EXEC or DYN image. */ + Elf_Addr LinkAddress; + /** The symbol section index. */ unsigned iSymSh; /** Number of symbols in the table. */ @@ -120,6 +124,11 @@ typedef struct RTLDRMODELF unsigned cbStr; /** Pointer to string table within RTLDRMODELF::pvBits. */ const char *pStr; + + /** Size of the section header string table. */ + unsigned cbShStr; + /** Pointer to section header string table within RTLDRMODELF::pvBits. */ + const char *pShStr; } RTLDRMODELF, *PRTLDRMODELF; @@ -136,7 +145,7 @@ static int RTLDRELF_NAME(MapBits)(PRTLDRMODELF pModElf, bool fNeedsBits) NOREF(fNeedsBits); if (pModElf->pvBits) return VINF_SUCCESS; - int rc = pModElf->pReader->pfnMap(pModElf->pReader, &pModElf->pvBits); + int rc = pModElf->Core.pReader->pfnMap(pModElf->Core.pReader, &pModElf->pvBits); if (RT_SUCCESS(rc)) { const uint8_t *pu8 = (const uint8_t *)pModElf->pvBits; @@ -144,11 +153,244 @@ static int RTLDRELF_NAME(MapBits)(PRTLDRMODELF pModElf, bool fNeedsBits) pModElf->paSyms = (const Elf_Sym *)(pu8 + pModElf->paShdrs[pModElf->iSymSh].sh_offset); if (pModElf->iStrSh != ~0U) pModElf->pStr = (const char *)(pu8 + pModElf->paShdrs[pModElf->iStrSh].sh_offset); + pModElf->pShStr = (const char *)(pu8 + pModElf->paShdrs[pModElf->Ehdr.e_shstrndx].sh_offset); } return rc; } +/* + * + * EXEC & DYN. + * EXEC & DYN. + * EXEC & DYN. + * EXEC & DYN. + * EXEC & DYN. + * + */ + + +/** + * Applies the fixups for a section in an executable image. + * + * @returns iprt status code. + * @param pModElf The ELF loader module instance data. + * @param BaseAddr The base address which the module is being fixedup to. + * @param pfnGetImport The callback function to use to resolve imports (aka unresolved externals). + * @param pvUser User argument to pass to the callback. + * @param SecAddr The section address. This is the address the relocations are relative to. + * @param cbSec The section size. The relocations must be inside this. + * @param pu8SecBaseR Where we read section bits from. + * @param pu8SecBaseW Where we write section bits to. + * @param pvRelocs Pointer to where we read the relocations from. + * @param cbRelocs Size of the relocations. + */ +static int RTLDRELF_NAME(RelocateSectionExecDyn)(PRTLDRMODELF pModElf, Elf_Addr BaseAddr, + PFNRTLDRIMPORT pfnGetImport, void *pvUser, + const Elf_Addr SecAddr, Elf_Size cbSec, + const uint8_t *pu8SecBaseR, uint8_t *pu8SecBaseW, + const void *pvRelocs, Elf_Size cbRelocs) +{ +#if ELF_MODE != 32 + NOREF(pu8SecBaseR); +#endif + + /* + * Iterate the relocations. + * The relocations are stored in an array of Elf32_Rel records and covers the entire relocation section. + */ + const Elf_Addr offDelta = BaseAddr - pModElf->LinkAddress; + const Elf_Reloc *paRels = (const Elf_Reloc *)pvRelocs; + const unsigned iRelMax = (unsigned)(cbRelocs / sizeof(paRels[0])); + AssertMsgReturn(iRelMax == cbRelocs / sizeof(paRels[0]), (FMT_ELF_SIZE "\n", cbRelocs / sizeof(paRels[0])), + VERR_IMAGE_TOO_BIG); + for (unsigned iRel = 0; iRel < iRelMax; iRel++) + { + /* + * Skip R_XXX_NONE entries early to avoid confusion in the symbol + * getter code. + */ +#if ELF_MODE == 32 + if (ELF_R_TYPE(paRels[iRel].r_info) == R_386_NONE) + continue; +#elif ELF_MODE == 64 + if (ELF_R_TYPE(paRels[iRel].r_info) == R_X86_64_NONE) + continue; +#endif + + /* + * Validate and find the symbol, resolve undefined ones. + */ + Elf_Size iSym = ELF_R_SYM(paRels[iRel].r_info); + if (iSym >= pModElf->cSyms) + { + AssertMsgFailed(("iSym=%d is an invalid symbol index!\n", iSym)); + return VERR_LDRELF_INVALID_SYMBOL_INDEX; + } + const Elf_Sym *pSym = &pModElf->paSyms[iSym]; + if (pSym->st_name >= pModElf->cbStr) + { + AssertMsgFailed(("iSym=%d st_name=%d str sh_size=%d\n", iSym, pSym->st_name, pModElf->cbStr)); + return VERR_LDRELF_INVALID_SYMBOL_NAME_OFFSET; + } + + Elf_Addr SymValue = 0; + if (pSym->st_shndx == SHN_UNDEF) + { + /* Try to resolve the symbol. */ + const char *pszName = ELF_STR(pModElf, pSym->st_name); + RTUINTPTR ExtValue; + int rc = pfnGetImport(&pModElf->Core, "", pszName, ~0, &ExtValue, pvUser); + AssertMsgRCReturn(rc, ("Failed to resolve '%s' rc=%Rrc\n", pszName, rc), rc); + SymValue = (Elf_Addr)ExtValue; + AssertMsgReturn((RTUINTPTR)SymValue == ExtValue, ("Symbol value overflowed! '%s'\n", pszName), + VERR_SYMBOL_VALUE_TOO_BIG); + Log2(("rtldrELF: #%-3d - UNDEF " FMT_ELF_ADDR " '%s'\n", iSym, SymValue, pszName)); + } + else + { + AssertReturn(pSym->st_shndx < pModElf->cSyms || pSym->st_shndx == SHN_ABS, ("%#x\n", pSym->st_shndx)); +#if ELF_MODE == 64 + SymValue = pSym->st_value; +#endif + } + +#if ELF_MODE == 64 + /* Calc the value. */ + Elf_Addr Value; + if (pSym->st_shndx < pModElf->cSyms) + Value = SymValue + offDelta; + else + Value = SymValue + paRels[iRel].r_addend; +#endif + + /* + * Apply the fixup. + */ + AssertMsgReturn(paRels[iRel].r_offset < cbSec, (FMT_ELF_ADDR " " FMT_ELF_SIZE "\n", paRels[iRel].r_offset, cbSec), VERR_LDRELF_INVALID_RELOCATION_OFFSET); +#if ELF_MODE == 32 + const Elf_Addr *pAddrR = (const Elf_Addr *)(pu8SecBaseR + paRels[iRel].r_offset); /* Where to read the addend. */ +#endif + Elf_Addr *pAddrW = (Elf_Addr *)(pu8SecBaseW + paRels[iRel].r_offset); /* Where to write the fixup. */ + switch (ELF_R_TYPE(paRels[iRel].r_info)) + { +#if ELF_MODE == 32 + /* + * Absolute addressing. + */ + case R_386_32: + { + Elf_Addr Value; + if (pSym->st_shndx < pModElf->Ehdr.e_shnum) + Value = *pAddrR + offDelta; /* Simplified. */ + else if (pSym->st_shndx == SHN_ABS) + continue; /* Internal fixup, no need to apply it. */ + else if (pSym->st_shndx == SHN_UNDEF) + Value = SymValue + *pAddrR; + else + AssertFailedReturn(VERR_LDR_GENERAL_FAILURE); /** @todo SHN_COMMON */ + *(uint32_t *)pAddrW = Value; + Log4((FMT_ELF_ADDR": R_386_32 Value=" FMT_ELF_ADDR "\n", SecAddr + paRels[iRel].r_offset + BaseAddr, Value)); + break; + } + + /* + * PC relative addressing. + */ + case R_386_PC32: + { + Elf_Addr Value; + if (pSym->st_shndx < pModElf->Ehdr.e_shnum) + continue; /* Internal fixup, no need to apply it. */ + else if (pSym->st_shndx == SHN_ABS) + Value = *pAddrR + offDelta; /* Simplified. */ + else if (pSym->st_shndx == SHN_UNDEF) + { + const Elf_Addr SourceAddr = SecAddr + paRels[iRel].r_offset + BaseAddr; /* Where the source really is. */ + Value = SymValue + *(uint32_t *)pAddrR - SourceAddr; + *(uint32_t *)pAddrW = Value; + } + else + AssertFailedReturn(VERR_LDR_GENERAL_FAILURE); /** @todo SHN_COMMON */ + Log4((FMT_ELF_ADDR": R_386_PC32 Value=" FMT_ELF_ADDR "\n", SecAddr + paRels[iRel].r_offset + BaseAddr, Value)); + break; + } + +#elif ELF_MODE == 64 + + /* + * Absolute addressing + */ + case R_X86_64_64: + { + *(uint64_t *)pAddrW = Value; + Log4((FMT_ELF_ADDR": R_X86_64_64 Value=" FMT_ELF_ADDR " SymValue=" FMT_ELF_ADDR "\n", + SecAddr + paRels[iRel].r_offset + BaseAddr, Value, SymValue)); + break; + } + + /* + * Truncated 32-bit value (zero-extendedable to the 64-bit value). + */ + case R_X86_64_32: + { + *(uint32_t *)pAddrW = (uint32_t)Value; + Log4((FMT_ELF_ADDR": R_X86_64_32 Value=" FMT_ELF_ADDR " SymValue=" FMT_ELF_ADDR "\n", + SecAddr + paRels[iRel].r_offset + BaseAddr, Value, SymValue)); + AssertMsgReturn((Elf_Addr)*(uint32_t *)pAddrW == SymValue, ("Value=" FMT_ELF_ADDR "\n", SymValue), + VERR_SYMBOL_VALUE_TOO_BIG); + break; + } + + /* + * Truncated 32-bit value (sign-extendedable to the 64-bit value). + */ + case R_X86_64_32S: + { + *(int32_t *)pAddrW = (int32_t)Value; + Log4((FMT_ELF_ADDR": R_X86_64_32S Value=" FMT_ELF_ADDR " SymValue=" FMT_ELF_ADDR "\n", + SecAddr + paRels[iRel].r_offset + BaseAddr, Value, SymValue)); + AssertMsgReturn((Elf_Addr)*(int32_t *)pAddrW == Value, ("Value=" FMT_ELF_ADDR "\n", Value), VERR_SYMBOL_VALUE_TOO_BIG); /** @todo check the sign-extending here. */ + break; + } + + /* + * PC relative addressing. + */ + case R_X86_64_PC32: + { + const Elf_Addr SourceAddr = SecAddr + paRels[iRel].r_offset + BaseAddr; /* Where the source really is. */ + Value -= SourceAddr; + *(int32_t *)pAddrW = (int32_t)Value; + Log4((FMT_ELF_ADDR": R_X86_64_PC32 Value=" FMT_ELF_ADDR " SymValue=" FMT_ELF_ADDR "\n", + SourceAddr, Value, SymValue)); + AssertMsgReturn((Elf_Addr)*(int32_t *)pAddrW == Value, ("Value=" FMT_ELF_ADDR "\n", Value), VERR_SYMBOL_VALUE_TOO_BIG); /** @todo check the sign-extending here. */ + break; + } +#endif + + default: + AssertMsgFailed(("Unknown relocation type: %d (iRel=%d iRelMax=%d)\n", + ELF_R_TYPE(paRels[iRel].r_info), iRel, iRelMax)); + return VERR_LDRELF_RELOCATION_NOT_SUPPORTED; + } + } + + return VINF_SUCCESS; +} + + + +/* + * + * REL + * REL + * REL + * REL + * REL + * + */ + /** * Get the symbol and symbol value. * @@ -279,9 +521,22 @@ static int RTLDRELF_NAME(RelocateSection)(PRTLDRMODELF pModElf, Elf_Addr BaseAdd for (unsigned iRel = 0; iRel < iRelMax; iRel++) { /* + * Skip R_XXX_NONE entries early to avoid confusion in the symbol + * getter code. + */ +#if ELF_MODE == 32 + if (ELF_R_TYPE(paRels[iRel].r_info) == R_386_NONE) + continue; +#elif ELF_MODE == 64 + if (ELF_R_TYPE(paRels[iRel].r_info) == R_X86_64_NONE) + continue; +#endif + + + /* * Get the symbol. */ - const Elf_Sym *pSym; + const Elf_Sym *pSym = NULL; /* shut up gcc */ Elf_Addr SymValue = 0; /* shut up gcc-4 */ int rc = RTLDRELF_NAME(Symbol)(pModElf, BaseAddr, pfnGetImport, pvUser, ELF_R_SYM(paRels[iRel].r_info), &pSym, &SymValue); if (RT_FAILURE(rc)) @@ -413,12 +668,6 @@ static DECLCALLBACK(int) RTLDRELF_NAME(Close)(PRTLDRMODINTERNAL pMod) pModElf->paShdrs = NULL; } - if (pModElf->pReader) - { - pModElf->pReader->pfnDestroy(pModElf->pReader); - pModElf->pReader = NULL; - } - pModElf->pvBits = NULL; return VINF_SUCCESS; @@ -474,8 +723,13 @@ static DECLCALLBACK(int) RTLDRELF_NAME(EnumSymbols)(PRTLDRMODINTERNAL pMod, unsi /* absolute symbols are not subject to any relocation. */ Value = paSyms[iSym].st_value; else if (paSyms[iSym].st_shndx < pModElf->Ehdr.e_shnum) - /* relative to the section. */ - Value = BaseAddr + paSyms[iSym].st_value + pModElf->paShdrs[paSyms[iSym].st_shndx].sh_addr; + { + if (pModElf->Ehdr.e_type == ET_REL) + /* relative to the section. */ + Value = BaseAddr + paSyms[iSym].st_value + pModElf->paShdrs[paSyms[iSym].st_shndx].sh_addr; + else /* Fixed up for link address. */ + Value = BaseAddr + paSyms[iSym].st_value - pModElf->LinkAddress; + } else { AssertMsgFailed(("Arg! paSyms[%u].st_shndx=" FMT_ELF_HALF "\n", iSym, paSyms[iSym].st_shndx)); @@ -524,10 +778,10 @@ static DECLCALLBACK(int) RTLDRELF_NAME(GetBits)(PRTLDRMODINTERNAL pMod, void *pv case ET_REL: break; case ET_EXEC: - Log(("RTLdrELF: %s: Executable images are not supported yet!\n", pModElf->pReader->pfnLogName(pModElf->pReader))); + Log(("RTLdrELF: %s: Executable images are not supported yet!\n", pModElf->Core.pReader->pfnLogName(pModElf->Core.pReader))); return VERR_LDRELF_EXEC; case ET_DYN: - Log(("RTLdrELF: %s: Dynamic images are not supported yet!\n", pModElf->pReader->pfnLogName(pModElf->pReader))); + Log(("RTLdrELF: %s: Dynamic images are not supported yet!\n", pModElf->Core.pReader->pfnLogName(pModElf->Core.pReader))); return VERR_LDRELF_DYN; default: AssertFailedReturn(VERR_BAD_EXE_FORMAT); } @@ -550,12 +804,12 @@ static DECLCALLBACK(int) RTLDRELF_NAME(GetBits)(PRTLDRMODINTERNAL pMod, void *pv case SHT_PROGBITS: default: { - int rc = pModElf->pReader->pfnRead(pModElf->pReader, (uint8_t *)pvBits + paShdrs[iShdr].sh_addr, - (size_t)paShdrs[iShdr].sh_size, paShdrs[iShdr].sh_offset); + int rc = pModElf->Core.pReader->pfnRead(pModElf->Core.pReader, (uint8_t *)pvBits + paShdrs[iShdr].sh_addr, + (size_t)paShdrs[iShdr].sh_size, paShdrs[iShdr].sh_offset); if (RT_FAILURE(rc)) { Log(("RTLdrELF: %s: Read error when reading " FMT_ELF_SIZE " bytes at " FMT_ELF_OFF ", iShdr=%d\n", - pModElf->pReader->pfnLogName(pModElf->pReader), + pModElf->Core.pReader->pfnLogName(pModElf->Core.pReader), paShdrs[iShdr].sh_size, paShdrs[iShdr].sh_offset, iShdr)); return rc; } @@ -577,7 +831,7 @@ static DECLCALLBACK(int) RTLDRELF_NAME(Relocate)(PRTLDRMODINTERNAL pMod, void *p { PRTLDRMODELF pModElf = (PRTLDRMODELF)pMod; #ifdef LOG_ENABLED - const char *pszLogName = pModElf->pReader->pfnLogName(pModElf->pReader); + const char *pszLogName = pModElf->Core.pReader->pfnLogName(pModElf->Core.pReader); #endif NOREF(OldBaseAddress); @@ -640,17 +894,26 @@ static DECLCALLBACK(int) RTLDRELF_NAME(Relocate)(PRTLDRMODINTERNAL pMod, void *p * Relocate the section. */ Log2(("rtldrELF: %s: Relocation records for #%d [%s] (sh_info=%d sh_link=%d) found in #%d [%s] (sh_info=%d sh_link=%d)\n", - pszLogName, (int)pShdrRel->sh_info, ELF_STR(pModElf, pShdr->sh_name), (int)pShdr->sh_info, (int)pShdr->sh_link, - iShdr, ELF_STR(pModElf, pShdrRel->sh_name), (int)pShdrRel->sh_info, (int)pShdrRel->sh_link)); + pszLogName, (int)pShdrRel->sh_info, ELF_SH_STR(pModElf, pShdr->sh_name), (int)pShdr->sh_info, (int)pShdr->sh_link, + iShdr, ELF_SH_STR(pModElf, pShdrRel->sh_name), (int)pShdrRel->sh_info, (int)pShdrRel->sh_link)); /** @todo Make RelocateSection a function pointer so we can select the one corresponding to the machine when opening the image. */ - rc = RTLDRELF_NAME(RelocateSection)(pModElf, BaseAddr, pfnGetImport, pvUser, - pShdr->sh_addr, - pShdr->sh_size, - (const uint8_t *)pModElf->pvBits + pShdr->sh_offset, - (uint8_t *)pvBits + pShdr->sh_addr, - (const uint8_t *)pModElf->pvBits + pShdrRel->sh_offset, - pShdrRel->sh_size); + if (pModElf->Ehdr.e_type == ET_REL) + rc = RTLDRELF_NAME(RelocateSection)(pModElf, BaseAddr, pfnGetImport, pvUser, + pShdr->sh_addr, + pShdr->sh_size, + (const uint8_t *)pModElf->pvBits + pShdr->sh_offset, + (uint8_t *)pvBits + pShdr->sh_addr, + (const uint8_t *)pModElf->pvBits + pShdrRel->sh_offset, + pShdrRel->sh_size); + else + rc = RTLDRELF_NAME(RelocateSectionExecDyn)(pModElf, BaseAddr, pfnGetImport, pvUser, + pShdr->sh_addr, + pShdr->sh_size, + (const uint8_t *)pModElf->pvBits + pShdr->sh_offset, + (uint8_t *)pvBits + pShdr->sh_addr, + (const uint8_t *)pModElf->pvBits + pShdrRel->sh_offset, + pShdrRel->sh_size); if (RT_FAILURE(rc)) return rc; } @@ -701,8 +964,13 @@ static DECLCALLBACK(int) RTLDRELF_NAME(GetSymbolEx)(PRTLDRMODINTERNAL pMod, cons /* absolute symbols are not subject to any relocation. */ Value = paSyms[iSym].st_value; else if (paSyms[iSym].st_shndx < pModElf->Ehdr.e_shnum) - /* relative to the section. */ - Value = BaseAddr + paSyms[iSym].st_value + pModElf->paShdrs[paSyms[iSym].st_shndx].sh_addr; + { + if (pModElf->Ehdr.e_type == ET_REL) + /* relative to the section. */ + Value = BaseAddr + paSyms[iSym].st_value + pModElf->paShdrs[paSyms[iSym].st_shndx].sh_addr; + else /* Fixed up for link address. */ + Value = BaseAddr + paSyms[iSym].st_value - pModElf->LinkAddress; + } else { AssertMsgFailed(("Arg. paSyms[iSym].st_shndx=%d\n", paSyms[iSym].st_shndx)); @@ -730,18 +998,163 @@ static DECLCALLBACK(int) RTLDRELF_NAME(EnumDbgInfo)(PRTLDRMODINTERNAL pMod, cons PFNRTLDRENUMDBG pfnCallback, void *pvUser) { PRTLDRMODELF pModElf = (PRTLDRMODELF)pMod; - NOREF(pvBits); - return VERR_NOT_IMPLEMENTED; NOREF(pModElf); NOREF(pfnCallback); NOREF(pvUser); + /* + * Map the image bits if not already done and setup pointer into it. + */ + int rc = RTLDRELF_NAME(MapBits)(pModElf, true); + if (RT_FAILURE(rc)) + return rc; + + /* + * Do the enumeration. + */ + const Elf_Shdr *paShdrs = pModElf->paOrgShdrs; + for (unsigned iShdr = 0; iShdr < pModElf->Ehdr.e_shnum; iShdr++) + { + /* Debug sections are expected to be PROGBITS and not allocated. */ + if (paShdrs[iShdr].sh_type != SHT_PROGBITS) + continue; + if (paShdrs[iShdr].sh_flags & SHF_ALLOC) + continue; + + RTLDRDBGINFO DbgInfo; + const char *pszSectName = ELF_SH_STR(pModElf, paShdrs[iShdr].sh_name); + if ( !strncmp(pszSectName, RT_STR_TUPLE(".debug_")) + || !strcmp(pszSectName, ".WATCOM_references") ) + { + RT_ZERO(DbgInfo.u); + DbgInfo.enmType = RTLDRDBGINFOTYPE_DWARF; + DbgInfo.pszExtFile = NULL; + DbgInfo.offFile = paShdrs[iShdr].sh_offset; + DbgInfo.cb = paShdrs[iShdr].sh_size; + DbgInfo.u.Dwarf.pszSection = pszSectName; + } + else if (!strcmp(pszSectName, ".gnu_debuglink")) + { + if ((paShdrs[iShdr].sh_size & 3) || paShdrs[iShdr].sh_size < 8) + return VERR_BAD_EXE_FORMAT; + + RT_ZERO(DbgInfo.u); + DbgInfo.enmType = RTLDRDBGINFOTYPE_DWARF_DWO; + DbgInfo.pszExtFile = (const char *)((uintptr_t)pModElf->pvBits + (uintptr_t)paShdrs[iShdr].sh_offset); + if (!RTStrEnd(DbgInfo.pszExtFile, paShdrs[iShdr].sh_size)) + return VERR_BAD_EXE_FORMAT; + DbgInfo.u.Dwo.uCrc32 = *(uint32_t *)((uintptr_t)DbgInfo.pszExtFile + (uintptr_t)paShdrs[iShdr].sh_size + - sizeof(uint32_t)); + DbgInfo.offFile = -1; + DbgInfo.cb = 0; + } + else + continue; + + DbgInfo.LinkAddress = NIL_RTLDRADDR; + DbgInfo.iDbgInfo = iShdr - 1; + + rc = pfnCallback(pMod, &DbgInfo, pvUser); + if (rc != VINF_SUCCESS) + return rc; + + } + + return VINF_SUCCESS; } +/** + * Helper that locates the first allocated section. + * + * @returns Pointer to the section header if found, NULL if none. + * @param pShdr The section header to start searching at. + * @param cLeft The number of section headers left to search. Can be 0. + */ +static const Elf_Shdr *RTLDRELF_NAME(GetFirstAllocatedSection)(const Elf_Shdr *pShdr, unsigned cLeft) +{ + while (cLeft-- > 0) + { + if (pShdr->sh_flags & SHF_ALLOC) + return pShdr; + pShdr++; + } + return NULL; +} + /** @copydoc RTLDROPS::pfnEnumSegments. */ static DECLCALLBACK(int) RTLDRELF_NAME(EnumSegments)(PRTLDRMODINTERNAL pMod, PFNRTLDRENUMSEGS pfnCallback, void *pvUser) { PRTLDRMODELF pModElf = (PRTLDRMODELF)pMod; - return VERR_NOT_IMPLEMENTED; NOREF(pModElf); NOREF(pfnCallback); NOREF(pvUser); + /* + * Map the image bits if not already done and setup pointer into it. + */ + int rc = RTLDRELF_NAME(MapBits)(pModElf, true); + if (RT_FAILURE(rc)) + return rc; + + /* + * Do the enumeration. + */ + char szName[32]; + Elf_Addr uPrevMappedRva = 0; + const Elf_Shdr *paShdrs = pModElf->paShdrs; + const Elf_Shdr *paOrgShdrs = pModElf->paOrgShdrs; + for (unsigned iShdr = 1; iShdr < pModElf->Ehdr.e_shnum; iShdr++) + { + RTLDRSEG Seg; + Seg.pszName = ELF_SH_STR(pModElf, paShdrs[iShdr].sh_name); + Seg.cchName = (uint32_t)strlen(Seg.pszName); + if (Seg.cchName == 0) + { + Seg.pszName = szName; + Seg.cchName = (uint32_t)RTStrPrintf(szName, sizeof(szName), "UnamedSect%02u", iShdr); + } + Seg.SelFlat = 0; + Seg.Sel16bit = 0; + Seg.fFlags = 0; + Seg.fProt = RTMEM_PROT_READ; + if (paShdrs[iShdr].sh_flags & SHF_WRITE) + Seg.fProt |= RTMEM_PROT_WRITE; + if (paShdrs[iShdr].sh_flags & SHF_EXECINSTR) + Seg.fProt |= RTMEM_PROT_EXEC; + Seg.cb = paShdrs[iShdr].sh_size; + Seg.Alignment = paShdrs[iShdr].sh_addralign; + if (paShdrs[iShdr].sh_flags & SHF_ALLOC) + { + Seg.LinkAddress = paOrgShdrs[iShdr].sh_addr; + Seg.RVA = paShdrs[iShdr].sh_addr; + const Elf_Shdr *pShdr2 = RTLDRELF_NAME(GetFirstAllocatedSection)(&paShdrs[iShdr + 1], + pModElf->Ehdr.e_shnum - iShdr - 1); + if ( pShdr2 + && pShdr2->sh_addr >= paShdrs[iShdr].sh_addr + && Seg.RVA >= uPrevMappedRva) + Seg.cbMapped = pShdr2->sh_addr - paShdrs[iShdr].sh_addr; + else + Seg.cbMapped = RT_MAX(paShdrs[iShdr].sh_size, paShdrs[iShdr].sh_addralign); + uPrevMappedRva = Seg.RVA; + } + else + { + Seg.LinkAddress = NIL_RTLDRADDR; + Seg.RVA = NIL_RTLDRADDR; + Seg.cbMapped = NIL_RTLDRADDR; + } + if (paShdrs[iShdr].sh_type != SHT_NOBITS) + { + Seg.offFile = paShdrs[iShdr].sh_offset; + Seg.cbFile = paShdrs[iShdr].sh_size; + } + else + { + Seg.offFile = -1; + Seg.cbFile = 0; + } + + rc = pfnCallback(pMod, &Seg, pvUser); + if (rc != VINF_SUCCESS) + return rc; + } + + return VINF_SUCCESS; } @@ -751,7 +1164,34 @@ static DECLCALLBACK(int) RTLDRELF_NAME(LinkAddressToSegOffset)(PRTLDRMODINTERNAL { PRTLDRMODELF pModElf = (PRTLDRMODELF)pMod; - return VERR_NOT_IMPLEMENTED; NOREF(pModElf); NOREF(LinkAddress); NOREF(piSeg); NOREF(poffSeg); + const Elf_Shdr *pShdrEnd = NULL; + unsigned cLeft = pModElf->Ehdr.e_shnum - 1; + const Elf_Shdr *pShdr = &pModElf->paOrgShdrs[cLeft]; + while (cLeft-- > 0) + { + if (pShdr->sh_flags & SHF_ALLOC) + { + RTLDRADDR offSeg = LinkAddress - pShdr->sh_addr; + if (offSeg < pShdr->sh_size) + { + *poffSeg = offSeg; + *piSeg = cLeft; + return VINF_SUCCESS; + } + if (offSeg == pShdr->sh_size) + pShdrEnd = pShdr; + } + pShdr--; + } + + if (pShdrEnd) + { + *poffSeg = pShdrEnd->sh_size; + *piSeg = pShdrEnd - pModElf->paOrgShdrs - 1; + return VINF_SUCCESS; + } + + return VERR_LDR_INVALID_LINK_ADDRESS; } @@ -759,8 +1199,12 @@ static DECLCALLBACK(int) RTLDRELF_NAME(LinkAddressToSegOffset)(PRTLDRMODINTERNAL static DECLCALLBACK(int) RTLDRELF_NAME(LinkAddressToRva)(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress, PRTLDRADDR pRva) { PRTLDRMODELF pModElf = (PRTLDRMODELF)pMod; - - return VERR_NOT_IMPLEMENTED; NOREF(pModElf); NOREF(LinkAddress); NOREF(pRva); + uint32_t iSeg; + RTLDRADDR offSeg; + int rc = RTLDRELF_NAME(LinkAddressToSegOffset)(pMod, LinkAddress, &iSeg, &offSeg); + if (RT_SUCCESS(rc)) + *pRva = pModElf->paShdrs[iSeg + 1].sh_addr + offSeg; + return rc; } @@ -769,8 +1213,24 @@ static DECLCALLBACK(int) RTLDRELF_NAME(SegOffsetToRva)(PRTLDRMODINTERNAL pMod, u PRTLDRADDR pRva) { PRTLDRMODELF pModElf = (PRTLDRMODELF)pMod; + if (iSeg >= pModElf->Ehdr.e_shnum - 1U) + return VERR_LDR_INVALID_SEG_OFFSET; + + iSeg++; /* skip section 0 */ + if (offSeg > pModElf->paShdrs[iSeg].sh_size) + { + const Elf_Shdr *pShdr2 = RTLDRELF_NAME(GetFirstAllocatedSection)(&pModElf->paShdrs[iSeg + 1], + pModElf->Ehdr.e_shnum - iSeg - 1); + if ( !pShdr2 + || offSeg > (pShdr2->sh_addr - pModElf->paShdrs[iSeg].sh_addr)) + return VERR_LDR_INVALID_SEG_OFFSET; + } + + if (!(pModElf->paShdrs[iSeg].sh_flags & SHF_ALLOC)) + return VERR_LDR_INVALID_SEG_OFFSET; - return VERR_NOT_IMPLEMENTED; NOREF(pModElf); NOREF(iSeg); NOREF(offSeg); NOREF(pRva); + *pRva = pModElf->paShdrs[iSeg].sh_addr; + return VINF_SUCCESS; } @@ -780,7 +1240,136 @@ static DECLCALLBACK(int) RTLDRELF_NAME(RvaToSegOffset)(PRTLDRMODINTERNAL pMod, R { PRTLDRMODELF pModElf = (PRTLDRMODELF)pMod; - return VERR_NOT_IMPLEMENTED; NOREF(pModElf); NOREF(Rva); NOREF(piSeg); NOREF(poffSeg); + Elf_Addr PrevAddr = 0; + unsigned cLeft = pModElf->Ehdr.e_shnum - 1; + const Elf_Shdr *pShdr = &pModElf->paShdrs[cLeft]; + while (cLeft-- > 0) + { + if (pShdr->sh_flags & SHF_ALLOC) + { + Elf_Addr cbSeg = PrevAddr ? PrevAddr - pShdr->sh_addr : pShdr->sh_size; + RTLDRADDR offSeg = Rva - pShdr->sh_addr; + if (offSeg <= cbSeg) + { + *poffSeg = offSeg; + *piSeg = cLeft; + return VINF_SUCCESS; + } + PrevAddr = pShdr->sh_addr; + } + pShdr--; + } + + return VERR_LDR_INVALID_RVA; +} + + +/** @callback_method_impl{FNRTLDRIMPORT, Stub used by ReadDbgInfo.} */ +static DECLCALLBACK(int) RTLDRELF_NAME(GetImportStubCallback)(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol, + unsigned uSymbol, PRTLDRADDR pValue, void *pvUser) +{ + return VERR_SYMBOL_NOT_FOUND; +} + + +/** @copydoc RTLDROPS::pfnRvaToSegOffset. */ +static DECLCALLBACK(int) RTLDRELF_NAME(ReadDbgInfo)(PRTLDRMODINTERNAL pMod, uint32_t iDbgInfo, RTFOFF off, + size_t cb, void *pvBuf) +{ + PRTLDRMODELF pThis = (PRTLDRMODELF)pMod; + LogFlow(("%s: iDbgInfo=%#x off=%RTfoff cb=%#zu\n", __FUNCTION__, iDbgInfo, off, cb)); + + /* + * Input validation. + */ + AssertReturn(iDbgInfo < pThis->Ehdr.e_shnum && iDbgInfo + 1 < pThis->Ehdr.e_shnum, VERR_INVALID_PARAMETER); + iDbgInfo++; + AssertReturn(!(pThis->paShdrs[iDbgInfo].sh_flags & SHF_ALLOC), VERR_INVALID_PARAMETER); + AssertReturn(pThis->paShdrs[iDbgInfo].sh_type == SHT_PROGBITS, VERR_INVALID_PARAMETER); + AssertReturn(pThis->paShdrs[iDbgInfo].sh_offset == (uint64_t)off, VERR_INVALID_PARAMETER); + AssertReturn(pThis->paShdrs[iDbgInfo].sh_size == cb, VERR_INVALID_PARAMETER); + RTFOFF cbRawImage = pThis->Core.pReader->pfnSize(pThis->Core.pReader); + AssertReturn(cbRawImage >= 0, VERR_INVALID_PARAMETER); + AssertReturn(off >= 0 && cb <= (uint64_t)cbRawImage && (uint64_t)off + cb <= (uint64_t)cbRawImage, VERR_INVALID_PARAMETER); + + /* + * Read it from the file and look for fixup sections. + */ + int rc; + if (pThis->pvBits) + memcpy(pvBuf, (const uint8_t *)pThis->pvBits + (size_t)off, cb); + else + { + rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pvBuf, cb, off); + if (RT_FAILURE(rc)) + return rc; + } + + uint32_t iRelocs = iDbgInfo + 1; + if ( iRelocs >= pThis->Ehdr.e_shnum + || pThis->paShdrs[iRelocs].sh_info != iDbgInfo + || ( pThis->paShdrs[iRelocs].sh_type != SHT_REL + && pThis->paShdrs[iRelocs].sh_type != SHT_RELA) ) + { + iRelocs = 0; + while ( iRelocs < pThis->Ehdr.e_shnum + && ( pThis->paShdrs[iRelocs].sh_info != iDbgInfo + || ( pThis->paShdrs[iRelocs].sh_type != SHT_REL + && pThis->paShdrs[iRelocs].sh_type != SHT_RELA)) ) + iRelocs++; + } + if ( iRelocs < pThis->Ehdr.e_shnum + && pThis->paShdrs[iRelocs].sh_size > 0) + { + /* + * Load the relocations. + */ + uint8_t *pbRelocsBuf = NULL; + const uint8_t *pbRelocs; + if (pThis->pvBits) + pbRelocs = (const uint8_t *)pThis->pvBits + pThis->paShdrs[iRelocs].sh_offset; + else + { + pbRelocs = pbRelocsBuf = (uint8_t *)RTMemTmpAlloc(pThis->paShdrs[iRelocs].sh_size); + if (!pbRelocsBuf) + return VERR_NO_TMP_MEMORY; + rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pbRelocsBuf, + pThis->paShdrs[iRelocs].sh_size, + pThis->paShdrs[iRelocs].sh_offset); + if (RT_FAILURE(rc)) + { + RTMemTmpFree(pbRelocsBuf); + return rc; + } + } + + /* + * Apply the relocations. + */ + if (pThis->Ehdr.e_type == ET_REL) + rc = RTLDRELF_NAME(RelocateSection)(pThis, pThis->LinkAddress, + RTLDRELF_NAME(GetImportStubCallback), NULL /*pvUser*/, + pThis->paShdrs[iDbgInfo].sh_addr, + pThis->paShdrs[iDbgInfo].sh_size, + (const uint8_t *)pvBuf, + (uint8_t *)pvBuf, + pbRelocs, + pThis->paShdrs[iRelocs].sh_size); + else + rc = RTLDRELF_NAME(RelocateSectionExecDyn)(pThis, pThis->LinkAddress, + RTLDRELF_NAME(GetImportStubCallback), NULL /*pvUser*/, + pThis->paShdrs[iDbgInfo].sh_addr, + pThis->paShdrs[iDbgInfo].sh_size, + (const uint8_t *)pvBuf, + (uint8_t *)pvBuf, + pbRelocs, + pThis->paShdrs[iRelocs].sh_size); + + RTMemTmpFree(pbRelocsBuf); + } + else + rc = VINF_SUCCESS; + return rc; } @@ -810,6 +1399,7 @@ static RTLDROPS RTLDRELF_MID(s_rtldrElf,Ops) = RTLDRELF_NAME(LinkAddressToRva), RTLDRELF_NAME(SegOffsetToRva), RTLDRELF_NAME(RvaToSegOffset), + RTLDRELF_NAME(ReadDbgInfo), 42 }; @@ -949,6 +1539,13 @@ static int RTLDRELF_NAME(ValidateElfHeader)(const Elf_Ehdr *pEhdr, const char *p return VERR_BAD_EXE_FORMAT; } + if (pEhdr->e_shstrndx == 0 || pEhdr->e_shstrndx > pEhdr->e_shnum) + { + Log(("RTLdrELF: %s: The section headers string table is out of bounds! e_shstrndx=" FMT_ELF_HALF " e_shnum=" FMT_ELF_HALF "\n", + pszLogName, pEhdr->e_shstrndx, pEhdr->e_shnum)); + return VERR_BAD_EXE_FORMAT; + } + return VINF_SUCCESS; } @@ -956,7 +1553,6 @@ static int RTLDRELF_NAME(ValidateElfHeader)(const Elf_Ehdr *pEhdr, const char *p * Gets the section header name. * * @returns pszName. - * @param pReader The loader reader instance. * @param pEhdr The elf header. * @param offName The offset of the section header name. * @param pszName Where to store the name. @@ -965,13 +1561,13 @@ static int RTLDRELF_NAME(ValidateElfHeader)(const Elf_Ehdr *pEhdr, const char *p const char *RTLDRELF_NAME(GetSHdrName)(PRTLDRMODELF pModElf, Elf_Word offName, char *pszName, size_t cbName) { RTFOFF off = pModElf->paShdrs[pModElf->Ehdr.e_shstrndx].sh_offset + offName; - int rc = pModElf->pReader->pfnRead(pModElf->pReader, pszName, cbName - 1, off); + int rc = pModElf->Core.pReader->pfnRead(pModElf->Core.pReader, pszName, cbName - 1, off); if (RT_FAILURE(rc)) { /* read by for byte. */ for (unsigned i = 0; i < cbName; i++, off++) { - rc = pModElf->pReader->pfnRead(pModElf->pReader, pszName + i, 1, off); + rc = pModElf->Core.pReader->pfnRead(pModElf->Core.pReader, pszName + i, 1, off); if (RT_FAILURE(rc)) { pszName[i] = '\0'; @@ -1016,6 +1612,31 @@ static int RTLDRELF_NAME(ValidateSectionHeader)(PRTLDRMODELF pModElf, unsigned i pShdr->sh_offset, pShdr->sh_size, pShdr->sh_link, pShdr->sh_info, pShdr->sh_addralign, pShdr->sh_entsize)); + if (iShdr == 0) + { + if ( pShdr->sh_name != 0 + || pShdr->sh_type != SHT_NULL + || pShdr->sh_flags != 0 + || pShdr->sh_addr != 0 + || pShdr->sh_size != 0 + || pShdr->sh_offset != 0 + || pShdr->sh_link != SHN_UNDEF + || pShdr->sh_addralign != 0 + || pShdr->sh_entsize != 0 ) + { + Log(("RTLdrELF: %s: Bad #0 section: %.*Rhxs\n", pszLogName, sizeof(*pShdr), pShdr )); + return VERR_BAD_EXE_FORMAT; + } + return VINF_SUCCESS; + } + + if (pShdr->sh_name >= pModElf->cbShStr) + { + Log(("RTLdrELF: %s: Shdr #%d: sh_name (%d) is beyond the end of the section header string table (%d)!\n", + pszLogName, iShdr, pShdr->sh_name, pModElf->cbShStr)); NOREF(pszLogName); + return VERR_BAD_EXE_FORMAT; + } + if (pShdr->sh_link >= pModElf->Ehdr.e_shnum) { Log(("RTLdrELF: %s: Shdr #%d: sh_link (%d) is beyond the end of the section table (%d)!\n", @@ -1036,6 +1657,7 @@ static int RTLDRELF_NAME(ValidateSectionHeader)(PRTLDRMODELF pModElf, unsigned i break; case SHT_NULL: + break; case SHT_PROGBITS: case SHT_SYMTAB: case SHT_STRTAB: @@ -1096,7 +1718,6 @@ static int RTLDRELF_NAME(Open)(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH { const char *pszLogName = pReader->pfnLogName(pReader); RTFOFF cbRawImage = pReader->pfnSize(pReader); - AssertReturn(!fFlags, VERR_INVALID_PARAMETER); /* * Create the loader module instance. @@ -1107,17 +1728,28 @@ static int RTLDRELF_NAME(Open)(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH pModElf->Core.u32Magic = RTLDRMOD_MAGIC; pModElf->Core.eState = LDR_STATE_INVALID; - pModElf->pReader = pReader; + pModElf->Core.pReader = pReader; + pModElf->Core.enmFormat = RTLDRFMT_ELF; + pModElf->Core.enmType = RTLDRTYPE_OBJECT; + pModElf->Core.enmEndian = RTLDRENDIAN_LITTLE; +#if ELF_MODE == 32 + pModElf->Core.enmArch = RTLDRARCH_X86_32; +#else + pModElf->Core.enmArch = RTLDRARCH_AMD64; +#endif //pModElf->pvBits = NULL; //pModElf->Ehdr = {0}; //pModElf->paShdrs = NULL; //pModElf->paSyms = NULL; pModElf->iSymSh = ~0U; - pModElf->cSyms = 0; + //pModElf->cSyms = 0; pModElf->iStrSh = ~0U; - pModElf->cbStr = 0; - pModElf->cbImage = 0; + //pModElf->cbStr = 0; + //pModElf->cbImage = 0; + //pModElf->LinkAddress = 0; //pModElf->pStr = NULL; + //pModElf->cbShStr = 0; + //pModElf->pShStr = NULL; /* * Read and validate the ELF header and match up the CPU architecture. @@ -1137,39 +1769,32 @@ static int RTLDRELF_NAME(Open)(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH if (RT_SUCCESS(rc)) { /* - * Read the section headers. + * Read the section headers, keeping a prestine copy for the module + * introspection methods. */ - Elf_Shdr *paShdrs = (Elf_Shdr *)RTMemAlloc(pModElf->Ehdr.e_shnum * sizeof(Elf_Shdr)); + size_t const cbShdrs = pModElf->Ehdr.e_shnum * sizeof(Elf_Shdr); + Elf_Shdr *paShdrs = (Elf_Shdr *)RTMemAlloc(cbShdrs * 2); if (paShdrs) { pModElf->paShdrs = paShdrs; - rc = pReader->pfnRead(pReader, paShdrs, pModElf->Ehdr.e_shnum * sizeof(Elf_Shdr), - pModElf->Ehdr.e_shoff); + rc = pReader->pfnRead(pReader, paShdrs, cbShdrs, pModElf->Ehdr.e_shoff); if (RT_SUCCESS(rc)) { + memcpy(&paShdrs[pModElf->Ehdr.e_shnum], paShdrs, cbShdrs); + pModElf->paOrgShdrs = &paShdrs[pModElf->Ehdr.e_shnum]; + + pModElf->cbShStr = paShdrs[pModElf->Ehdr.e_shstrndx].sh_size; + /* - * Validate the section headers, allocate memory for the sections (determine the image size), - * and find relevant sections. + * Validate the section headers and find relevant sections. */ + Elf_Addr uNextAddr = 0; for (unsigned i = 0; i < pModElf->Ehdr.e_shnum; i++) { rc = RTLDRELF_NAME(ValidateSectionHeader)(pModElf, i, pszLogName, cbRawImage); if (RT_FAILURE(rc)) break; - /* Allocate memory addresses for the section. */ - if (paShdrs[i].sh_flags & SHF_ALLOC) - { - paShdrs[i].sh_addr = paShdrs[i].sh_addralign - ? RT_ALIGN_T(pModElf->cbImage, paShdrs[i].sh_addralign, Elf_Addr) - : (Elf_Addr)pModElf->cbImage; - pModElf->cbImage = (size_t)paShdrs[i].sh_addr + (size_t)paShdrs[i].sh_size; - AssertMsgReturn(pModElf->cbImage == paShdrs[i].sh_addr + paShdrs[i].sh_size, - (FMT_ELF_ADDR "\n", paShdrs[i].sh_addr + paShdrs[i].sh_size), - VERR_IMAGE_TOO_BIG); - Log2(("RTLdrElf: %s: Assigned " FMT_ELF_ADDR " to section #%d\n", pszLogName, paShdrs[i].sh_addr, i)); - } - /* We're looking for symbol tables. */ if (paShdrs[i].sh_type == SHT_SYMTAB) { @@ -1186,19 +1811,81 @@ static int RTLDRELF_NAME(Open)(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH pModElf->cbStr = (unsigned)paShdrs[pModElf->iStrSh].sh_size; AssertReturn(pModElf->cbStr == paShdrs[pModElf->iStrSh].sh_size, VERR_IMAGE_TOO_BIG); } + + /* Special checks for the section string table. */ + if (i == pModElf->Ehdr.e_shstrndx) + { + if (paShdrs[i].sh_type != SHT_STRTAB) + { + Log(("RTLdrElf: Section header string table is not a SHT_STRTAB: %#x\n", paShdrs[i].sh_type)); + rc = VERR_BAD_EXE_FORMAT; + break; + } + if (paShdrs[i].sh_size == 0) + { + Log(("RTLdrElf: Section header string table is empty\n")); + rc = VERR_BAD_EXE_FORMAT; + break; + } + } + + /* Kluge for the .data..percpu segment in 64-bit linux kernels. */ + if (paShdrs[i].sh_flags & SHF_ALLOC) + { + if ( paShdrs[i].sh_addr == 0 + && paShdrs[i].sh_addr < uNextAddr) + { + Elf_Addr uAddr = RT_ALIGN_T(uNextAddr, paShdrs[i].sh_addralign, Elf_Addr); + Log(("RTLdrElf: Out of order section #%d; adjusting sh_addr from " FMT_ELF_ADDR " to " FMT_ELF_ADDR "\n", + paShdrs[i].sh_addr, uAddr)); + paShdrs[i].sh_addr = uAddr; + } + uNextAddr = paShdrs[i].sh_addr + paShdrs[i].sh_size; + } } /* for each section header */ - Log2(("RTLdrElf: iSymSh=%u cSyms=%u iStrSh=%u cbStr=%u rc=%Rrc cbImage=%#zx\n", - pModElf->iSymSh, pModElf->cSyms, pModElf->iStrSh, pModElf->cbStr, rc, pModElf->cbImage)); -#if 0 /* - * Are the section headers fine? - * We require there to be symbol & string tables (at least for the time being). + * Calculate the image base address if the image isn't relocatable. */ - if ( pModElf->iSymSh == ~0U - || pModElf->iStrSh == ~0U) - rc = VERR_LDRELF_NO_SYMBOL_OR_NO_STRING_TABS; -#endif + if (RT_SUCCESS(rc) && pModElf->Ehdr.e_type != ET_REL) + { + pModElf->LinkAddress = ~(Elf_Addr)0; + for (unsigned i = 0; i < pModElf->Ehdr.e_shnum; i++) + if ( (paShdrs[i].sh_flags & SHF_ALLOC) + && paShdrs[i].sh_addr < pModElf->LinkAddress) + pModElf->LinkAddress = paShdrs[i].sh_addr; + if (pModElf->LinkAddress == ~(Elf_Addr)0) + { + AssertFailed(); + rc = VERR_LDR_GENERAL_FAILURE; + } + } + + /* + * Perform allocations / RVA calculations, determine the image size. + */ + if (RT_SUCCESS(rc)) + for (unsigned i = 0; i < pModElf->Ehdr.e_shnum; i++) + if (paShdrs[i].sh_flags & SHF_ALLOC) + { + if (pModElf->Ehdr.e_type == ET_REL) + paShdrs[i].sh_addr = paShdrs[i].sh_addralign + ? RT_ALIGN_T(pModElf->cbImage, paShdrs[i].sh_addralign, Elf_Addr) + : (Elf_Addr)pModElf->cbImage; + else + paShdrs[i].sh_addr -= pModElf->LinkAddress; + Elf_Addr EndAddr = paShdrs[i].sh_addr + paShdrs[i].sh_size; + if (pModElf->cbImage < EndAddr) + { + pModElf->cbImage = (size_t)EndAddr; + AssertMsgReturn(pModElf->cbImage == EndAddr, (FMT_ELF_ADDR "\n", EndAddr), VERR_IMAGE_TOO_BIG); + } + Log2(("RTLdrElf: %s: Assigned " FMT_ELF_ADDR " to section #%d\n", pszLogName, paShdrs[i].sh_addr, i)); + } + + Log2(("RTLdrElf: iSymSh=%u cSyms=%u iStrSh=%u cbStr=%u rc=%Rrc cbImage=%#zx LinkAddress=" FMT_ELF_ADDR "\n", + pModElf->iSymSh, pModElf->cSyms, pModElf->iStrSh, pModElf->cbStr, rc, + pModElf->cbImage, pModElf->LinkAddress)); if (RT_SUCCESS(rc)) { pModElf->Core.pOps = &RTLDRELF_MID(s_rtldrElf,Ops); diff --git a/src/VBox/Runtime/common/ldr/ldrEx.cpp b/src/VBox/Runtime/common/ldr/ldrEx.cpp index 8bc6b9bd..ba73910d 100644 --- a/src/VBox/Runtime/common/ldr/ldrEx.cpp +++ b/src/VBox/Runtime/common/ldr/ldrEx.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -165,15 +165,6 @@ int rtldrOpenWithReader(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch } -/** - * Gets the size of the loaded image. - * This is only supported for modules which has been opened using RTLdrOpen() and RTLdrOpenBits(). - * - * @returns image size (in bytes). - * @returns ~(size_t)0 on if not opened by RTLdrOpen(). - * @param hLdrMod Handle to the loader module. - * @remark Not supported for RTLdrLoad() images. - */ RTDECL(size_t) RTLdrSize(RTLDRMOD hLdrMod) { LogFlow(("RTLdrSize: hLdrMod=%RTldrm\n", hLdrMod)); @@ -547,3 +538,34 @@ RTDECL(int) RTLdrRvaToSegOffset(RTLDRMOD hLdrMod, RTLDRADDR Rva, uint32_t *piSeg } RT_EXPORT_SYMBOL(RTLdrRvaToSegOffset); + +/** + * Internal method used by the IPRT debug bits. + * + * @returns IPRT status code. + * @param hLdrMod The loader handle which executable we wish to + * read from. + * @param pvBuf The output buffer. + * @param iDbgInfo The debug info ordinal number if the request + * corresponds exactly to a debug info part from + * pfnEnumDbgInfo. Otherwise, pass UINT32_MAX. + * @param off Where in the executable file to start reading. + * @param cb The number of bytes to read. + * + * @remarks Fixups will only be applied if @a iDbgInfo is specified. + */ +DECLHIDDEN(int) rtLdrReadAt(RTLDRMOD hLdrMod, void *pvBuf, uint32_t iDbgInfo, RTFOFF off, size_t cb) +{ + AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), VERR_INVALID_HANDLE); + PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod; + + if (iDbgInfo != UINT32_MAX) + { + AssertReturn(pMod->pOps->pfnReadDbgInfo, VERR_NOT_SUPPORTED); + return pMod->pOps->pfnReadDbgInfo(pMod, iDbgInfo, off, cb, pvBuf); + } + + AssertReturn(pMod->pReader, VERR_NOT_SUPPORTED); + return pMod->pReader->pfnRead(pMod->pReader, pvBuf, cb, off); +} + diff --git a/src/VBox/Runtime/common/ldr/ldrFile.cpp b/src/VBox/Runtime/common/ldr/ldrFile.cpp index 65f408d4..e10fe702 100644 --- a/src/VBox/Runtime/common/ldr/ldrFile.cpp +++ b/src/VBox/Runtime/common/ldr/ldrFile.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -253,7 +253,7 @@ RTDECL(int) RTLdrOpen(const char *pszFilename, uint32_t fFlags, RTLDRARCH enmArc { LogFlow(("RTLdrOpen: pszFilename=%p:{%s} fFlags=%#x enmArch=%d phLdrMod=%p\n", pszFilename, pszFilename, fFlags, enmArch, phLdrMod)); - AssertMsgReturn(!fFlags, ("%#x\n", fFlags), VERR_INVALID_PARAMETER); + AssertMsgReturn(!(fFlags & ~RTLDR_O_VALID_MASK), ("%#x\n", fFlags), VERR_INVALID_PARAMETER); AssertMsgReturn(enmArch > RTLDRARCH_INVALID && enmArch < RTLDRARCH_END, ("%d\n", enmArch), VERR_INVALID_PARAMETER); /* @@ -305,6 +305,7 @@ RTDECL(int) RTLdrOpenkLdr(const char *pszFilename, uint32_t fFlags, RTLDRARCH en #ifdef LDR_WITH_KLDR LogFlow(("RTLdrOpenkLdr: pszFilename=%p:{%s} fFlags=%#x enmArch=%d phLdrMod=%p\n", pszFilename, pszFilename, fFlags, enmArch, phLdrMod)); + AssertMsgReturn(!(fFlags & ~RTLDR_O_VALID_MASK), ("%#x\n", fFlags), VERR_INVALID_PARAMETER); /* * Resolve RTLDRARCH_HOST. diff --git a/src/VBox/Runtime/common/ldr/ldrMemory.cpp b/src/VBox/Runtime/common/ldr/ldrMemory.cpp new file mode 100644 index 00000000..7e82c90b --- /dev/null +++ b/src/VBox/Runtime/common/ldr/ldrMemory.cpp @@ -0,0 +1,324 @@ + +/* $Id: ldrMemory.cpp $ */ +/** @file + * IPRT - Binary Image Loader, The Memory/Debugger Oriented Parts. + */ + +/* + * Copyright (C) 2006-2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#define LOG_GROUP RTLOGGROUP_LDR +#include <iprt/ldr.h> +#include "internal/iprt.h" + +#include <iprt/alloc.h> +#include <iprt/assert.h> +#include <iprt/log.h> +#include <iprt/err.h> +#include <iprt/string.h> +#include "internal/ldr.h" + + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ +/** + * Memory reader (for debuggers) instance. + */ +typedef struct RTLDRRDRMEM +{ + /** The core. */ + RTLDRREADER Core; + /** The size of the image. */ + size_t cbImage; + /** The current offset. */ + size_t offCur; + + /** User parameter for the reader and destructor functions.*/ + void *pvUser; + /** Read function. */ + PFNRTLDRRDRMEMREAD pfnRead; + /** Destructor callback. */ + PFNRTLDRRDRMEMDTOR pfnDtor; + + /** Mapping of the file. */ + void *pvMapping; + /** Mapping usage counter. */ + uint32_t cMappings; + + /** The fake filename (variable size). */ + char szName[1]; +} RTLDRRDRMEM; +/** Memory based loader reader instance data. */ +typedef RTLDRRDRMEM *PRTLDRRDRMEM; + + +/** @callback_method_impl{FNRTLDRRDRMEMDTOR, + * Default destructor - pvUser points to the image memory block.} + */ +static DECLCALLBACK(void) rtldrRdrMemDefaultDtor(void *pvUser) +{ + RTMemFree(pvUser); +} + + +/** @callback_method_impl{FNRTLDRRDRMEMREAD, + * Default memory reader - pvUser points to the image memory block.} + */ +static DECLCALLBACK(int) rtldrRdrMemDefaultReader(void *pvBuf, size_t cb, size_t off, void *pvUser) +{ + memcpy(pvBuf, (uint8_t *)pvUser + off, cb); + return VINF_SUCCESS; +} + + +/** @copydoc RTLDRREADER::pfnRead */ +static DECLCALLBACK(int) rtldrRdrMem_Read(PRTLDRREADER pReader, void *pvBuf, size_t cb, RTFOFF off) +{ + PRTLDRRDRMEM pThis = (PRTLDRRDRMEM)pReader; + + AssertReturn(off >= 0, VERR_INVALID_PARAMETER); + if ( cb > pThis->cbImage + || off > (RTFOFF)pThis->cbImage + || off + (RTFOFF)cb > (RTFOFF)pThis->cbImage) + { + pThis->offCur = pThis->cbImage; + return VERR_EOF; + } + + int rc = pThis->pfnRead(pvBuf, cb, (size_t)off, pThis->pvUser); + if (RT_SUCCESS(rc)) + pThis->offCur = (size_t)off + cb; + else + pThis->offCur = ~(size_t)0; + return rc; +} + + +/** @copydoc RTLDRREADER::pfnTell */ +static DECLCALLBACK(RTFOFF) rtldrRdrMem_Tell(PRTLDRREADER pReader) +{ + PRTLDRRDRMEM pThis = (PRTLDRRDRMEM)pReader; + return pThis->offCur; +} + + +/** @copydoc RTLDRREADER::pfnSize */ +static DECLCALLBACK(RTFOFF) rtldrRdrMem_Size(PRTLDRREADER pReader) +{ + PRTLDRRDRMEM pThis = (PRTLDRRDRMEM)pReader; + return pThis->cbImage; +} + + +/** @copydoc RTLDRREADER::pfnLogName */ +static DECLCALLBACK(const char *) rtldrRdrMem_LogName(PRTLDRREADER pReader) +{ + PRTLDRRDRMEM pThis = (PRTLDRRDRMEM)pReader; + return pThis->szName; +} + + +/** @copydoc RTLDRREADER::pfnMap */ +static DECLCALLBACK(int) rtldrRdrMem_Map(PRTLDRREADER pReader, const void **ppvBits) +{ + PRTLDRRDRMEM pThis = (PRTLDRRDRMEM)pReader; + + /* + * Already mapped? + */ + if (pThis->pvMapping) + { + pThis->cMappings++; + *ppvBits = pThis->pvMapping; + return VINF_SUCCESS; + } + + /* + * Allocate memory. + */ + pThis->pvMapping = RTMemAlloc(pThis->cbImage); + if (!pThis->pvMapping) + return VERR_NO_MEMORY; + int rc = rtldrRdrMem_Read(pReader, pThis->pvMapping, pThis->cbImage, 0); + if (RT_SUCCESS(rc)) + { + pThis->cMappings = 1; + *ppvBits = pThis->pvMapping; + } + else + { + RTMemFree(pThis->pvMapping); + pThis->pvMapping = NULL; + } + + return rc; +} + + +/** @copydoc RTLDRREADER::pfnUnmap */ +static DECLCALLBACK(int) rtldrRdrMem_Unmap(PRTLDRREADER pReader, const void *pvBits) +{ + PRTLDRRDRMEM pThis = (PRTLDRRDRMEM)pReader; + AssertReturn(pThis->cMappings > 0, VERR_INVALID_PARAMETER); + + if (!--pThis->cMappings) + { + RTMemFree(pThis->pvMapping); + pThis->pvMapping = NULL; + } + + NOREF(pvBits); + return VINF_SUCCESS; +} + + +/** @copydoc RTLDRREADER::pfnDestroy */ +static DECLCALLBACK(int) rtldrRdrMem_Destroy(PRTLDRREADER pReader) +{ + PRTLDRRDRMEM pThis = (PRTLDRRDRMEM)pReader; + pThis->pfnDtor(pThis->pvUser); + RTMemFree(pThis); + return VINF_SUCCESS; +} + + +/** + * Opens a memory based loader reader. + * + * @returns iprt status code. + * @param ppReader Where to store the reader instance on success. + * @param pszName The name to give the image. + * @param cbImage The image size. + * @param pfnRead The reader function. If NULL, a default reader is + * used that assumes pvUser points to a memory buffer + * of at least @a cbImage size. + * @param pfnDtor The destructor. If NULL, a default destructore is + * used that will call RTMemFree on @a pvUser. + * @param pvUser User argument. If either @a pfnRead or @a pfnDtor + * is NULL, this must be a pointer to readable memory + * (see above). + */ +static int rtldrRdrMem_Create(PRTLDRREADER *ppReader, const char *pszName, size_t cbImage, + PFNRTLDRRDRMEMREAD pfnRead, PFNRTLDRRDRMEMDTOR pfnDtor, void *pvUser) +{ +#if ARCH_BITS > 32 /* 'ing gcc. */ + AssertReturn(cbImage < RTFOFF_MAX, VERR_INVALID_PARAMETER); +#endif + AssertReturn((RTFOFF)cbImage > 0, VERR_INVALID_PARAMETER); + + size_t cchName = strlen(pszName); + int rc = VERR_NO_MEMORY; + PRTLDRRDRMEM pThis = (PRTLDRRDRMEM)RTMemAlloc(sizeof(*pThis) + cchName); + if (pThis) + { + memcpy(pThis->szName, pszName, cchName + 1); + pThis->cbImage = cbImage; + pThis->pvUser = pvUser; + pThis->offCur = 0; + pThis->pvUser = pvUser; + pThis->pfnRead = pfnRead ? pfnRead : rtldrRdrMemDefaultReader; + pThis->pfnDtor = pfnDtor ? pfnDtor : rtldrRdrMemDefaultDtor; + pThis->pvMapping = NULL; + pThis->cMappings = 0; + pThis->Core.pszName = "rdrmem"; + pThis->Core.pfnRead = rtldrRdrMem_Read; + pThis->Core.pfnTell = rtldrRdrMem_Tell; + pThis->Core.pfnSize = rtldrRdrMem_Size; + pThis->Core.pfnLogName = rtldrRdrMem_LogName; + pThis->Core.pfnMap = rtldrRdrMem_Map; + pThis->Core.pfnUnmap = rtldrRdrMem_Unmap; + pThis->Core.pfnDestroy = rtldrRdrMem_Destroy; + *ppReader = &pThis->Core; + return VINF_SUCCESS; + } + + *ppReader = NULL; + return rc; +} + + +RTDECL(int) RTLdrOpenInMemory(const char *pszName, uint32_t fFlags, RTLDRARCH enmArch, size_t cbImage, + PFNRTLDRRDRMEMREAD pfnRead, PFNRTLDRRDRMEMDTOR pfnDtor, void *pvUser, + PRTLDRMOD phLdrMod) +{ + LogFlow(("RTLdrOpenInMemory: pszName=%p:{%s} fFlags=%#x enmArch=%d cbImage=%#zx pfnRead=%p pfnDtor=%p pvUser=%p phLdrMod=%p\n", + pszName, pszName, fFlags, enmArch, cbImage, pfnRead, pfnDtor, pvUser, phLdrMod)); + + if (!pfnRead || !pfnDtor) + AssertPtrReturn(pvUser, VERR_INVALID_POINTER); + if (!pfnDtor) + pfnDtor = rtldrRdrMemDefaultDtor; + else + AssertPtrReturn(pfnRead, VERR_INVALID_POINTER); + + /* The rest of the validations will call the destructor. */ + AssertMsgReturnStmt(!(fFlags & ~RTLDR_O_VALID_MASK), ("%#x\n", fFlags), + pfnDtor(pvUser), VERR_INVALID_PARAMETER); + AssertMsgReturnStmt(enmArch > RTLDRARCH_INVALID && enmArch < RTLDRARCH_END, ("%d\n", enmArch), + pfnDtor(pvUser), VERR_INVALID_PARAMETER); + if (!pfnRead) + pfnRead = rtldrRdrMemDefaultReader; + else + AssertReturnStmt(RT_VALID_PTR(pfnRead), pfnDtor(pvUser), VERR_INVALID_POINTER); + AssertReturnStmt(cbImage > 0, pfnDtor(pvUser), VERR_INVALID_PARAMETER); + + /* + * Resolve RTLDRARCH_HOST. + */ + if (enmArch == RTLDRARCH_HOST) +#if defined(RT_ARCH_AMD64) + enmArch = RTLDRARCH_AMD64; +#elif defined(RT_ARCH_X86) + enmArch = RTLDRARCH_X86_32; +#else + enmArch = RTLDRARCH_WHATEVER; +#endif + + /* + * Create file reader & invoke worker which identifies and calls the image interpreter. + */ + PRTLDRREADER pReader = NULL; /* gcc may be wrong */ + int rc = rtldrRdrMem_Create(&pReader, pszName, cbImage, pfnRead, pfnDtor, pvUser); + if (RT_SUCCESS(rc)) + { + rc = rtldrOpenWithReader(pReader, fFlags, enmArch, phLdrMod); + if (RT_SUCCESS(rc)) + { + LogFlow(("RTLdrOpen: return %Rrc *phLdrMod\n", rc, *phLdrMod)); + return rc; + } + + pReader->pfnDestroy(pReader); + } + else + pfnDtor(pvUser), + *phLdrMod = NIL_RTLDRMOD; + + LogFlow(("RTLdrOpen: return %Rrc\n", rc)); + return rc; +} +RT_EXPORT_SYMBOL(RTLdrOpenInMemory); + diff --git a/src/VBox/Runtime/common/ldr/ldrNative.cpp b/src/VBox/Runtime/common/ldr/ldrNative.cpp index ad94d1cf..38fe571f 100644 --- a/src/VBox/Runtime/common/ldr/ldrNative.cpp +++ b/src/VBox/Runtime/common/ldr/ldrNative.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -62,7 +62,7 @@ static DECLCALLBACK(int) rtldrNativeDone(PRTLDRMODINTERNAL pMod) /** * Operations for a native module. */ -static const RTLDROPS s_rtldrNativeOps = +static const RTLDROPS g_rtldrNativeOps = { "native", rtldrNativeClose, @@ -80,6 +80,7 @@ static const RTLDROPS s_rtldrNativeOps = NULL, NULL, NULL, + NULL, 42 }; @@ -127,10 +128,26 @@ RTDECL(int) RTLdrLoadEx(const char *pszFilename, PRTLDRMOD phLdrMod, uint32_t fF PRTLDRMODNATIVE pMod = (PRTLDRMODNATIVE)RTMemAlloc(sizeof(*pMod)); if (pMod) { - pMod->Core.u32Magic = RTLDRMOD_MAGIC; - pMod->Core.eState = LDR_STATE_LOADED; - pMod->Core.pOps = &s_rtldrNativeOps; - pMod->hNative = ~(uintptr_t)0; + pMod->Core.u32Magic = RTLDRMOD_MAGIC; + pMod->Core.eState = LDR_STATE_LOADED; + pMod->Core.pOps = &g_rtldrNativeOps; + pMod->Core.pReader = NULL; + pMod->Core.enmFormat = RTLDRFMT_NATIVE; + pMod->Core.enmType = RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE; /* approx */ +#ifdef RT_BIG_ENDIAN + pMod->Core.enmEndian = RTLDRENDIAN_BIG; +#else + pMod->Core.enmEndian = RTLDRENDIAN_LITTLE; +#endif +#ifdef RT_ARCH_AMD64 + pMod->Core.enmArch = RTLDRARCH_AMD64; +#elif defined(RT_ARCH_X86) + pMod->Core.enmArch = RTLDRARCH_X86_32; +#else + pMod->Core.enmArch = RTLDRARCH_HOST; +#endif + pMod->hNative = ~(uintptr_t)0; + pMod->fFlags = fFlags; /* * Attempt to open the module. @@ -154,6 +171,54 @@ RTDECL(int) RTLdrLoadEx(const char *pszFilename, PRTLDRMOD phLdrMod, uint32_t fF RT_EXPORT_SYMBOL(RTLdrLoadEx); +RTDECL(int) RTLdrLoadSystem(const char *pszFilename, bool fNoUnload, PRTLDRMOD phLdrMod) +{ + LogFlow(("RTLdrLoadSystem: pszFilename=%p:{%s} fNoUnload=%RTbool phLdrMod=%p\n", + pszFilename, pszFilename, fNoUnload, phLdrMod)); + + /* + * Validate input. + */ + AssertPtrReturn(phLdrMod, VERR_INVALID_PARAMETER); + *phLdrMod = NIL_RTLDRMOD; + AssertPtrReturn(pszFilename, VERR_INVALID_PARAMETER); + AssertMsgReturn(!RTPathHavePath(pszFilename), ("%s\n", pszFilename), VERR_INVALID_PARAMETER); + + /* + * Check the filename. + */ + size_t cchFilename = strlen(pszFilename); + AssertMsgReturn(cchFilename < (RTPATH_MAX / 4) * 3, ("%zu\n", cchFilename), VERR_INVALID_PARAMETER); + + const char *pszExt = ""; + if (!RTPathHaveExt(pszFilename)) + pszExt = RTLdrGetSuff(); + + /* + * Let the platform specific code do the rest. + */ + int rc = rtldrNativeLoadSystem(pszFilename, pszExt, fNoUnload ? RTLDRLOAD_FLAGS_NO_UNLOAD : 0, phLdrMod); + LogFlow(("RTLdrLoadSystem: returns %Rrc\n", rc)); + return rc; +} + + +RTDECL(void *) RTLdrGetSystemSymbol(const char *pszFilename, const char *pszSymbol) +{ + void *pvRet = NULL; + RTLDRMOD hLdrMod; + int rc = RTLdrLoadSystem(pszFilename, true /*fNoUnload*/, &hLdrMod); + if (RT_SUCCESS(rc)) + { + rc = RTLdrGetSymbol(hLdrMod, pszSymbol, &pvRet); + if (RT_FAILURE(rc)) + pvRet = NULL; /* paranoia */ + RTLdrClose(hLdrMod); + } + return pvRet; +} + + /** * Loads a dynamic load library (/shared object) image file residing in the * RTPathAppPrivateArch() directory. @@ -241,3 +306,14 @@ RTDECL(const char *) RTLdrGetSuff(void) } RT_EXPORT_SYMBOL(RTLdrGetSuff); + +RTDECL(uintptr_t) RTLdrGetNativeHandle(RTLDRMOD hLdrMod) +{ + PRTLDRMODNATIVE pThis = (PRTLDRMODNATIVE)hLdrMod; + AssertPtrReturn(pThis, ~(uintptr_t)0); + AssertReturn(pThis->Core.u32Magic == RTLDRMOD_MAGIC, ~(uintptr_t)0); + AssertReturn(pThis->Core.pOps == &g_rtldrNativeOps, ~(uintptr_t)0); + return pThis->hNative; +} +RT_EXPORT_SYMBOL(RTLdrGetNativeHandle); + diff --git a/src/VBox/Runtime/common/ldr/ldrPE.cpp b/src/VBox/Runtime/common/ldr/ldrPE.cpp index 259f8e93..283b861c 100644 --- a/src/VBox/Runtime/common/ldr/ldrPE.cpp +++ b/src/VBox/Runtime/common/ldr/ldrPE.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -35,8 +35,10 @@ #include <iprt/alloc.h> #include <iprt/assert.h> #include <iprt/log.h> +#include <iprt/path.h> #include <iprt/string.h> #include <iprt/err.h> +#include <iprt/formats/codeview.h> #include "internal/ldrPE.h" #include "internal/ldr.h" @@ -62,13 +64,13 @@ typedef struct RTLDRMODPE { /** Core module structure. */ RTLDRMODINTERNAL Core; - /** Pointer to the reader instance. */ - PRTLDRREADER pReader; /** Pointer to internal copy of image bits. * @todo the reader should take care of this. */ void *pvBits; /** The offset of the NT headers. */ RTFOFF offNtHdrs; + /** The offset of the first byte after the section table. */ + RTFOFF offEndOfHdrs; /** The machine type (IMAGE_FILE_HEADER::Machine). */ uint16_t u16Machine; @@ -87,12 +89,16 @@ typedef struct RTLDRMODPE uint32_t cbImage; /** Size of the header (IMAGE_OPTIONAL_HEADER32::SizeOfHeaders). */ uint32_t cbHeaders; + /** The image timestamp. */ + uint32_t uTimestamp; /** The import data directory entry. */ IMAGE_DATA_DIRECTORY ImportDir; /** The base relocation data directory entry. */ IMAGE_DATA_DIRECTORY RelocDir; /** The export data directory entry. */ IMAGE_DATA_DIRECTORY ExportDir; + /** The debug directory entry. */ + IMAGE_DATA_DIRECTORY DebugDir; } RTLDRMODPE, *PRTLDRMODPE; /** @@ -130,7 +136,227 @@ typedef struct RTLDROPSPE *******************************************************************************/ static void rtldrPEConvert32BitOptionalHeaderTo64Bit(PIMAGE_OPTIONAL_HEADER64 pOptHdr); static void rtldrPEConvert32BitLoadConfigTo64Bit(PIMAGE_LOAD_CONFIG_DIRECTORY64 pLoadCfg); -static int rtldrPEApplyFixups(PRTLDRMODPE pModPe, const void *pvBitsR, void *pvBitsW, RTUINTPTR BaseAddress, RTUINTPTR OldBaseAddress); +static int rtldrPEApplyFixups(PRTLDRMODPE pModPe, const void *pvBitsR, void *pvBitsW, RTUINTPTR BaseAddress, RTUINTPTR OldBaseAddress); + + + +/** + * Reads a section of a PE image given by RVA + size, using mapped bits if + * available or allocating heap memory and reading from the file. + * + * @returns IPRT status code. + * @param pThis Pointer to the PE loader module structure. + * @param pvBits Read only bits if available. NULL if not. + * @param uRva The RVA to read at. + * @param cbMem The number of bytes to read. + * @param ppvMem Where to return the memory on success (heap or + * inside pvBits). + */ +static int rtldrPEReadPartByRva(PRTLDRMODPE pThis, const void *pvBits, uint32_t uRva, uint32_t cbMem, void const **ppvMem) +{ + *ppvMem = NULL; + if (!cbMem) + return VINF_SUCCESS; + + /* + * Use bits if we've got some. + */ + if (pvBits) + { + *ppvMem = (uint8_t const *)pvBits + uRva; + return VINF_SUCCESS; + } + if (pThis->pvBits) + { + *ppvMem = (uint8_t const *)pThis->pvBits + uRva; + return VINF_SUCCESS; + } + + /* + * Allocate a buffer and read the bits from the file (or whatever). + */ + if (!pThis->Core.pReader) + return VERR_ACCESS_DENIED; + + uint8_t *pbMem = (uint8_t *)RTMemAllocZ(cbMem); + if (!pbMem) + return VERR_NO_MEMORY; + *ppvMem = pbMem; + + /* Do the reading on a per section base. */ + RTFOFF const cbFile = pThis->Core.pReader->pfnSize(pThis->Core.pReader); + for (;;) + { + /* Translate the RVA into a file offset. */ + uint32_t offFile = uRva; + uint32_t cbToRead = cbMem; + uint32_t cbToAdv = cbMem; + + if (uRva < pThis->paSections[0].VirtualAddress) + { + /* Special header section. */ + cbToRead = pThis->paSections[0].VirtualAddress - uRva; + if (cbToRead > cbMem) + cbToRead = cbMem; + cbToAdv = cbToRead; + + /* The following capping is an approximation. */ + uint32_t offFirstRawData = RT_ALIGN(pThis->cbHeaders, _4K); + if ( pThis->paSections[0].PointerToRawData > 0 + && pThis->paSections[0].SizeOfRawData > 0) + offFirstRawData = pThis->paSections[0].PointerToRawData; + if (offFile > offFirstRawData) + cbToRead = 0; + else if (offFile + cbToRead > offFirstRawData) + cbToRead = offFile + cbToRead - offFirstRawData; + } + else + { + /* Find the matching section and its mapping size. */ + uint32_t j = 0; + uint32_t cbMapping = 0; + while (j < pThis->cSections) + { + cbMapping = (j + 1 < pThis->cSections ? pThis->paSections[j + 1].VirtualAddress : pThis->cbImage) + - pThis->paSections[j].VirtualAddress; + if (uRva - pThis->paSections[j].VirtualAddress < cbMapping) + break; + j++; + } + if (j >= cbMapping) + break; /* This shouldn't happen, just return zeros if it does. */ + + /* Adjust the sizes and calc the file offset. */ + if (cbToAdv > cbMapping) + cbToAdv = cbToRead = cbMapping; + if ( pThis->paSections[j].PointerToRawData > 0 + && pThis->paSections[j].SizeOfRawData > 0) + { + offFile = uRva - pThis->paSections[j].VirtualAddress; + if (offFile + cbToRead > pThis->paSections[j].SizeOfRawData) + cbToRead = pThis->paSections[j].SizeOfRawData - offFile; + offFile += pThis->paSections[j].PointerToRawData; + } + else + { + offFile = UINT32_MAX; + cbToRead = 0; + } + } + + /* Perform the read after adjusting a little (paranoia). */ + if (offFile > cbFile) + cbToRead = 0; + if (cbToRead) + { + if ((RTFOFF)offFile + cbToRead > cbFile) + cbToRead = cbFile - (RTFOFF)offFile; + int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pbMem, cbToRead, offFile); + if (RT_FAILURE(rc)) + { + RTMemFree((void *)*ppvMem); + *ppvMem = NULL; + return rc; + } + } + + /* Advance */ + if (cbMem == cbToRead) + break; + cbMem -= cbToRead; + pbMem += cbToRead; + uRva += cbToRead; + } + + return VINF_SUCCESS; +} + + +/** + * Reads a part of a PE file from the file and into a heap block. + * + * @returns IRPT status code. + * @param pThis Pointer to the PE loader module structure.. + * @param offFile The file offset. + * @param cbMem The number of bytes to read. + * @param ppvMem Where to return the heap block with the bytes on + * success. + */ +static int rtldrPEReadPartFromFile(PRTLDRMODPE pThis, uint32_t offFile, uint32_t cbMem, void const **ppvMem) +{ + *ppvMem = NULL; + if (!cbMem) + return VINF_SUCCESS; + + /* + * Allocate a buffer and read the bits from the file (or whatever). + */ + if (!pThis->Core.pReader) + return VERR_ACCESS_DENIED; + + uint8_t *pbMem = (uint8_t *)RTMemAlloc(cbMem); + if (!pbMem) + return VERR_NO_MEMORY; + + int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pbMem, cbMem, offFile); + if (RT_FAILURE(rc)) + { + RTMemFree((void *)*ppvMem); + return rc; + } + + *ppvMem = pbMem; + return VINF_SUCCESS; +} + + +/** + * Reads a part of a PE image into memory one way or another. + * + * Either the RVA or the offFile must be valid. We'll prefer the RVA if + * possible. + * + * @returns IPRT status code. + * @param pThis Pointer to the PE loader module structure. + * @param pvBits Read only bits if available. NULL if not. + * @param uRva The RVA to read at. + * @param offFile The file offset. + * @param cbMem The number of bytes to read. + * @param ppvMem Where to return the memory on success (heap or + * inside pvBits). + */ +static int rtldrPEReadPart(PRTLDRMODPE pThis, const void *pvBits, RTFOFF offFile, RTLDRADDR uRva, + uint32_t cbMem, void const **ppvMem) +{ + if (uRva == NIL_RTLDRADDR || uRva > pThis->cbImage) + { + if (offFile < 0) + return VERR_INVALID_PARAMETER; + return rtldrPEReadPartFromFile(pThis, offFile, cbMem, ppvMem); + } + return rtldrPEReadPartByRva(pThis, pvBits, uRva, cbMem, ppvMem); +} + + +/** + * Frees up memory returned by rtldrPEReadPart*. + * + * @param pThis Pointer to the PE loader module structure.. + * @param pvBits Read only bits if available. NULL if not.. + * @param pvMem The memory we were given by the reader method. + */ +static void rtldrPEFreePart(PRTLDRMODPE pThis, const void *pvBits, void const *pvMem) +{ + if (!pvMem) + return; + + if (pvBits && (uintptr_t)pvBits - (uintptr_t)pvMem < pThis->cbImage) + return; + if (pThis->pvBits && (uintptr_t)pThis->pvBits - (uintptr_t)pvMem < pThis->cbImage) + return; + + RTMemFree((void *)pvMem); +} /** @copydoc RTLDROPS::pfnGetImageSize */ @@ -153,7 +379,7 @@ static int rtldrPEGetBitsNoImportsNorFixups(PRTLDRMODPE pModPe, void *pvBits) /* * Both these checks are related to pfnDone(). */ - PRTLDRREADER pReader = pModPe->pReader; + PRTLDRREADER pReader = pModPe->Core.pReader; if (!pReader) { AssertMsgFailed(("You've called done!\n")); @@ -666,6 +892,140 @@ static DECLCALLBACK(int) rtldrPEGetSymbolEx(PRTLDRMODINTERNAL pMod, const void * } +/** + * Slow version of rtldrPEEnumSymbols that'll work without all of the image + * being accessible. + * + * This is mainly for use in debuggers and similar. + */ +static int rtldrPEEnumSymbolsSlow(PRTLDRMODPE pThis, unsigned fFlags, RTUINTPTR BaseAddress, + PFNRTLDRENUMSYMS pfnCallback, void *pvUser) +{ + /* + * We enumerates by ordinal, which means using a slow linear search for + * getting any name + */ + PCIMAGE_EXPORT_DIRECTORY pExpDir = NULL; + int rc = rtldrPEReadPartByRva(pThis, NULL, pThis->ExportDir.VirtualAddress, pThis->ExportDir.Size, + (void const **)&pExpDir); + if (RT_FAILURE(rc)) + return rc; + uint32_t const cOrdinals = RT_MAX(pExpDir->NumberOfNames, pExpDir->NumberOfFunctions); + + uint32_t const *paAddress = NULL; + rc = rtldrPEReadPartByRva(pThis, NULL, pExpDir->AddressOfFunctions, cOrdinals * sizeof(uint32_t), + (void const **)&paAddress); + uint32_t const *paRVANames = NULL; + if (RT_SUCCESS(rc) && pExpDir->NumberOfNames) + rc = rtldrPEReadPartByRva(pThis, NULL, pExpDir->AddressOfNames, pExpDir->NumberOfNames * sizeof(uint32_t), + (void const **)&paRVANames); + uint16_t const *paOrdinals = NULL; + if (RT_SUCCESS(rc) && pExpDir->NumberOfNames) + rc = rtldrPEReadPartByRva(pThis, NULL, pExpDir->AddressOfNameOrdinals, pExpDir->NumberOfNames * sizeof(uint16_t), + (void const **)&paOrdinals); + if (RT_SUCCESS(rc)) + { + uintptr_t uNamePrev = 0; + for (uint32_t uOrdinal = 0; uOrdinal < cOrdinals; uOrdinal++) + { + if (paAddress[uOrdinal] /* needed? */) + { + /* + * Look for name. + */ + uint32_t uRvaName = UINT32_MAX; + /* Search from previous + 1 to the end. */ + unsigned uName = uNamePrev + 1; + while (uName < pExpDir->NumberOfNames) + { + if (paOrdinals[uName] == uOrdinal) + { + uRvaName = paRVANames[uName]; + uNamePrev = uName; + break; + } + uName++; + } + if (uRvaName == UINT32_MAX) + { + /* Search from start to the previous. */ + uName = 0; + for (uName = 0 ; uName <= uNamePrev; uName++) + { + if (paOrdinals[uName] == uOrdinal) + { + uRvaName = paRVANames[uName]; + uNamePrev = uName; + break; + } + } + } + + /* + * Get address. + */ + uintptr_t uRVAExport = paAddress[uOrdinal]; + RTUINTPTR Value; + if ( uRVAExport - (uintptr_t)pThis->ExportDir.VirtualAddress + < pThis->ExportDir.Size) + { + if (!(fFlags & RTLDR_ENUM_SYMBOL_FLAGS_NO_FWD)) + { + /* Resolve forwarder. */ + AssertMsgFailed(("Forwarders are not supported!\n")); + } + continue; + } + + /* Get plain export address */ + Value = PE_RVA2TYPE(BaseAddress, uRVAExport, RTUINTPTR); + + /* Read in the name if found one. */ + char szAltName[32]; + const char *pszName = NULL; + if (uRvaName != UINT32_MAX) + { + uint32_t cbName = 0x1000 - (uRvaName & 0xfff); + if (cbName < 10 || cbName > 512) + cbName = 128; + rc = rtldrPEReadPartByRva(pThis, NULL, uRvaName, cbName, (void const **)&pszName); + while (RT_SUCCESS(rc) && RTStrNLen(pszName, cbName) == cbName) + { + rtldrPEFreePart(pThis, NULL, pszName); + pszName = NULL; + if (cbName >= _4K) + break; + cbName += 128; + rc = rtldrPEReadPartByRva(pThis, NULL, uRvaName, cbName, (void const **)&pszName); + } + } + if (!pszName) + { + RTStrPrintf(szAltName, sizeof(szAltName), "Ordinal%#x", uOrdinal); + pszName = szAltName; + } + + /* + * Call back. + */ + rc = pfnCallback(&pThis->Core, pszName, uOrdinal + pExpDir->Base, Value, pvUser); + if (pszName != szAltName && pszName) + rtldrPEFreePart(pThis, NULL, pszName); + if (rc) + break; + } + } + } + + rtldrPEFreePart(pThis, NULL, paOrdinals); + rtldrPEFreePart(pThis, NULL, paRVANames); + rtldrPEFreePart(pThis, NULL, paAddress); + rtldrPEFreePart(pThis, NULL, pExpDir); + return rc; + +} + + /** @copydoc RTLDROPS::pfnEnumSymbols */ static DECLCALLBACK(int) rtldrPEEnumSymbols(PRTLDRMODINTERNAL pMod, unsigned fFlags, const void *pvBits, RTUINTPTR BaseAddress, PFNRTLDRENUMSYMS pfnCallback, void *pvUser) @@ -689,7 +1049,7 @@ static DECLCALLBACK(int) rtldrPEEnumSymbols(PRTLDRMODINTERNAL pMod, unsigned fFl { int rc = rtldrPEReadBits(pModPe); if (RT_FAILURE(rc)) - return rc; + return rtldrPEEnumSymbolsSlow(pModPe, fFlags, BaseAddress, pfnCallback, pvUser); } pvBits = pModPe->pvBits; } @@ -744,16 +1104,19 @@ static DECLCALLBACK(int) rtldrPEEnumSymbols(PRTLDRMODINTERNAL pMod, unsigned fFl */ uintptr_t uRVAExport = paAddress[uOrdinal]; RTUINTPTR Value; - if ( uRVAExport - (uintptr_t)pModPe->ExportDir.VirtualAddress - < pModPe->ExportDir.Size) + if ( uRVAExport - (uintptr_t)pModPe->ExportDir.VirtualAddress + < pModPe->ExportDir.Size) { - /* Resolve forwarder. */ - AssertMsgFailed(("Forwarders are not supported!\n")); + if (!(fFlags & RTLDR_ENUM_SYMBOL_FLAGS_NO_FWD)) + { + /* Resolve forwarder. */ + AssertMsgFailed(("Forwarders are not supported!\n")); + } continue; } - else - /* Get plain export address */ - Value = PE_RVA2TYPE(BaseAddress, uRVAExport, RTUINTPTR); + + /* Get plain export address */ + Value = PE_RVA2TYPE(BaseAddress, uRVAExport, RTUINTPTR); /* * Call back. @@ -772,16 +1135,271 @@ static DECLCALLBACK(int) rtldrPEEnumSymbols(PRTLDRMODINTERNAL pMod, unsigned fFl static DECLCALLBACK(int) rtldrPE_EnumDbgInfo(PRTLDRMODINTERNAL pMod, const void *pvBits, PFNRTLDRENUMDBG pfnCallback, void *pvUser) { - NOREF(pMod); NOREF(pvBits); NOREF(pfnCallback); NOREF(pvUser); - return VINF_NOT_SUPPORTED; + PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod; + int rc; + + /* + * Debug info directory empty? + */ + if ( !pModPe->DebugDir.VirtualAddress + || !pModPe->DebugDir.Size) + return VINF_SUCCESS; + + /* + * Get the debug directory. + */ + if (!pvBits) + pvBits = pModPe->pvBits; + + PCIMAGE_DEBUG_DIRECTORY paDbgDir; + int rcRet = rtldrPEReadPartByRva(pModPe, pvBits, pModPe->DebugDir.VirtualAddress, pModPe->DebugDir.Size, + (void const **)&paDbgDir); + if (RT_FAILURE(rcRet)) + return rcRet; + + /* + * Enumerate the debug directory. + */ + uint32_t const cEntries = pModPe->DebugDir.Size / sizeof(paDbgDir[0]); + for (uint32_t i = 0; i < cEntries; i++) + { + if (paDbgDir[i].PointerToRawData < pModPe->offEndOfHdrs) + continue; + if (paDbgDir[i].SizeOfData < 4) + continue; + + void const *pvPart = NULL; + char szPath[RTPATH_MAX]; + RTLDRDBGINFO DbgInfo; + RT_ZERO(DbgInfo.u); + DbgInfo.iDbgInfo = i; + DbgInfo.offFile = paDbgDir[i].PointerToRawData; + DbgInfo.LinkAddress = paDbgDir[i].AddressOfRawData < pModPe->cbImage + && paDbgDir[i].AddressOfRawData >= pModPe->offEndOfHdrs + ? paDbgDir[i].AddressOfRawData : NIL_RTLDRADDR; + DbgInfo.cb = paDbgDir[i].SizeOfData; + DbgInfo.pszExtFile = NULL; + + rc = VINF_SUCCESS; + switch (paDbgDir[i].Type) + { + case IMAGE_DEBUG_TYPE_CODEVIEW: + DbgInfo.enmType = RTLDRDBGINFOTYPE_CODEVIEW; + DbgInfo.u.Cv.cbImage = pModPe->cbImage; + DbgInfo.u.Cv.uMajorVer = paDbgDir[i].MajorVersion; + DbgInfo.u.Cv.uMinorVer = paDbgDir[i].MinorVersion; + DbgInfo.u.Cv.uTimestamp = paDbgDir[i].TimeDateStamp; + if ( paDbgDir[i].SizeOfData < sizeof(szPath) + && paDbgDir[i].SizeOfData > 16 + && ( DbgInfo.LinkAddress != NIL_RTLDRADDR + || DbgInfo.offFile > 0) + ) + { + rc = rtldrPEReadPart(pModPe, pvBits, DbgInfo.offFile, DbgInfo.LinkAddress, paDbgDir[i].SizeOfData, &pvPart); + if (RT_SUCCESS(rc)) + { + PCCVPDB20INFO pCv20 = (PCCVPDB20INFO)pvPart; + if ( pCv20->u32Magic == CVPDB20INFO_MAGIC + && pCv20->offDbgInfo == 0 + && paDbgDir[i].SizeOfData > RT_UOFFSETOF(CVPDB20INFO, szPdbFilename) ) + { + DbgInfo.enmType = RTLDRDBGINFOTYPE_CODEVIEW_PDB20; + DbgInfo.u.Pdb20.cbImage = pModPe->cbImage; + DbgInfo.u.Pdb20.uTimestamp = pCv20->uTimestamp; + DbgInfo.u.Pdb20.uAge = pCv20->uAge; + DbgInfo.pszExtFile = (const char *)&pCv20->szPdbFilename[0]; + } + else if ( pCv20->u32Magic == CVPDB70INFO_MAGIC + && paDbgDir[i].SizeOfData > RT_UOFFSETOF(CVPDB70INFO, szPdbFilename) ) + { + PCCVPDB70INFO pCv70 = (PCCVPDB70INFO)pCv20; + DbgInfo.enmType = RTLDRDBGINFOTYPE_CODEVIEW_PDB70; + DbgInfo.u.Pdb70.cbImage = pModPe->cbImage; + DbgInfo.u.Pdb70.Uuid = pCv70->PdbUuid; + DbgInfo.u.Pdb70.uAge = pCv70->uAge; + DbgInfo.pszExtFile = (const char *)&pCv70->szPdbFilename[0]; + } + } + else + rcRet = rc; + } + break; + + case IMAGE_DEBUG_TYPE_MISC: + DbgInfo.enmType = RTLDRDBGINFOTYPE_UNKNOWN; + if ( paDbgDir[i].SizeOfData < sizeof(szPath) + && paDbgDir[i].SizeOfData > RT_UOFFSETOF(IMAGE_DEBUG_MISC, Data)) + { + DbgInfo.enmType = RTLDRDBGINFOTYPE_CODEVIEW_DBG; + DbgInfo.u.Dbg.cbImage = pModPe->cbImage; + if (DbgInfo.LinkAddress != NIL_RTLDRADDR) + DbgInfo.u.Dbg.uTimestamp = paDbgDir[i].TimeDateStamp; + else + DbgInfo.u.Dbg.uTimestamp = pModPe->uTimestamp; /* NT4 SP1 ntfs.sys hack. Generic? */ + + rc = rtldrPEReadPart(pModPe, pvBits, DbgInfo.offFile, DbgInfo.LinkAddress, paDbgDir[i].SizeOfData, &pvPart); + if (RT_SUCCESS(rc)) + { + PCIMAGE_DEBUG_MISC pMisc = (PCIMAGE_DEBUG_MISC)pvPart; + if ( pMisc->DataType == IMAGE_DEBUG_MISC_EXENAME + && pMisc->Length == paDbgDir[i].SizeOfData) + { + if (!pMisc->Unicode) + DbgInfo.pszExtFile = (const char *)&pMisc->Data[0]; + else + { + char *pszPath = szPath; + rc = RTUtf16ToUtf8Ex((PCRTUTF16)&pMisc->Data[0], + (pMisc->Length - RT_OFFSETOF(IMAGE_DEBUG_MISC, Data)) / sizeof(RTUTF16), + &pszPath, sizeof(szPath), NULL); + if (RT_SUCCESS(rc)) + DbgInfo.pszExtFile = szPath; + else + rcRet = rc; /* continue without a filename. */ + } + } + } + else + rcRet = rc; /* continue without a filename. */ + } + break; + + case IMAGE_DEBUG_TYPE_COFF: + DbgInfo.enmType = RTLDRDBGINFOTYPE_COFF; + DbgInfo.u.Coff.cbImage = pModPe->cbImage; + DbgInfo.u.Coff.uMajorVer = paDbgDir[i].MajorVersion; + DbgInfo.u.Coff.uMinorVer = paDbgDir[i].MinorVersion; + DbgInfo.u.Coff.uTimestamp = paDbgDir[i].TimeDateStamp; + break; + + default: + DbgInfo.enmType = RTLDRDBGINFOTYPE_UNKNOWN; + break; + } + + /* Fix (hack) the file name encoding. We don't have Windows-1252 handy, + so we'll be using Latin-1 as a reasonable approximation. + (I don't think we know exactly which encoding this is anyway, as + it's probably the current ANSI/Windows code page for the process + generating the image anyways.) */ + if (DbgInfo.pszExtFile && DbgInfo.pszExtFile != szPath) + { + char *pszPath = szPath; + rc = RTLatin1ToUtf8Ex(DbgInfo.pszExtFile, + paDbgDir[i].SizeOfData - ((uintptr_t)DbgInfo.pszExtFile - (uintptr_t)pvBits), + &pszPath, sizeof(szPath), NULL); + if (RT_FAILURE(rc)) + { + rcRet = rc; + DbgInfo.pszExtFile = NULL; + } + } + if (DbgInfo.pszExtFile) + RTPathChangeToUnixSlashes(szPath, true /*fForce*/); + + rc = pfnCallback(pMod, &DbgInfo, pvUser); + rtldrPEFreePart(pModPe, pvBits, pvPart); + if (rc != VINF_SUCCESS) + { + rcRet = rc; + break; + } + } + + rtldrPEFreePart(pModPe, pvBits, paDbgDir); + return rcRet; } /** @copydoc RTLDROPS::pfnEnumSegments. */ static DECLCALLBACK(int) rtldrPE_EnumSegments(PRTLDRMODINTERNAL pMod, PFNRTLDRENUMSEGS pfnCallback, void *pvUser) { - NOREF(pMod); NOREF(pfnCallback); NOREF(pvUser); - return VINF_NOT_SUPPORTED; + PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod; + RTLDRSEG SegInfo; + + /* + * The first section is a fake one covering the headers. + */ + SegInfo.pszName = "NtHdrs"; + SegInfo.cchName = 6; + SegInfo.SelFlat = 0; + SegInfo.Sel16bit = 0; + SegInfo.fFlags = 0; + SegInfo.fProt = RTMEM_PROT_READ; + SegInfo.Alignment = 1; + SegInfo.LinkAddress = pModPe->uImageBase; + SegInfo.RVA = 0; + SegInfo.offFile = 0; + SegInfo.cb = pModPe->cbHeaders; + SegInfo.cbFile = pModPe->cbHeaders; + SegInfo.cbMapped = pModPe->cbHeaders; + if ((pModPe->paSections[0].Characteristics & IMAGE_SCN_TYPE_NOLOAD)) + SegInfo.cbMapped = pModPe->paSections[0].VirtualAddress; + int rc = pfnCallback(pMod, &SegInfo, pvUser); + + /* + * Then all the normal sections. + */ + PCIMAGE_SECTION_HEADER pSh = pModPe->paSections; + for (uint32_t i = 0; i < pModPe->cSections && rc == VINF_SUCCESS; i++, pSh++) + { + char szName[32]; + SegInfo.pszName = (const char *)&pSh->Name[0]; + SegInfo.cchName = (uint32_t)RTStrNLen(SegInfo.pszName, sizeof(pSh->Name)); + if (SegInfo.cchName >= sizeof(pSh->Name)) + { + memcpy(szName, &pSh->Name[0], sizeof(pSh->Name)); + szName[sizeof(sizeof(pSh->Name))] = '\0'; + SegInfo.pszName = szName; + } + else if (SegInfo.cchName == 0) + { + SegInfo.pszName = szName; + SegInfo.cchName = (uint32_t)RTStrPrintf(szName, sizeof(szName), "UnamedSect%02u", i); + } + SegInfo.SelFlat = 0; + SegInfo.Sel16bit = 0; + SegInfo.fFlags = 0; + SegInfo.fProt = RTMEM_PROT_NONE; + if (pSh->Characteristics & IMAGE_SCN_MEM_READ) + SegInfo.fProt |= RTMEM_PROT_READ; + if (pSh->Characteristics & IMAGE_SCN_MEM_WRITE) + SegInfo.fProt |= RTMEM_PROT_WRITE; + if (pSh->Characteristics & IMAGE_SCN_MEM_EXECUTE) + SegInfo.fProt |= RTMEM_PROT_EXEC; + SegInfo.Alignment = (pSh->Characteristics & IMAGE_SCN_ALIGN_MASK) >> IMAGE_SCN_ALIGN_SHIFT; + if (SegInfo.Alignment > 0) + SegInfo.Alignment = RT_BIT_64(SegInfo.Alignment - 1); + if (pSh->Characteristics & IMAGE_SCN_TYPE_NOLOAD) + { + SegInfo.LinkAddress = NIL_RTLDRADDR; + SegInfo.RVA = NIL_RTLDRADDR; + SegInfo.cbMapped = pSh->Misc.VirtualSize; + } + else + { + SegInfo.LinkAddress = pSh->VirtualAddress + pModPe->uImageBase ; + SegInfo.RVA = pSh->VirtualAddress; + SegInfo.cbMapped = RT_ALIGN(SegInfo.cb, SegInfo.Alignment); + if (i + 1 < pModPe->cSections && !(pSh[1].Characteristics & IMAGE_SCN_TYPE_NOLOAD)) + SegInfo.cbMapped = pSh[1].VirtualAddress - pSh->VirtualAddress; + } + SegInfo.cb = pSh->Misc.VirtualSize; + if (pSh->PointerToRawData == 0 || pSh->SizeOfRawData == 0) + { + SegInfo.offFile = -1; + SegInfo.cbFile = 0; + } + else + { + SegInfo.offFile = pSh->PointerToRawData; + SegInfo.cbFile = pSh->SizeOfRawData; + } + + rc = pfnCallback(pMod, &SegInfo, pvUser); + } + + return rc; } @@ -789,16 +1407,53 @@ static DECLCALLBACK(int) rtldrPE_EnumSegments(PRTLDRMODINTERNAL pMod, PFNRTLDREN static DECLCALLBACK(int) rtldrPE_LinkAddressToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress, uint32_t *piSeg, PRTLDRADDR poffSeg) { - NOREF(pMod); NOREF(LinkAddress); NOREF(piSeg); NOREF(poffSeg); - return VERR_NOT_IMPLEMENTED; + PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod; + + LinkAddress -= pModPe->uImageBase; + + /* Special header segment. */ + if (LinkAddress < pModPe->paSections[0].VirtualAddress) + { + *piSeg = 0; + *poffSeg = LinkAddress; + return VINF_SUCCESS; + } + + /* + * Search the normal sections. (Could do this in binary fashion, they're + * sorted, but too much bother right now.) + */ + if (LinkAddress > pModPe->cbImage) + return VERR_LDR_INVALID_LINK_ADDRESS; + uint32_t i = pModPe->cSections; + PCIMAGE_SECTION_HEADER paShs = pModPe->paSections; + while (i-- > 0) + if (!(paShs[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD)) + { + uint32_t uAddr = paShs[i].VirtualAddress; + if (LinkAddress >= uAddr) + { + *poffSeg = LinkAddress - uAddr; + *piSeg = i + 1; + return VINF_SUCCESS; + } + } + + return VERR_LDR_INVALID_LINK_ADDRESS; } /** @copydoc RTLDROPS::pfnLinkAddressToRva. */ static DECLCALLBACK(int) rtldrPE_LinkAddressToRva(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress, PRTLDRADDR pRva) { - NOREF(pMod); NOREF(LinkAddress); NOREF(pRva); - return VERR_NOT_IMPLEMENTED; + PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod; + + LinkAddress -= pModPe->uImageBase; + if (LinkAddress > pModPe->cbImage) + return VERR_LDR_INVALID_LINK_ADDRESS; + *pRva = LinkAddress; + + return VINF_SUCCESS; } @@ -806,8 +1461,19 @@ static DECLCALLBACK(int) rtldrPE_LinkAddressToRva(PRTLDRMODINTERNAL pMod, RTLDRA static DECLCALLBACK(int) rtldrPE_SegOffsetToRva(PRTLDRMODINTERNAL pMod, uint32_t iSeg, RTLDRADDR offSeg, PRTLDRADDR pRva) { - NOREF(pMod); NOREF(iSeg); NOREF(offSeg); NOREF(pRva); - return VERR_NOT_IMPLEMENTED; + PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod; + + if (iSeg > pModPe->cSections) + return VERR_LDR_INVALID_SEG_OFFSET; + + /** @todo should validate offSeg here... too lazy right now. */ + if (iSeg == 0) + *pRva = offSeg; + else if (pModPe->paSections[iSeg].Characteristics & IMAGE_SCN_TYPE_NOLOAD) + return VERR_LDR_INVALID_SEG_OFFSET; + else + *pRva = offSeg + pModPe->paSections[iSeg].VirtualAddress; + return VINF_SUCCESS; } @@ -815,8 +1481,11 @@ static DECLCALLBACK(int) rtldrPE_SegOffsetToRva(PRTLDRMODINTERNAL pMod, uint32_t static DECLCALLBACK(int) rtldrPE_RvaToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR Rva, uint32_t *piSeg, PRTLDRADDR poffSeg) { - NOREF(pMod); NOREF(Rva); NOREF(piSeg); NOREF(poffSeg); - return VERR_NOT_IMPLEMENTED; + PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod; + int rc = rtldrPE_LinkAddressToSegOffset(pMod, Rva + pModPe->uImageBase, piSeg, poffSeg); + if (RT_FAILURE(rc)) + rc = VERR_LDR_INVALID_RVA; + return rc; } @@ -829,12 +1498,6 @@ static DECLCALLBACK(int) rtldrPEDone(PRTLDRMODINTERNAL pMod) RTMemFree(pModPe->pvBits); pModPe->pvBits = NULL; } - if (pModPe->pReader) - { - int rc = pModPe->pReader->pfnDestroy(pModPe->pReader); - AssertRC(rc); - pModPe->pReader = NULL; - } return VINF_SUCCESS; } @@ -852,12 +1515,6 @@ static DECLCALLBACK(int) rtldrPEClose(PRTLDRMODINTERNAL pMod) RTMemFree(pModPe->pvBits); pModPe->pvBits = NULL; } - if (pModPe->pReader) - { - int rc = pModPe->pReader->pfnDestroy(pModPe->pReader); - AssertRC(rc); - pModPe->pReader = NULL; - } return VINF_SUCCESS; } @@ -884,6 +1541,7 @@ static const RTLDROPSPE s_rtldrPE32Ops = rtldrPE_LinkAddressToRva, rtldrPE_SegOffsetToRva, rtldrPE_RvaToSegOffset, + NULL, 42 }, rtldrPEResolveImports32, @@ -913,6 +1571,7 @@ static const RTLDROPSPE s_rtldrPE64Ops = rtldrPE_LinkAddressToRva, rtldrPE_SegOffsetToRva, rtldrPE_RvaToSegOffset, + NULL, 42 }, rtldrPEResolveImports64, @@ -1004,10 +1663,11 @@ static void rtldrPEConvert32BitLoadConfigTo64Bit(PIMAGE_LOAD_CONFIG_DIRECTORY64 * * @returns iprt status code. * @param pFileHdr Pointer to the file header that needs validating. + * @param fFlags Valid RTLDR_O_XXX combination. * @param pszLogName The log name to prefix the errors with. * @param penmArch Where to store the CPU architecture. */ -int rtldrPEValidateFileHeader(PIMAGE_FILE_HEADER pFileHdr, const char *pszLogName, PRTLDRARCH penmArch) +static int rtldrPEValidateFileHeader(PIMAGE_FILE_HEADER pFileHdr, uint32_t fFlags, const char *pszLogName, PRTLDRARCH penmArch) { size_t cbOptionalHeader; switch (pFileHdr->Machine) @@ -1034,7 +1694,8 @@ int rtldrPEValidateFileHeader(PIMAGE_FILE_HEADER pFileHdr, const char *pszLogNam return VERR_BAD_EXE_FORMAT; } /* This restriction needs to be implemented elsewhere. */ - if (pFileHdr->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) + if ( (pFileHdr->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) + && !(fFlags & RTLDR_O_FOR_DEBUG)) { Log(("rtldrPEOpen: %s: IMAGE_FILE_RELOCS_STRIPPED\n", pszLogName)); return VERR_BAD_EXE_FORMAT; @@ -1064,9 +1725,10 @@ int rtldrPEValidateFileHeader(PIMAGE_FILE_HEADER pFileHdr, const char *pszLogNam * @param offNtHdrs The offset of the NT headers from the start of the file. * @param pFileHdr Pointer to the file header (valid). * @param cbRawImage The raw image size. + * @param fFlags Loader flags, RTLDR_O_XXX. */ static int rtldrPEValidateOptionalHeader(const IMAGE_OPTIONAL_HEADER64 *pOptHdr, const char *pszLogName, RTFOFF offNtHdrs, - const IMAGE_FILE_HEADER *pFileHdr, RTFOFF cbRawImage) + const IMAGE_FILE_HEADER *pFileHdr, RTFOFF cbRawImage, uint32_t fFlags) { const uint16_t CorrectMagic = pFileHdr->SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32) ? IMAGE_NT_OPTIONAL_HDR32_MAGIC : IMAGE_NT_OPTIONAL_HDR64_MAGIC; @@ -1193,6 +1855,12 @@ static int rtldrPEValidateOptionalHeader(const IMAGE_OPTIONAL_HEADER64 *pOptHdr, Log(("rtldrPEOpen: %s: Security directory is misaligned: %#x\n", pszLogName, i, pDir->VirtualAddress)); return VERR_LDRPE_CERT_MALFORMED; } + /* When using the in-memory reader with a debugger, we may get + into trouble here since we might not have access to the whole + physical file. So skip the tests below. Makes VBoxGuest.sys + load and check out just fine, for instance. */ + if (fFlags & RTLDR_O_FOR_DEBUG) + continue; break; case IMAGE_DIRECTORY_ENTRY_GLOBALPTR: // 8 /* (MIPS GP) */ @@ -1241,9 +1909,10 @@ static int rtldrPEValidateOptionalHeader(const IMAGE_OPTIONAL_HEADER64 *pOptHdr, * @param pszLogName The log name to prefix the errors with. * @param pOptHdr Pointer to the optional header (valid). * @param cbRawImage The raw image size. + * @param fFlags Loader flags, RTLDR_O_XXX. */ -int rtldrPEValidateSectionHeaders(const IMAGE_SECTION_HEADER *paSections, unsigned cSections, const char *pszLogName, - const IMAGE_OPTIONAL_HEADER64 *pOptHdr, RTFOFF cbRawImage) +static int rtldrPEValidateSectionHeaders(const IMAGE_SECTION_HEADER *paSections, unsigned cSections, const char *pszLogName, + const IMAGE_OPTIONAL_HEADER64 *pOptHdr, RTFOFF cbRawImage, uint32_t fFlags) { const uint32_t cbImage = pOptHdr->SizeOfImage; const IMAGE_SECTION_HEADER *pSH = &paSections[0]; @@ -1262,7 +1931,10 @@ int rtldrPEValidateSectionHeaders(const IMAGE_SECTION_HEADER *paSections, unsign pSH->PointerToRawData, pSH->SizeOfRawData, pSH->PointerToRelocations, pSH->NumberOfRelocations, pSH->PointerToLinenumbers, pSH->NumberOfLinenumbers)); - if (pSH->Characteristics & (IMAGE_SCN_MEM_16BIT | IMAGE_SCN_MEM_FARDATA | IMAGE_SCN_MEM_PURGEABLE | IMAGE_SCN_MEM_PRELOAD)) + + AssertCompile(IMAGE_SCN_MEM_16BIT == IMAGE_SCN_MEM_PURGEABLE); + if ( ( pSH->Characteristics & (IMAGE_SCN_MEM_PURGEABLE | IMAGE_SCN_MEM_PRELOAD | IMAGE_SCN_MEM_FARDATA) ) + && !(fFlags & RTLDR_O_FOR_DEBUG)) /* purgable/16-bit seen on w2ksp0 hal.dll, ignore the bunch. */ { Log(("rtldrPEOpen: %s: Unsupported section flag(s) %#x section #%d '%.*s'!!!\n", pszLogName, pSH->Characteristics, iSH, sizeof(pSH->Name), pSH->Name)); @@ -1344,7 +2016,7 @@ int rtldrPEValidateSectionHeaders(const IMAGE_SECTION_HEADER *paSections, unsign static int rtldrPEReadRVA(PRTLDRMODPE pModPe, void *pvBuf, uint32_t cb, uint32_t RVA) { const IMAGE_SECTION_HEADER *pSH = pModPe->paSections; - PRTLDRREADER pReader = pModPe->pReader; + PRTLDRREADER pReader = pModPe->Core.pReader; uint32_t cbRead; int rc; @@ -1421,10 +2093,11 @@ static int rtldrPEReadRVA(PRTLDRMODPE pModPe, void *pvBuf, uint32_t cb, uint32_t * @returns iprt status code. * @param pModPe The PE module instance. * @param pOptHdr Pointer to the optional header (valid). + * @param fFlags Loader flags, RTLDR_O_XXX. */ -int rtldrPEValidateDirectories(PRTLDRMODPE pModPe, const IMAGE_OPTIONAL_HEADER64 *pOptHdr) +static int rtldrPEValidateDirectories(PRTLDRMODPE pModPe, const IMAGE_OPTIONAL_HEADER64 *pOptHdr, uint32_t fFlags) { - const char *pszLogName = pModPe->pReader->pfnLogName(pModPe->pReader); NOREF(pszLogName); + const char *pszLogName = pModPe->Core.pReader->pfnLogName(pModPe->Core.pReader); NOREF(pszLogName); union /* combine stuff we're reading to help reduce stack usage. */ { IMAGE_LOAD_CONFIG_DIRECTORY64 Cfg64; @@ -1490,15 +2163,16 @@ int rtldrPEValidateDirectories(PRTLDRMODPE pModPe, const IMAGE_OPTIONAL_HEADER64 } /* - * If the image is signed, take a look at the signature. + * If the image is signed and we're not doing this for debug purposes, + * take a look at the signature. */ Dir = pOptHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]; - if (Dir.Size) + if (Dir.Size && !(fFlags & RTLDR_O_FOR_DEBUG)) { PWIN_CERTIFICATE pFirst = (PWIN_CERTIFICATE)RTMemTmpAlloc(Dir.Size); if (!pFirst) return VERR_NO_TMP_MEMORY; - int rc = pModPe->pReader->pfnRead(pModPe->pReader, pFirst, Dir.Size, Dir.VirtualAddress); + int rc = pModPe->Core.pReader->pfnRead(pModPe->Core.pReader, pFirst, Dir.Size, Dir.VirtualAddress); if (RT_SUCCESS(rc)) { uint32_t off = 0; @@ -1556,15 +2230,13 @@ int rtldrPEValidateDirectories(PRTLDRMODPE pModPe, const IMAGE_OPTIONAL_HEADER64 * * @returns iprt status code. * @param pReader The loader reader instance which will provide the raw image bits. - * @param fFlags Reserved, MBZ. + * @param fFlags Loader flags, RTLDR_O_XXX. * @param enmArch Architecture specifier. * @param offNtHdrs The offset of the NT headers (where you find "PE\0\0"). * @param phLdrMod Where to store the handle. */ int rtldrPEOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, RTFOFF offNtHdrs, PRTLDRMOD phLdrMod) { - AssertReturn(!fFlags, VERR_INVALID_PARAMETER); - /* * Read and validate the file header. */ @@ -1574,7 +2246,7 @@ int rtldrPEOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, RTFOFF return rc; RTLDRARCH enmArchImage; const char *pszLogName = pReader->pfnLogName(pReader); - rc = rtldrPEValidateFileHeader(&FileHdr, pszLogName, &enmArchImage); + rc = rtldrPEValidateFileHeader(&FileHdr, fFlags, pszLogName, &enmArchImage); if (RT_FAILURE(rc)) return rc; @@ -1594,7 +2266,7 @@ int rtldrPEOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, RTFOFF return rc; if (FileHdr.SizeOfOptionalHeader != sizeof(OptHdr)) rtldrPEConvert32BitOptionalHeaderTo64Bit(&OptHdr); - rc = rtldrPEValidateOptionalHeader(&OptHdr, pszLogName, offNtHdrs, &FileHdr, pReader->pfnSize(pReader)); + rc = rtldrPEValidateOptionalHeader(&OptHdr, pszLogName, offNtHdrs, &FileHdr, pReader->pfnSize(pReader), fFlags); if (RT_FAILURE(rc)) return rc; @@ -1605,11 +2277,12 @@ int rtldrPEOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, RTFOFF PIMAGE_SECTION_HEADER paSections = (PIMAGE_SECTION_HEADER)RTMemAlloc(cbSections); if (!paSections) return VERR_NO_MEMORY; - rc = pReader->pfnRead(pReader, paSections, cbSections, offNtHdrs + 4 + sizeof(IMAGE_FILE_HEADER) + FileHdr.SizeOfOptionalHeader); + rc = pReader->pfnRead(pReader, paSections, cbSections, + offNtHdrs + 4 + sizeof(IMAGE_FILE_HEADER) + FileHdr.SizeOfOptionalHeader); if (RT_SUCCESS(rc)) { rc = rtldrPEValidateSectionHeaders(paSections, FileHdr.NumberOfSections, pszLogName, - &OptHdr, pReader->pfnSize(pReader)); + &OptHdr, pReader->pfnSize(pReader), fFlags); if (RT_SUCCESS(rc)) { /* @@ -1624,9 +2297,24 @@ int rtldrPEOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, RTFOFF pModPe->Core.pOps = &s_rtldrPE64Ops.Core; else pModPe->Core.pOps = &s_rtldrPE32Ops.Core; - pModPe->pReader = pReader; + pModPe->Core.pReader = pReader; + pModPe->Core.enmFormat= RTLDRFMT_PE; + pModPe->Core.enmType = FileHdr.Characteristics & IMAGE_FILE_DLL + ? FileHdr.Characteristics & IMAGE_FILE_RELOCS_STRIPPED + ? RTLDRTYPE_EXECUTABLE_FIXED + : RTLDRTYPE_EXECUTABLE_RELOCATABLE + : FileHdr.Characteristics & IMAGE_FILE_RELOCS_STRIPPED + ? RTLDRTYPE_SHARED_LIBRARY_FIXED + : RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE; + pModPe->Core.enmEndian= RTLDRENDIAN_LITTLE; + pModPe->Core.enmArch = FileHdr.Machine == IMAGE_FILE_MACHINE_I386 + ? RTLDRARCH_X86_32 + : FileHdr.Machine == IMAGE_FILE_MACHINE_AMD64 + ? RTLDRARCH_AMD64 + : RTLDRARCH_WHATEVER; pModPe->pvBits = NULL; pModPe->offNtHdrs = offNtHdrs; + pModPe->offEndOfHdrs = offNtHdrs + 4 + sizeof(IMAGE_FILE_HEADER) + FileHdr.SizeOfOptionalHeader + cbSections; pModPe->u16Machine = FileHdr.Machine; pModPe->fFile = FileHdr.Characteristics; pModPe->cSections = FileHdr.NumberOfSections; @@ -1635,15 +2323,17 @@ int rtldrPEOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, RTFOFF pModPe->uImageBase = (RTUINTPTR)OptHdr.ImageBase; pModPe->cbImage = OptHdr.SizeOfImage; pModPe->cbHeaders = OptHdr.SizeOfHeaders; + pModPe->uTimestamp = FileHdr.TimeDateStamp; pModPe->ImportDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; pModPe->RelocDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; pModPe->ExportDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; + pModPe->DebugDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]; /* * Perform validation of some selected data directories which requires * inspection of the actual data. */ - rc = rtldrPEValidateDirectories(pModPe, &OptHdr); + rc = rtldrPEValidateDirectories(pModPe, &OptHdr, fFlags); if (RT_SUCCESS(rc)) { *phLdrMod = &pModPe->Core; diff --git a/src/VBox/Runtime/common/ldr/ldrkStuff.cpp b/src/VBox/Runtime/common/ldr/ldrkStuff.cpp index 61b36663..da53c29f 100644 --- a/src/VBox/Runtime/common/ldr/ldrkStuff.cpp +++ b/src/VBox/Runtime/common/ldr/ldrkStuff.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -240,9 +240,10 @@ static int rtkldrRdr_Create( PPKRDR ppRdr, const char *pszFilename) /** @copydoc KLDRRDROPS::pfnDestroy */ static int rtkldrRdr_Destroy( PKRDR pRdr) { - PRTLDRREADER pReader = ((PRTKLDRRDR)pRdr)->pReader; - int rc = pReader->pfnDestroy(pReader); - return rtkldrConvertErrorFromIPRT(rc); + PRTKLDRRDR pThis = (PRTKLDRRDR)pRdr; + pThis->pReader = NULL; + RTMemFree(pThis); + return 0; } @@ -598,23 +599,49 @@ static int rtkldrEnumDbgInfoWrapper(PKLDRMOD pMod, KU32 iDbgInfo, KLDRDBGINFOTYP PRTLDRMODKLDRARGS pArgs = (PRTLDRMODKLDRARGS)pvUser; NOREF(pMod); - RTLDRDBGINFOTYPE enmMyType; + RTLDRDBGINFO DbgInfo; + RT_ZERO(DbgInfo.u); + DbgInfo.iDbgInfo = iDbgInfo; + DbgInfo.offFile = offFile; + DbgInfo.LinkAddress = LinkAddress; + DbgInfo.cb = cb; + DbgInfo.pszExtFile = pszExtFile; + switch (enmType) { - case KLDRDBGINFOTYPE_UNKNOWN: enmMyType = RTLDRDBGINFOTYPE_UNKNOWN; break; - case KLDRDBGINFOTYPE_STABS: enmMyType = RTLDRDBGINFOTYPE_STABS; break; - case KLDRDBGINFOTYPE_DWARF: enmMyType = RTLDRDBGINFOTYPE_DWARF; break; - case KLDRDBGINFOTYPE_CODEVIEW: enmMyType = RTLDRDBGINFOTYPE_CODEVIEW; break; - case KLDRDBGINFOTYPE_WATCOM: enmMyType = RTLDRDBGINFOTYPE_WATCOM; break; - case KLDRDBGINFOTYPE_HLL: enmMyType = RTLDRDBGINFOTYPE_HLL; break; + case KLDRDBGINFOTYPE_UNKNOWN: + DbgInfo.enmType = RTLDRDBGINFOTYPE_UNKNOWN; + break; + case KLDRDBGINFOTYPE_STABS: + DbgInfo.enmType = RTLDRDBGINFOTYPE_STABS; + break; + case KLDRDBGINFOTYPE_DWARF: + DbgInfo.enmType = RTLDRDBGINFOTYPE_DWARF; + if (!pszExtFile) + DbgInfo.u.Dwarf.pszSection = pszPartNm; + else + { + DbgInfo.enmType = RTLDRDBGINFOTYPE_DWARF_DWO; + DbgInfo.u.Dwo.uCrc32 = 0; + } + break; + case KLDRDBGINFOTYPE_CODEVIEW: + DbgInfo.enmType = RTLDRDBGINFOTYPE_CODEVIEW; + /* Should make some more effort here... Assume the IPRT loader will kick in before we get here! */ + break; + case KLDRDBGINFOTYPE_WATCOM: + DbgInfo.enmType = RTLDRDBGINFOTYPE_WATCOM; + break; + case KLDRDBGINFOTYPE_HLL: + DbgInfo.enmType = RTLDRDBGINFOTYPE_HLL; + break; default: AssertFailed(); - enmMyType = RTLDRDBGINFOTYPE_UNKNOWN; + DbgInfo.enmType = RTLDRDBGINFOTYPE_UNKNOWN; break; } - int rc = pArgs->u.pfnEnumDbgInfo(&pArgs->pMod->Core, iDbgInfo, enmMyType, iMajorVer, iMinorVer, pszPartNm, - offFile, LinkAddress, cb, pszExtFile, pArgs->pvUser); + int rc = pArgs->u.pfnEnumDbgInfo(&pArgs->pMod->Core, &DbgInfo, pArgs->pvUser); if (RT_FAILURE(rc)) return rc; /* don't bother converting. */ return 0; @@ -645,13 +672,29 @@ static DECLCALLBACK(int) rtkldr_EnumSegments(PRTLDRMODINTERNAL pMod, PFNRTLDRENU PRTLDRMODKLDR pThis = (PRTLDRMODKLDR)pMod; uint32_t const cSegments = pThis->pMod->cSegments; PCKLDRSEG paSegments = &pThis->pMod->aSegments[0]; + char szName[128]; for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++) { RTLDRSEG Seg; - Seg.pchName = paSegments[iSeg].pchName; - Seg.cchName = paSegments[iSeg].cchName; + if (!paSegments[iSeg].cchName) + { + Seg.pszName = szName; + Seg.cchName = (uint32_t)RTStrPrintf(szName, sizeof(szName), "Seg%02u", iSeg); + } + else if (paSegments[iSeg].pchName[paSegments[iSeg].cchName]) + { + AssertReturn(paSegments[iSeg].cchName < sizeof(szName), VERR_INTERNAL_ERROR_3); + RTStrCopyEx(szName, sizeof(szName), paSegments[iSeg].pchName, paSegments[iSeg].cchName); + Seg.pszName = szName; + Seg.cchName = paSegments[iSeg].cchName; + } + else + { + Seg.pszName = paSegments[iSeg].pchName; + Seg.cchName = paSegments[iSeg].cchName; + } Seg.SelFlat = paSegments[iSeg].SelFlat; Seg.Sel16bit = paSegments[iSeg].Sel16bit; Seg.fFlags = paSegments[iSeg].fFlags; @@ -784,6 +827,15 @@ static DECLCALLBACK(int) rtkldr_RvaToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR } +/** @copydoc RTLDROPS::pfnReadDbgInfo. */ +static DECLCALLBACK(int) rtkldr_ReadDbgInfo(PRTLDRMODINTERNAL pMod, uint32_t iDbgInfo, RTFOFF off, size_t cb, void *pvBuf) +{ + PRTLDRMODKLDR pThis = (PRTLDRMODKLDR)pMod; + /** @todo May have to apply fixups here. */ + return pThis->Core.pReader->pfnRead(pThis->Core.pReader, pvBuf, cb, off); +} + + /** * Operations for a kLdr module. */ @@ -805,6 +857,7 @@ static const RTLDROPS g_rtkldrOps = rtkldr_LinkAddressToRva, rtkldr_SegOffsetToRva, rtkldr_RvaToSegOffset, + rtkldr_ReadDbgInfo, 42 }; @@ -836,6 +889,9 @@ int rtldrkLdrOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, PRTL default: return VERR_INVALID_PARAMETER; } + KU32 fKFlags = 0; + if (fFlags & RTLDR_O_FOR_DEBUG) + fKFlags |= KLDRMOD_OPEN_FLAGS_FOR_INFO; /* Create a rtkldrRdr_ instance. */ PRTKLDRRDR pRdr = (PRTKLDRRDR)RTMemAllocZ(sizeof(*pRdr)); @@ -847,7 +903,7 @@ int rtldrkLdrOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, PRTL /* Try open it. */ PKLDRMOD pMod; - int krc = kLdrModOpenFromRdr(&pRdr->Core, fFlags, enmCpuArch, &pMod); + int krc = kLdrModOpenFromRdr(&pRdr->Core, fKFlags, enmCpuArch, &pMod); if (!krc) { /* Create a module wrapper for it. */ @@ -855,9 +911,59 @@ int rtldrkLdrOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, PRTL if (pNewMod) { pNewMod->Core.u32Magic = RTLDRMOD_MAGIC; - pNewMod->Core.eState = LDR_STATE_OPENED; - pNewMod->Core.pOps = &g_rtkldrOps; - pNewMod->pMod = pMod; + pNewMod->Core.eState = LDR_STATE_OPENED; + pNewMod->Core.pOps = &g_rtkldrOps; + pNewMod->Core.pReader = pReader; + switch (pMod->enmFmt) + { + case KLDRFMT_NATIVE: pNewMod->Core.enmFormat = RTLDRFMT_NATIVE; break; + case KLDRFMT_AOUT: pNewMod->Core.enmFormat = RTLDRFMT_AOUT; break; + case KLDRFMT_ELF: pNewMod->Core.enmFormat = RTLDRFMT_ELF; break; + case KLDRFMT_LX: pNewMod->Core.enmFormat = RTLDRFMT_LX; break; + case KLDRFMT_MACHO: pNewMod->Core.enmFormat = RTLDRFMT_MACHO; break; + case KLDRFMT_PE: pNewMod->Core.enmFormat = RTLDRFMT_PE; break; + default: + AssertMsgFailed(("%d\n", pMod->enmFmt)); + pNewMod->Core.enmFormat = RTLDRFMT_NATIVE; + break; + } + switch (pMod->enmType) + { + case KLDRTYPE_OBJECT: pNewMod->Core.enmType = RTLDRTYPE_OBJECT; break; + case KLDRTYPE_EXECUTABLE_FIXED: pNewMod->Core.enmType = RTLDRTYPE_EXECUTABLE_FIXED; break; + case KLDRTYPE_EXECUTABLE_RELOCATABLE: pNewMod->Core.enmType = RTLDRTYPE_EXECUTABLE_RELOCATABLE; break; + case KLDRTYPE_EXECUTABLE_PIC: pNewMod->Core.enmType = RTLDRTYPE_EXECUTABLE_PIC; break; + case KLDRTYPE_SHARED_LIBRARY_FIXED: pNewMod->Core.enmType = RTLDRTYPE_SHARED_LIBRARY_FIXED; break; + case KLDRTYPE_SHARED_LIBRARY_RELOCATABLE: pNewMod->Core.enmType = RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE; break; + case KLDRTYPE_SHARED_LIBRARY_PIC: pNewMod->Core.enmType = RTLDRTYPE_SHARED_LIBRARY_PIC; break; + case KLDRTYPE_FORWARDER_DLL: pNewMod->Core.enmType = RTLDRTYPE_FORWARDER_DLL; break; + case KLDRTYPE_CORE: pNewMod->Core.enmType = RTLDRTYPE_CORE; break; + case KLDRTYPE_DEBUG_INFO: pNewMod->Core.enmType = RTLDRTYPE_DEBUG_INFO; break; + default: + AssertMsgFailed(("%d\n", pMod->enmType)); + pNewMod->Core.enmType = RTLDRTYPE_OBJECT; + break; + } + switch (pMod->enmEndian) + { + case KLDRENDIAN_LITTLE: pNewMod->Core.enmEndian = RTLDRENDIAN_LITTLE; break; + case KLDRENDIAN_BIG: pNewMod->Core.enmEndian = RTLDRENDIAN_BIG; break; + case KLDRENDIAN_NA: pNewMod->Core.enmEndian = RTLDRENDIAN_NA; break; + default: + AssertMsgFailed(("%d\n", pMod->enmEndian)); + pNewMod->Core.enmEndian = RTLDRENDIAN_NA; + break; + } + switch (pMod->enmArch) + { + case KCPUARCH_X86_32: pNewMod->Core.enmArch = RTLDRARCH_X86_32; break; + case KCPUARCH_AMD64: pNewMod->Core.enmArch = RTLDRARCH_AMD64; break; + default: + AssertMsgFailed(("%d\n", pMod->enmArch)); + pNewMod->Core.enmArch = RTLDRARCH_WHATEVER; + break; + } + pNewMod->pMod = pMod; *phLdrMod = &pNewMod->Core; #ifdef LOG_ENABLED @@ -874,9 +980,14 @@ int rtldrkLdrOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, PRTL #endif return VINF_SUCCESS; } + + /* bail out */ kLdrModClose(pMod); krc = KERR_NO_MEMORY; } + else + RTMemFree(pRdr); + return rtkldrConvertError(krc); } |