summaryrefslogtreecommitdiffstats
path: root/src/VBox/Debugger/DBGPlugInCommonELFTmpl.cpp.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Debugger/DBGPlugInCommonELFTmpl.cpp.h')
-rw-r--r--src/VBox/Debugger/DBGPlugInCommonELFTmpl.cpp.h347
1 files changed, 347 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..16be826a
--- /dev/null
+++ b/src/VBox/Debugger/DBGPlugInCommonELFTmpl.cpp.h
@@ -0,0 +1,347 @@
+/* $Id: DBGPlugInCommonELFTmpl.cpp.h $ */
+/** @file
+ * DBGPlugInCommonELF - Code Template for dealing with one kind of ELF.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#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 pVMM The VMM function table.
+ * @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, PCVMMR3VTABLE pVMM, 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(pVMM, 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 = pVMM->pfnDBGFR3AsResolveAndRetain(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
+