From patchwork Tue May 14 22:38:07 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 1203 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 8F45660E4C for ; Wed, 15 May 2019 00:38:21 +0200 (CEST) Received: from localhost.localdomain (unknown [96.44.9.117]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 96FED1952; Wed, 15 May 2019 00:38:20 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1557873501; bh=Ss9Qakknn9rHg37nXbGZmo+pzblrcUBYoTjcqIfXu6k=; h=From:To:Cc:Subject:Date:From; b=I9z1WDsxwl9lBYkwHtVc4K/g9J12Npe03yJ1eGJC3sDmxYUCzXeizzBOgwwhDR6dH PdhIexrY9mPnmMOQd2MgmwM8qU4GgyCvWLqWbT5OksIyfDTZy5Qp2ByHuT/zwoc1hE DdSqthHn3mWRItLgZFlwgOxEmbhaMyk7dTKRX2Qs= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Tue, 14 May 2019 18:38:07 -0400 Message-Id: <20190514223808.27351-1-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.20.1 MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 1/2] libcamera: ipa_module: add IPA shared library loader X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 14 May 2019 22:38:21 -0000 Implement a class to load a struct IPAModuleInfo of a given symbol name from a .so shared object library. Signed-off-by: Paul Elder --- Changes in v2: - renamed LibLoader class to IPAModule - added documentation - added logging - check that bitness of the shared object is the same as libcamera - moved symbol loading ("init") to the constructor, and added isValid() - added elfPointer() to prevent segfaults when reading data from mmap - moved struct IPAModuleInfo out of IPAModule - rename getIPAModuleInfo() to IPAModuleInfo(), and make it return a const reference - added munmap after the mmap src/libcamera/include/ipa_module.h | 43 +++++ src/libcamera/ipa_module.cpp | 277 +++++++++++++++++++++++++++++ src/libcamera/meson.build | 2 + 3 files changed, 322 insertions(+) create mode 100644 src/libcamera/include/ipa_module.h create mode 100644 src/libcamera/ipa_module.cpp diff --git a/src/libcamera/include/ipa_module.h b/src/libcamera/include/ipa_module.h new file mode 100644 index 0000000..9eb0094 --- /dev/null +++ b/src/libcamera/include/ipa_module.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * ipa_module.h - load IPA module information from shared library at runtime + */ +#ifndef __LIBCAMERA_IPA_MODULE_H__ +#define __LIBCAMERA_IPA_MODULE_H__ + +#include + +namespace libcamera { + +struct IPAModuleInfo { + char name[256]; + unsigned int version; +}; + +class IPAModule +{ +public: + explicit IPAModule(const std::string &libPath, const std::string &symbol); + + bool isValid() const; + + struct IPAModuleInfo const &IPAModuleInfo() const; + +private: + struct IPAModuleInfo info_; + + bool loaded_; + int bitClass_; + + int loadELFIdent(int fd); + template + int loadSymbol(void *data, size_t size, char *map, size_t soSize, + const char *symbol); + int loadIPAModuleInfo(const char *libPath, const char *symbol); +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_IPA_MODULE_H__ */ diff --git a/src/libcamera/ipa_module.cpp b/src/libcamera/ipa_module.cpp new file mode 100644 index 0000000..5ca16e8 --- /dev/null +++ b/src/libcamera/ipa_module.cpp @@ -0,0 +1,277 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * ipa_module.cpp - load IPA module information from shared library at runtime + */ + +#include "ipa_module.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "log.h" + +/** + * \file ipa_module.h + * \brief IPA module information loading + */ + +namespace libcamera { + +/** + * \struct IPAModuleInfo + * \brief Information of an IPA plugin + * + * This structure contains the information of an IPA plugin. It is loaded, + * read, and validated before anything else is loaded from the plugin. + * + * \var IPAModuleInfo::name + * \brief The name of the IPA plugin + * + * \var IPAModuleInfo::version + * \brief The version of the IPA plugin + */ + +LOG_DEFINE_CATEGORY(IPAModule) + +template +typename std::remove_extent::type *elfPointer(void *map, off_t offset, + size_t fileSize) +{ + size_t size = offset + sizeof(T); + if (size > fileSize || size < sizeof(T)) + return nullptr; + + return reinterpret_cast::type *> + (static_cast(map) + offset); +} + +/** + * \class IPAModule + * \brief Load IPA module information from an IPA plugin + */ + +/** + * \brief Construct an IPAModule instance + * \param[in] libPath path to IPA plugin + * \param[in] symbol name of IPAModuleInfo to load from libPath + * + * Loads the IPAModuleInfo of the given symbol from the IPA plugin at libPath. + * The IPA plugin shared object file must be of the same endianness and + * bitness as libcamera. + * + * Note that isValid() should be called to check if this loading was successful + * or not. + */ + +IPAModule::IPAModule(const std::string &libPath, const std::string &symbol) + : loaded_(false) +{ + int ret = loadIPAModuleInfo(libPath.c_str(), symbol.c_str()); + if (ret < 0) { + loaded_ = false; + return; + } + + loaded_ = true; +} + +int IPAModule::loadELFIdent(int fd) +{ + int ret = lseek(fd, 0, SEEK_SET); + if (ret < 0) + return -errno; + + unsigned char e_ident[EI_NIDENT]; + ret = read(fd, e_ident, EI_NIDENT); + if (ret < 0) + return -errno; + + if (e_ident[EI_MAG0] != ELFMAG0 || + e_ident[EI_MAG1] != ELFMAG1 || + e_ident[EI_MAG2] != ELFMAG2 || + e_ident[EI_MAG3] != ELFMAG3 || + e_ident[EI_VERSION] != EV_CURRENT) + return -ENOEXEC; + + if ((e_ident[EI_CLASS] == ELFCLASS32 && sizeof(unsigned long) == 4) || + (e_ident[EI_CLASS] == ELFCLASS64 && sizeof(unsigned long) == 8)) + bitClass_ = e_ident[EI_CLASS]; + else + return -ENOEXEC; + + int a = 1; + unsigned char endianness = *reinterpret_cast(&a) == 1 + ? ELFDATA2LSB : ELFDATA2MSB; + if (e_ident[EI_DATA] != endianness) + return -ENOEXEC; + + return 0; +} + +template +int IPAModule::loadSymbol(void *dst, size_t size, char *map, size_t soSize, + const char *symbol) +{ + ElfHeader *eHdr = elfPointer(map, 0, soSize); + if (!eHdr) + return -ENOEXEC; + + off_t offset = eHdr->e_shoff + eHdr->e_shentsize * eHdr->e_shstrndx; + SecHeader *sHdr = elfPointer(map, offset, soSize); + if (!sHdr) + return -ENOEXEC; + off_t shnameoff = sHdr->sh_offset; + + /* Locate .dynsym section header. */ + bool found = false; + SecHeader *dynsym; + for (unsigned int i = 0; i < eHdr->e_shnum; i++) { + offset = eHdr->e_shoff + eHdr->e_shentsize * i; + sHdr = elfPointer(map, offset, soSize); + if (!sHdr) + return -ENOEXEC; + + offset = shnameoff + sHdr->sh_name; + char *name = elfPointer(map, offset, soSize); + if (!name) + return -ENOEXEC; + + if (sHdr->sh_type == SHT_DYNSYM && !strcmp(name, ".dynsym")) { + found = true; + dynsym = sHdr; + break; + } + } + + if (!found) { + LOG(IPAModule, Error) << "ELF has no .dynsym section"; + return -ENOEXEC; + } + + offset = eHdr->e_shoff + eHdr->e_shentsize * dynsym->sh_link; + sHdr = elfPointer(map, offset, soSize); + if (!sHdr) + return -ENOEXEC; + off_t dynsym_nameoff = sHdr->sh_offset; + + /* Locate symbol in the .dynsym section. */ + found = false; + SymHeader *target_symbol; + unsigned int dynsym_num = dynsym->sh_size / dynsym->sh_entsize; + for (unsigned int i = 0; i < dynsym_num; i++) { + offset = dynsym->sh_offset + dynsym->sh_entsize * i; + SymHeader *sym = elfPointer(map, offset, soSize); + if (!sym) + return -ENOEXEC; + + offset = dynsym_nameoff + sym->st_name; + char *name = elfPointer(map, offset, soSize); + if (!name) + return -ENOEXEC; + + if (!strcmp(name, symbol) && + sym->st_info & STB_GLOBAL && + sym->st_size == sizeof(struct IPAModuleInfo)) { + found = true; + target_symbol = sym; + break; + } + } + + if (!found) { + LOG(IPAModule, Error) << "IPAModuleInfo symbol not found"; + return -ENOEXEC; + } + + /* Locate and return data of symbol. */ + offset = eHdr->e_shoff + target_symbol->st_shndx * eHdr->e_shentsize; + sHdr = elfPointer(map, offset, soSize); + if (!sHdr) + return -ENOEXEC; + offset = sHdr->sh_offset + (target_symbol->st_value - sHdr->sh_addr); + char *data = elfPointer(map, offset, soSize); + if (!data) + return -ENOEXEC; + memcpy(dst, data, size); + + return 0; +} + +int IPAModule::loadIPAModuleInfo(const char *libPath, const char *symbol) +{ + int fd = open(libPath, O_RDONLY); + if (fd < 0) { + LOG(IPAModule, Error) << "Failed to open IPA library: " + << strerror(errno); + return fd; + } + + int ret = loadELFIdent(fd); + if (ret) + goto close; + + size_t soSize; + char *map; + struct stat st; + ret = fstat(fd, &st); + if (ret < 0) + goto close; + soSize = st.st_size; + map = static_cast(mmap(NULL, soSize, PROT_READ, + MAP_PRIVATE, fd, 0)); + if (map == MAP_FAILED) { + ret = -errno; + goto close; + } + + if (bitClass_ == ELFCLASS32) + ret = loadSymbol + (&info_, sizeof(info_), map, soSize, symbol); + else if (bitClass_ == ELFCLASS64) + ret = loadSymbol + (&info_, sizeof(info_), map, soSize, symbol); + if (ret) + goto unmap; + +unmap: + munmap(map, soSize); +close: + close(fd); + return ret; +} + +/** + * \brief Check if construction of the IPAModule instance succeeded + * + * \return true if the constructor succeeded with no errors, false otherwise + */ + +bool IPAModule::isValid() const +{ + return loaded_; +} + +/** + * \brief Get the loaded IPAModuleInfo + * + * Check isValid() before calling this. + * + * \return IPAModuleInfo + */ + +struct IPAModuleInfo const &IPAModule::IPAModuleInfo() const +{ + return info_; +} + +} /* namespace libcamera */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 8796f49..e5b48f2 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -10,6 +10,7 @@ libcamera_sources = files([ 'event_notifier.cpp', 'formats.cpp', 'geometry.cpp', + 'ipa_module.cpp', 'log.cpp', 'media_device.cpp', 'media_object.cpp', @@ -31,6 +32,7 @@ libcamera_headers = files([ 'include/device_enumerator_udev.h', 'include/event_dispatcher_poll.h', 'include/formats.h', + 'include/ipa_module.h', 'include/log.h', 'include/media_device.h', 'include/media_object.h', From patchwork Tue May 14 22:38:08 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 1204 Return-Path: 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 0DEFD60E4C for ; Wed, 15 May 2019 00:38:22 +0200 (CEST) Received: from localhost.localdomain (unknown [96.44.9.117]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 56F891953; Wed, 15 May 2019 00:38:21 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1557873501; bh=bmwMP7ZBZbb0t9wKzhuwuT4VTKbmg/80cCUD3ce93q4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=UBBzaIT3a2jHzUzj55Ov9ouyGtKip4GhTyvVCTKpZ2mAChUxN15PGc4F5dJ+tyylk Uc2mmzmN7Pfa5mE9Ny9dU4yBn88g+PGQ5JuriHY3p84BjybbvIdEFvDTaPjzx6XnKQ Fat4IESBMtiLq3gCs/OMZRu5dMjGy8gfj3k7QDRI= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Tue, 14 May 2019 18:38:08 -0400 Message-Id: <20190514223808.27351-2-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190514223808.27351-1-paul.elder@ideasonboard.com> References: <20190514223808.27351-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 2/2] tests: ipa: add tests to test IPAModule X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 14 May 2019 22:38:22 -0000 Add tests to test the the IPAModule class for loading struct IPAModuleInfo of a given symbol from a .so library. Signed-off-by: Paul Elder --- Changes in v2: - added source for test .so - updated tests to work with new (v2, see 1/2) IPAModule API test/ipa/ipa_test.cpp | 76 ++++++++++++++++++++++++++++++++++++++++++ test/ipa/meson.build | 23 +++++++++++++ test/ipa/shared_test.c | 16 +++++++++ test/meson.build | 1 + 4 files changed, 116 insertions(+) create mode 100644 test/ipa/ipa_test.cpp create mode 100644 test/ipa/meson.build create mode 100644 test/ipa/shared_test.c diff --git a/test/ipa/ipa_test.cpp b/test/ipa/ipa_test.cpp new file mode 100644 index 0000000..96849bf --- /dev/null +++ b/test/ipa/ipa_test.cpp @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * load-so.cpp - loading .so tests + */ + +#include + +#include "ipa_module.h" + +#include "test.h" + +using namespace std; +using namespace libcamera; + +class IPAModuleTest : public Test +{ +protected: + int init() + { + return 0; + } + + int run_test(const string path, const string symbol) + { + cout << "running lib loader test" << endl; + + IPAModule *ll = new IPAModule(path, symbol); + + if (!ll->isValid()) { + cout << "failed to load" << endl; + return TestFail; + } + + struct IPAModuleInfo info = ll->IPAModuleInfo(); + cout << "loaded!" << endl; + cout << "name = " << info.name << ", version = " << info.version << endl; + + delete ll; + return TestPass; + } + + int run() + { + int count = 0; + + cout << endl + << "testing 32-bit so from .data" << endl; + count += run_test("test/ipa/libfoo.32.so", "asdf"); + + cout << endl + << "testing 32-bit so from .rodata" << endl; + count += run_test("test/ipa/libfoo.32.so", "hjkl"); + + cout << endl + << "testing 64-bit so from .data" << endl; + count += run_test("test/ipa/libfoo.64.so", "asdf"); + + cout << endl + << "testing 64-bit so from .rodata" << endl; + count += run_test("test/ipa/libfoo.64.so", "hjkl"); + + /* Two of these should fail due to bitness mismatch. */ + if (count < -2) + return TestFail; + + return TestPass; + } + + void cleanup() + { + } +}; + +TEST_REGISTER(IPAModuleTest) diff --git a/test/ipa/meson.build b/test/ipa/meson.build new file mode 100644 index 0000000..994d9ef --- /dev/null +++ b/test/ipa/meson.build @@ -0,0 +1,23 @@ +ipa_test = [ + ['ipa_test', 'ipa_test.cpp'], +] + +foreach t : ipa_test + exe = executable(t[0], t[1], + link_with : test_libraries, + include_directories : test_includes_internal) + + test(t[0], exe, suite: 'ipa', is_parallel: false) +endforeach + +libfoo_sources = files([ + 'shared_test.c' +]) + +libfoo_32 = shared_library('foo.32', + libfoo_sources, + c_args: '-m32', + link_args: '-m32') + +libfoo_64 = shared_library('foo.64', + libfoo_sources) diff --git a/test/ipa/shared_test.c b/test/ipa/shared_test.c new file mode 100644 index 0000000..67acfa4 --- /dev/null +++ b/test/ipa/shared_test.c @@ -0,0 +1,16 @@ +#include + +struct IPAModuleInfo { + const char name[256]; + unsigned int version; +}; + +const struct IPAModuleInfo hjkl = { + .name = "Answer to the Ultimate Question of Life, the Universe, and Everything", + .version = 42, +}; + +struct IPAModuleInfo asdf = { + .name = "It's over nine thousand!", + .version = 9001, +}; diff --git a/test/meson.build b/test/meson.build index d501f2b..ef41367 100644 --- a/test/meson.build +++ b/test/meson.build @@ -1,6 +1,7 @@ subdir('libtest') subdir('camera') +subdir('ipa') subdir('media_device') subdir('pipeline') subdir('v4l2_device')