[{"id":34793,"web_url":"https://patchwork.libcamera.org/comment/34793/","msgid":"<175188298808.40598.5801068176889994026@localhost>","date":"2025-07-07T10:09:48","subject":"Re: [PATCH v2 2/8] libcamera: ipa_module: Factor out ELF file\n\thandling","submitter":{"id":184,"url":"https://patchwork.libcamera.org/api/people/184/","name":"Stefan Klug","email":"stefan.klug@ideasonboard.com"},"content":"Hi Paul,\n\nThank you for the patch.\n\nQuoting Paul Elder (2025-07-03 13:42:17)\n> In the near future we will add support for layers/shoes on top of\n> libcamera, which will need to validate and load symbols from ELF files\n> similar to how IPAModule does. To avoid code duplication, move this code\n> out of IPAModule into internal utils.\n> \n> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> \n> ---\n> No change in v2\n> ---\n>  include/libcamera/internal/utils.h |   6 ++\n>  src/libcamera/ipa_module.cpp       | 152 +---------------------------\n>  src/libcamera/utils.cpp            | 157 +++++++++++++++++++++++++++++\n>  3 files changed, 166 insertions(+), 149 deletions(-)\n> \n> diff --git a/include/libcamera/internal/utils.h b/include/libcamera/internal/utils.h\n> index 742657bebb28..98d5094eb88d 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> @@ -21,6 +24,9 @@ void parseDir(const char *libDir, unsigned int maxDepth,\n>  unsigned int addDir(const char *libDir, unsigned int maxDepth,\n>                     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 */\n> diff --git a/src/libcamera/ipa_module.cpp b/src/libcamera/ipa_module.cpp\n> index 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> -                                            off_t offset, size_t objSize)\n> -{\n> -       size_t size = offset + objSize;\n> -       if (size > elf.size() || size < objSize)\n> -               return nullptr;\n> -\n> -       return reinterpret_cast<typename std::remove_extent_t<T> *>(\n> -               reinterpret_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> -                                            off_t offset)\n> -{\n> -       return elfPointer<T>(elf, offset, sizeof(T));\n> -}\n> -\n> -int elfVerifyIdent(Span<const uint8_t> elf)\n> -{\n> -       const char *e_ident = elfPointer<const char[EI_NIDENT]>(elf, 0);\n> -       if (!e_ident)\n> -               return -ENOEXEC;\n> -\n> -       if (e_ident[EI_MAG0] != ELFMAG0 ||\n> -           e_ident[EI_MAG1] != ELFMAG1 ||\n> -           e_ident[EI_MAG2] != ELFMAG2 ||\n> -           e_ident[EI_MAG3] != ELFMAG3 ||\n> -           e_ident[EI_VERSION] != EV_CURRENT)\n> -               return -ENOEXEC;\n> -\n> -       int bitClass = sizeof(unsigned long) == 4 ? ELFCLASS32 : ELFCLASS64;\n> -       if (e_ident[EI_CLASS] != bitClass)\n> -               return -ENOEXEC;\n> -\n> -       int a = 1;\n> -       unsigned char endianness = *reinterpret_cast<char *>(&a) == 1\n> -                                ? ELFDATA2LSB : ELFDATA2MSB;\n> -       if (e_ident[EI_DATA] != endianness)\n> -               return -ENOEXEC;\n> -\n> -       return 0;\n> -}\n> -\n> -const ElfW(Shdr) *elfSection(Span<const uint8_t> elf, const ElfW(Ehdr) *eHdr,\n> -                            ElfW(Half) idx)\n> -{\n> -       if (idx >= eHdr->e_shnum)\n> -               return nullptr;\n> -\n> -       off_t offset = eHdr->e_shoff + idx *\n> -                                      static_cast<uint32_t>(eHdr->e_shentsize);\n> -       return 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> -       const ElfW(Ehdr) *eHdr = elfPointer<const ElfW(Ehdr)>(elf, 0);\n> -       if (!eHdr)\n> -               return {};\n> -\n> -       const ElfW(Shdr) *sHdr = elfSection(elf, eHdr, eHdr->e_shstrndx);\n> -       if (!sHdr)\n> -               return {};\n> -       off_t shnameoff = sHdr->sh_offset;\n> -\n> -       /* Locate .dynsym section header. */\n> -       const ElfW(Shdr) *dynsym = nullptr;\n> -       for (unsigned int i = 0; i < eHdr->e_shnum; i++) {\n> -               sHdr = elfSection(elf, eHdr, i);\n> -               if (!sHdr)\n> -                       return {};\n> -\n> -               off_t offset = shnameoff + sHdr->sh_name;\n> -               const char *name = elfPointer<const char[8]>(elf, offset);\n> -               if (!name)\n> -                       return {};\n> -\n> -               if (sHdr->sh_type == SHT_DYNSYM && !strcmp(name, \".dynsym\")) {\n> -                       dynsym = sHdr;\n> -                       break;\n> -               }\n> -       }\n> -\n> -       if (dynsym == nullptr) {\n> -               LOG(IPAModule, Error) << \"ELF has no .dynsym section\";\n> -               return {};\n> -       }\n> -\n> -       sHdr = elfSection(elf, eHdr, dynsym->sh_link);\n> -       if (!sHdr)\n> -               return {};\n> -       off_t dynsym_nameoff = sHdr->sh_offset;\n> -\n> -       /* Locate symbol in the .dynsym section. */\n> -       const ElfW(Sym) *targetSymbol = nullptr;\n> -       unsigned int dynsym_num = dynsym->sh_size / dynsym->sh_entsize;\n> -       for (unsigned int i = 0; i < dynsym_num; i++) {\n> -               off_t offset = dynsym->sh_offset + dynsym->sh_entsize * i;\n> -               const ElfW(Sym) *sym = elfPointer<const ElfW(Sym)>(elf, offset);\n> -               if (!sym)\n> -                       return {};\n> -\n> -               offset = dynsym_nameoff + sym->st_name;\n> -               const char *name = elfPointer<const char>(elf, offset,\n> -                                                         strlen(symbol) + 1);\n> -               if (!name)\n> -                       return {};\n> -\n> -               if (!strcmp(name, symbol) &&\n> -                   sym->st_info & STB_GLOBAL) {\n> -                       targetSymbol = sym;\n> -                       break;\n> -               }\n> -       }\n> -\n> -       if (targetSymbol == nullptr) {\n> -               LOG(IPAModule, Error) << \"Symbol \" << symbol << \" not found\";\n> -               return {};\n> -       }\n> -\n> -       /* Locate and return data of symbol. */\n> -       sHdr = elfSection(elf, eHdr, targetSymbol->st_shndx);\n> -       if (!sHdr)\n> -               return {};\n> -       off_t offset = sHdr->sh_offset + (targetSymbol->st_value - sHdr->sh_addr);\n> -       const uint8_t *data = elfPointer<const uint8_t>(elf, offset,\n> -                                                       targetSymbol->st_size);\n> -       if (!data)\n> -               return {};\n> -\n> -       return { 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>         }\n>  \n>         Span<const uint8_t> data = file.map();\n> -       int ret = elfVerifyIdent(data);\n> +       int ret = utils::elfVerifyIdent(data);\n>         if (ret) {\n>                 LOG(IPAModule, Error) << \"IPA module is not an ELF file\";\n>                 return ret;\n>         }\n>  \n> -       Span<const uint8_t> info = elfLoadSymbol(data, \"ipaModuleInfo\");\n> +       Span<const uint8_t> info = utils::elfLoadSymbol(data, \"ipaModuleInfo\");\n>         if (info.size() < sizeof(info_)) {\n>                 LOG(IPAModule, Error) << \"IPA module has no valid info\";\n>                 return -EINVAL;\n> diff --git a/src/libcamera/utils.cpp b/src/libcamera/utils.cpp\n> index ef046ac3134e..f7bc5c54ff57 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> +                                            off_t offset, size_t objSize)\n> +{\n> +       size_t size = offset + objSize;\n> +       if (size > elf.size() || size < objSize)\n> +               return nullptr;\n> +\n> +       return reinterpret_cast<typename std::remove_extent_t<T> *>(\n> +               reinterpret_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> +                                            off_t offset)\n> +{\n> +       return elfPointer<T>(elf, offset, sizeof(T));\n> +}\n> +\n> +const ElfW(Shdr) *elfSection(Span<const uint8_t> elf, const ElfW(Ehdr) *eHdr,\n> +                            ElfW(Half) idx)\n> +{\n> +       if (idx >= eHdr->e_shnum)\n> +               return nullptr;\n> +\n> +       off_t offset = eHdr->e_shoff + idx *\n> +                                      static_cast<uint32_t>(eHdr->e_shentsize);\n> +       return 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,120 @@ unsigned int addDir(const char *libDir, unsigned int maxDepth,\n>         return count;\n>  }\n>  \n> +int elfVerifyIdent(Span<const uint8_t> elf)\n\nI couldn't spot the documentation for this function. Is it missing?\n\nOtherwise looks good to me.\n\nReviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>\n\n> +{\n> +       const char *e_ident = elfPointer<const char[EI_NIDENT]>(elf, 0);\n> +       if (!e_ident)\n> +               return -ENOEXEC;\n> +\n> +       if (e_ident[EI_MAG0] != ELFMAG0 ||\n> +           e_ident[EI_MAG1] != ELFMAG1 ||\n> +           e_ident[EI_MAG2] != ELFMAG2 ||\n> +           e_ident[EI_MAG3] != ELFMAG3 ||\n> +           e_ident[EI_VERSION] != EV_CURRENT)\n> +               return -ENOEXEC;\n> +\n> +       int bitClass = sizeof(unsigned long) == 4 ? ELFCLASS32 : ELFCLASS64;\n> +       if (e_ident[EI_CLASS] != bitClass)\n> +               return -ENOEXEC;\n> +\n> +       int a = 1;\n> +       unsigned char endianness = *reinterpret_cast<char *>(&a) == 1\n> +                                ? ELFDATA2LSB : ELFDATA2MSB;\n> +       if (e_ident[EI_DATA] != endianness)\n> +               return -ENOEXEC;\n> +\n> +       return 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> +       const ElfW(Ehdr) *eHdr = elfPointer<const ElfW(Ehdr)>(elf, 0);\n> +       if (!eHdr)\n> +               return {};\n> +\n> +       const ElfW(Shdr) *sHdr = elfSection(elf, eHdr, eHdr->e_shstrndx);\n> +       if (!sHdr)\n> +               return {};\n> +       off_t shnameoff = sHdr->sh_offset;\n> +\n> +       /* Locate .dynsym section header. */\n> +       const ElfW(Shdr) *dynsym = nullptr;\n> +       for (unsigned int i = 0; i < eHdr->e_shnum; i++) {\n> +               sHdr = elfSection(elf, eHdr, i);\n> +               if (!sHdr)\n> +                       return {};\n> +\n> +               off_t offset = shnameoff + sHdr->sh_name;\n> +               const char *name = elfPointer<const char[8]>(elf, offset);\n> +               if (!name)\n> +                       return {};\n> +\n> +               if (sHdr->sh_type == SHT_DYNSYM && !strcmp(name, \".dynsym\")) {\n> +                       dynsym = sHdr;\n> +                       break;\n> +               }\n> +       }\n> +\n> +       if (dynsym == nullptr) {\n> +               LOG(Utils, Error) << \"ELF has no .dynsym section\";\n> +               return {};\n> +       }\n> +\n> +       sHdr = elfSection(elf, eHdr, dynsym->sh_link);\n> +       if (!sHdr)\n> +               return {};\n> +       off_t dynsym_nameoff = sHdr->sh_offset;\n> +\n> +       /* Locate symbol in the .dynsym section. */\n> +       const ElfW(Sym) *targetSymbol = nullptr;\n> +       unsigned int dynsym_num = dynsym->sh_size / dynsym->sh_entsize;\n> +       for (unsigned int i = 0; i < dynsym_num; i++) {\n> +               off_t offset = dynsym->sh_offset + dynsym->sh_entsize * i;\n> +               const ElfW(Sym) *sym = elfPointer<const ElfW(Sym)>(elf, offset);\n> +               if (!sym)\n> +                       return {};\n> +\n> +               offset = dynsym_nameoff + sym->st_name;\n> +               const char *name = elfPointer<const char>(elf, offset,\n> +                                                         strlen(symbol) + 1);\n> +               if (!name)\n> +                       return {};\n> +\n> +               if (!strcmp(name, symbol) &&\n> +                   sym->st_info & STB_GLOBAL) {\n> +                       targetSymbol = sym;\n> +                       break;\n> +               }\n> +       }\n> +\n> +       if (targetSymbol == nullptr) {\n> +               LOG(Utils, Error) << \"Symbol \" << symbol << \" not found\";\n> +               return {};\n> +       }\n> +\n> +       /* Locate and return data of symbol. */\n> +       sHdr = elfSection(elf, eHdr, targetSymbol->st_shndx);\n> +       if (!sHdr)\n> +               return {};\n> +       off_t offset = sHdr->sh_offset + (targetSymbol->st_value - sHdr->sh_addr);\n> +       const uint8_t *data = elfPointer<const uint8_t>(elf, offset,\n> +                                                       targetSymbol->st_size);\n> +       if (!data)\n> +               return {};\n> +\n> +       return { data, targetSymbol->st_size };\n> +}\n> +\n> +\n>  } /* namespace utils */\n>  \n>  } /* namespace libcamera */\n> -- \n> 2.47.2\n>","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 9ED48C3237\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon,  7 Jul 2025 10:10:07 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 24DAF68E93;\n\tMon,  7 Jul 2025 12:10:06 +0200 (CEST)","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 B85AB68E74\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon,  7 Jul 2025 12:09:51 +0200 (CEST)","from ideasonboard.com (unknown\n\t[IPv6:2a00:6020:448c:6c00:c79f:85df:e7f5:4c31])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id A3A8E667;\n\tMon,  7 Jul 2025 12:09:24 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"YgHuoHNS\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1751882964;\n\tbh=noog8KikJdwjWIfkaY2+2i0/gtn3QXyZYiedtnEBlVY=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=YgHuoHNSf3pAfq3lVddvSs4GwERFK9wxb052lt7DMsAvmHqbdHSnYmd2pfjM7gwox\n\tU8ab8vASQs96B+ReadgNJ7+cwcFxMXnAezB9IfN64Gw5rpnqLRj6kZrTSKV4fjwmrj\n\tgEsQuiwo3iYOV0/FiSBS/I3FDOQKrhm8qAE59w9U=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20250703114225.2074071-3-paul.elder@ideasonboard.com>","References":"<20250703114225.2074071-1-paul.elder@ideasonboard.com>\n\t<20250703114225.2074071-3-paul.elder@ideasonboard.com>","Subject":"Re: [PATCH v2 2/8] libcamera: ipa_module: Factor out ELF file\n\thandling","From":"Stefan Klug <stefan.klug@ideasonboard.com>","Cc":"Paul Elder <paul.elder@ideasonboard.com>, kieran.bingham@ideasonboard.com,\n\tbarnabas.pocze@ideasonboard.com","To":"Paul Elder <paul.elder@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","Date":"Mon, 07 Jul 2025 12:09:48 +0200","Message-ID":"<175188298808.40598.5801068176889994026@localhost>","User-Agent":"alot/0.12.dev8+g2c003385c862.d20250602","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>"}},{"id":35035,"web_url":"https://patchwork.libcamera.org/comment/35035/","msgid":"<175325694674.774561.6915676655497001401@neptunite.rasen.tech>","date":"2025-07-23T07:49:06","subject":"Re: [PATCH v2 2/8] libcamera: ipa_module: Factor out ELF file\n\thandling","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/people/17/","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"content":"Quoting Stefan Klug (2025-07-07 19:09:48)\n> Hi Paul,\n> \n> Thank you for the patch.\n> \n> Quoting Paul Elder (2025-07-03 13:42:17)\n> > In the near future we will add support for layers/shoes on top of\n> > libcamera, which will need to validate and load symbols from ELF files\n> > similar to how IPAModule does. To avoid code duplication, move this code\n> > out of IPAModule into internal utils.\n> > \n> > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> > \n> > ---\n> > No change in v2\n> > ---\n> >  include/libcamera/internal/utils.h |   6 ++\n> >  src/libcamera/ipa_module.cpp       | 152 +---------------------------\n> >  src/libcamera/utils.cpp            | 157 +++++++++++++++++++++++++++++\n> >  3 files changed, 166 insertions(+), 149 deletions(-)\n> > \n> > diff --git a/include/libcamera/internal/utils.h b/include/libcamera/internal/utils.h\n> > index 742657bebb28..98d5094eb88d 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> > @@ -21,6 +24,9 @@ void parseDir(const char *libDir, unsigned int maxDepth,\n> >  unsigned int addDir(const char *libDir, unsigned int maxDepth,\n> >                     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 */\n> > diff --git a/src/libcamera/ipa_module.cpp b/src/libcamera/ipa_module.cpp\n> > index 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> > -                                            off_t offset, size_t objSize)\n> > -{\n> > -       size_t size = offset + objSize;\n> > -       if (size > elf.size() || size < objSize)\n> > -               return nullptr;\n> > -\n> > -       return reinterpret_cast<typename std::remove_extent_t<T> *>(\n> > -               reinterpret_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> > -                                            off_t offset)\n> > -{\n> > -       return elfPointer<T>(elf, offset, sizeof(T));\n> > -}\n> > -\n> > -int elfVerifyIdent(Span<const uint8_t> elf)\n> > -{\n> > -       const char *e_ident = elfPointer<const char[EI_NIDENT]>(elf, 0);\n> > -       if (!e_ident)\n> > -               return -ENOEXEC;\n> > -\n> > -       if (e_ident[EI_MAG0] != ELFMAG0 ||\n> > -           e_ident[EI_MAG1] != ELFMAG1 ||\n> > -           e_ident[EI_MAG2] != ELFMAG2 ||\n> > -           e_ident[EI_MAG3] != ELFMAG3 ||\n> > -           e_ident[EI_VERSION] != EV_CURRENT)\n> > -               return -ENOEXEC;\n> > -\n> > -       int bitClass = sizeof(unsigned long) == 4 ? ELFCLASS32 : ELFCLASS64;\n> > -       if (e_ident[EI_CLASS] != bitClass)\n> > -               return -ENOEXEC;\n> > -\n> > -       int a = 1;\n> > -       unsigned char endianness = *reinterpret_cast<char *>(&a) == 1\n> > -                                ? ELFDATA2LSB : ELFDATA2MSB;\n> > -       if (e_ident[EI_DATA] != endianness)\n> > -               return -ENOEXEC;\n> > -\n> > -       return 0;\n> > -}\n> > -\n> > -const ElfW(Shdr) *elfSection(Span<const uint8_t> elf, const ElfW(Ehdr) *eHdr,\n> > -                            ElfW(Half) idx)\n> > -{\n> > -       if (idx >= eHdr->e_shnum)\n> > -               return nullptr;\n> > -\n> > -       off_t offset = eHdr->e_shoff + idx *\n> > -                                      static_cast<uint32_t>(eHdr->e_shentsize);\n> > -       return 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> > -       const ElfW(Ehdr) *eHdr = elfPointer<const ElfW(Ehdr)>(elf, 0);\n> > -       if (!eHdr)\n> > -               return {};\n> > -\n> > -       const ElfW(Shdr) *sHdr = elfSection(elf, eHdr, eHdr->e_shstrndx);\n> > -       if (!sHdr)\n> > -               return {};\n> > -       off_t shnameoff = sHdr->sh_offset;\n> > -\n> > -       /* Locate .dynsym section header. */\n> > -       const ElfW(Shdr) *dynsym = nullptr;\n> > -       for (unsigned int i = 0; i < eHdr->e_shnum; i++) {\n> > -               sHdr = elfSection(elf, eHdr, i);\n> > -               if (!sHdr)\n> > -                       return {};\n> > -\n> > -               off_t offset = shnameoff + sHdr->sh_name;\n> > -               const char *name = elfPointer<const char[8]>(elf, offset);\n> > -               if (!name)\n> > -                       return {};\n> > -\n> > -               if (sHdr->sh_type == SHT_DYNSYM && !strcmp(name, \".dynsym\")) {\n> > -                       dynsym = sHdr;\n> > -                       break;\n> > -               }\n> > -       }\n> > -\n> > -       if (dynsym == nullptr) {\n> > -               LOG(IPAModule, Error) << \"ELF has no .dynsym section\";\n> > -               return {};\n> > -       }\n> > -\n> > -       sHdr = elfSection(elf, eHdr, dynsym->sh_link);\n> > -       if (!sHdr)\n> > -               return {};\n> > -       off_t dynsym_nameoff = sHdr->sh_offset;\n> > -\n> > -       /* Locate symbol in the .dynsym section. */\n> > -       const ElfW(Sym) *targetSymbol = nullptr;\n> > -       unsigned int dynsym_num = dynsym->sh_size / dynsym->sh_entsize;\n> > -       for (unsigned int i = 0; i < dynsym_num; i++) {\n> > -               off_t offset = dynsym->sh_offset + dynsym->sh_entsize * i;\n> > -               const ElfW(Sym) *sym = elfPointer<const ElfW(Sym)>(elf, offset);\n> > -               if (!sym)\n> > -                       return {};\n> > -\n> > -               offset = dynsym_nameoff + sym->st_name;\n> > -               const char *name = elfPointer<const char>(elf, offset,\n> > -                                                         strlen(symbol) + 1);\n> > -               if (!name)\n> > -                       return {};\n> > -\n> > -               if (!strcmp(name, symbol) &&\n> > -                   sym->st_info & STB_GLOBAL) {\n> > -                       targetSymbol = sym;\n> > -                       break;\n> > -               }\n> > -       }\n> > -\n> > -       if (targetSymbol == nullptr) {\n> > -               LOG(IPAModule, Error) << \"Symbol \" << symbol << \" not found\";\n> > -               return {};\n> > -       }\n> > -\n> > -       /* Locate and return data of symbol. */\n> > -       sHdr = elfSection(elf, eHdr, targetSymbol->st_shndx);\n> > -       if (!sHdr)\n> > -               return {};\n> > -       off_t offset = sHdr->sh_offset + (targetSymbol->st_value - sHdr->sh_addr);\n> > -       const uint8_t *data = elfPointer<const uint8_t>(elf, offset,\n> > -                                                       targetSymbol->st_size);\n> > -       if (!data)\n> > -               return {};\n> > -\n> > -       return { 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> >         }\n> >  \n> >         Span<const uint8_t> data = file.map();\n> > -       int ret = elfVerifyIdent(data);\n> > +       int ret = utils::elfVerifyIdent(data);\n> >         if (ret) {\n> >                 LOG(IPAModule, Error) << \"IPA module is not an ELF file\";\n> >                 return ret;\n> >         }\n> >  \n> > -       Span<const uint8_t> info = elfLoadSymbol(data, \"ipaModuleInfo\");\n> > +       Span<const uint8_t> info = utils::elfLoadSymbol(data, \"ipaModuleInfo\");\n> >         if (info.size() < sizeof(info_)) {\n> >                 LOG(IPAModule, Error) << \"IPA module has no valid info\";\n> >                 return -EINVAL;\n> > diff --git a/src/libcamera/utils.cpp b/src/libcamera/utils.cpp\n> > index ef046ac3134e..f7bc5c54ff57 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> > +                                            off_t offset, size_t objSize)\n> > +{\n> > +       size_t size = offset + objSize;\n> > +       if (size > elf.size() || size < objSize)\n> > +               return nullptr;\n> > +\n> > +       return reinterpret_cast<typename std::remove_extent_t<T> *>(\n> > +               reinterpret_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> > +                                            off_t offset)\n> > +{\n> > +       return elfPointer<T>(elf, offset, sizeof(T));\n> > +}\n> > +\n> > +const ElfW(Shdr) *elfSection(Span<const uint8_t> elf, const ElfW(Ehdr) *eHdr,\n> > +                            ElfW(Half) idx)\n> > +{\n> > +       if (idx >= eHdr->e_shnum)\n> > +               return nullptr;\n> > +\n> > +       off_t offset = eHdr->e_shoff + idx *\n> > +                                      static_cast<uint32_t>(eHdr->e_shentsize);\n> > +       return 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,120 @@ unsigned int addDir(const char *libDir, unsigned int maxDepth,\n> >         return count;\n> >  }\n> >  \n> > +int elfVerifyIdent(Span<const uint8_t> elf)\n> \n> I couldn't spot the documentation for this function. Is it missing?\n\nIt is. I'll add it.\n\n> \n> Otherwise looks good to me.\n> \n> Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>\n\n\nThanks,\n\nPaul\n\n> \n> > +{\n> > +       const char *e_ident = elfPointer<const char[EI_NIDENT]>(elf, 0);\n> > +       if (!e_ident)\n> > +               return -ENOEXEC;\n> > +\n> > +       if (e_ident[EI_MAG0] != ELFMAG0 ||\n> > +           e_ident[EI_MAG1] != ELFMAG1 ||\n> > +           e_ident[EI_MAG2] != ELFMAG2 ||\n> > +           e_ident[EI_MAG3] != ELFMAG3 ||\n> > +           e_ident[EI_VERSION] != EV_CURRENT)\n> > +               return -ENOEXEC;\n> > +\n> > +       int bitClass = sizeof(unsigned long) == 4 ? ELFCLASS32 : ELFCLASS64;\n> > +       if (e_ident[EI_CLASS] != bitClass)\n> > +               return -ENOEXEC;\n> > +\n> > +       int a = 1;\n> > +       unsigned char endianness = *reinterpret_cast<char *>(&a) == 1\n> > +                                ? ELFDATA2LSB : ELFDATA2MSB;\n> > +       if (e_ident[EI_DATA] != endianness)\n> > +               return -ENOEXEC;\n> > +\n> > +       return 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> > +       const ElfW(Ehdr) *eHdr = elfPointer<const ElfW(Ehdr)>(elf, 0);\n> > +       if (!eHdr)\n> > +               return {};\n> > +\n> > +       const ElfW(Shdr) *sHdr = elfSection(elf, eHdr, eHdr->e_shstrndx);\n> > +       if (!sHdr)\n> > +               return {};\n> > +       off_t shnameoff = sHdr->sh_offset;\n> > +\n> > +       /* Locate .dynsym section header. */\n> > +       const ElfW(Shdr) *dynsym = nullptr;\n> > +       for (unsigned int i = 0; i < eHdr->e_shnum; i++) {\n> > +               sHdr = elfSection(elf, eHdr, i);\n> > +               if (!sHdr)\n> > +                       return {};\n> > +\n> > +               off_t offset = shnameoff + sHdr->sh_name;\n> > +               const char *name = elfPointer<const char[8]>(elf, offset);\n> > +               if (!name)\n> > +                       return {};\n> > +\n> > +               if (sHdr->sh_type == SHT_DYNSYM && !strcmp(name, \".dynsym\")) {\n> > +                       dynsym = sHdr;\n> > +                       break;\n> > +               }\n> > +       }\n> > +\n> > +       if (dynsym == nullptr) {\n> > +               LOG(Utils, Error) << \"ELF has no .dynsym section\";\n> > +               return {};\n> > +       }\n> > +\n> > +       sHdr = elfSection(elf, eHdr, dynsym->sh_link);\n> > +       if (!sHdr)\n> > +               return {};\n> > +       off_t dynsym_nameoff = sHdr->sh_offset;\n> > +\n> > +       /* Locate symbol in the .dynsym section. */\n> > +       const ElfW(Sym) *targetSymbol = nullptr;\n> > +       unsigned int dynsym_num = dynsym->sh_size / dynsym->sh_entsize;\n> > +       for (unsigned int i = 0; i < dynsym_num; i++) {\n> > +               off_t offset = dynsym->sh_offset + dynsym->sh_entsize * i;\n> > +               const ElfW(Sym) *sym = elfPointer<const ElfW(Sym)>(elf, offset);\n> > +               if (!sym)\n> > +                       return {};\n> > +\n> > +               offset = dynsym_nameoff + sym->st_name;\n> > +               const char *name = elfPointer<const char>(elf, offset,\n> > +                                                         strlen(symbol) + 1);\n> > +               if (!name)\n> > +                       return {};\n> > +\n> > +               if (!strcmp(name, symbol) &&\n> > +                   sym->st_info & STB_GLOBAL) {\n> > +                       targetSymbol = sym;\n> > +                       break;\n> > +               }\n> > +       }\n> > +\n> > +       if (targetSymbol == nullptr) {\n> > +               LOG(Utils, Error) << \"Symbol \" << symbol << \" not found\";\n> > +               return {};\n> > +       }\n> > +\n> > +       /* Locate and return data of symbol. */\n> > +       sHdr = elfSection(elf, eHdr, targetSymbol->st_shndx);\n> > +       if (!sHdr)\n> > +               return {};\n> > +       off_t offset = sHdr->sh_offset + (targetSymbol->st_value - sHdr->sh_addr);\n> > +       const uint8_t *data = elfPointer<const uint8_t>(elf, offset,\n> > +                                                       targetSymbol->st_size);\n> > +       if (!data)\n> > +               return {};\n> > +\n> > +       return { data, targetSymbol->st_size };\n> > +}\n> > +\n> > +\n> >  } /* namespace utils */\n> >  \n> >  } /* namespace libcamera */\n> > -- \n> > 2.47.2\n> >","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 21DE5C3237\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 23 Jul 2025 07:49:15 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C472A69048;\n\tWed, 23 Jul 2025 09:49:14 +0200 (CEST)","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 727C768FB6\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 23 Jul 2025 09:49:13 +0200 (CEST)","from neptunite.rasen.tech (unknown\n\t[IPv6:2404:7a81:160:2100:1d36:d4e9:a0a5:d2df])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id D770310D4; \n\tWed, 23 Jul 2025 09:48:34 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"FWzNfYHz\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1753256915;\n\tbh=AT10gv/A09UHaKIRV4SoMyPI1lsy4aUsy112GrQBTLs=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=FWzNfYHzgtCeuGEbbFPOa0VMIJZtHwz+nQyuYrDSUCRqcRtt6U/vcRFCoPAvw7T0A\n\teKLJWGtv2VztmbrMTg0FaymQomNhGQCBHyWFQicw23Y39rD3U6Of7LpJ+K8bIBcOsk\n\tQKvYk95IisfD/zDwrTjBbS4K0PuAZRuI+/+lToEM=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<175188298808.40598.5801068176889994026@localhost>","References":"<20250703114225.2074071-1-paul.elder@ideasonboard.com>\n\t<20250703114225.2074071-3-paul.elder@ideasonboard.com>\n\t<175188298808.40598.5801068176889994026@localhost>","Subject":"Re: [PATCH v2 2/8] libcamera: ipa_module: Factor out ELF file\n\thandling","From":"Paul Elder <paul.elder@ideasonboard.com>","Cc":"kieran.bingham@ideasonboard.com, barnabas.pocze@ideasonboard.com","To":"Stefan Klug <stefan.klug@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","Date":"Wed, 23 Jul 2025 16:49:06 +0900","Message-ID":"<175325694674.774561.6915676655497001401@neptunite.rasen.tech>","User-Agent":"alot/0.0.0","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>"}},{"id":35038,"web_url":"https://patchwork.libcamera.org/comment/35038/","msgid":"<175326050083.50296.4416013721949230351@ping.linuxembedded.co.uk>","date":"2025-07-23T08:48:20","subject":"Re: [PATCH v2 2/8] libcamera: ipa_module: Factor out ELF file\n\thandling","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Paul Elder (2025-07-23 08:49:06)\n> Quoting Stefan Klug (2025-07-07 19:09:48)\n> > Hi Paul,\n> > \n> > Thank you for the patch.\n> > \n> > Quoting Paul Elder (2025-07-03 13:42:17)\n> > > In the near future we will add support for layers/shoes on top of\n> > > libcamera, which will need to validate and load symbols from ELF files\n> > > similar to how IPAModule does. To avoid code duplication, move this code\n> > > out of IPAModule into internal utils.\n> > > \n\nI'm curious if we're moving enough code that handles 'shared object\nhandling' around if it should go to it's own .cpp/.h file as it's all\ngrouped around elf handling and .so files.\n\nshared_object.cpp/.h?\nelf.cpp/.h ?\n\nAnyway - not required - just if it makes sense with the large code\nmoves.\n\n--\nKieran\n\n\n\n> > > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> > > \n> > > ---\n> > > No change in v2\n> > > ---\n> > >  include/libcamera/internal/utils.h |   6 ++\n> > >  src/libcamera/ipa_module.cpp       | 152 +---------------------------\n> > >  src/libcamera/utils.cpp            | 157 +++++++++++++++++++++++++++++\n> > >  3 files changed, 166 insertions(+), 149 deletions(-)\n> > > \n> > > diff --git a/include/libcamera/internal/utils.h b/include/libcamera/internal/utils.h\n> > > index 742657bebb28..98d5094eb88d 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> > > @@ -21,6 +24,9 @@ void parseDir(const char *libDir, unsigned int maxDepth,\n> > >  unsigned int addDir(const char *libDir, unsigned int maxDepth,\n> > >                     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 */\n> > > diff --git a/src/libcamera/ipa_module.cpp b/src/libcamera/ipa_module.cpp\n> > > index 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> > > -                                            off_t offset, size_t objSize)\n> > > -{\n> > > -       size_t size = offset + objSize;\n> > > -       if (size > elf.size() || size < objSize)\n> > > -               return nullptr;\n> > > -\n> > > -       return reinterpret_cast<typename std::remove_extent_t<T> *>(\n> > > -               reinterpret_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> > > -                                            off_t offset)\n> > > -{\n> > > -       return elfPointer<T>(elf, offset, sizeof(T));\n> > > -}\n> > > -\n> > > -int elfVerifyIdent(Span<const uint8_t> elf)\n> > > -{\n> > > -       const char *e_ident = elfPointer<const char[EI_NIDENT]>(elf, 0);\n> > > -       if (!e_ident)\n> > > -               return -ENOEXEC;\n> > > -\n> > > -       if (e_ident[EI_MAG0] != ELFMAG0 ||\n> > > -           e_ident[EI_MAG1] != ELFMAG1 ||\n> > > -           e_ident[EI_MAG2] != ELFMAG2 ||\n> > > -           e_ident[EI_MAG3] != ELFMAG3 ||\n> > > -           e_ident[EI_VERSION] != EV_CURRENT)\n> > > -               return -ENOEXEC;\n> > > -\n> > > -       int bitClass = sizeof(unsigned long) == 4 ? ELFCLASS32 : ELFCLASS64;\n> > > -       if (e_ident[EI_CLASS] != bitClass)\n> > > -               return -ENOEXEC;\n> > > -\n> > > -       int a = 1;\n> > > -       unsigned char endianness = *reinterpret_cast<char *>(&a) == 1\n> > > -                                ? ELFDATA2LSB : ELFDATA2MSB;\n> > > -       if (e_ident[EI_DATA] != endianness)\n> > > -               return -ENOEXEC;\n> > > -\n> > > -       return 0;\n> > > -}\n> > > -\n> > > -const ElfW(Shdr) *elfSection(Span<const uint8_t> elf, const ElfW(Ehdr) *eHdr,\n> > > -                            ElfW(Half) idx)\n> > > -{\n> > > -       if (idx >= eHdr->e_shnum)\n> > > -               return nullptr;\n> > > -\n> > > -       off_t offset = eHdr->e_shoff + idx *\n> > > -                                      static_cast<uint32_t>(eHdr->e_shentsize);\n> > > -       return 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> > > -       const ElfW(Ehdr) *eHdr = elfPointer<const ElfW(Ehdr)>(elf, 0);\n> > > -       if (!eHdr)\n> > > -               return {};\n> > > -\n> > > -       const ElfW(Shdr) *sHdr = elfSection(elf, eHdr, eHdr->e_shstrndx);\n> > > -       if (!sHdr)\n> > > -               return {};\n> > > -       off_t shnameoff = sHdr->sh_offset;\n> > > -\n> > > -       /* Locate .dynsym section header. */\n> > > -       const ElfW(Shdr) *dynsym = nullptr;\n> > > -       for (unsigned int i = 0; i < eHdr->e_shnum; i++) {\n> > > -               sHdr = elfSection(elf, eHdr, i);\n> > > -               if (!sHdr)\n> > > -                       return {};\n> > > -\n> > > -               off_t offset = shnameoff + sHdr->sh_name;\n> > > -               const char *name = elfPointer<const char[8]>(elf, offset);\n> > > -               if (!name)\n> > > -                       return {};\n> > > -\n> > > -               if (sHdr->sh_type == SHT_DYNSYM && !strcmp(name, \".dynsym\")) {\n> > > -                       dynsym = sHdr;\n> > > -                       break;\n> > > -               }\n> > > -       }\n> > > -\n> > > -       if (dynsym == nullptr) {\n> > > -               LOG(IPAModule, Error) << \"ELF has no .dynsym section\";\n> > > -               return {};\n> > > -       }\n> > > -\n> > > -       sHdr = elfSection(elf, eHdr, dynsym->sh_link);\n> > > -       if (!sHdr)\n> > > -               return {};\n> > > -       off_t dynsym_nameoff = sHdr->sh_offset;\n> > > -\n> > > -       /* Locate symbol in the .dynsym section. */\n> > > -       const ElfW(Sym) *targetSymbol = nullptr;\n> > > -       unsigned int dynsym_num = dynsym->sh_size / dynsym->sh_entsize;\n> > > -       for (unsigned int i = 0; i < dynsym_num; i++) {\n> > > -               off_t offset = dynsym->sh_offset + dynsym->sh_entsize * i;\n> > > -               const ElfW(Sym) *sym = elfPointer<const ElfW(Sym)>(elf, offset);\n> > > -               if (!sym)\n> > > -                       return {};\n> > > -\n> > > -               offset = dynsym_nameoff + sym->st_name;\n> > > -               const char *name = elfPointer<const char>(elf, offset,\n> > > -                                                         strlen(symbol) + 1);\n> > > -               if (!name)\n> > > -                       return {};\n> > > -\n> > > -               if (!strcmp(name, symbol) &&\n> > > -                   sym->st_info & STB_GLOBAL) {\n> > > -                       targetSymbol = sym;\n> > > -                       break;\n> > > -               }\n> > > -       }\n> > > -\n> > > -       if (targetSymbol == nullptr) {\n> > > -               LOG(IPAModule, Error) << \"Symbol \" << symbol << \" not found\";\n> > > -               return {};\n> > > -       }\n> > > -\n> > > -       /* Locate and return data of symbol. */\n> > > -       sHdr = elfSection(elf, eHdr, targetSymbol->st_shndx);\n> > > -       if (!sHdr)\n> > > -               return {};\n> > > -       off_t offset = sHdr->sh_offset + (targetSymbol->st_value - sHdr->sh_addr);\n> > > -       const uint8_t *data = elfPointer<const uint8_t>(elf, offset,\n> > > -                                                       targetSymbol->st_size);\n> > > -       if (!data)\n> > > -               return {};\n> > > -\n> > > -       return { 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> > >         }\n> > >  \n> > >         Span<const uint8_t> data = file.map();\n> > > -       int ret = elfVerifyIdent(data);\n> > > +       int ret = utils::elfVerifyIdent(data);\n> > >         if (ret) {\n> > >                 LOG(IPAModule, Error) << \"IPA module is not an ELF file\";\n> > >                 return ret;\n> > >         }\n> > >  \n> > > -       Span<const uint8_t> info = elfLoadSymbol(data, \"ipaModuleInfo\");\n> > > +       Span<const uint8_t> info = utils::elfLoadSymbol(data, \"ipaModuleInfo\");\n> > >         if (info.size() < sizeof(info_)) {\n> > >                 LOG(IPAModule, Error) << \"IPA module has no valid info\";\n> > >                 return -EINVAL;\n> > > diff --git a/src/libcamera/utils.cpp b/src/libcamera/utils.cpp\n> > > index ef046ac3134e..f7bc5c54ff57 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> > > +                                            off_t offset, size_t objSize)\n> > > +{\n> > > +       size_t size = offset + objSize;\n> > > +       if (size > elf.size() || size < objSize)\n> > > +               return nullptr;\n> > > +\n> > > +       return reinterpret_cast<typename std::remove_extent_t<T> *>(\n> > > +               reinterpret_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> > > +                                            off_t offset)\n> > > +{\n> > > +       return elfPointer<T>(elf, offset, sizeof(T));\n> > > +}\n> > > +\n> > > +const ElfW(Shdr) *elfSection(Span<const uint8_t> elf, const ElfW(Ehdr) *eHdr,\n> > > +                            ElfW(Half) idx)\n> > > +{\n> > > +       if (idx >= eHdr->e_shnum)\n> > > +               return nullptr;\n> > > +\n> > > +       off_t offset = eHdr->e_shoff + idx *\n> > > +                                      static_cast<uint32_t>(eHdr->e_shentsize);\n> > > +       return 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,120 @@ unsigned int addDir(const char *libDir, unsigned int maxDepth,\n> > >         return count;\n> > >  }\n> > >  \n> > > +int elfVerifyIdent(Span<const uint8_t> elf)\n> > \n> > I couldn't spot the documentation for this function. Is it missing?\n> \n> It is. I'll add it.\n> \n> > \n> > Otherwise looks good to me.\n> > \n> > Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>\n> \n> \n> Thanks,\n> \n> Paul\n> \n> > \n> > > +{\n> > > +       const char *e_ident = elfPointer<const char[EI_NIDENT]>(elf, 0);\n> > > +       if (!e_ident)\n> > > +               return -ENOEXEC;\n> > > +\n> > > +       if (e_ident[EI_MAG0] != ELFMAG0 ||\n> > > +           e_ident[EI_MAG1] != ELFMAG1 ||\n> > > +           e_ident[EI_MAG2] != ELFMAG2 ||\n> > > +           e_ident[EI_MAG3] != ELFMAG3 ||\n> > > +           e_ident[EI_VERSION] != EV_CURRENT)\n> > > +               return -ENOEXEC;\n> > > +\n> > > +       int bitClass = sizeof(unsigned long) == 4 ? ELFCLASS32 : ELFCLASS64;\n> > > +       if (e_ident[EI_CLASS] != bitClass)\n> > > +               return -ENOEXEC;\n> > > +\n> > > +       int a = 1;\n> > > +       unsigned char endianness = *reinterpret_cast<char *>(&a) == 1\n> > > +                                ? ELFDATA2LSB : ELFDATA2MSB;\n> > > +       if (e_ident[EI_DATA] != endianness)\n> > > +               return -ENOEXEC;\n> > > +\n> > > +       return 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> > > +       const ElfW(Ehdr) *eHdr = elfPointer<const ElfW(Ehdr)>(elf, 0);\n> > > +       if (!eHdr)\n> > > +               return {};\n> > > +\n> > > +       const ElfW(Shdr) *sHdr = elfSection(elf, eHdr, eHdr->e_shstrndx);\n> > > +       if (!sHdr)\n> > > +               return {};\n> > > +       off_t shnameoff = sHdr->sh_offset;\n> > > +\n> > > +       /* Locate .dynsym section header. */\n> > > +       const ElfW(Shdr) *dynsym = nullptr;\n> > > +       for (unsigned int i = 0; i < eHdr->e_shnum; i++) {\n> > > +               sHdr = elfSection(elf, eHdr, i);\n> > > +               if (!sHdr)\n> > > +                       return {};\n> > > +\n> > > +               off_t offset = shnameoff + sHdr->sh_name;\n> > > +               const char *name = elfPointer<const char[8]>(elf, offset);\n> > > +               if (!name)\n> > > +                       return {};\n> > > +\n> > > +               if (sHdr->sh_type == SHT_DYNSYM && !strcmp(name, \".dynsym\")) {\n> > > +                       dynsym = sHdr;\n> > > +                       break;\n> > > +               }\n> > > +       }\n> > > +\n> > > +       if (dynsym == nullptr) {\n> > > +               LOG(Utils, Error) << \"ELF has no .dynsym section\";\n> > > +               return {};\n> > > +       }\n> > > +\n> > > +       sHdr = elfSection(elf, eHdr, dynsym->sh_link);\n> > > +       if (!sHdr)\n> > > +               return {};\n> > > +       off_t dynsym_nameoff = sHdr->sh_offset;\n> > > +\n> > > +       /* Locate symbol in the .dynsym section. */\n> > > +       const ElfW(Sym) *targetSymbol = nullptr;\n> > > +       unsigned int dynsym_num = dynsym->sh_size / dynsym->sh_entsize;\n> > > +       for (unsigned int i = 0; i < dynsym_num; i++) {\n> > > +               off_t offset = dynsym->sh_offset + dynsym->sh_entsize * i;\n> > > +               const ElfW(Sym) *sym = elfPointer<const ElfW(Sym)>(elf, offset);\n> > > +               if (!sym)\n> > > +                       return {};\n> > > +\n> > > +               offset = dynsym_nameoff + sym->st_name;\n> > > +               const char *name = elfPointer<const char>(elf, offset,\n> > > +                                                         strlen(symbol) + 1);\n> > > +               if (!name)\n> > > +                       return {};\n> > > +\n> > > +               if (!strcmp(name, symbol) &&\n> > > +                   sym->st_info & STB_GLOBAL) {\n> > > +                       targetSymbol = sym;\n> > > +                       break;\n> > > +               }\n> > > +       }\n> > > +\n> > > +       if (targetSymbol == nullptr) {\n> > > +               LOG(Utils, Error) << \"Symbol \" << symbol << \" not found\";\n> > > +               return {};\n> > > +       }\n> > > +\n> > > +       /* Locate and return data of symbol. */\n> > > +       sHdr = elfSection(elf, eHdr, targetSymbol->st_shndx);\n> > > +       if (!sHdr)\n> > > +               return {};\n> > > +       off_t offset = sHdr->sh_offset + (targetSymbol->st_value - sHdr->sh_addr);\n> > > +       const uint8_t *data = elfPointer<const uint8_t>(elf, offset,\n> > > +                                                       targetSymbol->st_size);\n> > > +       if (!data)\n> > > +               return {};\n> > > +\n> > > +       return { data, targetSymbol->st_size };\n> > > +}\n> > > +\n> > > +\n> > >  } /* namespace utils */\n> > >  \n> > >  } /* namespace libcamera */\n> > > -- \n> > > 2.47.2\n> > >","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 00055BDCC1\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 23 Jul 2025 08:48:26 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 5DD566904C;\n\tWed, 23 Jul 2025 10:48:25 +0200 (CEST)","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 B010168FB6\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 23 Jul 2025 10:48:23 +0200 (CEST)","from pendragon.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 7C112FDB;\n\tWed, 23 Jul 2025 10:47:45 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"I7C/ZGvZ\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1753260465;\n\tbh=qsdSi4x+ElQ9NesrqxqVyoDvIWetvj9kmGOH53ASOME=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=I7C/ZGvZEIB3Sdo2NzJKrDagbJHI19zjbLgT4VqEXQi3TR7xqXljPTZgBWr0lWPzt\n\t+BucBh7eol9oCzFt3O9Y1YhwrNb5xVq2hFMzrto1spO4Reke5BHN4CyxY6lpqmD8H7\n\tcMOVq4h0BKSiLaBeWCoQMd62TFPuHJe+ntAApfTc=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<175325694674.774561.6915676655497001401@neptunite.rasen.tech>","References":"<20250703114225.2074071-1-paul.elder@ideasonboard.com>\n\t<20250703114225.2074071-3-paul.elder@ideasonboard.com>\n\t<175188298808.40598.5801068176889994026@localhost>\n\t<175325694674.774561.6915676655497001401@neptunite.rasen.tech>","Subject":"Re: [PATCH v2 2/8] libcamera: ipa_module: Factor out ELF file\n\thandling","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"barnabas.pocze@ideasonboard.com","To":"Paul Elder <paul.elder@ideasonboard.com>,\n\tStefan Klug <stefan.klug@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","Date":"Wed, 23 Jul 2025 09:48:20 +0100","Message-ID":"<175326050083.50296.4416013721949230351@ping.linuxembedded.co.uk>","User-Agent":"alot/0.9.1","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>"}}]