diff options
Diffstat (limited to 'src/VBox/Debugger/DBGPlugInCommonELFTmpl.cpp.h')
-rw-r--r-- | src/VBox/Debugger/DBGPlugInCommonELFTmpl.cpp.h | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/src/VBox/Debugger/DBGPlugInCommonELFTmpl.cpp.h b/src/VBox/Debugger/DBGPlugInCommonELFTmpl.cpp.h new file mode 100644 index 00000000..12d32b22 --- /dev/null +++ b/src/VBox/Debugger/DBGPlugInCommonELFTmpl.cpp.h @@ -0,0 +1,335 @@ +/* $Id: DBGPlugInCommonELFTmpl.cpp.h $ */ +/** @file + * DBGPlugInCommonELF - Code Template for dealing with one kind of ELF. + */ + +/* + * Copyright (C) 2008-2020 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. + */ + +#if ELF_MODE == 32 +# define Elf_Ehdr Elf32_Ehdr +# define Elf_Shdr Elf32_Shdr +# define Elf_Phdr Elf32_Phdr +# define Elf_Sym Elf32_Sym +# define MY_ELFCLASS ELFCLASS32 +# define ELF_ST_BIND ELF32_ST_BIND +# define DBGDiggerCommonParseElfMod DBGDiggerCommonParseElf32Mod +#else +# define Elf_Ehdr Elf64_Ehdr +# define Elf_Shdr Elf64_Shdr +# define Elf_Phdr Elf64_Phdr +# define Elf_Sym Elf64_Sym +# define MY_ELFCLASS ELFCLASS64 +# define ELF_ST_BIND ELF64_ST_BIND +# define DBGDiggerCommonParseElfMod DBGDiggerCommonParseElf64Mod +#endif + + +/** + * Common ELF module parser. + * + * It takes the essential bits of the ELF module (elf header, section headers, + * symbol table and string table), and inserts/updates the module and symbols. + * + * + * @returns VBox status code. + * + * @param pUVM The user mode VM handle. + * @param pszModName The module name. + * @param pszFilename The filename. optional. + * @param fFlags Flags. + * @param pEhdr Pointer to the ELF header. + * @param paShdrs Pointer to the section headers. The caller must verify that + * the e_shnum member of the ELF header is within the bounds of + * this table. The caller should also adjust the section addresses + * so these correspond to actual load addresses. + * @param paSyms Pointer to the symbol table. + * @param cMaxSyms The maximum number of symbols paSyms may hold. This isn't + * the exact count, it's just a cap for avoiding SIGSEGVs + * and general corruption. + * @param pbStrings Pointer to the string table. + * @param cbMaxStrings The size of the memory pbStrings points to. This doesn't + * have to match the string table size exactly, it's just to + * avoid SIGSEGV when a bad string index is encountered. + * @param MinAddr Min address to care about. + * @param MaxAddr Max address to care about (inclusive). Together + * with MinAddr this forms a valid address range for + * symbols and sections that we care about. Anything + * outside the range is ignored, except when doing + * sanity checks.. + * @param uModTag Module tag. Pass 0 if tagging is of no interest. + */ +int DBGDiggerCommonParseElfMod(PUVM pUVM, const char *pszModName, const char *pszFilename, uint32_t fFlags, + Elf_Ehdr const *pEhdr, Elf_Shdr const *paShdrs, + Elf_Sym const *paSyms, size_t cMaxSyms, + char const *pbStrings, size_t cbMaxStrings, + RTGCPTR MinAddr, RTGCPTR MaxAddr, uint64_t uModTag) +{ + AssertPtrReturn(pUVM, VERR_INVALID_POINTER); + AssertPtrReturn(pszModName, VERR_INVALID_POINTER); + AssertPtrReturn(pszFilename, VERR_INVALID_POINTER); + AssertReturn(!(fFlags & ~DBG_DIGGER_ELF_MASK), VERR_INVALID_PARAMETER); + AssertReturn((fFlags & (DBG_DIGGER_ELF_FUNNY_SHDRS | DBG_DIGGER_ELF_ADJUST_SYM_VALUE)) + != (DBG_DIGGER_ELF_FUNNY_SHDRS | DBG_DIGGER_ELF_ADJUST_SYM_VALUE), VERR_INVALID_PARAMETER); + AssertPtrReturn(paShdrs, VERR_INVALID_POINTER); + AssertPtrReturn(paSyms, VERR_INVALID_POINTER); + AssertPtrReturn(pbStrings, VERR_INVALID_POINTER); + + /* + * Validate the ELF header. + */ + if ( pEhdr->e_ident[EI_MAG0] != ELFMAG0 + || pEhdr->e_ident[EI_MAG1] != ELFMAG1 + || pEhdr->e_ident[EI_MAG2] != ELFMAG2 + || pEhdr->e_ident[EI_MAG3] != ELFMAG3) + return VERR_INVALID_EXE_SIGNATURE; + if (pEhdr->e_ident[EI_CLASS] != MY_ELFCLASS) + return VERR_LDRELF_MACHINE; + + if (pEhdr->e_ident[EI_DATA] != ELFDATA2LSB) + return VERR_LDRELF_ODD_ENDIAN; + if (pEhdr->e_ident[EI_VERSION] != EV_CURRENT) + return VERR_LDRELF_VERSION; + if (pEhdr->e_version != EV_CURRENT) + return VERR_LDRELF_VERSION; + if (pEhdr->e_ehsize != sizeof(*pEhdr)) + return VERR_BAD_EXE_FORMAT; + +#if ELF_MODE == 32 + if ( pEhdr->e_machine != EM_386 + && pEhdr->e_machine != EM_486) + return VERR_LDRELF_MACHINE; +#else + if (pEhdr->e_machine != EM_X86_64) + return VERR_LDRELF_MACHINE; +#endif + + if ( pEhdr->e_type != ET_DYN + && pEhdr->e_type != ET_REL + && pEhdr->e_type != ET_EXEC) //?? + return VERR_BAD_EXE_FORMAT; + if ( pEhdr->e_phentsize != sizeof(Elf_Phdr) + && pEhdr->e_phentsize) //?? + return VERR_BAD_EXE_FORMAT; + if (pEhdr->e_shentsize != sizeof(Elf_Shdr)) + return VERR_BAD_EXE_FORMAT; + if (pEhdr->e_shentsize != sizeof(Elf_Shdr)) + return VERR_BAD_EXE_FORMAT; + if (!ASMMemIsZero(&pEhdr->e_ident[EI_PAD], EI_NIDENT - EI_PAD)) //?? + return VERR_BAD_EXE_FORMAT; + + /* + * Validate the section headers, finding the string and symbol table + * headers and the load address while at it. + */ + uint64_t uLoadAddr = UINT64_MAX; + const Elf_Shdr *pSymShdr = NULL; + const Elf_Shdr *pStrShdr = NULL; + for (unsigned iSh = fFlags & DBG_DIGGER_ELF_FUNNY_SHDRS ? 1 : 0; iSh < pEhdr->e_shnum; iSh++) + { + /* Minimal validation. */ + if (paShdrs[iSh].sh_link >= pEhdr->e_shnum) + return VERR_BAD_EXE_FORMAT; + + /* Is it the symbol table?*/ + if (paShdrs[iSh].sh_type == SHT_SYMTAB) + { + if (pSymShdr) + return VERR_LDRELF_MULTIPLE_SYMTABS; + pSymShdr = &paShdrs[iSh]; + if (pSymShdr->sh_entsize != sizeof(Elf32_Sym)) + return VERR_BAD_EXE_FORMAT; + pStrShdr = &paShdrs[paShdrs[iSh].sh_link]; + } + if (uLoadAddr > paShdrs[iSh].sh_addr) + uLoadAddr = paShdrs[iSh].sh_addr; + } + + /* + * Validate the symbol table and determine the max section index + * when DBG_DIGGER_ELF_FUNNY_SHDRS is flagged. + */ + uint32_t uMaxShIdx = fFlags & DBG_DIGGER_ELF_FUNNY_SHDRS ? 0 : pEhdr->e_shnum - 1; + size_t const cbStrings = pStrShdr ? pStrShdr->sh_size : cbMaxStrings; + size_t const cSyms = pSymShdr + ? RT_MIN(cMaxSyms, pSymShdr->sh_size / sizeof(Elf_Sym)) + : cMaxSyms; + for (size_t iSym = 1; iSym < cSyms; iSym++) + { + if (paSyms[iSym].st_name >= cbStrings) + return VERR_LDRELF_INVALID_SYMBOL_NAME_OFFSET; + if (fFlags & DBG_DIGGER_ELF_FUNNY_SHDRS) + { + if ( paSyms[iSym].st_shndx > uMaxShIdx + && paSyms[iSym].st_shndx < SHN_LORESERVE) + uMaxShIdx = paSyms[iSym].st_shndx; + } + else if ( paSyms[iSym].st_shndx >= pEhdr->e_shnum + && paSyms[iSym].st_shndx != SHN_UNDEF + && ( paSyms[iSym].st_shndx < SHN_LORESERVE + /*|| paSyms[iSym].st_shndx > SHN_HIRESERVE*/ + || ELF_ST_BIND(paSyms[iSym].st_info) == STB_GLOBAL + || ELF_ST_BIND(paSyms[iSym].st_info) == STB_WEAK) ) + return VERR_BAD_EXE_FORMAT; + } + if (uMaxShIdx > 4096) + return VERR_BAD_EXE_FORMAT; + + /* + * Create new module. + * The funny ELF section headers on solaris makes this very complicated. + */ + uint32_t cSegs = uMaxShIdx + 1; + PDBGDIGGERELFSEG paSegs = (PDBGDIGGERELFSEG)alloca(sizeof(paSegs[0]) * cSegs); + for (uint32_t i = 0; i < cSegs; i++) + { + paSegs[i].uLoadAddr = RTGCPTR_MAX; + paSegs[i].uLastAddr = 0; + paSegs[i].iSeg = NIL_RTDBGSEGIDX; + } + + RTDBGMOD hMod; + int rc = RTDbgModCreate(&hMod, pszModName, 0 /*cbSeg*/, 0 /*fFlags*/); + if (RT_FAILURE(rc)) + return rc; + rc = RTDbgModSetTag(hMod, uModTag); AssertRC(rc); + + if (fFlags & DBG_DIGGER_ELF_FUNNY_SHDRS) + { + /* Seek out the min and max symbol values for each section. */ + for (uint32_t iSym = 1; iSym < cSyms; iSym++) + { + /* Ignore undefined, absolute and weak symbols in this pass, + but include local ones as well as nameless. */ + uint32_t iSh = paSyms[iSym].st_shndx; + if ( iSh != SHN_UNDEF + && iSh < cSegs + && ( ELF_ST_BIND(paSyms[iSym].st_info) == STB_GLOBAL + || ELF_ST_BIND(paSyms[iSym].st_info) == STB_LOCAL)) + { + /* Calc the address and check that it doesn't wrap with the size. */ + RTGCUINTPTR Address = paSyms[iSym].st_value; + RTGCUINTPTR AddressLast = Address + RT_MAX(paSyms[iSym].st_size, 1) - 1; + if (AddressLast < Address) + continue; + if ( Address < MinAddr + || AddressLast > MaxAddr) + continue; + + /* update min/max. */ + if (Address < paSegs[iSh].uLoadAddr) + paSegs[iSh].uLoadAddr = Address; + if (AddressLast > paSegs[iSh].uLastAddr) + paSegs[iSh].uLastAddr = AddressLast; + } + } + + /* Add the segments and fill in the translation table. */ + RTGCPTR uRvaNext = 0; + for (unsigned i = 0; i < cSegs; i++) + if (paSegs[i].uLastAddr != 0) + { + char szSeg[32]; + RTStrPrintf(szSeg, sizeof(szSeg), "sec%02u", i); + RTGCPTR cbSeg = paSegs[i].uLastAddr - paSegs[i].uLoadAddr + 1; + rc = RTDbgModSegmentAdd(hMod, uRvaNext, cbSeg, szSeg, 0 /*fFlags*/, &paSegs[i].iSeg); + if (RT_FAILURE(rc)) + break; + uRvaNext += RT_ALIGN_T(cbSeg, 32, RTGCPTR); + } + } + else + { + /* Add the segments and fill in the translation table. */ + for (unsigned i = 0; i < cSegs; i++) + if (paShdrs[i].sh_flags & SHF_ALLOC) + { + char szSeg[32]; + RTStrPrintf(szSeg, sizeof(szSeg), "sec%02u", i); + rc = RTDbgModSegmentAdd(hMod, paShdrs[i].sh_addr - uLoadAddr, paShdrs[i].sh_size, szSeg, 0 /*fFlags*/, &paSegs[i].iSeg); + if (RT_FAILURE(rc)) + break; + paSegs[i].uLoadAddr = paShdrs[i].sh_addr; + paSegs[i].uLastAddr = paShdrs[i].sh_addr + paShdrs[i].sh_size - 1; + } + } + if (RT_FAILURE(rc)) + { + RTDbgModRelease(hMod); + return rc; + } + + + /* + * Add all relevant symbols in the module + */ + for (uint32_t iSym = 1; iSym < cSyms; iSym++) + { + /* Undefined symbols are not exports, they are imports. */ + RTDBGSEGIDX iSeg = paSyms[iSym].st_shndx; + if ( iSeg != SHN_UNDEF + && ( ELF_ST_BIND(paSyms[iSym].st_info) == STB_GLOBAL + || ELF_ST_BIND(paSyms[iSym].st_info) == STB_LOCAL + || ELF_ST_BIND(paSyms[iSym].st_info) == STB_WEAK)) + { + /* Get the symbol name. */ + if (paSyms[iSym].st_name >= cbMaxStrings) + continue; + const char *pszSymbol = pbStrings + paSyms[iSym].st_name; + if (!*pszSymbol) + continue; + + /* Calc the address (value) and size. */ + RTGCUINTPTR cbSym = paSyms[iSym].st_size; + RTGCUINTPTR offSeg = paSyms[iSym].st_value; + if (iSeg == SHN_ABS) + iSeg = RTDBGSEGIDX_ABS; /* absolute symbols are not subject to any relocation. */ + else + { + Assert(iSeg < cSegs); + if (fFlags & (DBG_DIGGER_ELF_FUNNY_SHDRS | DBG_DIGGER_ELF_ADJUST_SYM_VALUE)) + offSeg -= paSegs[iSeg].uLoadAddr; + iSeg = paSegs[iSeg].iSeg; + if (iSeg == NIL_RTDBGSEGIDX) + continue; + } + if (offSeg + cbSym < offSeg) + continue; + + rc = RTDbgModSymbolAdd(hMod, pszSymbol, iSeg, offSeg, cbSym, 0 /*fFlags*/, NULL); + Log(("%02x:%RGv %RGv %s!%s (rc=%Rrc)\n", paSyms[iSym].st_shndx, offSeg, cbSym, pszModName, pszSymbol, rc)); + } + /*else: silently ignore */ + } + + /* + * Link it into the address space. + */ + RTDBGAS hAs = DBGFR3AsResolveAndRetain(pUVM, DBGF_AS_KERNEL); + if (hAs != NIL_RTDBGAS) + rc = dbgDiggerCommonLinkElfSegs(hAs, hMod, paSegs, cSegs); + else + rc = VERR_INTERNAL_ERROR; + RTDbgModRelease(hMod); + RTDbgAsRelease(hAs); + return rc; +} + + +#undef Elf_Ehdr +#undef Elf_Shdr +#undef Elf_Phdr +#undef Elf_Sym +#undef MY_ELFCLASS +#undef ELF_ST_BIND +#undef DBGDiggerCommonParseElfMod + |