[RFC,2/7] libcamera: ipa_module: Factor out ELF file handling
diff mbox series

Message ID 20250626095944.1746345-3-paul.elder@ideasonboard.com
State New
Headers show
Series
  • Add Layers support
Related show

Commit Message

Paul Elder June 26, 2025, 9:59 a.m. UTC
In the near future we will add support for layers/shoes on top of
libcamera, which will need to validate and load symbols from ELF files
similar to how IPAModule does. To avoid code duplication, move this code
out of IPAModule into internal utils.

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
---
 include/libcamera/internal/utils.h |   6 ++
 src/libcamera/ipa_module.cpp       | 152 +---------------------------
 src/libcamera/utils.cpp            | 157 +++++++++++++++++++++++++++++
 3 files changed, 166 insertions(+), 149 deletions(-)

Patch
diff mbox series

diff --git a/include/libcamera/internal/utils.h b/include/libcamera/internal/utils.h
index 742657bebb28..98d5094eb88d 100644
--- a/include/libcamera/internal/utils.h
+++ b/include/libcamera/internal/utils.h
@@ -8,9 +8,12 @@ 
 #pragma once
 
 #include <functional>
+#include <stdint.h>
 #include <string>
 #include <vector>
 
+#include <libcamera/base/span.h>
+
 namespace libcamera {
 
 namespace utils {
@@ -21,6 +24,9 @@  void parseDir(const char *libDir, unsigned int maxDepth,
 unsigned int addDir(const char *libDir, unsigned int maxDepth,
 		    std::function<int(const std::string &)> func);
 
+int elfVerifyIdent(Span<const uint8_t> elf);
+Span<const uint8_t> elfLoadSymbol(Span<const uint8_t> elf, const char *symbol);
+
 } /* namespace utils */
 
 } /* namespace libcamera */
diff --git a/src/libcamera/ipa_module.cpp b/src/libcamera/ipa_module.cpp
index e6ea61e44829..c8559ec8b74c 100644
--- a/src/libcamera/ipa_module.cpp
+++ b/src/libcamera/ipa_module.cpp
@@ -25,6 +25,7 @@ 
 #include <libcamera/base/utils.h>
 
 #include "libcamera/internal/pipeline_handler.h"
+#include "libcamera/internal/utils.h"
 
 /**
  * \file ipa_module.h
@@ -40,153 +41,6 @@  namespace libcamera {
 
 LOG_DEFINE_CATEGORY(IPAModule)
 
-namespace {
-
-template<typename T>
-typename std::remove_extent_t<T> *elfPointer(Span<const uint8_t> elf,
-					     off_t offset, size_t objSize)
-{
-	size_t size = offset + objSize;
-	if (size > elf.size() || size < objSize)
-		return nullptr;
-
-	return reinterpret_cast<typename std::remove_extent_t<T> *>(
-		reinterpret_cast<const char *>(elf.data()) + offset);
-}
-
-template<typename T>
-typename std::remove_extent_t<T> *elfPointer(Span<const uint8_t> elf,
-					     off_t offset)
-{
-	return elfPointer<T>(elf, offset, sizeof(T));
-}
-
-int elfVerifyIdent(Span<const uint8_t> elf)
-{
-	const char *e_ident = elfPointer<const char[EI_NIDENT]>(elf, 0);
-	if (!e_ident)
-		return -ENOEXEC;
-
-	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;
-
-	int bitClass = sizeof(unsigned long) == 4 ? ELFCLASS32 : ELFCLASS64;
-	if (e_ident[EI_CLASS] != bitClass)
-		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;
-}
-
-const ElfW(Shdr) *elfSection(Span<const uint8_t> elf, const ElfW(Ehdr) *eHdr,
-			     ElfW(Half) idx)
-{
-	if (idx >= eHdr->e_shnum)
-		return nullptr;
-
-	off_t offset = eHdr->e_shoff + idx *
-				       static_cast<uint32_t>(eHdr->e_shentsize);
-	return elfPointer<const ElfW(Shdr)>(elf, offset);
-}
-
-/**
- * \brief Retrieve address and size of a symbol from an mmap'ed ELF file
- * \param[in] elf Address and size of mmap'ed ELF file
- * \param[in] symbol Symbol name
- *
- * \return The memory region storing the symbol on success, or an empty span
- * otherwise
- */
-Span<const uint8_t> elfLoadSymbol(Span<const uint8_t> elf, const char *symbol)
-{
-	const ElfW(Ehdr) *eHdr = elfPointer<const ElfW(Ehdr)>(elf, 0);
-	if (!eHdr)
-		return {};
-
-	const ElfW(Shdr) *sHdr = elfSection(elf, eHdr, eHdr->e_shstrndx);
-	if (!sHdr)
-		return {};
-	off_t shnameoff = sHdr->sh_offset;
-
-	/* Locate .dynsym section header. */
-	const ElfW(Shdr) *dynsym = nullptr;
-	for (unsigned int i = 0; i < eHdr->e_shnum; i++) {
-		sHdr = elfSection(elf, eHdr, i);
-		if (!sHdr)
-			return {};
-
-		off_t offset = shnameoff + sHdr->sh_name;
-		const char *name = elfPointer<const char[8]>(elf, offset);
-		if (!name)
-			return {};
-
-		if (sHdr->sh_type == SHT_DYNSYM && !strcmp(name, ".dynsym")) {
-			dynsym = sHdr;
-			break;
-		}
-	}
-
-	if (dynsym == nullptr) {
-		LOG(IPAModule, Error) << "ELF has no .dynsym section";
-		return {};
-	}
-
-	sHdr = elfSection(elf, eHdr, dynsym->sh_link);
-	if (!sHdr)
-		return {};
-	off_t dynsym_nameoff = sHdr->sh_offset;
-
-	/* Locate symbol in the .dynsym section. */
-	const ElfW(Sym) *targetSymbol = nullptr;
-	unsigned int dynsym_num = dynsym->sh_size / dynsym->sh_entsize;
-	for (unsigned int i = 0; i < dynsym_num; i++) {
-		off_t offset = dynsym->sh_offset + dynsym->sh_entsize * i;
-		const ElfW(Sym) *sym = elfPointer<const ElfW(Sym)>(elf, offset);
-		if (!sym)
-			return {};
-
-		offset = dynsym_nameoff + sym->st_name;
-		const char *name = elfPointer<const char>(elf, offset,
-							  strlen(symbol) + 1);
-		if (!name)
-			return {};
-
-		if (!strcmp(name, symbol) &&
-		    sym->st_info & STB_GLOBAL) {
-			targetSymbol = sym;
-			break;
-		}
-	}
-
-	if (targetSymbol == nullptr) {
-		LOG(IPAModule, Error) << "Symbol " << symbol << " not found";
-		return {};
-	}
-
-	/* Locate and return data of symbol. */
-	sHdr = elfSection(elf, eHdr, targetSymbol->st_shndx);
-	if (!sHdr)
-		return {};
-	off_t offset = sHdr->sh_offset + (targetSymbol->st_value - sHdr->sh_addr);
-	const uint8_t *data = elfPointer<const uint8_t>(elf, offset,
-							targetSymbol->st_size);
-	if (!data)
-		return {};
-
-	return { data, targetSymbol->st_size };
-}
-
-} /* namespace */
-
 /**
  * \def IPA_MODULE_API_VERSION
  * \brief The IPA module API version
@@ -280,13 +134,13 @@  int IPAModule::loadIPAModuleInfo()
 	}
 
 	Span<const uint8_t> data = file.map();
-	int ret = elfVerifyIdent(data);
+	int ret = utils::elfVerifyIdent(data);
 	if (ret) {
 		LOG(IPAModule, Error) << "IPA module is not an ELF file";
 		return ret;
 	}
 
-	Span<const uint8_t> info = elfLoadSymbol(data, "ipaModuleInfo");
+	Span<const uint8_t> info = utils::elfLoadSymbol(data, "ipaModuleInfo");
 	if (info.size() < sizeof(info_)) {
 		LOG(IPAModule, Error) << "IPA module has no valid info";
 		return -EINVAL;
diff --git a/src/libcamera/utils.cpp b/src/libcamera/utils.cpp
index ef046ac3134e..f7bc5c54ff57 100644
--- a/src/libcamera/utils.cpp
+++ b/src/libcamera/utils.cpp
@@ -10,20 +10,63 @@ 
 #include <algorithm>
 #include <dirent.h>
 #include <functional>
+#include <link.h>
+#include <stdint.h>
 #include <string.h>
 #include <string>
 #include <sys/types.h>
 #include <vector>
 
+#include <libcamera/base/log.h>
+
 /**
  * \file internal/utils.h
  * \brief Miscellaneous utility functions (internal)
  */
 
