Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mumble-voip/mumble.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/mumble_positional_audio_linux.h')
-rw-r--r--plugins/mumble_positional_audio_linux.h321
1 files changed, 321 insertions, 0 deletions
diff --git a/plugins/mumble_positional_audio_linux.h b/plugins/mumble_positional_audio_linux.h
new file mode 100644
index 000000000..6758e63b8
--- /dev/null
+++ b/plugins/mumble_positional_audio_linux.h
@@ -0,0 +1,321 @@
+// Copyright 2021 The Mumble Developers. All rights reserved.
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file at the root of the
+// Mumble source tree or at <https://www.mumble.info/LICENSE>.
+
+#ifndef MUMBLE_POSITIONAL_AUDIO_LINUX_H_
+#define MUMBLE_POSITIONAL_AUDIO_LINUX_H_
+
+#ifndef MUMBLE_POSITIONAL_AUDIO_MAIN_H_
+# error "Include mumble_positional_audio_main.h instead of mumble_positional_audio_linux.h"
+#endif
+
+#include "mumble_positional_audio_utils.h"
+
+#include <cstring>
+#include <elf.h>
+#include <iostream>
+#include <sstream>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+#include <sys/uio.h>
+
+// This function returns:
+// -1 in case of failure.
+// 0 if the process is 32-bit.
+// 1 if the process is 64-bit.
+static inline int isProcess64Bit(const procptr_t &baseAddress) {
+ if (isWin32) {
+ return isWin32Process64Bit(baseAddress);
+ }
+
+ // We can know the process architecture by looking at its ELF header.
+ uint8_t elf[5];
+
+ peekProc(baseAddress, elf, sizeof(elf));
+
+ // The first 4 bytes constitute the magical number in ASCII: 0x7F 45 4c 46.
+ if (!(elf[0] == 0x7f && elf[1] == 'E' && elf[2] == 'L' && elf[3] == 'F')) {
+ return -1;
+ }
+
+ // The fifth byte is 1 in case the process is 32-bit or 2 in case it's 64-bit.
+ return elf[4] != 1;
+}
+
+// This function returns:
+// -1 in case of failure.
+// 0 if the process is not running through Wine.
+// 1 if the process is running through Wine.
+static inline int8_t isProcessWin32(const procid_t &pid) {
+ std::stringstream ss;
+ ss << "/proc/";
+ ss << static_cast< unsigned long >(pid);
+ ss << "/exe";
+
+ char *path = realpath(ss.str().c_str(), nullptr);
+ if (!path) {
+ return -1;
+ }
+
+ const char *filename = basename(path);
+
+ if (strcmp(filename, "wine-preloader") == 0 || strcmp(filename, "wine64-preloader") == 0) {
+ free(path);
+ return 1;
+ }
+
+ // basename() returns a pointer to the basename's position in the string passed as argument.
+ // For that reason we cannot free 'path' before the if statement.
+ free(path);
+
+ return 0;
+}
+
+static inline procptr_t getModuleAddr(const procid_t &pid, const wchar_t *modname) {
+ std::wstring modnameWide(modname);
+ std::string modnameNonWide(modnameWide.begin(), modnameWide.end());
+
+ std::stringstream ss;
+ ss << std::string("/proc/");
+ ss << static_cast< unsigned long >(pid);
+ ss << std::string("/maps");
+ std::string mapsFn = ss.str();
+ std::string maps = readFile(mapsFn);
+
+ if (maps.size() == 0) {
+ return 0;
+ }
+
+ std::stringstream ssPath(maps);
+ while (ssPath.good()) {
+ std::string baseaddr;
+
+ int ch;
+ while (1) {
+ ch = ssPath.get();
+ if (ch == '-') {
+ break;
+ } else if (ch == EOF) {
+ return 0;
+ }
+ baseaddr.push_back(static_cast< char >(ch));
+ }
+
+ // seek to perms
+ do {
+ ch = ssPath.get();
+ if (ch == EOF) {
+ return 0;
+ }
+ } while (ch != ' ');
+
+ // seek to offset
+ do {
+ ch = ssPath.get();
+ if (ch == EOF) {
+ return 0;
+ }
+ } while (ch != ' ');
+
+ // seek to dev
+ do {
+ ch = ssPath.get();
+ if (ch == EOF) {
+ return 0;
+ }
+ } while (ch != ' ');
+
+ // seek to inode
+ do {
+ ch = ssPath.get();
+ if (ch == EOF) {
+ return 0;
+ }
+ } while (ch != ' ');
+
+ // seek to pathname
+ do {
+ ch = ssPath.get();
+ if (ch == EOF) {
+ return 0;
+ }
+ } while (ch != ' ');
+
+ // eat spaces until we're at the beginning of pathname.
+ while (ch == ' ') {
+ if (ch == EOF) {
+ return 0;
+ }
+ ch = ssPath.get();
+ }
+ ssPath.unget();
+
+ std::string pathname;
+ while (1) {
+ ch = ssPath.get();
+ if (ch == '\n') {
+ break;
+ } else if (ch == EOF) {
+ return 0;
+ }
+ pathname.push_back(static_cast< char >(ch));
+ };
+
+ // OK, we found 'em!
+ // Only treat path as a real path if it starts with /.
+ if (pathname.size() > 0 && pathname.at(0) == '/') {
+ // Find the basename.
+ size_t lastSlash = pathname.find_last_of('/');
+ if (pathname.size() > lastSlash + 1) {
+ std::string basename = pathname.substr(lastSlash + 1);
+ if (basename == modnameNonWide) {
+ unsigned long addr = strtoul(baseaddr.c_str(), nullptr, 16);
+ return addr;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static inline bool peekProc(const procptr_t &addr, void *dest, const size_t &len) {
+ struct iovec in;
+ in.iov_base = reinterpret_cast< void * >(addr); // Address from target process
+ in.iov_len = len; // Length
+
+ struct iovec out;
+ out.iov_base = dest;
+ out.iov_len = len;
+
+ ssize_t nread = process_vm_readv(pPid, &out, 1, &in, 1, 0);
+
+ return (nread != -1 && static_cast< size_t >(nread) == in.iov_len);
+}
+
+template< typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Dyn, typename Elf_Sym >
+static inline procptr_t getExportedSymbolInternal(const std::string &symbol, const procptr_t module) {
+ procptr_t hashTable = 0;
+ procptr_t strTable = 0;
+ procptr_t symTable = 0;
+
+ const auto ehdr = peekProc< Elf_Ehdr >(module);
+ const auto phdr = peekProcVector< Elf_Phdr >(module + ehdr.e_phoff, ehdr.e_phnum);
+
+ for (size_t i = 0; i < phdr.size(); ++i) {
+ if (phdr[i].p_type == PT_DYNAMIC) {
+ const auto dyn = peekProcVector< Elf_Dyn >(module + phdr[i].p_vaddr, phdr[i].p_memsz / sizeof(Elf_Dyn));
+
+ for (size_t j = 0; j < dyn.size(); ++j) {
+ switch (dyn[j].d_tag) {
+ case DT_HASH:
+ hashTable = dyn[j].d_un.d_ptr;
+ break;
+ case DT_STRTAB:
+ strTable = dyn[j].d_un.d_ptr;
+ break;
+ case DT_SYMTAB:
+ symTable = dyn[j].d_un.d_ptr;
+ break;
+ }
+
+ if (hashTable && strTable && symTable) {
+ break;
+ }
+ }
+
+ break;
+ }
+ }
+
+ // Hash table pseudo-struct:
+ // uint32_t nBucket;
+ // uint32_t nChain;
+ // uint32_t bucket[nBucket];
+ // uint32_t chain[nChain];
+ const auto nChain = peekProc< uint32_t >(hashTable + sizeof(uint32_t));
+
+ for (uint32_t i = 0; i < nChain; ++i) {
+ const auto sym = peekProc< Elf_Sym >(symTable + sizeof(Elf_Sym) * i);
+ const auto name = peekProcString(strTable + sym.st_name, symbol.size());
+
+ if (name == symbol) {
+ return module + sym.st_value;
+ }
+ }
+
+ return 0;
+}
+
+static inline procptr_t getExportedSymbol(const std::string &symbol, const procptr_t module) {
+ if (isWin32) {
+ return getWin32ExportedSymbol(symbol, module);
+ }
+
+ if (is64Bit) {
+ return getExportedSymbolInternal< Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, Elf64_Sym >(symbol, module);
+ } else {
+ return getExportedSymbolInternal< Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, Elf32_Sym >(symbol, module);
+ }
+}
+
+static void generic_unlock() {
+ pModule = 0;
+ pPid = 0;
+}
+
+static bool initialize(const std::multimap< std::wstring, unsigned long long int > &pids, const wchar_t *procname,
+ const wchar_t *modname = nullptr) {
+ pModule = 0;
+
+ if (!pids.empty()) {
+ auto iter = pids.find(std::wstring(procname));
+ if (iter != pids.end()) {
+ pPid = static_cast< procid_t >(iter->second);
+ } else {
+ pPid = 0;
+ }
+ } else {
+ pPid = 0;
+ }
+
+ if (!pPid) {
+ return false;
+ }
+
+ pModule = getModuleAddr(procname);
+ if (!pModule) {
+ pPid = 0;
+ return false;
+ }
+
+ int8_t ret = isProcessWin32(pPid);
+ if (ret == -1) {
+ generic_unlock();
+ return false;
+ }
+
+ isWin32 = ret;
+
+ ret = isProcess64Bit(pModule);
+ if (ret == -1) {
+ generic_unlock();
+ return false;
+ }
+
+ is64Bit = ret;
+
+ if (modname) {
+ pModule = getModuleAddr(modname);
+ if (!pModule) {
+ pPid = 0;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+#endif