From patchwork Fri May 10 23:22:34 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 1183 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 9221260E68 for ; Sat, 11 May 2019 01:22:49 +0200 (CEST) Received: from localhost.localdomain (unknown [96.44.9.117]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id C158D2DF; Sat, 11 May 2019 01:22:48 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1557530569; bh=6i0vBzhqiVZbXXrbFF9v6hdecuUyGR8ZUECieYJ5IhI=; h=From:To:Cc:Subject:Date:From; b=CzUJhWrAIUjDPFD/ZIez07Ne5yxU207o8u1Uqhhprfwh7S8+ejRhdUO+NMASOM95t pkSCAOvn9MbNPSyxfBh0Fe4VKONUVDWcoFYF0Qb8pweXhHNRckZNh997WN0cZk0kLW 5OHxrnqu7gjEC6j8THeTSAERlJUXFqzhpiQUwlss= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 10 May 2019 19:22:34 -0400 Message-Id: <20190510232235.8724-1-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.20.1 MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 1/2] libcamera: lib_loader: add 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: Fri, 10 May 2019 23:22:49 -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 --- I will remove and clean up the comments in lib_loader.h, but I've left them there for now, although I would like comments on the API (and the commented out unload()). I will also add logging later, as well as checking (guessing) if the bitness is acceptable or not. The generics to allow duck-typing of the ELF structs was the best I could think of with my limited C++ knowledge of how to deal with the different size and field layouts of the 32-bit and 64-bit structs. src/libcamera/include/lib_loader.h | 59 ++++++++++ src/libcamera/lib_loader.cpp | 175 +++++++++++++++++++++++++++++ src/libcamera/meson.build | 2 + 3 files changed, 236 insertions(+) create mode 100644 src/libcamera/include/lib_loader.h create mode 100644 src/libcamera/lib_loader.cpp diff --git a/src/libcamera/include/lib_loader.h b/src/libcamera/include/lib_loader.h new file mode 100644 index 0000000..d8eb100 --- /dev/null +++ b/src/libcamera/include/lib_loader.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * lib_loader.h - load shared libraries at runtime + */ +#ifndef __LIBCAMERA_LIB_LOADER_H__ +#define __LIBCAMERA_LIB_LOADER_H__ + +#include + +namespace libcamera { + +class LibLoader +{ +public: + struct IPAModuleInfo { + char name[256]; + unsigned int version; + }; + + // doesn't actually do anything? + explicit LibLoader(const std::string &lib_path); + + // if you try it while it's already loaded it'll just load it again, + // no big deal + /* returns 0 on success, err code on failure */ + int loadIPAModuleInfo(const std::string &symbol); + + bool isLoaded() const; + + // just a getter (or returns nullptr if module hasn't been loaded) + struct IPAModuleInfo getIPAModuleInfo() const; + + // necessary to allow multiple users of an instance of LibLoader + /* returns 0 on success, err code on failure, pos num of others loading the lib */ + //int unload(); + + //void (*)() resolve(const std::string &symbol); + // resolve(const std::string &lib_path, const std::string symbol) + +private: + struct IPAModuleInfo info_; + + bool loaded_; + std::string lib_path_; + + int bitclass_; + + int loadElfIdent(int fd); + + template + int loadSymbol(struct IPAModuleInfo *dst, int fd, + const std::string &symbol); +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_LIB_LOADER_H__ */ diff --git a/src/libcamera/lib_loader.cpp b/src/libcamera/lib_loader.cpp new file mode 100644 index 0000000..d7788d6 --- /dev/null +++ b/src/libcamera/lib_loader.cpp @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * lib_loader.cpp - load shared libraries at runtime + */ + +#include "lib_loader.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "log.h" + +namespace libcamera { + +LOG_DECLARE_CATEGORY(LibLoader) + +LibLoader::LibLoader(const std::string &lib_path) + : lib_path_(lib_path) +{ +} + +int LibLoader::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 -1; + + if (e_ident[EI_CLASS] == ELFCLASS32 || + e_ident[EI_CLASS] == ELFCLASS64) + bitclass_ = e_ident[EI_CLASS]; + else + return -1; + + int a = 1; + bool little_endian = *(char *)&a == 1; + if (!(e_ident[EI_DATA] == ELFDATA2LSB || + e_ident[EI_DATA] == ELFDATA2MSB) || + !(e_ident[EI_DATA] == (little_endian ? ELFDATA2LSB : ELFDATA2MSB))) + return -1; + + return 0; +} + +template +int LibLoader::loadSymbol(struct LibLoader::IPAModuleInfo *dst, int fd, + const std::string &symbol) +{ + unsigned long soSize = lseek(fd, 0, SEEK_END); + unsigned char *map = (unsigned char *)mmap(NULL, soSize, PROT_READ, + MAP_PRIVATE, fd, 0); + if (map == MAP_FAILED) + return errno; + + elfHeader *eHdr = (elfHeader *)map; + + unsigned long shnameoff = + ((secHeader *)(map + eHdr->e_shoff + + eHdr->e_shentsize * eHdr->e_shstrndx)) + ->sh_offset; + + // walk section header table, find .dynsym + bool found = false; + secHeader *dynsym; + for (unsigned int i = 0; i < eHdr->e_shnum; i++) { + unsigned long i_shoff = eHdr->e_shoff + eHdr->e_shentsize * i; + secHeader *sHdr = (secHeader *)(map + i_shoff); + + char *name = (char *)map + shnameoff + sHdr->sh_name; + + if (sHdr->sh_type == SHT_DYNSYM && !strcmp(name, ".dynsym")) { + found = true; + dynsym = sHdr; + break; + } + } + + if (!found) + return -1; + + unsigned long dynsym_nameoff = + ((secHeader *)(map + eHdr->e_shoff + + eHdr->e_shentsize * dynsym->sh_link)) + ->sh_offset; + + // walk dynsym symbol table, find desired symbol + found = false; + symHeader *target_symbol; + unsigned int dynsym_num = dynsym->sh_size / dynsym->sh_entsize; + for (unsigned int i = 0; i < dynsym_num; i++) { + unsigned long i_symoff = dynsym->sh_offset + dynsym->sh_entsize * i; + symHeader *sym = (symHeader *)(map + i_symoff); + + char *name = (char *)map + dynsym_nameoff + sym->st_name; + + if (!strcmp(name, symbol.c_str()) && + sym->st_info & STB_GLOBAL && + sym->st_size == sizeof(struct IPAModuleInfo)) { + found = true; + target_symbol = sym; + break; + } + } + + if (!found) + return -1; + + secHeader *target_section = + (secHeader *)(map + eHdr->e_shoff + + target_symbol->st_shndx * eHdr->e_shentsize); + unsigned long final_addr = target_section->sh_offset + + (target_symbol->st_value - target_section->sh_addr); + memcpy(dst, map + final_addr, sizeof(struct IPAModuleInfo)); + + return 0; +} + +int LibLoader::loadIPAModuleInfo(const std::string &symbol) +{ + int fd = open(lib_path_.c_str(), O_RDONLY); + if (fd < 0) + return fd; + + int ret = loadElfIdent(fd); + if (ret) + goto close; + + if (bitclass_ == ELFCLASS32) + ret = loadSymbol(&info_, fd, symbol); + else if (bitclass_ == ELFCLASS64) + ret = loadSymbol(&info_, fd, symbol); + else + ret = -1; + if (ret) + goto close; + + loaded_ = true; + +close: + close(fd); + return ret; +} + +bool LibLoader::isLoaded() const +{ + return loaded_; +} + +struct LibLoader::IPAModuleInfo LibLoader::getIPAModuleInfo() const +{ + return info_; +} + +} /* namespace libcamera */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 8796f49..4e7ab10 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', + 'lib_loader.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/lib_loader.h', 'include/log.h', 'include/media_device.h', 'include/media_object.h', From patchwork Fri May 10 23:22:35 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 1184 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3ECBE60E6A for ; Sat, 11 May 2019 01:22:50 +0200 (CEST) Received: from localhost.localdomain (unknown [96.44.9.117]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 84FED305; Sat, 11 May 2019 01:22:49 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1557530570; bh=urKqOi2Hu1I8rDaOwYxbIzNAXxJmpIWTOTgNim/YdS8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=XWUMCdN1JYiJVeRSyBj2+/2236nkkCUPoYnmqADJdIx/x//5lKN6iMV9qRqmlDV38 UAAPoo1HBO/90mZG5yVKkIKTEO5PiVOdrU0zelRbwxkt5aL86XlddgcQyyHz0zw6fD +4FydHk9A4A+L26imE1I2bcTZD8VN/kz9IZRkdfI= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 10 May 2019 19:22:35 -0400 Message-Id: <20190510232235.8724-2-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190510232235.8724-1-paul.elder@ideasonboard.com> References: <20190510232235.8724-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 2/2] tests: load-so: add tests to test LibLoader 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: Fri, 10 May 2019 23:22:50 -0000 Add tests to test the the LibLoader class for loading struct IPAModuleInfo of a given symbol from a .so library. Signed-off-by: Paul Elder --- This *should* Just Work (TM). test/load-so/libfoo.so.32 | Bin 0 -> 15344 bytes test/load-so/libfoo.so.64 | Bin 0 -> 16320 bytes test/load-so/load-so.cpp | 79 ++++++++++++++++++++++++++++++++++++++ test/load-so/meson.build | 19 +++++++++ test/meson.build | 1 + 5 files changed, 99 insertions(+) create mode 100755 test/load-so/libfoo.so.32 create mode 100755 test/load-so/libfoo.so.64 create mode 100644 test/load-so/load-so.cpp create mode 100644 test/load-so/meson.build diff --git a/test/load-so/libfoo.so.32 b/test/load-so/libfoo.so.32 new file mode 100755 index 0000000000000000000000000000000000000000..8633f992f256d3f6fc50e8658caf39d3cf267e54 GIT binary patch literal 15344 zcmeHOZ)_Y#6`%7tjh$TGBsDEGiMkC=nxJNJTGzNlsB8OdC&*3{#}R@EZP&MZzH9Gp z&F#9_B@#)kNJ$S%E<%C|gdzpOhav=OgrEp$xqwonppZ{Ud`X3Z9ARiG3PdRC@%zp0 z*lX89fcgQ@ywltF-n=(&X5P&HyPfy>f#JbaDkT)8MOsigyh(^I(D{!%L0J;S@n2>jXXB!F(FdVA_1B?wkxLJrE&@Cb(J_Tx77zZkD z7War_kcFY-HdL@D%6`QTLZ=$qwy+P5jo7YN^(Klz=vBu?`b&Pr8*|1>UZh)FW9OzF zJ726grQ)-mZ~`|k3Qs*<5_#VjlhtA+%!^WSJZA>J*)4{K5BBxj51aeUN0#_b_DeL$ zICGTIuSqNY8&VIjznmj&)`@fd*d2R8|QE0ke_D(p|-7~o}XX- zEnGw4sh@onzUQ9L;)QDNIUXOo6mG9Qw-~i`_p!6rXBXR#u*prY*XP1rV!l3iVHf=^ zoRjsBqdu&TCVk8D%xb zdCSk!$-*aKbgI4{KH=l!kJuqG^Ra*p3qTSa=ll7qc`>t@L-} zI5f5PjyfH~+X7#%*S%y4@ycU(Jw(Z9ZT=Q)M(5_6-vBvVU--d0(&@bPRBP_2ql%n| zpF(k}?xV7dJ&HBx2bgGg;n@86BS^B<4)Hye#CXcs8|?f4k}8{midDbRDE{Ju{} zJv%B=Ct6ZlGtJFs@b`d$vTeXie`++yi?)`*w#>)x+88!Zi^uQV|DlIFcJSM@1-u9w z^}mV%c?|eNw8!u8qPMN(bW`7*>$0=y$)DHZJ^H3S4b^4Xu#Xya1at&+1at&+1at&+ z1at&+1at&+1at)cy$D#?`+O5GW&8}xy~F&%%?-rZC@juE_W@Au6W&S+ybs9z!hFx8 z{Fiqc{xm9cA9ok_0&^etHz02SeIB?G$ag;O`R0osw+r*VkMEFhFYX0eP?Dz+_i$Ul zyMeUBS7HAOD&Z@#&pzT6)c*x~zkiqT^}Vs`HB-V-rvj-Gi5gecQO2(Fz-#-swc zb_mSHAaG3)(sH#4!MTrQt~epMHVEk`LxM~!Gm{`%Tcl~Zn!v>KoJK|m!B~>HVqw1| zbLB?4Af)N&%#i0qB=Zab81e``asvR&t01JsU6S@J$&q88ON4Z^;^ny%$z1uUzoh3G zAO!c~6VmEm7fXte-zOPkNxnzQ*osd;LS$`j7VF3Xt#D9qLQhZv)2;~?I&|*U7?0yciWugC|%q=;Y(C>hr zc|r;dFfUOC6WORe){6t^xu>Hi~;L(9Gd#tx_ zcmQ4E0RCxWPn4l!St>E7GfLjES7h}ptC_d6Ds4z+{4m)^<`1ozBP7gVrW`uspkXzl z1;thT&@(40Q|9;#`|7ou>1 zyj#&uI)NiAW?4jgJ}dG1OS?XxMH?W?*)H!E%I+EP>?@zue z@W&#;i$GTHsn{n+mRU3Gpk{7z!1;8GO?2>)k@UsVXJx{s9y{&E^0A literal 0 HcmV?d00001 diff --git a/test/load-so/libfoo.so.64 b/test/load-so/libfoo.so.64 new file mode 100755 index 0000000000000000000000000000000000000000..adaef585ec27dce37a505f93780134fb787c4e67 GIT binary patch literal 16320 zcmeHOU2Ggz6~4P`H=Edroi;5dsg!BtKvSg~lMvS-O|$;Z202LywgiZ1GWLwUo9<7v zGj8n?z#;-_RY8$J0*UvkNIcO#RDHoAC`Cd-sRSYqfGk2K2LVkUKiyfV(IyA&E%zFc|a-zc{QLm)Aez+ z%hzpdHm$4OmqwrxzuZSVYl|GkBQhi)+L`uU8f;9(@UH>>SnPnUb`OziHmCKo&xP^X zjF$6)uuBuoc0FR(BX+=L=?|FIZu~LwI0x844EAbT0sVU-?)mpTo9mrVXoV<39|*h8 zksXioK?}luR{Gm0F8AgBfFk<$w}U$4aGf|ksownSt=+|r-}N22mdP)@3aY7E-@EZc)UhT!!&UZ$MD>T5yS7_fFC1% zPmI1v+{3C1j>sp*_s%k`@h<0prP&4amvxx@b0^i)jKd%=IYaP0zBF(2l3mNqMuR-Uv6&xy!% zbAIW@^CSGSI56^>z;gik)^W{aE{K_0Y2oqcaocJO4`0mRZQ(KZnZMt{`|ApmRC@A0 z`14HPzm}705`iQFNd%GzBoRm=kVGJfKoWuf#|RWI|0P>^wf#2-lq!64*-JNW7B2rh zdp!t6G@bnV)mry9#eZojkAptAQTB+p!hLE*}=dwt~VO8cJ~j@0k5p55LW@Z>?}`EmK&!Te!A*ypXz4cz4a)K@LX6LAJ3n)3tk~ zX}@^vt7NC<4onsThC{^ zI$p{ys^dG4K6R*f57TT<|GrSaAidV#HQDyd^qQl7{NI=dw{?FjJ-qcHn>rvqDM8iIqOTSe{@yN`>E^P;;T z3+@!exl#VU!v)`M{=U|zv3y<*Y2plLQ4WRjxlt}xrG1MQ)uLdBV2_}f@nLnph?zn= zzQ^(TV=f=lpjO&+1-v0L#x4#IpUnEzqEP2o8S^X&@rEzHx?B=b7V zZ&H}=VLlr?7h#^B0+}aaev2|<`3{wj{zc_TQhXxAL~?@e?-MSFB$r;4$ilS^&bt^pD_QJihHgyv4$_;XH{{OnfTDvmfLK zur8McdZl9G`~%|S#=p^hz2^JrR*vqg#HT24BO3pAe184=d(-c0 z{k*y@ehP!WLU4KC=hwgQ?Q~;l%9>JaJUEl4;fJ)`IC&z50ThsaqAtwC5_71u|)oOw#m1 zs&3J$JGSXLu16y?&9SqCr$)`u(<7XF!;ZO6w`LZts?8}kBVRl{ctwR5^E{;#pIKUR`4mwQ9|CaYzegJY*&CS+G^qhIR`eDx1YqsTCD(4jC9V^=81d=w@ zZ=JS4oqDh;rD^KE=CNlQobPAKbybe$Ple`0a~l4uHQuN4UN0>U@OcdEjW~{eY|>dl z^$*(1!T!8dfPG?*eIE9aa63R1-n+sc`&-~GQRIH<5e)Xr*mvfMW8bjHJ^;8#ZLNiU zw3Y%1yjO)i_6tC)K^Q;ufSm8fdtb=GzCr?JNN3UU3$1Lba2xCm*aM4u>(Sn?P*nB7r%MpI41ah5vlKLM!$F;zd^RF0(BU`0cTD*O_V_;o zcnqf%G54UucwYL)I>P_I*iSFqH`Wz?J}`Ew1|{FGtKUIK(h?E + +#include "lib_loader.h" + +#include "test.h" + +using namespace std; +using namespace libcamera; + +class LibLoaderTest : public Test +{ +protected: + int init() + { + cout << "!!! make sure to run this test from the build directory" << endl; + + ll32 = new LibLoader("test/load-so/libfoo.so.32"); + ll64 = new LibLoader("test/load-so/libfoo.so.64"); + + return 0; + } + + int run_test(LibLoader *ll, const std::string &symbol) + { + cout << "running lib loader test: " << symbol << endl; + int ret = ll->loadIPAModuleInfo(symbol); + + bool loaded = ll->isLoaded(); + + if (ret < 0 && !loaded) { + cout << "failed to load" << endl; + return TestFail; + } + + if ((ret < 0 && loaded) || (ret >= 0 && !loaded)) { + cout << "CRITICAL: isLoaded() and return value disagree!" << endl; + return TestFail; + } + + struct LibLoader::IPAModuleInfo info = ll->getIPAModuleInfo(); + cout << "loaded!" << endl; + cout << "name = " << info.name << ", version = " << info.version << endl; + return TestPass; + } + + int run() + { + cout << endl + << "testing 32-bit so: asdf from .rodata, hjkl from .data" << endl; + run_test(ll32, "asdf"); + run_test(ll32, "hjkl"); + + cout << endl + << "testing 64-bit so: asdf from .rodata, hjkl from .data" << endl; + run_test(ll64, "asdf"); + run_test(ll64, "hjkl"); + + return TestPass; + } + + void cleanup() + { + delete ll32; + delete ll64; + } + +private: + LibLoader *ll32; + LibLoader *ll64; +}; + +TEST_REGISTER(LibLoaderTest) diff --git a/test/load-so/meson.build b/test/load-so/meson.build new file mode 100644 index 0000000..376665b --- /dev/null +++ b/test/load-so/meson.build @@ -0,0 +1,19 @@ +load_so_test = [ + ['load-so', 'load-so.cpp'], +] + +foreach t : load_so_test + exe = executable(t[0], t[1], + link_with : test_libraries, + include_directories : test_includes_internal) + + test(t[0], exe, suite: 'load-so', is_parallel: false) +endforeach + +configure_file(input : 'libfoo.so.32', + output : 'libfoo.so.32', + command: ['cp', '@INPUT@', '@OUTPUT@']) + +configure_file(input : 'libfoo.so.64', + output : 'libfoo.so.64', + command: ['cp', '@INPUT@', '@OUTPUT@']) diff --git a/test/meson.build b/test/meson.build index d501f2b..a5937c2 100644 --- a/test/meson.build +++ b/test/meson.build @@ -1,6 +1,7 @@ subdir('libtest') subdir('camera') +subdir('load-so') subdir('media_device') subdir('pipeline') subdir('v4l2_device')