{"id":25986,"url":"https://patchwork.libcamera.org/api/patches/25986/?format=json","web_url":"https://patchwork.libcamera.org/patch/25986/","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":"<20260128074956.760538-3-paul.elder@ideasonboard.com>","date":"2026-01-28T07:49:50","name":"[v5,2/8] libcamera: ipa_module: Factor out ELF file handling","commit_ref":null,"pull_url":null,"state":"superseded","archived":false,"hash":"dba6406d74eee09c6dd6fb75394303e70f1b5483","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/25986/mbox/","series":[{"id":5747,"url":"https://patchwork.libcamera.org/api/series/5747/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=5747","date":"2026-01-28T07:49:48","name":"Add Layers support","version":5,"mbox":"https://patchwork.libcamera.org/series/5747/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/25986/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/25986/checks/","tags":{},"headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id A68BBC3200\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 28 Jan 2026 07:50:16 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C4F0461FD4;\n\tWed, 28 Jan 2026 08:50:15 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 480E961A35\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 28 Jan 2026 08:50:12 +0100 (CET)","from neptunite.hamster-moth.ts.net (unknown\n\t[IPv6:2404:7a81:160:2100:508d:7983:72a6:2eeb])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 405E714B0;\n\tWed, 28 Jan 2026 08:49:33 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"VHt7EStY\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1769586575;\n\tbh=a1ZalkDBN3G4/s1M4XGCRZj5Gi28SN+CbIQywA8Dfwc=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=VHt7EStYLlIVniMiYWvXf9zu7yDswsGTx7+v8R912SPqDPtMX/Vdmft+gnv1k1Myv\n\txst675x6r+KVrK0FHOWYuBoRBAly1COJNg9ZdehLBca0GfFwTf9b9+gIkB+2sOeIt3\n\t/ztVfdwTsrAXFT5IqSP54hwSQx4A+aMPRwwUJ/fI=","From":"Paul Elder <paul.elder@ideasonboard.com>","To":"libcamera-devel@lists.libcamera.org","Cc":"Paul Elder <paul.elder@ideasonboard.com>,\n\tStefan Klug <stefan.klug@ideasonboard.com>","Subject":"[PATCH v5 2/8] libcamera: ipa_module: Factor out ELF file handling","Date":"Wed, 28 Jan 2026 16:49:50 +0900","Message-ID":"<20260128074956.760538-3-paul.elder@ideasonboard.com>","X-Mailer":"git-send-email 2.47.2","In-Reply-To":"<20260128074956.760538-1-paul.elder@ideasonboard.com>","References":"<20260128074956.760538-1-paul.elder@ideasonboard.com>","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","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>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"},"content":"In the near future we will add support for layers/shoes on top of\nlibcamera, which will need to validate and load symbols from ELF files\nsimilar to how IPAModule does. To avoid code duplication, move this code\nout of IPAModule into internal utils.\n\nSigned-off-by: Paul Elder <paul.elder@ideasonboard.com>\nReviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>\n---\nNo change in v4\n\nChanges in v3:\n- add documentation for elfVerifyIdent\n\nNo change in v2\n---\n include/libcamera/internal/utils.h |   6 ++\n src/libcamera/ipa_module.cpp       | 152 +--------------------------\n src/libcamera/utils.cpp            | 163 +++++++++++++++++++++++++++++\n 3 files changed, 172 insertions(+), 149 deletions(-)","diff":"diff --git a/include/libcamera/internal/utils.h b/include/libcamera/internal/utils.h\nindex 5b7957b5d77e..7d69849b1c19 100644\n--- a/include/libcamera/internal/utils.h\n+++ b/include/libcamera/internal/utils.h\n@@ -8,9 +8,12 @@\n #pragma once\n \n #include <functional>\n+#include <stdint.h>\n #include <string>\n #include <vector>\n \n+#include <libcamera/base/span.h>\n+\n namespace libcamera {\n \n namespace utils {\n@@ -18,6 +21,9 @@ namespace utils {\n unsigned int findSharedObjects(const char *libDir, unsigned int maxDepth,\n \t\t\t       std::function<int(const std::string &)> func);\n \n+int elfVerifyIdent(Span<const uint8_t> elf);\n+Span<const uint8_t> elfLoadSymbol(Span<const uint8_t> elf, const char *symbol);\n+\n } /* namespace utils */\n \n } /* namespace libcamera */\ndiff --git a/src/libcamera/ipa_module.cpp b/src/libcamera/ipa_module.cpp\nindex e6ea61e44829..c8559ec8b74c 100644\n--- a/src/libcamera/ipa_module.cpp\n+++ b/src/libcamera/ipa_module.cpp\n@@ -25,6 +25,7 @@\n #include <libcamera/base/utils.h>\n \n #include \"libcamera/internal/pipeline_handler.h\"\n+#include \"libcamera/internal/utils.h\"\n \n /**\n  * \\file ipa_module.h\n@@ -40,153 +41,6 @@ namespace libcamera {\n \n LOG_DEFINE_CATEGORY(IPAModule)\n \n-namespace {\n-\n-template<typename T>\n-typename std::remove_extent_t<T> *elfPointer(Span<const uint8_t> elf,\n-\t\t\t\t\t     off_t offset, size_t objSize)\n-{\n-\tsize_t size = offset + objSize;\n-\tif (size > elf.size() || size < objSize)\n-\t\treturn nullptr;\n-\n-\treturn reinterpret_cast<typename std::remove_extent_t<T> *>(\n-\t\treinterpret_cast<const char *>(elf.data()) + offset);\n-}\n-\n-template<typename T>\n-typename std::remove_extent_t<T> *elfPointer(Span<const uint8_t> elf,\n-\t\t\t\t\t     off_t offset)\n-{\n-\treturn elfPointer<T>(elf, offset, sizeof(T));\n-}\n-\n-int elfVerifyIdent(Span<const uint8_t> elf)\n-{\n-\tconst char *e_ident = elfPointer<const char[EI_NIDENT]>(elf, 0);\n-\tif (!e_ident)\n-\t\treturn -ENOEXEC;\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-\tint bitClass = sizeof(unsigned long) == 4 ? ELFCLASS32 : ELFCLASS64;\n-\tif (e_ident[EI_CLASS] != bitClass)\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-const ElfW(Shdr) *elfSection(Span<const uint8_t> elf, const ElfW(Ehdr) *eHdr,\n-\t\t\t     ElfW(Half) idx)\n-{\n-\tif (idx >= eHdr->e_shnum)\n-\t\treturn nullptr;\n-\n-\toff_t offset = eHdr->e_shoff + idx *\n-\t\t\t\t       static_cast<uint32_t>(eHdr->e_shentsize);\n-\treturn elfPointer<const ElfW(Shdr)>(elf, offset);\n-}\n-\n-/**\n- * \\brief Retrieve address and size of a symbol from an mmap'ed ELF file\n- * \\param[in] elf Address and size of mmap'ed ELF file\n- * \\param[in] symbol Symbol name\n- *\n- * \\return The memory region storing the symbol on success, or an empty span\n- * otherwise\n- */\n-Span<const uint8_t> elfLoadSymbol(Span<const uint8_t> elf, const char *symbol)\n-{\n-\tconst ElfW(Ehdr) *eHdr = elfPointer<const ElfW(Ehdr)>(elf, 0);\n-\tif (!eHdr)\n-\t\treturn {};\n-\n-\tconst ElfW(Shdr) *sHdr = elfSection(elf, eHdr, eHdr->e_shstrndx);\n-\tif (!sHdr)\n-\t\treturn {};\n-\toff_t shnameoff = sHdr->sh_offset;\n-\n-\t/* Locate .dynsym section header. */\n-\tconst ElfW(Shdr) *dynsym = nullptr;\n-\tfor (unsigned int i = 0; i < eHdr->e_shnum; i++) {\n-\t\tsHdr = elfSection(elf, eHdr, i);\n-\t\tif (!sHdr)\n-\t\t\treturn {};\n-\n-\t\toff_t offset = shnameoff + sHdr->sh_name;\n-\t\tconst char *name = elfPointer<const char[8]>(elf, offset);\n-\t\tif (!name)\n-\t\t\treturn {};\n-\n-\t\tif (sHdr->sh_type == SHT_DYNSYM && !strcmp(name, \".dynsym\")) {\n-\t\t\tdynsym = sHdr;\n-\t\t\tbreak;\n-\t\t}\n-\t}\n-\n-\tif (dynsym == nullptr) {\n-\t\tLOG(IPAModule, Error) << \"ELF has no .dynsym section\";\n-\t\treturn {};\n-\t}\n-\n-\tsHdr = elfSection(elf, eHdr, dynsym->sh_link);\n-\tif (!sHdr)\n-\t\treturn {};\n-\toff_t dynsym_nameoff = sHdr->sh_offset;\n-\n-\t/* Locate symbol in the .dynsym section. */\n-\tconst ElfW(Sym) *targetSymbol = nullptr;\n-\tunsigned int dynsym_num = dynsym->sh_size / dynsym->sh_entsize;\n-\tfor (unsigned int i = 0; i < dynsym_num; i++) {\n-\t\toff_t offset = dynsym->sh_offset + dynsym->sh_entsize * i;\n-\t\tconst ElfW(Sym) *sym = elfPointer<const ElfW(Sym)>(elf, offset);\n-\t\tif (!sym)\n-\t\t\treturn {};\n-\n-\t\toffset = dynsym_nameoff + sym->st_name;\n-\t\tconst char *name = elfPointer<const char>(elf, offset,\n-\t\t\t\t\t\t\t  strlen(symbol) + 1);\n-\t\tif (!name)\n-\t\t\treturn {};\n-\n-\t\tif (!strcmp(name, symbol) &&\n-\t\t    sym->st_info & STB_GLOBAL) {\n-\t\t\ttargetSymbol = sym;\n-\t\t\tbreak;\n-\t\t}\n-\t}\n-\n-\tif (targetSymbol == nullptr) {\n-\t\tLOG(IPAModule, Error) << \"Symbol \" << symbol << \" not found\";\n-\t\treturn {};\n-\t}\n-\n-\t/* Locate and return data of symbol. */\n-\tsHdr = elfSection(elf, eHdr, targetSymbol->st_shndx);\n-\tif (!sHdr)\n-\t\treturn {};\n-\toff_t offset = sHdr->sh_offset + (targetSymbol->st_value - sHdr->sh_addr);\n-\tconst uint8_t *data = elfPointer<const uint8_t>(elf, offset,\n-\t\t\t\t\t\t\ttargetSymbol->st_size);\n-\tif (!data)\n-\t\treturn {};\n-\n-\treturn { data, targetSymbol->st_size };\n-}\n-\n-} /* namespace */\n-\n /**\n  * \\def IPA_MODULE_API_VERSION\n  * \\brief The IPA module API version\n@@ -280,13 +134,13 @@ int IPAModule::loadIPAModuleInfo()\n \t}\n \n \tSpan<const uint8_t> data = file.map();\n-\tint ret = elfVerifyIdent(data);\n+\tint ret = utils::elfVerifyIdent(data);\n \tif (ret) {\n \t\tLOG(IPAModule, Error) << \"IPA module is not an ELF file\";\n \t\treturn ret;\n \t}\n \n-\tSpan<const uint8_t> info = elfLoadSymbol(data, \"ipaModuleInfo\");\n+\tSpan<const uint8_t> info = utils::elfLoadSymbol(data, \"ipaModuleInfo\");\n \tif (info.size() < sizeof(info_)) {\n \t\tLOG(IPAModule, Error) << \"IPA module has no valid info\";\n \t\treturn -EINVAL;\ndiff --git a/src/libcamera/utils.cpp b/src/libcamera/utils.cpp\nindex 2ce62f236cea..2cfc72e03590 100644\n--- a/src/libcamera/utils.cpp\n+++ b/src/libcamera/utils.cpp\n@@ -10,20 +10,63 @@\n #include <algorithm>\n #include <dirent.h>\n #include <functional>\n+#include <link.h>\n+#include <stdint.h>\n #include <string.h>\n #include <string>\n #include <sys/types.h>\n #include <vector>\n \n+#include <libcamera/base/log.h>\n+\n /**\n  * \\file internal/utils.h\n  * \\brief Miscellaneous utility functions (internal)\n  */\n \n+/* musl doesn't declare _DYNAMIC in link.h, declare it manually. */\n+extern ElfW(Dyn) _DYNAMIC[];\n+\n namespace libcamera {\n \n+namespace {\n+\n+template<typename T>\n+typename std::remove_extent_t<T> *elfPointer(Span<const uint8_t> elf,\n+\t\t\t\t\t     off_t offset, size_t objSize)\n+{\n+\tsize_t size = offset + objSize;\n+\tif (size > elf.size() || size < objSize)\n+\t\treturn nullptr;\n+\n+\treturn reinterpret_cast<typename std::remove_extent_t<T> *>(\n+\t\treinterpret_cast<const char *>(elf.data()) + offset);\n+}\n+\n+template<typename T>\n+typename std::remove_extent_t<T> *elfPointer(Span<const uint8_t> elf,\n+\t\t\t\t\t     off_t offset)\n+{\n+\treturn elfPointer<T>(elf, offset, sizeof(T));\n+}\n+\n+const ElfW(Shdr) *elfSection(Span<const uint8_t> elf, const ElfW(Ehdr) *eHdr,\n+\t\t\t     ElfW(Half) idx)\n+{\n+\tif (idx >= eHdr->e_shnum)\n+\t\treturn nullptr;\n+\n+\toff_t offset = eHdr->e_shoff + idx *\n+\t\t\t\t       static_cast<uint32_t>(eHdr->e_shentsize);\n+\treturn elfPointer<const ElfW(Shdr)>(elf, offset);\n+}\n+\n+} /* namespace */\n+\n namespace utils {\n \n+LOG_DEFINE_CATEGORY(Utils)\n+\n /**\n  * \\brief Identify shared library objects within a directory\n  * \\param[in] libDir The directory to search for shared objects\n@@ -102,6 +145,126 @@ unsigned int findSharedObjects(const char *libDir, unsigned int maxDepth,\n \treturn count;\n }\n \n+/**\n+ * \\brief Verify that a binary blob is an ELF\n+ * \\param[in] elf The binary blob as a Span\n+ *\n+ * \\return 0 for success, and -ENOEXEC otherwise\n+ */\n+int elfVerifyIdent(Span<const uint8_t> elf)\n+{\n+\tconst char *e_ident = elfPointer<const char[EI_NIDENT]>(elf, 0);\n+\tif (!e_ident)\n+\t\treturn -ENOEXEC;\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+\tint bitClass = sizeof(unsigned long) == 4 ? ELFCLASS32 : ELFCLASS64;\n+\tif (e_ident[EI_CLASS] != bitClass)\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+/**\n+ * \\brief Retrieve address and size of a symbol from an mmap'ed ELF file\n+ * \\param[in] elf Address and size of mmap'ed ELF file\n+ * \\param[in] symbol Symbol name\n+ *\n+ * \\return The memory region storing the symbol on success, or an empty span\n+ * otherwise\n+ */\n+Span<const uint8_t> elfLoadSymbol(Span<const uint8_t> elf, const char *symbol)\n+{\n+\tconst ElfW(Ehdr) *eHdr = elfPointer<const ElfW(Ehdr)>(elf, 0);\n+\tif (!eHdr)\n+\t\treturn {};\n+\n+\tconst ElfW(Shdr) *sHdr = elfSection(elf, eHdr, eHdr->e_shstrndx);\n+\tif (!sHdr)\n+\t\treturn {};\n+\toff_t shnameoff = sHdr->sh_offset;\n+\n+\t/* Locate .dynsym section header. */\n+\tconst ElfW(Shdr) *dynsym = nullptr;\n+\tfor (unsigned int i = 0; i < eHdr->e_shnum; i++) {\n+\t\tsHdr = elfSection(elf, eHdr, i);\n+\t\tif (!sHdr)\n+\t\t\treturn {};\n+\n+\t\toff_t offset = shnameoff + sHdr->sh_name;\n+\t\tconst char *name = elfPointer<const char[8]>(elf, offset);\n+\t\tif (!name)\n+\t\t\treturn {};\n+\n+\t\tif (sHdr->sh_type == SHT_DYNSYM && !strcmp(name, \".dynsym\")) {\n+\t\t\tdynsym = sHdr;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (dynsym == nullptr) {\n+\t\tLOG(Utils, Error) << \"ELF has no .dynsym section\";\n+\t\treturn {};\n+\t}\n+\n+\tsHdr = elfSection(elf, eHdr, dynsym->sh_link);\n+\tif (!sHdr)\n+\t\treturn {};\n+\toff_t dynsym_nameoff = sHdr->sh_offset;\n+\n+\t/* Locate symbol in the .dynsym section. */\n+\tconst ElfW(Sym) *targetSymbol = nullptr;\n+\tunsigned int dynsym_num = dynsym->sh_size / dynsym->sh_entsize;\n+\tfor (unsigned int i = 0; i < dynsym_num; i++) {\n+\t\toff_t offset = dynsym->sh_offset + dynsym->sh_entsize * i;\n+\t\tconst ElfW(Sym) *sym = elfPointer<const ElfW(Sym)>(elf, offset);\n+\t\tif (!sym)\n+\t\t\treturn {};\n+\n+\t\toffset = dynsym_nameoff + sym->st_name;\n+\t\tconst char *name = elfPointer<const char>(elf, offset,\n+\t\t\t\t\t\t\t  strlen(symbol) + 1);\n+\t\tif (!name)\n+\t\t\treturn {};\n+\n+\t\tif (!strcmp(name, symbol) &&\n+\t\t    sym->st_info & STB_GLOBAL) {\n+\t\t\ttargetSymbol = sym;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (targetSymbol == nullptr) {\n+\t\tLOG(Utils, Error) << \"Symbol \" << symbol << \" not found\";\n+\t\treturn {};\n+\t}\n+\n+\t/* Locate and return data of symbol. */\n+\tsHdr = elfSection(elf, eHdr, targetSymbol->st_shndx);\n+\tif (!sHdr)\n+\t\treturn {};\n+\toff_t offset = sHdr->sh_offset + (targetSymbol->st_value - sHdr->sh_addr);\n+\tconst uint8_t *data = elfPointer<const uint8_t>(elf, offset,\n+\t\t\t\t\t\t\ttargetSymbol->st_size);\n+\tif (!data)\n+\t\treturn {};\n+\n+\treturn { data, targetSymbol->st_size };\n+}\n+\n+\n } /* namespace utils */\n \n } /* namespace libcamera */\n","prefixes":["v5","2/8"]}