Allow invoking the linker on an executable.
The executable can be inside a zip file using the same syntax used for
shared objects: path.zip!/libentry.so.
The linker currently requires an absolute path. This restriction could be
loosened, but it didn't seem important? If it allowed non-absolute paths,
we'd need to decide how to handle:
- foo/bar (relative to CWD?)
- foo (search PATH / LD_LIBRARY_PATH, or also relative to CWD?)
- foo.zip!/bar (normalize_path() requires an absolute path)
The linker adjusts the argc/argv passed to main() and to constructor
functions to hide the initial linker argument, but doesn't adjust the auxv
vector or files like /proc/self/{exe,cmdline,auxv,stat}. Those files will
report that the kernel loaded the linker as an executable.
I think the linker_logger.cpp change guarding against (g_argv == NULL)
isn't actually necessary, but it seemed like a good idea given that I'm
delaying initialization of g_argv until after C++ constructors have run.
Bug: http://b/112050209
Test: bionic unit tests
Change-Id: I846faf98b16fd34218946f6167e8b451897debe5
diff --git a/tests/Android.bp b/tests/Android.bp
index 5ba6b3d..4ad51da 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -466,6 +466,8 @@
required: [
"cfi_test_helper",
"cfi_test_helper2",
+ "exec_linker_helper",
+ "exec_linker_helper_lib",
"libtest_dt_runpath_a",
"libtest_dt_runpath_b",
"libtest_dt_runpath_c",
diff --git a/tests/dl_test.cpp b/tests/dl_test.cpp
index 44fab01..cb98cae 100644
--- a/tests/dl_test.cpp
+++ b/tests/dl_test.cpp
@@ -79,21 +79,61 @@
ASSERT_EQ(3370318, lib_global_protected_get_serial());
}
-TEST(dl, exec_linker) {
#if defined(__BIONIC__)
#if defined(__LP64__)
static constexpr const char* kPathToLinker = "/system/bin/linker64";
#else
static constexpr const char* kPathToLinker = "/system/bin/linker";
#endif
+#endif
+
+TEST(dl, exec_linker) {
+#if defined(__BIONIC__)
+ std::string usage_prefix = std::string("Usage: ") + kPathToLinker;
ExecTestHelper eth;
- std::string expected_output = std::string("This is ") + kPathToLinker +
- ", the helper program for dynamic executables.\n";
- eth.SetArgs( { kPathToLinker, nullptr });
+ eth.SetArgs({ kPathToLinker, nullptr });
+ eth.Run([&]() { execve(kPathToLinker, eth.GetArgs(), eth.GetEnv()); }, 0, nullptr);
+ ASSERT_EQ(0u, eth.GetOutput().find(usage_prefix)) << "Test output:\n" << eth.GetOutput();
+#endif
+}
+
+TEST(dl, exec_linker_load_file) {
+#if defined(__BIONIC__)
+ std::string helper = GetTestlibRoot() +
+ "/exec_linker_helper/exec_linker_helper";
+ std::string expected_output =
+ "ctor: argc=1 argv[0]=" + helper + "\n" +
+ "main: argc=1 argv[0]=" + helper + "\n" +
+ "helper_func called\n";
+ ExecTestHelper eth;
+ eth.SetArgs({ kPathToLinker, helper.c_str(), nullptr });
eth.Run([&]() { execve(kPathToLinker, eth.GetArgs(), eth.GetEnv()); }, 0, expected_output.c_str());
#endif
}
+TEST(dl, exec_linker_load_from_zip) {
+#if defined(__BIONIC__)
+ std::string helper = GetTestlibRoot() +
+ "/libdlext_test_zip/libdlext_test_zip_zipaligned.zip!/libdir/exec_linker_helper";
+ std::string expected_output =
+ "ctor: argc=1 argv[0]=" + helper + "\n" +
+ "main: argc=1 argv[0]=" + helper + "\n" +
+ "helper_func called\n";
+ ExecTestHelper eth;
+ eth.SetArgs({ kPathToLinker, helper.c_str(), nullptr });
+ eth.Run([&]() { execve(kPathToLinker, eth.GetArgs(), eth.GetEnv()); }, 0, expected_output.c_str());
+#endif
+}
+
+TEST(dl, exec_linker_load_self) {
+#if defined(__BIONIC__)
+ std::string error_message = "error: linker cannot load itself\n";
+ ExecTestHelper eth;
+ eth.SetArgs({ kPathToLinker, kPathToLinker, nullptr });
+ eth.Run([&]() { execve(kPathToLinker, eth.GetArgs(), eth.GetEnv()); }, EXIT_FAILURE, error_message.c_str());
+#endif
+}
+
TEST(dl, preinit_system_calls) {
#if defined(__BIONIC__)
std::string helper = GetTestlibRoot() +
diff --git a/tests/libs/Android.bp b/tests/libs/Android.bp
index 2d35c51..5c4eb42 100644
--- a/tests/libs/Android.bp
+++ b/tests/libs/Android.bp
@@ -852,3 +852,19 @@
defaults: ["bionic_testlib_defaults"],
srcs: ["ld_config_test_helper_lib3.cpp"],
}
+
+cc_test {
+ name: "exec_linker_helper",
+ host_supported: false,
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["exec_linker_helper.cpp"],
+ shared_libs: ["exec_linker_helper_lib"],
+ ldflags: ["-Wl,--rpath,${ORIGIN}/.."],
+}
+
+cc_test_library {
+ name: "exec_linker_helper_lib",
+ host_supported: false,
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["exec_linker_helper_lib.cpp"],
+}
diff --git a/tests/libs/Android.build.dlext_testzip.mk b/tests/libs/Android.build.dlext_testzip.mk
index 62ed4c7..19fd64b 100644
--- a/tests/libs/Android.build.dlext_testzip.mk
+++ b/tests/libs/Android.build.dlext_testzip.mk
@@ -32,7 +32,9 @@
my_shared_libs := \
$(call intermediates-dir-for,SHARED_LIBRARIES,libdlext_test_zip,,,$(bionic_2nd_arch_prefix))/libdlext_test_zip.so \
- $(call intermediates-dir-for,SHARED_LIBRARIES,libatest_simple_zip,,,$(bionic_2nd_arch_prefix))/libatest_simple_zip.so
+ $(call intermediates-dir-for,SHARED_LIBRARIES,libatest_simple_zip,,,$(bionic_2nd_arch_prefix))/libatest_simple_zip.so \
+ $(call intermediates-dir-for,NATIVE_TESTS,exec_linker_helper,,,$(bionic_2nd_arch_prefix))/exec_linker_helper \
+ $(call intermediates-dir-for,SHARED_LIBRARIES,exec_linker_helper_lib,,,$(bionic_2nd_arch_prefix))/exec_linker_helper_lib.so
$(LOCAL_BUILT_MODULE): PRIVATE_SHARED_LIBS := $(my_shared_libs)
$(LOCAL_BUILT_MODULE): $(my_shared_libs) $(BIONIC_TESTS_ZIPALIGN)
diff --git a/tests/libs/exec_linker_helper.cpp b/tests/libs/exec_linker_helper.cpp
new file mode 100644
index 0000000..01a61e0
--- /dev/null
+++ b/tests/libs/exec_linker_helper.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+extern "C" void _start();
+const char* helper_func();
+
+__attribute__((constructor))
+static void ctor(int argc, char* argv[]) {
+ printf("ctor: argc=%d argv[0]=%s\n", argc, argv[0]);
+}
+
+int main(int argc, char* argv[]) {
+ printf("main: argc=%d argv[0]=%s\n", argc, argv[0]);
+ printf("%s\n", helper_func());
+ return 0;
+}
diff --git a/tests/libs/exec_linker_helper_lib.cpp b/tests/libs/exec_linker_helper_lib.cpp
new file mode 100644
index 0000000..b46f482
--- /dev/null
+++ b/tests/libs/exec_linker_helper_lib.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+// Verify that the linker can find exec_linker_helper_lib.so using the
+// executable's $ORIGIN runpath, even when the executable is inside a zip file.
+
+const char* helper_func() {
+ return "helper_func called";
+}
diff --git a/tests/utils.h b/tests/utils.h
index f07bd58..c8656dc 100644
--- a/tests/utils.h
+++ b/tests/utils.h
@@ -184,6 +184,9 @@
char** GetEnv() {
return const_cast<char**>(env_.data());
}
+ const std::string& GetOutput() {
+ return output_;
+ }
void SetArgs(const std::vector<const char*>& args) {
args_ = args;
@@ -212,24 +215,25 @@
// Parent.
close(fds[1]);
- std::string output;
+ output_.clear();
char buf[BUFSIZ];
ssize_t bytes_read;
while ((bytes_read = TEMP_FAILURE_RETRY(read(fds[0], buf, sizeof(buf)))) > 0) {
- output.append(buf, bytes_read);
+ output_.append(buf, bytes_read);
}
close(fds[0]);
- std::string error_msg("Test output:\n" + output);
+ std::string error_msg("Test output:\n" + output_);
AssertChildExited(pid, expected_exit_status, &error_msg);
if (expected_output != nullptr) {
- ASSERT_EQ(expected_output, output);
+ ASSERT_EQ(expected_output, output_);
}
}
private:
std::vector<const char*> args_;
std::vector<const char*> env_;
+ std::string output_;
};
#endif