[libcamera-devel] [PATCH 1/2] libcamera: lib_loader: add shared library loader

Paul Elder paul.elder at ideasonboard.com
Sat May 11 01:22:34 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>
---
I will remove and clean up the comments in lib_loader.h, but I've left
them there for now, although I would like comments on the API (and the
commented out unload()).

I will also add logging later, as well as checking (guessing) if the
bitness is acceptable or not.

The generics to allow duck-typing of the ELF structs was the best I
could think of with my limited C++ knowledge of how to deal with the
different size and field layouts of the 32-bit and 64-bit structs.

 src/libcamera/include/lib_loader.h |  59 ++++++++++
 src/libcamera/lib_loader.cpp       | 175 +++++++++++++++++++++++++++++
 src/libcamera/meson.build          |   2 +
 3 files changed, 236 insertions(+)
 create mode 100644 src/libcamera/include/lib_loader.h
 create mode 100644 src/libcamera/lib_loader.cpp

diff --git a/src/libcamera/include/lib_loader.h b/src/libcamera/include/lib_loader.h
new file mode 100644
index 0000000..d8eb100
--- /dev/null
+++ b/src/libcamera/include/lib_loader.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * lib_loader.h - load shared libraries at runtime
+ */
+#ifndef __LIBCAMERA_LIB_LOADER_H__
+#define __LIBCAMERA_LIB_LOADER_H__
+
+#include <string>
+
+namespace libcamera {
+
+class LibLoader
+{
+public:
+	struct IPAModuleInfo {
+		char name[256];
+		unsigned int version;
+	};
+
+	// doesn't actually do anything?
+	explicit LibLoader(const std::string &lib_path);
+
+	// if you try it while it's already loaded it'll just load it again,
+	// no big deal
+	/* returns 0 on success, err code on failure */
+	int loadIPAModuleInfo(const std::string &symbol);
+
+	bool isLoaded() const;
+
+	// just a getter (or returns nullptr if module hasn't been loaded)
+	struct IPAModuleInfo getIPAModuleInfo() const;
+
+	// necessary to allow multiple users of an instance of LibLoader
+	/* returns 0 on success, err code on failure, pos num of others loading the lib */
+	//int unload();
+
+	//void (*)() resolve(const std::string &symbol);
+	// resolve(const std::string &lib_path, const std::string symbol)
+
+private:
+	struct IPAModuleInfo info_;
+
+	bool loaded_;
+	std::string lib_path_;
+
+	int bitclass_;
+
+	int loadElfIdent(int fd);
+
+	template<class elfHeader, class secHeader, class symHeader>
+	int loadSymbol(struct IPAModuleInfo *dst, int fd,
+		       const std::string &symbol);
+};
+
+} /* namespace libcamera */
+
+#endif /* __LIBCAMERA_LIB_LOADER_H__ */
diff --git a/src/libcamera/lib_loader.cpp b/src/libcamera/lib_loader.cpp
new file mode 100644
index 0000000..d7788d6
--- /dev/null
+++ b/src/libcamera/lib_loader.cpp
@@ -0,0 +1,175 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * lib_loader.cpp - load shared libraries at runtime
+ */
+
+#include "lib_loader.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"
+
+namespace libcamera {
+
+LOG_DECLARE_CATEGORY(LibLoader)
+
+LibLoader::LibLoader(const std::string &lib_path)
+	: lib_path_(lib_path)
+{
+}
+
+int LibLoader::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 -1;
+
+	if (e_ident[EI_CLASS] == ELFCLASS32 ||
+	    e_ident[EI_CLASS] == ELFCLASS64)
+		bitclass_ = e_ident[EI_CLASS];
+	else
+		return -1;
+
+	int a = 1;
+	bool little_endian = *(char *)&a == 1;
+	if (!(e_ident[EI_DATA] == ELFDATA2LSB ||
+	      e_ident[EI_DATA] == ELFDATA2MSB) ||
+	    !(e_ident[EI_DATA] == (little_endian ? ELFDATA2LSB : ELFDATA2MSB)))
+		return -1;
+
+	return 0;
+}
+
+template<class elfHeader, class secHeader, class symHeader>
+int LibLoader::loadSymbol(struct LibLoader::IPAModuleInfo *dst, int fd,
+			  const std::string &symbol)
+{
+	unsigned long soSize = lseek(fd, 0, SEEK_END);
+	unsigned char *map = (unsigned char *)mmap(NULL, soSize, PROT_READ,
+						   MAP_PRIVATE, fd, 0);
+	if (map == MAP_FAILED)
+		return errno;
+
+	elfHeader *eHdr = (elfHeader *)map;
+
+	unsigned long shnameoff =
+		((secHeader *)(map + eHdr->e_shoff +
+			       eHdr->e_shentsize * eHdr->e_shstrndx))
+			->sh_offset;
+
+	// walk section header table, find .dynsym
+	bool found = false;
+	secHeader *dynsym;
+	for (unsigned int i = 0; i < eHdr->e_shnum; i++) {
+		unsigned long i_shoff = eHdr->e_shoff + eHdr->e_shentsize * i;
+		secHeader *sHdr = (secHeader *)(map + i_shoff);
+
+		char *name = (char *)map + shnameoff + sHdr->sh_name;
+
+		if (sHdr->sh_type == SHT_DYNSYM && !strcmp(name, ".dynsym")) {
+			found = true;
+			dynsym = sHdr;
+			break;
+		}
+	}
+
+	if (!found)
+		return -1;
+
+	unsigned long dynsym_nameoff =
+		((secHeader *)(map + eHdr->e_shoff +
+			       eHdr->e_shentsize * dynsym->sh_link))
+			->sh_offset;
+
+	// walk dynsym symbol table, find desired symbol
+	found = false;
+	symHeader *target_symbol;
+	unsigned int dynsym_num = dynsym->sh_size / dynsym->sh_entsize;
+	for (unsigned int i = 0; i < dynsym_num; i++) {
+		unsigned long i_symoff = dynsym->sh_offset + dynsym->sh_entsize * i;
+		symHeader *sym = (symHeader *)(map + i_symoff);
+
+		char *name = (char *)map + dynsym_nameoff + sym->st_name;
+
+		if (!strcmp(name, symbol.c_str()) &&
+		    sym->st_info & STB_GLOBAL &&
+		    sym->st_size == sizeof(struct IPAModuleInfo)) {
+			found = true;
+			target_symbol = sym;
+			break;
+		}
+	}
+
+	if (!found)
+		return -1;
+
+	secHeader *target_section =
+		(secHeader *)(map + eHdr->e_shoff +
+			      target_symbol->st_shndx * eHdr->e_shentsize);
+	unsigned long final_addr = target_section->sh_offset +
+				   (target_symbol->st_value - target_section->sh_addr);
+	memcpy(dst, map + final_addr, sizeof(struct IPAModuleInfo));
+
+	return 0;
+}
+
+int LibLoader::loadIPAModuleInfo(const std::string &symbol)
+{
+	int fd = open(lib_path_.c_str(), O_RDONLY);
+	if (fd < 0)
+		return fd;
+
+	int ret = loadElfIdent(fd);
+	if (ret)
+		goto close;
+
+	if (bitclass_ == ELFCLASS32)
+		ret = loadSymbol<Elf32_Ehdr, Elf32_Shdr, Elf32_Sym>(&info_, fd, symbol);
+	else if (bitclass_ == ELFCLASS64)
+		ret = loadSymbol<Elf64_Ehdr, Elf64_Shdr, Elf64_Sym>(&info_, fd, symbol);
+	else
+		ret = -1;
+	if (ret)
+		goto close;
+
+	loaded_ = true;
+
+close:
+	close(fd);
+	return ret;
+}
+
+bool LibLoader::isLoaded() const
+{
+	return loaded_;
+}
+
+struct LibLoader::IPAModuleInfo LibLoader::getIPAModuleInfo() const
+{
+	return info_;
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
index 8796f49..4e7ab10 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',
+    'lib_loader.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/lib_loader.h',
     'include/log.h',
     'include/media_device.h',
     'include/media_object.h',
-- 
2.20.1



More information about the libcamera-devel mailing list