linker: support ldd(1)-like behavior via --list.
Given that we have both linker and linker64, I didn't really want to have
to have ldd and ldd64, so this change just adds the --list option to the
linkers and a shell script wrapper "ldd" that calls the appropriate
linker behind the scenes.
Test: adb shell linker --list `which app_process32`
Test: adb shell linker64 --list `which date`
Test: adb shell ldd `which app_process32`
Test: adb shell ldd `which date`
Change-Id: I33494bda1cc3cafee54e091f97c0f2ae52d1f74b
diff --git a/linker/Android.bp b/linker/Android.bp
index 23ef5ac..5e7a921 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -307,6 +307,11 @@
xom: false,
}
+sh_binary {
+ name: "ldd",
+ src: "ldd",
+}
+
cc_library {
// NOTE: --exclude-libs=libgcc.a makes sure that any symbols ld-android.so pulls from
// libgcc.a are made static to ld-android.so. This in turn ensures that libraries that
diff --git a/linker/ldd b/linker/ldd
new file mode 100644
index 0000000..3a0aff9
--- /dev/null
+++ b/linker/ldd
@@ -0,0 +1,23 @@
+#!/system/bin/sh
+
+# Rather than have ldd and ldd64, this script does the right thing depending
+# on the argument.
+
+function error() {
+ echo "$1"
+ exit 1
+}
+
+[ $# -eq 1 ] || error "usage: ldd FILE"
+
+case `file -L "$1"` in
+ *32-bit*)
+ linker --list "$1"
+ ;;
+ *64-bit*)
+ linker64 --list "$1"
+ ;;
+ *)
+ error "$1: not an ELF file"
+ ;;
+esac
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 0de17f7..f68775c 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -2596,6 +2596,8 @@
}
ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr) {
+ if (g_is_ldd) return 0;
+
typedef ElfW(Addr) (*ifunc_resolver_t)(void);
ifunc_resolver_t ifunc_resolver = reinterpret_cast<ifunc_resolver_t>(resolver_addr);
ElfW(Addr) ifunc_addr = ifunc_resolver();
@@ -3876,6 +3878,11 @@
return true;
}
+ if (g_is_ldd && !is_main_executable()) {
+ async_safe_format_fd(STDOUT_FILENO, "\t%s => %s (%p)\n", get_soname(),
+ get_realpath(), reinterpret_cast<void*>(base));
+ }
+
local_group_root_ = local_group.front();
if (local_group_root_ == nullptr) {
local_group_root_ = this;
diff --git a/linker/linker_globals.h b/linker/linker_globals.h
index 32aa09d..de05238 100644
--- a/linker/linker_globals.h
+++ b/linker/linker_globals.h
@@ -87,3 +87,5 @@
private:
std::string saved_error_msg_;
};
+
+__LIBC_HIDDEN__ extern bool g_is_ldd;
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
index f6e4f67..f576023 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -117,6 +117,7 @@
return vdso;
}
+bool g_is_ldd;
int g_ld_debug_verbosity;
static std::vector<std::string> g_ld_preload_names;
@@ -397,7 +398,7 @@
"\"%s\": error: Android 5.0 and later only support "
"position-independent executables (-fPIE).\n",
g_argv[0]);
- exit(EXIT_FAILURE);
+ _exit(EXIT_FAILURE);
}
// Use LD_LIBRARY_PATH and LD_PRELOAD (but only if we aren't setuid/setgid).
@@ -660,22 +661,29 @@
// linker's _start.
const char* exe_to_load = nullptr;
if (getauxval(AT_ENTRY) == reinterpret_cast<uintptr_t>(&_start)) {
- if (args.argc <= 1 || !strcmp(args.argv[1], "--help")) {
+ if (args.argc == 3 && !strcmp(args.argv[1], "--list")) {
+ // We're being asked to behave like ldd(1).
+ g_is_ldd = true;
+ exe_to_load = args.argv[2];
+ } else if (args.argc <= 1 || !strcmp(args.argv[1], "--help")) {
async_safe_format_fd(STDOUT_FILENO,
- "Usage: %s program [arguments...]\n"
- " %s path.zip!/program [arguments...]\n"
+ "Usage: %s [--list] PROGRAM [ARGS-FOR-PROGRAM...]\n"
+ " %s [--list] path.zip!/PROGRAM [ARGS-FOR-PROGRAM...]\n"
"\n"
"A helper program for linking dynamic executables. Typically, the kernel loads\n"
"this program because it's the PT_INTERP of a dynamic executable.\n"
"\n"
"This program can also be run directly to load and run a dynamic executable. The\n"
"executable can be inside a zip file if it's stored uncompressed and at a\n"
- "page-aligned offset.\n",
+ "page-aligned offset.\n"
+ "\n"
+ "The --list option gives behavior equivalent to ldd(1) on other systems.\n",
args.argv[0], args.argv[0]);
- exit(0);
+ _exit(EXIT_SUCCESS);
+ } else {
+ exe_to_load = args.argv[1];
+ __libc_shared_globals()->initial_linker_arg_count = 1;
}
- exe_to_load = args.argv[1];
- __libc_shared_globals()->initial_linker_arg_count = 1;
}
// store argc/argv/envp to use them for calling constructors
@@ -693,6 +701,8 @@
ElfW(Addr) start_address = linker_main(args, exe_to_load);
+ if (g_is_ldd) _exit(EXIT_SUCCESS);
+
INFO("[ Jumping to _start (%p)... ]", reinterpret_cast<void*>(start_address));
// Return the address that the calling assembly stub should jump to.
diff --git a/linker/linker_soinfo.cpp b/linker/linker_soinfo.cpp
index 31ee74c..5f40528 100644
--- a/linker/linker_soinfo.cpp
+++ b/linker/linker_soinfo.cpp
@@ -391,13 +391,15 @@
}
void soinfo::call_pre_init_constructors() {
+ if (g_is_ldd) return;
+
// DT_PREINIT_ARRAY functions are called before any other constructors for executables,
// but ignored in a shared library.
call_array("DT_PREINIT_ARRAY", preinit_array_, preinit_array_count_, false, get_realpath());
}
void soinfo::call_constructors() {
- if (constructors_called) {
+ if (constructors_called || g_is_ldd) {
return;
}