From patchwork Mon Aug 25 11:42:04 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 24223 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 530F4BEFBE for ; Mon, 25 Aug 2025 11:42:40 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 03986692EE; Mon, 25 Aug 2025 13:42:40 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="ZNf7sOYP"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 604B9692E3 for ; Mon, 25 Aug 2025 13:42:36 +0200 (CEST) Received: from neptunite.infra.iob (unknown [IPv6:2404:7a81:160:2100:39d7:37aa:64a2:5533]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 8D9CB2285; Mon, 25 Aug 2025 13:41:32 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1756122094; bh=a1ZalkDBN3G4/s1M4XGCRZj5Gi28SN+CbIQywA8Dfwc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ZNf7sOYPXnIDR0pd4hAW3+aR8B9xhHBG+0wi5educMBOfYEOc4AOU8TUlLV0AbHNy UoLWAURh1erVhejGyLDpX5MO4SbA4TNHZnVFyUHxzxG2YDjb0NwDUzm5oJdR+WbBU/ sc2fMLXlx9Xm9hjA7wFlW0P1QbDCObflP2IZ55cI= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder , kieran.bingham@ideasonboard.com, stefan.klug@ideasonboard.com, barnabas.pocze@ideasonboard.com Subject: [PATCH v4 2/8] libcamera: ipa_module: Factor out ELF file handling Date: Mon, 25 Aug 2025 20:42:04 +0900 Message-ID: <20250825114219.562831-3-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250825114219.562831-1-paul.elder@ideasonboard.com> References: <20250825114219.562831-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" 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 Reviewed-by: Stefan Klug --- No change in v4 Changes in v3: - add documentation for elfVerifyIdent No change in v2 --- include/libcamera/internal/utils.h | 6 ++ src/libcamera/ipa_module.cpp | 152 +-------------------------- src/libcamera/utils.cpp | 163 +++++++++++++++++++++++++++++ 3 files changed, 172 insertions(+), 149 deletions(-) diff --git a/include/libcamera/internal/utils.h b/include/libcamera/internal/utils.h index 5b7957b5d77e..7d69849b1c19 100644 --- a/include/libcamera/internal/utils.h +++ b/include/libcamera/internal/utils.h @@ -8,9 +8,12 @@ #pragma once #include +#include #include #include +#include + namespace libcamera { namespace utils { @@ -18,6 +21,9 @@ namespace utils { unsigned int findSharedObjects(const char *libDir, unsigned int maxDepth, std::function func); +int elfVerifyIdent(Span elf); +Span elfLoadSymbol(Span 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 #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 std::remove_extent_t *elfPointer(Span elf, - off_t offset, size_t objSize) -{ - size_t size = offset + objSize; - if (size > elf.size() || size < objSize) - return nullptr; - - return reinterpret_cast *>( - reinterpret_cast(elf.data()) + offset); -} - -template -typename std::remove_extent_t *elfPointer(Span elf, - off_t offset) -{ - return elfPointer(elf, offset, sizeof(T)); -} - -int elfVerifyIdent(Span elf) -{ - const char *e_ident = elfPointer(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(&a) == 1 - ? ELFDATA2LSB : ELFDATA2MSB; - if (e_ident[EI_DATA] != endianness) - return -ENOEXEC; - - return 0; -} - -const ElfW(Shdr) *elfSection(Span elf, const ElfW(Ehdr) *eHdr, - ElfW(Half) idx) -{ - if (idx >= eHdr->e_shnum) - return nullptr; - - off_t offset = eHdr->e_shoff + idx * - static_cast(eHdr->e_shentsize); - return elfPointer(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 elfLoadSymbol(Span elf, const char *symbol) -{ - const ElfW(Ehdr) *eHdr = elfPointer(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(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(elf, offset); - if (!sym) - return {}; - - offset = dynsym_nameoff + sym->st_name; - const char *name = elfPointer(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(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 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 info = elfLoadSymbol(data, "ipaModuleInfo"); + Span 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 2ce62f236cea..2cfc72e03590 100644 --- a/src/libcamera/utils.cpp +++ b/src/libcamera/utils.cpp @@ -10,20 +10,63 @@ #include #include #include +#include +#include #include #include #include #include +#include + /** * \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 std::remove_extent_t *elfPointer(Span elf, + off_t offset, size_t objSize) +{ + size_t size = offset + objSize; + if (size > elf.size() || size < objSize) + return nullptr; + + return reinterpret_cast *>( + reinterpret_cast(elf.data()) + offset); +} + +template +typename std::remove_extent_t *elfPointer(Span elf, + off_t offset) +{ + return elfPointer(elf, offset, sizeof(T)); +} + +const ElfW(Shdr) *elfSection(Span elf, const ElfW(Ehdr) *eHdr, + ElfW(Half) idx) +{ + if (idx >= eHdr->e_shnum) + return nullptr; + + off_t offset = eHdr->e_shoff + idx * + static_cast(eHdr->e_shentsize); + return elfPointer(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,126 @@ unsigned int findSharedObjects(const char *libDir, unsigned int maxDepth, return count; } +/** + * \brief Verify that a binary blob is an ELF + * \param[in] elf The binary blob as a Span + * + * \return 0 for success, and -ENOEXEC otherwise + */ +int elfVerifyIdent(Span elf) +{ + const char *e_ident = elfPointer(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(&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 elfLoadSymbol(Span elf, const char *symbol) +{ + const ElfW(Ehdr) *eHdr = elfPointer(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(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(elf, offset); + if (!sym) + return {}; + + offset = dynsym_nameoff + sym->st_name; + const char *name = elfPointer(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(elf, offset, + targetSymbol->st_size); + if (!data) + return {}; + + return { data, targetSymbol->st_size }; +} + + } /* namespace utils */ } /* namespace libcamera */