From 04dc91ae763adc403a14c88b4c46f77b3d2d71a3 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Tue, 1 Jul 2014 14:10:16 -0700 Subject: [PATCH] Load library using file handle. * This patch enables dlopen by file descriptor instead of path/name. Bug: 15984217 Change-Id: Ib39051e00567fb97070bf96d8ce63993877c0a01 --- libc/include/android/dlext.h | 9 +++++- libc/private/ScopedFd.h | 59 ++++++++++++++++++++++++++++++++++++ linker/linker.cpp | 20 +++++++++--- linker/linker_phdr.cpp | 3 -- tests/Android.build.mk | 5 +++ tests/dlext_test.cpp | 22 ++++++++++++++ tests/libs/Android.mk | 13 ++++++++ 7 files changed, 122 insertions(+), 9 deletions(-) create mode 100644 libc/private/ScopedFd.h diff --git a/libc/include/android/dlext.h b/libc/include/android/dlext.h index 90962fa46..616e08ed3 100644 --- a/libc/include/android/dlext.h +++ b/libc/include/android/dlext.h @@ -49,11 +49,17 @@ enum { */ ANDROID_DLEXT_USE_RELRO = 0x8, + /* Instruct dlopen to use library_fd instead of opening file by name. + * The filename parameter is still used to identify the library. + */ + ANDROID_DLEXT_USE_LIBRARY_FD = 0x10, + /* Mask of valid bits */ ANDROID_DLEXT_VALID_FLAG_BITS = ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_RESERVED_ADDRESS_HINT | ANDROID_DLEXT_WRITE_RELRO | - ANDROID_DLEXT_USE_RELRO, + ANDROID_DLEXT_USE_RELRO | + ANDROID_DLEXT_USE_LIBRARY_FD, }; typedef struct { @@ -61,6 +67,7 @@ typedef struct { void* reserved_addr; size_t reserved_size; int relro_fd; + int library_fd; } android_dlextinfo; extern void* android_dlopen_ext(const char* filename, int flag, const android_dlextinfo* extinfo); diff --git a/libc/private/ScopedFd.h b/libc/private/ScopedFd.h new file mode 100644 index 000000000..e56c139b3 --- /dev/null +++ b/libc/private/ScopedFd.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SCOPED_FD_H +#define SCOPED_FD_H + +#include +#include "bionic_macros.h" + +// A smart pointer that closes the given fd on going out of scope. +// Use this when the fd is incidental to the purpose of your function, +// but needs to be cleaned up on exit. +class ScopedFd { +public: + explicit ScopedFd(int fd) : fd(fd) { + } + + ~ScopedFd() { + reset(); + } + + int get() const { + return fd; + } + + int release() __attribute__((warn_unused_result)) { + int localFd = fd; + fd = -1; + return localFd; + } + + void reset(int new_fd = -1) { + if (fd != -1) { + TEMP_FAILURE_RETRY(close(fd)); + } + fd = new_fd; + } + +private: + int fd; + + // Disallow copy and assignment. + DISALLOW_COPY_AND_ASSIGN(ScopedFd); +}; + +#endif // SCOPED_FD_H diff --git a/linker/linker.cpp b/linker/linker.cpp index bf923c197..206523122 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -42,6 +42,7 @@ #include "private/bionic_tls.h" #include "private/KernelArgumentBlock.h" #include "private/ScopedPthreadMutexLocker.h" +#include "private/ScopedFd.h" #include "linker.h" #include "linker_debug.h" @@ -696,11 +697,20 @@ static int open_library(const char* name) { } static soinfo* load_library(const char* name, int dlflags, const android_dlextinfo* extinfo) { - // Open the file. - int fd = open_library(name); - if (fd == -1) { + int fd = -1; + ScopedFd file_guard(-1); + + if (extinfo != NULL && (extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD) != 0) { + fd = extinfo->library_fd; + } else { + // Open the file. + fd = open_library(name); + if (fd == -1) { DL_ERR("library \"%s\" not found", name); return NULL; + } + + file_guard.reset(fd); } ElfReader elf_reader(name, fd); @@ -744,7 +754,7 @@ static soinfo* load_library(const char* name, int dlflags, const android_dlextin // At this point we know that whatever is loaded @ base is a valid ELF // shared library whose segments are properly mapped in. - TRACE("[ find_library_internal base=%p size=%zu name='%s' ]", + TRACE("[ load_library base=%p size=%zu name='%s' ]", reinterpret_cast(si->base), si->size, si->name); if (!soinfo_link_image(si, extinfo)) { @@ -847,7 +857,7 @@ soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo) return NULL; } if (extinfo != NULL && ((extinfo->flags & ~(ANDROID_DLEXT_VALID_FLAG_BITS)) != 0)) { - DL_ERR("invalid extended flags to android_dlopen_ext: %x", extinfo->flags); + DL_ERR("invalid extended flags to android_dlopen_ext: %llx", extinfo->flags); return NULL; } protect_data(PROT_READ | PROT_WRITE); diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp index 12e977940..11585afe7 100644 --- a/linker/linker_phdr.cpp +++ b/linker/linker_phdr.cpp @@ -127,9 +127,6 @@ ElfReader::ElfReader(const char* name, int fd) } ElfReader::~ElfReader() { - if (fd_ != -1) { - close(fd_); - } if (phdr_mmap_ != NULL) { munmap(phdr_mmap_, phdr_size_); } diff --git a/tests/Android.build.mk b/tests/Android.build.mk index 62e971864..bb00648d4 100644 --- a/tests/Android.build.mk +++ b/tests/Android.build.mk @@ -26,6 +26,11 @@ endif ifneq ($(findstring LIBRARY, $(build_target)),LIBRARY) LOCAL_MODULE_STEM_32 := $(module)32 LOCAL_MODULE_STEM_64 := $(module)64 +else +ifeq ($($(module)_install_to_out_data),true) + LOCAL_MODULE_PATH_32 := $(TARGET_OUT_DATA_NATIVE_TESTS)/$(module) + LOCAL_MODULE_PATH_64 := $(TARGET_OUT_DATA_NATIVE_TESTS)64/$(module) +endif endif LOCAL_CLANG := $($(module)_clang_$(build_type)) diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp index b56fc4102..da630463d 100644 --- a/tests/dlext_test.cpp +++ b/tests/dlext_test.cpp @@ -45,6 +45,11 @@ typedef int (*fn)(void); #define LIBNAME_NORELRO "libdlext_test_norelro.so" #define LIBSIZE 1024*1024 // how much address space to reserve for it +#if defined(__LP64__) +#define LIBPATH "%s/nativetest64/libdlext_test_fd/libdlext_test_fd.so" +#else +#define LIBPATH "%s/nativetest/libdlext_test_fd/libdlext_test_fd.so" +#endif class DlExtTest : public ::testing::Test { protected: @@ -83,6 +88,23 @@ TEST_F(DlExtTest, ExtInfoNoFlags) { EXPECT_EQ(4, f()); } +TEST_F(DlExtTest, ExtInfoUseFd) { + const char* android_data = getenv("ANDROID_DATA"); + ASSERT_TRUE(android_data != NULL); + char lib_path[PATH_MAX]; + snprintf(lib_path, sizeof(lib_path), LIBPATH, android_data); + + android_dlextinfo extinfo; + extinfo.flags = ANDROID_DLEXT_USE_LIBRARY_FD; + extinfo.library_fd = TEMP_FAILURE_RETRY(open(lib_path, O_RDONLY | O_CLOEXEC)); + ASSERT_TRUE(extinfo.library_fd != -1); + handle_ = android_dlopen_ext(lib_path, RTLD_NOW, &extinfo); + ASSERT_DL_NOTNULL(handle_); + fn f = reinterpret_cast(dlsym(handle_, "getRandomNumber")); + ASSERT_DL_NOTNULL(f); + EXPECT_EQ(4, f()); +} + TEST_F(DlExtTest, Reserved) { void* start = mmap(NULL, LIBSIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); diff --git a/tests/libs/Android.mk b/tests/libs/Android.mk index efce5b53a..a374e4839 100644 --- a/tests/libs/Android.mk +++ b/tests/libs/Android.mk @@ -77,6 +77,19 @@ build_type := target build_target := SHARED_LIBRARY include $(TEST_PATH)/Android.build.mk +# ----------------------------------------------------------------------------- +# Library used by dlext tests - different name non-default location +# ----------------------------------------------------------------------------- +libdlext_test_fd_src_files := \ + dlext_test_library.cpp \ + +libdlext_test_fd_install_to_out_data := true +module := libdlext_test_fd +module_tag := optional +build_type := target +build_target := SHARED_LIBRARY +include $(TEST_PATH)/Android.build.mk + # ----------------------------------------------------------------------------- # Library used by dlfcn tests # -----------------------------------------------------------------------------