{"id":1203,"url":"https://patchwork.libcamera.org/api/patches/1203/?format=json","web_url":"https://patchwork.libcamera.org/patch/1203/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20190514223808.27351-1-paul.elder@ideasonboard.com>","date":"2019-05-14T22:38:07","name":"[libcamera-devel,v2,1/2] libcamera: ipa_module: add IPA shared library loader","commit_ref":null,"pull_url":null,"state":"superseded","archived":false,"hash":"3b90d4eeb2b0df4c4a3cd0b6157bbcbf94e458fd","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/people/17/?format=json","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/1203/mbox/","series":[{"id":305,"url":"https://patchwork.libcamera.org/api/series/305/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=305","date":"2019-05-14T22:38:07","name":"[libcamera-devel,v2,1/2] libcamera: ipa_module: add IPA shared library loader","version":2,"mbox":"https://patchwork.libcamera.org/series/305/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/1203/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/1203/checks/","tags":{},"headers":{"Return-Path":"<paul.elder@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 8F45660E4C\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 15 May 2019 00:38:21 +0200 (CEST)","from localhost.localdomain (unknown [96.44.9.117])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 96FED1952;\n\tWed, 15 May 2019 00:38:20 +0200 (CEST)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1557873501;\n\tbh=Ss9Qakknn9rHg37nXbGZmo+pzblrcUBYoTjcqIfXu6k=;\n\th=From:To:Cc:Subject:Date:From;\n\tb=I9z1WDsxwl9lBYkwHtVc4K/g9J12Npe03yJ1eGJC3sDmxYUCzXeizzBOgwwhDR6dH\n\tPdhIexrY9mPnmMOQd2MgmwM8qU4GgyCvWLqWbT5OksIyfDTZy5Qp2ByHuT/zwoc1hE\n\tDdSqthHn3mWRItLgZFlwgOxEmbhaMyk7dTKRX2Qs=","From":"Paul Elder <paul.elder@ideasonboard.com>","To":"libcamera-devel@lists.libcamera.org","Date":"Tue, 14 May 2019 18:38:07 -0400","Message-Id":"<20190514223808.27351-1-paul.elder@ideasonboard.com>","X-Mailer":"git-send-email 2.20.1","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","Subject":"[libcamera-devel] [PATCH v2 1/2] libcamera: ipa_module: add IPA\n\tshared library loader","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.23","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Tue, 14 May 2019 22:38:21 -0000"},"content":"Implement a class to load a struct IPAModuleInfo of a given symbol name\nfrom a .so shared object library.\n\nSigned-off-by: Paul Elder <paul.elder@ideasonboard.com>\n---\nChanges in v2:\n- renamed LibLoader class to IPAModule\n- added documentation\n- added logging\n- check that bitness of the shared object is the same as libcamera\n- moved symbol loading (\"init\") to the constructor, and added isValid()\n- added elfPointer() to prevent segfaults when reading data from mmap\n- moved struct IPAModuleInfo out of IPAModule\n- rename getIPAModuleInfo() to IPAModuleInfo(), and make it return a\n  const reference\n- added munmap after the mmap\n\n src/libcamera/include/ipa_module.h |  43 +++++\n src/libcamera/ipa_module.cpp       | 277 +++++++++++++++++++++++++++++\n src/libcamera/meson.build          |   2 +\n 3 files changed, 322 insertions(+)\n create mode 100644 src/libcamera/include/ipa_module.h\n create mode 100644 src/libcamera/ipa_module.cpp","diff":"diff --git a/src/libcamera/include/ipa_module.h b/src/libcamera/include/ipa_module.h\nnew file mode 100644\nindex 0000000..9eb0094\n--- /dev/null\n+++ b/src/libcamera/include/ipa_module.h\n@@ -0,0 +1,43 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * ipa_module.h - load IPA module information from shared library at runtime\n+ */\n+#ifndef __LIBCAMERA_IPA_MODULE_H__\n+#define __LIBCAMERA_IPA_MODULE_H__\n+\n+#include <string>\n+\n+namespace libcamera {\n+\n+struct IPAModuleInfo {\n+\tchar name[256];\n+\tunsigned int version;\n+};\n+\n+class IPAModule\n+{\n+public:\n+\texplicit IPAModule(const std::string &libPath, const std::string &symbol);\n+\n+\tbool isValid() const;\n+\n+\tstruct IPAModuleInfo const &IPAModuleInfo() const;\n+\n+private:\n+\tstruct IPAModuleInfo info_;\n+\n+\tbool loaded_;\n+\tint bitClass_;\n+\n+\tint loadELFIdent(int fd);\n+\ttemplate<class ElfHeader, class SecHeader, class SymHeader>\n+\tint loadSymbol(void *data, size_t size, char *map, size_t soSize,\n+\t\t       const char *symbol);\n+\tint loadIPAModuleInfo(const char *libPath, const char *symbol);\n+};\n+\n+} /* namespace libcamera */\n+\n+#endif /* __LIBCAMERA_IPA_MODULE_H__ */\ndiff --git a/src/libcamera/ipa_module.cpp b/src/libcamera/ipa_module.cpp\nnew file mode 100644\nindex 0000000..5ca16e8\n--- /dev/null\n+++ b/src/libcamera/ipa_module.cpp\n@@ -0,0 +1,277 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * ipa_module.cpp - load IPA module information from shared library at runtime\n+ */\n+\n+#include \"ipa_module.h\"\n+\n+#include <elf.h>\n+#include <errno.h>\n+#include <fcntl.h>\n+#include <string.h>\n+#include <sys/mman.h>\n+#include <sys/stat.h>\n+#include <sys/types.h>\n+#include <unistd.h>\n+\n+#include <iostream>\n+\n+#include \"log.h\"\n+\n+/**\n+ * \\file ipa_module.h\n+ * \\brief IPA module information loading\n+ */\n+\n+namespace libcamera {\n+\n+/**\n+ * \\struct IPAModuleInfo\n+ * \\brief Information of an IPA plugin\n+ *\n+ * This structure contains the information of an IPA plugin. It is loaded,\n+ * read, and validated before anything else is loaded from the plugin.\n+ *\n+ * \\var IPAModuleInfo::name\n+ * \\brief The name of the IPA plugin\n+ *\n+ * \\var IPAModuleInfo::version\n+ * \\brief The version of the IPA plugin\n+ */\n+\n+LOG_DEFINE_CATEGORY(IPAModule)\n+\n+template<typename T>\n+typename std::remove_extent<T>::type *elfPointer(void *map, off_t offset,\n+\t\t\t\t\t\t size_t fileSize)\n+{\n+\tsize_t size = offset + sizeof(T);\n+\tif (size > fileSize || size < sizeof(T))\n+\t\treturn nullptr;\n+\n+\treturn reinterpret_cast<typename std::remove_extent<T>::type *>\n+\t\t(static_cast<char *>(map) + offset);\n+}\n+\n+/**\n+ * \\class IPAModule\n+ * \\brief Load IPA module information from an IPA plugin\n+ */\n+\n+/**\n+ * \\brief Construct an IPAModule instance\n+ * \\param[in] libPath path to IPA plugin\n+ * \\param[in] symbol name of IPAModuleInfo to load from libPath\n+ *\n+ * Loads the IPAModuleInfo of the given symbol from the IPA plugin at libPath.\n+ * The IPA plugin shared object file must be of the same endianness and\n+ * bitness as libcamera.\n+ *\n+ * Note that isValid() should be called to check if this loading was successful\n+ * or not.\n+ */\n+\n+IPAModule::IPAModule(const std::string &libPath, const std::string &symbol)\n+\t: loaded_(false)\n+{\n+\tint ret = loadIPAModuleInfo(libPath.c_str(), symbol.c_str());\n+\tif (ret < 0) {\n+\t\tloaded_ = false;\n+\t\treturn;\n+\t}\n+\n+\tloaded_ = true;\n+}\n+\n+int IPAModule::loadELFIdent(int fd)\n+{\n+\tint ret = lseek(fd, 0, SEEK_SET);\n+\tif (ret < 0)\n+\t\treturn -errno;\n+\n+\tunsigned char e_ident[EI_NIDENT];\n+\tret = read(fd, e_ident, EI_NIDENT);\n+\tif (ret < 0)\n+\t\treturn -errno;\n+\n+\tif (e_ident[EI_MAG0] != ELFMAG0 ||\n+\t    e_ident[EI_MAG1] != ELFMAG1 ||\n+\t    e_ident[EI_MAG2] != ELFMAG2 ||\n+\t    e_ident[EI_MAG3] != ELFMAG3 ||\n+\t    e_ident[EI_VERSION] != EV_CURRENT)\n+\t\treturn -ENOEXEC;\n+\n+\tif ((e_ident[EI_CLASS] == ELFCLASS32 && sizeof(unsigned long) == 4) ||\n+\t    (e_ident[EI_CLASS] == ELFCLASS64 && sizeof(unsigned long) == 8))\n+\t\tbitClass_ = e_ident[EI_CLASS];\n+\telse\n+\t\treturn -ENOEXEC;\n+\n+\tint a = 1;\n+\tunsigned char endianness = *reinterpret_cast<char *>(&a) == 1\n+\t\t\t\t ? ELFDATA2LSB : ELFDATA2MSB;\n+\tif (e_ident[EI_DATA] != endianness)\n+\t\treturn -ENOEXEC;\n+\n+\treturn 0;\n+}\n+\n+template<class ElfHeader, class SecHeader, class SymHeader>\n+int IPAModule::loadSymbol(void *dst, size_t size, char *map, size_t soSize,\n+\t\t\t  const char *symbol)\n+{\n+\tElfHeader *eHdr = elfPointer<ElfHeader>(map, 0, soSize);\n+\tif (!eHdr)\n+\t\treturn -ENOEXEC;\n+\n+\toff_t offset = eHdr->e_shoff + eHdr->e_shentsize * eHdr->e_shstrndx;\n+\tSecHeader *sHdr = elfPointer<SecHeader>(map, offset, soSize);\n+\tif (!sHdr)\n+\t\treturn -ENOEXEC;\n+\toff_t shnameoff = sHdr->sh_offset;\n+\n+\t/* Locate .dynsym section header. */\n+\tbool found = false;\n+\tSecHeader *dynsym;\n+\tfor (unsigned int i = 0; i < eHdr->e_shnum; i++) {\n+\t\toffset = eHdr->e_shoff + eHdr->e_shentsize * i;\n+\t\tsHdr = elfPointer<SecHeader>(map, offset, soSize);\n+\t\tif (!sHdr)\n+\t\t\treturn -ENOEXEC;\n+\n+\t\toffset = shnameoff + sHdr->sh_name;\n+\t\tchar *name = elfPointer<char>(map, offset, soSize);\n+\t\tif (!name)\n+\t\t\treturn -ENOEXEC;\n+\n+\t\tif (sHdr->sh_type == SHT_DYNSYM && !strcmp(name, \".dynsym\")) {\n+\t\t\tfound = true;\n+\t\t\tdynsym = sHdr;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (!found) {\n+\t\tLOG(IPAModule, Error) << \"ELF has no .dynsym section\";\n+\t\treturn -ENOEXEC;\n+\t}\n+\n+\toffset = eHdr->e_shoff + eHdr->e_shentsize * dynsym->sh_link;\n+\tsHdr = elfPointer<SecHeader>(map, offset, soSize);\n+\tif (!sHdr)\n+\t\treturn -ENOEXEC;\n+\toff_t dynsym_nameoff = sHdr->sh_offset;\n+\n+\t/* Locate symbol in the .dynsym section. */\n+\tfound = false;\n+\tSymHeader *target_symbol;\n+\tunsigned int dynsym_num = dynsym->sh_size / dynsym->sh_entsize;\n+\tfor (unsigned int i = 0; i < dynsym_num; i++) {\n+\t\toffset = dynsym->sh_offset + dynsym->sh_entsize * i;\n+\t\tSymHeader *sym = elfPointer<SymHeader>(map, offset, soSize);\n+\t\tif (!sym)\n+\t\t\treturn -ENOEXEC;\n+\n+\t\toffset = dynsym_nameoff + sym->st_name;\n+\t\tchar *name = elfPointer<char>(map, offset, soSize);\n+\t\tif (!name)\n+\t\t\treturn -ENOEXEC;\n+\n+\t\tif (!strcmp(name, symbol) &&\n+\t\t    sym->st_info & STB_GLOBAL &&\n+\t\t    sym->st_size == sizeof(struct IPAModuleInfo)) {\n+\t\t\tfound = true;\n+\t\t\ttarget_symbol = sym;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (!found) {\n+\t\tLOG(IPAModule, Error) << \"IPAModuleInfo symbol not found\";\n+\t\treturn -ENOEXEC;\n+\t}\n+\n+\t/* Locate and return data of symbol. */\n+\toffset = eHdr->e_shoff + target_symbol->st_shndx * eHdr->e_shentsize;\n+\tsHdr = elfPointer<SecHeader>(map, offset, soSize);\n+\tif (!sHdr)\n+\t\treturn -ENOEXEC;\n+\toffset = sHdr->sh_offset + (target_symbol->st_value - sHdr->sh_addr);\n+\tchar *data = elfPointer<char>(map, offset, soSize);\n+\tif (!data)\n+\t\treturn -ENOEXEC;\n+\tmemcpy(dst, data, size);\n+\n+\treturn 0;\n+}\n+\n+int IPAModule::loadIPAModuleInfo(const char *libPath, const char *symbol)\n+{\n+\tint fd = open(libPath, O_RDONLY);\n+\tif (fd < 0) {\n+\t\tLOG(IPAModule, Error) << \"Failed to open IPA library: \"\n+\t\t\t\t      << strerror(errno);\n+\t\treturn fd;\n+\t}\n+\n+\tint ret = loadELFIdent(fd);\n+\tif (ret)\n+\t\tgoto close;\n+\n+\tsize_t soSize;\n+\tchar *map;\n+\tstruct stat st;\n+\tret = fstat(fd, &st);\n+\tif (ret < 0)\n+\t\tgoto close;\n+\tsoSize = st.st_size;\n+\tmap = static_cast<char *>(mmap(NULL, soSize, PROT_READ,\n+\t\t\t\t       MAP_PRIVATE, fd, 0));\n+\tif (map == MAP_FAILED) {\n+\t\tret = -errno;\n+\t\tgoto close;\n+\t}\n+\n+\tif (bitClass_ == ELFCLASS32)\n+\t\tret = loadSymbol<Elf32_Ehdr, Elf32_Shdr, Elf32_Sym>\n+\t\t\t\t(&info_, sizeof(info_), map, soSize, symbol);\n+\telse if (bitClass_ == ELFCLASS64)\n+\t\tret = loadSymbol<Elf64_Ehdr, Elf64_Shdr, Elf64_Sym>\n+\t\t\t\t(&info_, sizeof(info_), map, soSize, symbol);\n+\tif (ret)\n+\t\tgoto unmap;\n+\n+unmap:\n+\tmunmap(map, soSize);\n+close:\n+\tclose(fd);\n+\treturn ret;\n+}\n+\n+/**\n+ * \\brief Check if construction of the IPAModule instance succeeded\n+ *\n+ * \\return true if the constructor succeeded with no errors, false otherwise\n+ */\n+\n+bool IPAModule::isValid() const\n+{\n+\treturn loaded_;\n+}\n+\n+/**\n+ * \\brief Get the loaded IPAModuleInfo\n+ *\n+ * Check isValid() before calling this.\n+ *\n+ * \\return IPAModuleInfo\n+ */\n+\n+struct IPAModuleInfo const &IPAModule::IPAModuleInfo() const\n+{\n+\treturn info_;\n+}\n+\n+} /* namespace libcamera */\ndiff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\nindex 8796f49..e5b48f2 100644\n--- a/src/libcamera/meson.build\n+++ b/src/libcamera/meson.build\n@@ -10,6 +10,7 @@ libcamera_sources = files([\n     'event_notifier.cpp',\n     'formats.cpp',\n     'geometry.cpp',\n+    'ipa_module.cpp',\n     'log.cpp',\n     'media_device.cpp',\n     'media_object.cpp',\n@@ -31,6 +32,7 @@ libcamera_headers = files([\n     'include/device_enumerator_udev.h',\n     'include/event_dispatcher_poll.h',\n     'include/formats.h',\n+    'include/ipa_module.h',\n     'include/log.h',\n     'include/media_device.h',\n     'include/media_object.h',\n","prefixes":["libcamera-devel","v2","1/2"]}