+/* musl doesn't declare _DYNAMIC in link.h, declare it manually. */
+extern ElfW(Dyn) _DYNAMIC[];
+
 namespace libcamera {
 
+namespace {
+
+template<typename T>
+typename std::remove_extent_t<T> *elfPointer(Span<const uint8_t> elf,
+					     off_t offset, size_t objSize)
+{
+	size_t size = offset + objSize;
+	if (size > elf.size() || size < objSize)
+		return nullptr;
+
+	return reinterpret_cast<typename std::remove_extent_t<T> *>(
+		reinterpret_cast<const char *>(elf.data()) + offset);
+}
+
+template<typename T>
+typename std::remove_extent_t<T> *elfPointer(Span<const uint8_t> elf,
+					     off_t offset)
+{
+	return elfPointer<T>(elf, offset, sizeof(T));
+}
+
+const ElfW(Shdr) *elfSection(Span<const uint8_t> elf, const ElfW(Ehdr) *eHdr,
+			     ElfW(Half) idx)
+{
+	if (idx >= eHdr->e_shnum)
+		return nullptr;
+
+	off_t offset = eHdr->e_shoff + idx *
+				       static_cast<uint32_t>(eHdr->e_shentsize);
+	return elfPointer<const ElfW(Shdr)>(elf, offset);
+}
+
+} /* namespace */
+
 namespace utils {
 
+LOG_DEFINE_CATEGORY(Utils)
+
 /**
  * \brief Identify shared library objects within a directory
  * \param[in] libDir The directory to search for shared objects
@@ -102,6 +145,120 @@  unsigned int addDir(const char *libDir, unsigned int maxDepth,
 	return count;
 }
 
+int elfVerifyIdent(Span<const uint8_t> elf)
+{
+	const char *e_ident = elfPointer<const char[EI_NIDENT]>(elf, 0);
+	if (!e_ident)
+		return -ENOEXEC;
+
+	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;
+
+	int bitClass = sizeof(unsigned long) == 4 ? ELFCLASS32 : ELFCLASS64;
+	if (e_ident[EI_CLASS] != bitClass)
+		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;
+}
+
+/**
+ * \brief Retrieve address and size of a symbol from an mmap'ed ELF file
+ * \param[in] elf Address and size of mmap'ed ELF file
+ * \param[in] symbol Symbol name
+ *
+ * \return The memory region storing the symbol on success, or an empty span
+ * otherwise
+ */
+Span<const uint8_t> elfLoadSymbol(Span<const uint8_t> elf, const char *symbol)
+{
+	const ElfW(Ehdr) *eHdr = elfPointer<const ElfW(Ehdr)>(elf, 0);
+	if (!eHdr)
+		return {};
+
+	const ElfW(Shdr) *sHdr = elfSection(elf, eHdr, eHdr->e_shstrndx);
+	if (!sHdr)
+		return {};
+	off_t shnameoff = sHdr->sh_offset;
+
+	/* Locate .dynsym section header. */
+	const ElfW(Shdr) *dynsym = nullptr;
+	for (unsigned int i = 0; i < eHdr->e_shnum; i++) {
+		sHdr = elfSection(elf, eHdr, i);
+		if (!sHdr)
+			return {};
+
+		off_t offset = shnameoff + sHdr->sh_name;
+		const char *name = elfPointer<const char[8]>(elf, offset);
+		if (!name)
+			return {};
+
+		if (sHdr->sh_type == SHT_DYNSYM && !strcmp(name, ".dynsym")) {
+			dynsym = sHdr;
+			break;
+		}
+	}
+
+	if (dynsym == nullptr) {
+		LOG(Utils, Error) << "ELF has no .dynsym section";
+		return {};
+	}
+
+	sHdr = elfSection(elf, eHdr, dynsym->sh_link);
+	if (!sHdr)
+		return {};
+	off_t dynsym_nameoff = sHdr->sh_offset;
+
+	/* Locate symbol in the .dynsym section. */
+	const ElfW(Sym) *targetSymbol = nullptr;
+	unsigned int dynsym_num = dynsym->sh_size / dynsym->sh_entsize;
+	for (unsigned int i = 0; i < dynsym_num; i++) {
+		off_t offset = dynsym->sh_offset + dynsym->sh_entsize * i;
+		const ElfW(Sym) *sym = elfPointer<const ElfW(Sym)>(elf, offset);
+		if (!sym)
+			return {};
+
+		offset = dynsym_nameoff + sym->st_name;
+		const char *name = elfPointer<const char>(elf, offset,
+							  strlen(symbol) + 1);
+		if (!name)
+			return {};
+
+		if (!strcmp(name, symbol) &&
+		    sym->st_info & STB_GLOBAL) {
+			targetSymbol = sym;
+			break;
+		}
+	}
+
+	if (targetSymbol == nullptr) {
+		LOG(Utils, Error) << "Symbol " << symbol << " not found";
+		return {};
+	}
+
+	/* Locate and return data of symbol. */
+	sHdr = elfSection(elf, eHdr, targetSymbol->st_shndx);
+	if (!sHdr)
+		return {};
+	off_t offset = sHdr->sh_offset + (targetSymbol->st_value - sHdr->sh_addr);
+	const uint8_t *data = elfPointer<const uint8_t>(elf, offset,
+							targetSymbol->st_size);
+	if (!data)
+		return {};
+
+	return { data, targetSymbol->st_size };
+}
+
+
 } /* namespace utils */
 
 } /* namespace libcamera */