[libcamera-devel] [PATCH v2 1/2] libcamera: ipa_module: add IPA shared library loader

Paul Elder paul.elder at ideasonboard.com
Wed May 15 00:38:07 CEST 2019


Implement a class to load a struct IPAModuleInfo of a given symbol name
from a .so shared object library.

Signed-off-by: Paul Elder <paul.elder at ideasonboard.com>
---
Changes in v2:
- renamed LibLoader class to IPAModule
- added documentation
- added logging
- check that bitness of the shared object is the same as libcamera
- moved symbol loading ("init") to the constructor, and added isValid()
- added elfPointer() to prevent segfaults when reading data from mmap
- moved struct IPAModuleInfo out of IPAModule
- rename getIPAModuleInfo() to IPAModuleInfo(), and make it return a
  const reference
- added munmap after the mmap

 src/libcamera/include/ipa_module.h |  43 +++++
 src/libcamera/ipa_module.cpp       | 277 +++++++++++++++++++++++++++++
 src/libcamera/meson.build          |   2 +
 3 files changed, 322 insertions(+)
 create mode 100644 src/libcamera/include/ipa_module.h
 create mode 100644 src/libcamera/ipa_module.cpp

diff --git a/src/libcamera/include/ipa_module.h b/src/libcamera/include/ipa_module.h
new file mode 100644
index 0000000..9eb0094
--- /dev/null
+++ b/src/libcamera/include/ipa_module.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * ipa_module.h - load IPA module information from shared library at runtime
+ */
+#ifndef __LIBCAMERA_IPA_MODULE_H__
+#define __LIBCAMERA_IPA_MODULE_H__
+
+#include <string>
+
+namespace libcamera {
+
+struct IPAModuleInfo {
+	char name[256];
+	unsigned int version;
+};
+
+class IPAModule
+{
+public:
+	explicit IPAModule(const std::string &libPath, const std::string &symbol);
+
+	bool isValid() const;
+
+	struct IPAModuleInfo const &IPAModuleInfo() const;
+
+private:
+	struct IPAModuleInfo info_;
+
+	bool loaded_;
+	int bitClass_;
+
+	int loadELFIdent(int fd);
+	template<class ElfHeader, class SecHeader, class SymHeader>
+	int loadSymbol(void *data, size_t size, char *map, size_t soSize,
+		       const char *symbol);
+	int loadIPAModuleInfo(const char *libPath, const char *symbol);
+};
+
+} /* namespace libcamera */
+
+#endif /* __LIBCAMERA_IPA_MODULE_H__ */
diff --git a/src/libcamera/ipa_module.cpp b/src/libcamera/ipa_module.cpp
new file mode 100644
index 0000000..5ca16e8
--- /dev/null
+++ b/src/libcamera/ipa_module.cpp
@@ -0,0 +1,277 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * ipa_module.cpp - load IPA module information from shared library at runtime
+ */
+
+#include "ipa_module.h"
+
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <iostream>
+
+#include "log.h"
+
+/**
+ * \file ipa_module.h
+ * \brief IPA module information loading
+ */
+
+namespace libcamera {
+
+/**
+ * \struct IPAModuleInfo
+ * \brief Information of an IPA plugin
+ *
+ * This structure contains the information of an IPA plugin. It is loaded,
+ * read, and validated before anything else is loaded from the plugin.
+ *
+ * \var IPAModuleInfo::name
+ * \brief The name of the IPA plugin
+ *
+ * \var IPAModuleInfo::version
+ * \brief The version of the IPA plugin
+ */
+
+LOG_DEFINE_CATEGORY(IPAModule)
+
+template<typename T>
+typename std::remove_extent<T>::type *elfPointer(void *map, off_t offset,
+						 size_t fileSize)
+{
+	size_t size = offset + sizeof(T);
+	if (size > fileSize || size < sizeof(T))
+		return nullptr;
+
+	return reinterpret_cast<typename std::remove_extent<T>::type *>
+		(static_cast<char *>(map) + offset);
+}
+
+/**
+ * \class IPAModule
+ * \brief Load IPA module information from an IPA plugin
+ */
+
+/**
+ * \brief Construct an IPAModule instance
+ * \param[in] libPath path to IPA plugin
+ * \param[in] symbol name of IPAModuleInfo to load from libPath
+ *
+ * Loads the IPAModuleInfo of the given symbol from the IPA plugin at libPath.
+ * The IPA plugin shared object file must be of the same endianness and
+ * bitness as libcamera.
+ *
+ * Note that isValid() should be called to check if this loading was successful
+ * or not.
+ */
+
+IPAModule::IPAModule(const std::string &libPath, const std::string &symbol)
+	: loaded_(false)
+{
+	int ret = loadIPAModuleInfo(libPath.c_str(), symbol.c_str());
+	if (ret < 0) {
+		loaded_ = false;
+		return;
+	}
+
+	loaded_ = true;
+}
+
+int IPAModule::loadELFIdent(int fd)
+{
+	int ret = lseek(fd, 0, SEEK_SET);
+	if (ret < 0)
+		return -errno;
+
+	unsigned char e_ident[EI_NIDENT];
+	ret = read(fd, e_ident, EI_NIDENT);
+	if (ret < 0)
+		return -errno;
+
+	if (e_ident[EI_MAG0] != ELFMAG0 ||
+	    e_ident[EI_MAG1] != ELFMAG1 ||
+	    e_ident[EI_MAG2] != ELFMAG2 ||
+	    e_ident[EI_MAG3] != ELFMAG3 ||
+	    e_ident[EI_VERSION] != EV_CURRENT)
+		return -ENOEXEC;
+
+	if ((e_ident[EI_CLASS] == ELFCLASS32 && sizeof(unsigned long) == 4) ||
+	    (e_ident[EI_CLASS] == ELFCLASS64 && sizeof(unsigned long) == 8))
+		bitClass_ = e_ident[EI_CLASS];
+	else
+		return -ENOEXEC;
+
+	int a = 1;
+	unsigned char endianness = *reinterpret_cast<char *>(&a) == 1
+				 ? ELFDATA2LSB : ELFDATA2MSB;
+	if (e_ident[EI_DATA] != endianness)
+		return -ENOEXEC;
+
+	return 0;
+}
+
+template<class ElfHeader, class SecHeader, class SymHeader>
+int IPAModule::loadSymbol(void *dst, size_t size, char *map, size_t soSize,
+			  const char *symbol)
+{
+	ElfHeader *eHdr = elfPointer<ElfHeader>(map, 0, soSize);
+	if (!eHdr)
+		return -ENOEXEC;
+
+	off_t offset = eHdr->e_shoff + eHdr->e_shentsize * eHdr->e_shstrndx;
+	SecHeader *sHdr = elfPointer<SecHeader>(map, offset, soSize);
+	if (!sHdr)
+		return -ENOEXEC;
+	off_t shnameoff = sHdr->sh_offset;
+
+	/* Locate .dynsym section header. */
+	bool found = false;
+	SecHeader *dynsym;
+	for (unsigned int i = 0; i < eHdr->e_shnum; i++) {
+		offset = eHdr->e_shoff + eHdr->e_shentsize * i;
+		sHdr = elfPointer<SecHeader>(map, offset, soSize);
+		if (!sHdr)
+			return -ENOEXEC;
+
+		offset = shnameoff + sHdr->sh_name;
+		char *name = elfPointer<char>(map, offset, soSize);
+		if (!name)
+			return -ENOEXEC;
+
+		if (sHdr->sh_type == SHT_DYNSYM && !strcmp(name, ".dynsym")) {
+			found = true;
+			dynsym = sHdr;
+			break;
+		}
+	}
+
+	if (!found) {
+		LOG(IPAModule, Error) << "ELF has no .dynsym section";
+		return -ENOEXEC;
+	}
+
+	offset = eHdr->e_shoff + eHdr->e_shentsize * dynsym->sh_link;
+	sHdr = elfPointer<SecHeader>(map, offset, soSize);
+	if (!sHdr)
+		return -ENOEXEC;
+	off_t dynsym_nameoff = sHdr->sh_offset;
+
+	/* Locate symbol in the .dynsym section. */
+	found = false;
+	SymHeader *target_symbol;
+	unsigned int dynsym_num = dynsym->sh_size / dynsym->sh_entsize;
+	for (unsigned int i = 0; i < dynsym_num; i++) {
+		offset = dynsym->sh_offset + dynsym->sh_entsize * i;
+		SymHeader *sym = elfPointer<SymHeader>(map, offset, soSize);
+		if (!sym)
+			return -ENOEXEC;
+
+		offset = dynsym_nameoff + sym->st_name;
+		char *name = elfPointer<char>(map, offset, soSize);
+		if (!name)
+			return -ENOEXEC;
+
+		if (!strcmp(name, symbol) &&
+		    sym->st_info & STB_GLOBAL &&
+		    sym->st_size == sizeof(struct IPAModuleInfo)) {
+			found = true;
+			target_symbol = sym;
+			break;
+		}
+	}
+
+	if (!found) {
+		LOG(IPAModule, Error) << "IPAModuleInfo symbol not found";
+		return -ENOEXEC;
+	}
+
+	/* Locate and return data of symbol. */
+	offset = eHdr->e_shoff + target_symbol->st_shndx * eHdr->e_shentsize;
+	sHdr = elfPointer<SecHeader>(map, offset, soSize);
+	if (!sHdr)
+		return -ENOEXEC;
+	offset = sHdr->sh_offset + (target_symbol->st_value - sHdr->sh_addr);
+	char *data = elfPointer<char>(map, offset, soSize);
+	if (!data)
+		return -ENOEXEC;
+	memcpy(dst, data, size);
+
+	return 0;
+}
+
+int IPAModule::loadIPAModuleInfo(const char *libPath, const char *symbol)
+{
+	int fd = open(libPath, O_RDONLY);
+	if (fd < 0) {
+		LOG(IPAModule, Error) << "Failed to open IPA library: "
+				      << strerror(errno);
+		return fd;
+	}
+
+	int ret = loadELFIdent(fd);
+	if (ret)
+		goto close;
+
+	size_t soSize;
+	char *map;
+	struct stat st;
+	ret = fstat(fd, &st);
+	if (ret < 0)
+		goto close;
+	soSize = st.st_size;
+	map = static_cast<char *>(mmap(NULL, soSize, PROT_READ,
+				       MAP_PRIVATE, fd, 0));
+	if (map == MAP_FAILED) {
+		ret = -errno;
+		goto close;
+	}
+
+	if (bitClass_ == ELFCLASS32)
+		ret = loadSymbol<Elf32_Ehdr, Elf32_Shdr, Elf32_Sym>
+				(&info_, sizeof(info_), map, soSize, symbol);
+	else if (bitClass_ == ELFCLASS64)
+		ret = loadSymbol<Elf64_Ehdr, Elf64_Shdr, Elf64_Sym>
+				(&info_, sizeof(info_), map, soSize, symbol);
+	if (ret)
+		goto unmap;
+
+unmap:
+	munmap(map, soSize);
+close:
+	close(fd);
+	return ret;
+}
+
+/**
+ * \brief Check if construction of the IPAModule instance succeeded
+ *
+ * \return true if the constructor succeeded with no errors, false otherwise
+ */
+
+bool IPAModule::isValid() const
+{
+	return loaded_;
+}
+
+/**
+ * \brief Get the loaded IPAModuleInfo
+ *
+ * Check isValid() before calling this.
+ *
+ * \return IPAModuleInfo
+ */
+
+struct IPAModuleInfo const &IPAModule::IPAModuleInfo() const
+{
+	return info_;
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
index 8796f49..e5b48f2 100644
--- a/src/libcamera/meson.build
+++ b/src/libcamera/meson.build
@@ -10,6 +10,7 @@ libcamera_sources = files([
     'event_notifier.cpp',
     'formats.cpp',
     'geometry.cpp',
+    'ipa_module.cpp',
     'log.cpp',
     'media_device.cpp',
     'media_object.cpp',
@@ -31,6 +32,7 @@ libcamera_headers = files([
     'include/device_enumerator_udev.h',
     'include/event_dispatcher_poll.h',
     'include/formats.h',
+    'include/ipa_module.h',
     'include/log.h',
     'include/media_device.h',
     'include/media_object.h',
-- 
2.20.1



More information about the libcamera-devel mailing list