aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2018-10-25 15:40:00 -0700
committerElliott Hughes <enh@google.com>2018-10-25 15:40:00 -0700
commit8acc709e8553349d14afcc4d647078ca73c2ffc0 (patch)
tree0f8776533b725694eccee788045133d97178b80e
parent18d21f17ca12370e7958f492281813a6c55ff91e (diff)
downloadndk-8acc709e8553349d14afcc4d647078ca73c2ffc0.tar.gz
Remove ndk-depends.
Judging by the lack of interest on the web, it seems like this probably isn't much used. ReLinker (https://github.com/KeepSafe/ReLinker) seems like a better solution to native library loading problems anyway. Rather than rewrite ndk-depends in Python now, let's see if anyone actually still has a reasonable use for it. (Folks who just want readelf/llvm-readobj should probably just be using those already.) Bug: http://b/22085867 Test: builds Change-Id: I938ddd55586c4ff8768bc7b5e0b356f1c31abed1
-rw-r--r--README.md2
-rwxr-xr-xcheckbuild.py21
-rw-r--r--docs/changelogs/Changelog-r19.md4
-rw-r--r--sources/host-tools/ndk-depends/GNUmakefile72
-rw-r--r--sources/host-tools/ndk-depends/NOTICE13
-rwxr-xr-xsources/host-tools/ndk-depends/build-ndk-depends.sh116
-rwxr-xr-xsources/host-tools/ndk-depends/build.py47
-rw-r--r--sources/host-tools/ndk-depends/ndk-depends.cc1465
8 files changed, 5 insertions, 1735 deletions
diff --git a/README.md b/README.md
index 136c4cafe..74c94b89a 100644
--- a/README.md
+++ b/README.md
@@ -48,7 +48,7 @@ prebuilts, build systems, and support libraries.
when this toolchain is used.
* `prebuilt/$HOST_TAG` contains build dependencies and additional tools.
* make, python, yasm, and for Windows: cmp.exe and echo.exe
- * `ndk-depends`, `ndk-stack` and `ndk-gdb` can also be found here.
+ * `ndk-stack` and `ndk-gdb` can also be found here.
### Target Prebuilts
diff --git a/checkbuild.py b/checkbuild.py
index 2ebdc4fdf..49206c7b8 100755
--- a/checkbuild.py
+++ b/checkbuild.py
@@ -735,18 +735,6 @@ def install_exe(out_dir, install_dir, name, system):
shutil.copy2(src, dst)
-class NdkDepends(ndk.builds.InvokeExternalBuildModule):
- name = 'ndk-depends'
- path = 'prebuilt/{host}/bin'
- script = 'ndk/sources/host-tools/ndk-depends/build.py'
- notice = ndk.paths.ndk_path('sources/host-tools/ndk-depends/NOTICE')
-
- def install(self):
- src = os.path.join(self.out_dir, self.host, self.name)
- install_dir = self.get_install_path()
- install_exe(src, install_dir, self.name, self.host)
-
-
class GdbServer(ndk.builds.InvokeBuildModule):
name = 'gdbserver'
path = 'prebuilt/android-{arch}/gdbserver'
@@ -2002,13 +1990,6 @@ class NdkWhichShortcut(ndk.builds.ScriptShortcutModule):
windows_ext = '' # There isn't really a Windows ndk-which.
-class NdkDependsShortcut(ndk.builds.ScriptShortcutModule):
- name = 'ndk-depends-shortcut'
- path = 'ndk-depends'
- script = 'prebuilt/{host}/bin/ndk-depends'
- windows_ext = '.exe'
-
-
class NdkStackShortcut(ndk.builds.ScriptShortcutModule):
name = 'ndk-stack-shortcut'
path = 'ndk-stack'
@@ -2309,8 +2290,6 @@ ALL_MODULES = [
NativeAppGlue(),
NdkBuild(),
NdkBuildShortcut(),
- NdkDepends(),
- NdkDependsShortcut(),
NdkGdb(),
NdkGdbShortcut(),
NdkHelper(),
diff --git a/docs/changelogs/Changelog-r19.md b/docs/changelogs/Changelog-r19.md
index 31db229d2..d9556b12d 100644
--- a/docs/changelogs/Changelog-r19.md
+++ b/docs/changelogs/Changelog-r19.md
@@ -41,8 +41,12 @@ Announcements
Android-specific code. For more information, see the [Build System
Maintainers] guide.
+ * ndk-depends has been removed. We believe that [ReLinker] is a better
+ solution to native library loading issues on old Android versions.
+
[Build System Maintainers]: https://android.googlesource.com/platform/ndk/+/master/docs/BuildSystemMaintainers.md
[Issue 780]: https://github.com/android-ndk/ndk/issues/780
+[ReLinker]: https://github.com/KeepSafe/ReLinker
Changes
-------
diff --git a/sources/host-tools/ndk-depends/GNUmakefile b/sources/host-tools/ndk-depends/GNUmakefile
deleted file mode 100644
index 49102a97b..000000000
--- a/sources/host-tools/ndk-depends/GNUmakefile
+++ /dev/null
@@ -1,72 +0,0 @@
-# Copyright (C) 2013 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.
-#
-
-# The following variables can be over-ridden by the caller
-CC := gcc
-CXX := g++
-STRIP := strip
-BUILD_DIR := /tmp/ndk-$(USER)/build/build-ndk-depends
-PROGNAME := /tmp/ndk-$(USER)/ndk-depends
-
-EXECUTABLE := $(PROGNAME)
-
-all: $(EXECUTABLE)
-
-# The rest should be left alone
-EXTRA_CFLAGS := -Wall -Werror -fno-exceptions -fno-rtti
-EXTRA_LDFLAGS := -lstdc++
-
-ifneq (,$(strip $(DEBUG)))
- CFLAGS += -O0 -g
- hide = @
- strip-cmd =
-else
- CFLAGS += -O2 -s
- hide =
- strip-cmd = $(STRIP) $1
-endif
-
-SOURCES := ndk-depends.cc
-
-OBJECTS=
-
-define build-c-object
-OBJECTS += $1
-$1: $2
- mkdir -p $$(dir $1)
- $$(CC) $$(CFLAGS) $$(EXTRA_CFLAGS) -c -o $1 $2
-endef
-
-define build-cxx-object
-OBJECTS += $1
-$1: $2
- mkdir -p $$(dir $1)
- $$(CXX) $$(CFLAGS) $$(EXTRA_CFLAGS) -c -o $1 $2
-endef
-
-$(foreach src,$(filter %.c,$(SOURCES)),\
- $(eval $(call build-c-object,$(BUILD_DIR)/$(src:%.c=%.o),$(src)))\
-)
-
-$(foreach src,$(filter %.cc,$(SOURCES)),\
- $(eval $(call build-cxx-object,$(BUILD_DIR)/$(src:%.cc=%.o),$(src)))\
-)
-
-clean:
- rm -f $(EXECUTABLE)
-
-$(EXECUTABLE): $(OBJECTS)
- $(CXX) $(LDFLAGS) $(OBJECTS) -o $@ $(EXTRA_LDFLAGS)
- $(call strip-cmd,$@)
diff --git a/sources/host-tools/ndk-depends/NOTICE b/sources/host-tools/ndk-depends/NOTICE
deleted file mode 100644
index d6c092292..000000000
--- a/sources/host-tools/ndk-depends/NOTICE
+++ /dev/null
@@ -1,13 +0,0 @@
-Copyright (C) 2016 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.
diff --git a/sources/host-tools/ndk-depends/build-ndk-depends.sh b/sources/host-tools/ndk-depends/build-ndk-depends.sh
deleted file mode 100755
index 31c992d7a..000000000
--- a/sources/host-tools/ndk-depends/build-ndk-depends.sh
+++ /dev/null
@@ -1,116 +0,0 @@
-#!/bin/sh
-#
-# Copyright (C) 2011 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.
-#
-# This script is used to rebuild the host 'ndk-depends' tool.
-#
-# Note: The tool is installed under prebuilt/$HOST_TAG/bin/ndk-depends
-# by default.
-#
-PROGDIR=$(dirname $0)
-. $NDK_BUILDTOOLS_PATH/prebuilt-common.sh
-
-PROGRAM_PARAMETERS=""
-PROGRAM_DESCRIPTION=\
-"This script is used to rebuild the host ndk-depends binary program."
-
-register_jobs_option
-
-BUILD_DIR=
-register_var_option "--build-dir=<path>" BUILD_DIR "Specify build directory"
-
-NDK_DIR=$ANDROID_NDK_ROOT
-register_var_option "--ndk-dir=<path>" NDK_DIR "Place binary in NDK installation path"
-
-GNUMAKE=
-register_var_option "--make=<path>" GNUMAKE "Specify GNU Make program"
-
-DEBUG=
-register_var_option "--debug" DEBUG "Build debug version"
-
-SRC_DIR=
-register_var_option "--src-dir=<path>" SRC_DIR "Specify binutils source dir. Must be set for --with-libbfd"
-
-PACKAGE_DIR=
-register_var_option "--package-dir=<path>" PACKAGE_DIR "Archive binary into specific directory"
-
-register_canadian_option
-register_try64_option
-
-extract_parameters "$@"
-
-prepare_abi_configure_build
-prepare_host_build
-
-rm -rf $BUILD_DIR
-mkdir -p $BUILD_DIR
-
-prepare_canadian_toolchain $BUILD_DIR
-
-CFLAGS=$HOST_CFLAGS" -O2 -s -ffunction-sections -fdata-sections"
-LDFLAGS=$HOST_LDFLAGS
-EXTRA_CONFIG=
-
-if [ "$HOST_OS" != "darwin" -a "$DARWIN" != "yes" ]; then
- LDFLAGS=$LDFLAGS" -Wl,-gc-sections"
-else
- # In darwin libbfd has to be built with some *linux* target or it won't understand ELF
- EXTRA_CONFIG="-target=arm-linux-androideabi"
-fi
-
-if [ "$MINGW" = "yes" ]; then
- LDFLAGS=$LDFLAGS" -static"
-fi
-
-NAME=$(get_host_exec_name ndk-depends)
-INSTALL_SUBDIR=host-tools/bin
-OUT=$BUILD_DIR/$NAME
-
-# GNU Make
-if [ -z "$GNUMAKE" ]; then
- GNUMAKE=make
- log "Auto-config: --make=$GNUMAKE"
-fi
-
-if [ "$PACKAGE_DIR" ]; then
- mkdir -p "$PACKAGE_DIR"
- fail_panic "Could not create package directory: $PACKAGE_DIR"
-fi
-
-# Create output directory
-mkdir -p $(dirname $OUT)
-if [ $? != 0 ]; then
- echo "ERROR: Could not create output directory: $(dirname $OUT)"
- exit 1
-fi
-
-SRCDIR=$ANDROID_NDK_ROOT/sources/host-tools/ndk-depends
-
-export CFLAGS LDFLAGS
-run $GNUMAKE -C $SRCDIR -f $SRCDIR/GNUmakefile \
- -B -j$NUM_JOBS \
- PROGNAME="$OUT" \
- BUILD_DIR="$BUILD_DIR" \
- CC="$CC" CXX="$CXX" \
- STRIP="$STRIP" \
- DEBUG=$DEBUG
-
-if [ $? != 0 ]; then
- echo "ERROR: Could not build host program!"
- exit 1
-fi
-
-log "Done!"
-exit 0
diff --git a/sources/host-tools/ndk-depends/build.py b/sources/host-tools/ndk-depends/build.py
deleted file mode 100755
index 65dc54711..000000000
--- a/sources/host-tools/ndk-depends/build.py
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2015 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.
-#
-"""Builds ndk-depends."""
-from __future__ import print_function
-
-import os
-import site
-
-site.addsitedir(os.path.join(os.path.dirname(__file__), '../../../build/lib'))
-
-import build_support # pylint: disable=import-error
-
-
-def main(args):
- src_dir_arg = '--src-dir={}'.format(build_support.toolchain_path())
-
- build_cmd = [
- 'bash', 'build-ndk-depends.sh', src_dir_arg,
- ]
-
- if args.host in ('windows', 'windows64'):
- build_cmd.append('--mingw')
-
- if args.host != 'windows':
- build_cmd.append('--try-64')
-
- build_cmd.append(
- '--build-dir=' + os.path.join(args.out_dir, 'ndk-depends'))
-
- build_support.build(build_cmd, args)
-
-if __name__ == '__main__':
- build_support.run(main)
diff --git a/sources/host-tools/ndk-depends/ndk-depends.cc b/sources/host-tools/ndk-depends/ndk-depends.cc
deleted file mode 100644
index 803b4a2f0..000000000
--- a/sources/host-tools/ndk-depends/ndk-depends.cc
+++ /dev/null
@@ -1,1465 +0,0 @@
-//
-// Copyright (C) 2013 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.
-//
-
-// A small portable program used to dump the dynamic dependencies of a
-// shared library. Requirements:
-// - Must support both 32-bit and 64-bit ELF binaries.
-// - Must support both little and big endian binaries.
-// - Must be compiled as a Unicode program on Windows.
-// - Follows Chromium coding-style guide.
-// - Single source file to make it easier to build anywhere.
-
-//
-// Work-around Windows Unicode support.
-//
-
-// Enable Windows Unicode support by default. Override this by
-// setting WINDOWS_UNICODE at build time.
-#if !defined(WINDOWS_UNICODE) && defined(_WIN32)
-#define WINDOWS_UNICODE 1
-#endif
-
-#ifdef _WIN32
-#undef UNICODE
-#undef _UNICODE
-#ifdef WINDOWS_UNICODE
-#define UNICODE 1
-#define _UNICODE 1
-#endif
-#include <windows.h>
-#include <tchar.h>
-#else
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#endif
-
-#include <string>
-
-// Define String as a typedef for either std::string or std::wstring
-// depending on the platform.
-#if WINDOWS_UNICODE
-typedef std::wstring String;
-#else
-typedef std::string String;
-#endif
-
-// Use the following functions instead of their standard library equivalent.
-#if !WINDOWS_UNICODE
-#define TCHAR char
-#define _T(x) x
-#define _tgetenv getenv
-#define _tcslen strlen
-#define _tcschr strchr
-#define _tcscmp strcmp
-#define _tcsncmp strncmp
-#define _tfopen fopen
-#define _tprintf printf
-#define _vftprintf vfprintf
-#define _ftprintf fprintf
-#define _tstat stat
-#define _vtprintf vprintf
-#define _stat stat
-#endif
-
-// Use TO_STRING(xxx) to convert a C-string into the equivalent String.
-#if WINDOWS_UNICODE == 1
-static inline std::wstring __s2ws(const std::string& s) {
- int s_len = static_cast<int>(s.length() + 1);
- int len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), s_len, 0, 0);
- std::wstring result(len, L'\0');
- MultiByteToWideChar(CP_ACP, 0, s.c_str(), s_len, &result[0], len);
- return result;
-}
-#define TO_STRING(x) __s2ws(x)
-#else
-#define TO_STRING(x) (x)
-#endif
-
-// Use TO_CONST_TCHAR(xxx) to convert a const char* to a const char/wchar*
-#if WINDOWS_UNICODE == 1
-#define TO_CONST_TCHAR(x) TO_STRING(x).c_str()
-#else
-#define TO_CONST_TCHAR(x) (x)
-#endif
-
-
-//
-// Start the real program now
-//
-
-#include <errno.h>
-#ifdef __linux__
-#include <glob.h>
-#endif
-#include <limits.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/stat.h>
-
-#include <algorithm>
-#include <list>
-#include <map>
-#include <string>
-#include <vector>
-
-namespace {
-
-// Utility functions.
-
-enum {
- MESSAGE_FLAG_PANIC = (1 << 0),
- MESSAGE_FLAG_ERRNO = (1 << 1),
-};
-
-void vmessage(int flags, const TCHAR* fmt, va_list args) {
- int old_errno = errno;
- if (flags & MESSAGE_FLAG_PANIC)
- fprintf(stderr, "ERROR: ");
-
- _vftprintf(stderr, fmt, args);
- if (flags & MESSAGE_FLAG_ERRNO) {
- fprintf(stderr, ": %s", strerror(old_errno));
- }
- fprintf(stderr, "\n");
-
- if (flags & MESSAGE_FLAG_PANIC)
- exit(1);
-
- errno = old_errno;
-}
-
-void panic(const TCHAR* fmt, ...) {
- va_list args;
- va_start(args, fmt);
- vmessage(MESSAGE_FLAG_PANIC, fmt, args);
- va_end(args);
-}
-
-int g_verbose = 0;
-
-void log_n(int n, const TCHAR* fmt, ...) {
- if (g_verbose >= n) {
- va_list args;
- va_start(args, fmt);
- _vtprintf(fmt, args);
- va_end(args);
- }
-}
-
-#define LOG_N(level,...) \
- ({ if (g_verbose >= (level)) log_n((level), __VA_ARGS__); })
-
-#define LOG(...) LOG_N(1,__VA_ARGS__)
-#define LOG2(...) LOG_N(2,__VA_ARGS__)
-
-#ifndef DEBUG
-#define DEBUG 0
-#endif
-
-#if DEBUG
-#define DLOG(...) _tprintf(__VA_ARGS__)
-#else
-#define DLOG(...) ((void)0)
-#endif
-
-// Path utilites
-
-// Return the position of the last directory separator in a path,
-// or std::string::npos if none is found.
-size_t path_last_dirsep(const String& filepath) {
-#ifdef _WIN32
- size_t sep_slash = filepath.rfind(_T('/'));
- size_t sep_backslash = filepath.rfind(_T('\\'));
- size_t sep;
- if (sep_slash == std::string::npos)
- sep = sep_backslash;
- else if (sep_backslash == std::string::npos)
- sep = sep_slash;
- else
- sep = std::max(sep_slash, sep_backslash);
-#else
- size_t sep = filepath.rfind(_T('/'));
-#endif
- return sep;
-}
-
-// Return the directory name of a given path.
-String path_dirname(const String& filepath) {
- size_t sep = path_last_dirsep(filepath);
- if (sep == std::string::npos)
- return String(_T("."));
- else if (sep == 0)
- return String(_T("/"));
- else
- return filepath.substr(0, sep);
-}
-
-// Return the basename of a given path.
-String path_basename(const String& filepath) {
- size_t sep = path_last_dirsep(filepath);
- if (sep == std::string::npos)
- return filepath;
- else
- return filepath.substr(sep + 1);
-}
-
-
-// Reading utilities.
-
-uint16_t get_u16_le(const uint8_t* bytes) {
- return static_cast<uint16_t>(bytes[0] | (bytes[1] << 8));
-}
-
-uint32_t get_u32_le(const uint8_t* bytes) {
- return static_cast<uint32_t>(
- bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24));
-}
-
-uint64_t get_u64_le(const uint8_t* bytes) {
- uint64_t lo = static_cast<uint64_t>(get_u32_le(bytes));
- uint64_t hi = static_cast<uint64_t>(get_u32_le(bytes + 4));
- return lo | (hi << 32);
-}
-
-uint16_t get_u16_be(const uint8_t* bytes) {
- return static_cast<uint16_t>((bytes[0] << 8) | bytes[1]);
-}
-
-uint32_t get_u32_be(const uint8_t* bytes) {
- return static_cast<uint32_t>(
- (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]);
-}
-
-uint64_t get_u64_be(const uint8_t* bytes) {
- uint64_t hi = static_cast<uint64_t>(get_u32_be(bytes));
- uint64_t lo = static_cast<uint64_t>(get_u32_be(bytes + 4));
- return lo | (hi << 32);
-}
-
-// FileReader utility classes
-
-class Reader {
-public:
- Reader() {}
-
- virtual const uint8_t* GetBytesAt(off_t pos, size_t size) = 0;
- virtual uint16_t GetU16At(off_t pos) = 0;
- virtual uint32_t GetU32At(off_t pos) = 0;
- virtual uint64_t GetU64At(off_t pos) = 0;
-
- virtual ~Reader() {}
-};
-
-class FileReader : public Reader {
-public:
- FileReader(FILE* file) : file_(file) {}
-
- virtual const uint8_t* GetBytesAt(off_t pos, size_t size) {
- if (size < kMaxBytes &&
- fseek(file_, pos, SEEK_SET) == 0 &&
- fread(buffer_, size, 1, file_) == 1) {
- return buffer_;
- } else
- return NULL;
- }
-
-private:
- static const size_t kMaxBytes = 32;
- FILE* file_;
- uint8_t buffer_[kMaxBytes];
-};
-
-class FileLittleEndianReader : public FileReader {
-public:
- FileLittleEndianReader(FILE* file) : FileReader(file) {}
-
- virtual uint16_t GetU16At(off_t pos) {
- const uint8_t* buf = GetBytesAt(pos, 2);
- return (buf != NULL) ? get_u16_le(buf) : 0;
- }
-
- virtual uint32_t GetU32At(off_t pos) {
- const uint8_t* buf = GetBytesAt(pos, 4);
- return (buf != NULL) ? get_u32_le(buf) : 0;
- }
-
- virtual uint64_t GetU64At(off_t pos) {
- const uint8_t* buf = GetBytesAt(pos, 8);
- return (buf != NULL) ? get_u64_le(buf) : 0ULL;
- }
-};
-
-class FileBigEndianReader : public FileReader {
-public:
- FileBigEndianReader(FILE* file) : FileReader(file) {}
-
- virtual uint16_t GetU16At(off_t pos) {
- const uint8_t* buf = GetBytesAt(pos, 2);
- return (buf != NULL) ? get_u16_be(buf) : 0;
- }
-
- virtual uint32_t GetU32At(off_t pos) {
- const uint8_t* buf = GetBytesAt(pos, 4);
- return (buf != NULL) ? get_u32_be(buf) : 0;
- }
-
- virtual uint64_t GetU64At(off_t pos) {
- const uint8_t* buf = GetBytesAt(pos, 8);
- return (buf != NULL) ? get_u64_be(buf) : 0ULL;
- }
-};
-
-// ELF utility functions.
-
-// The first 16 common bytes.
-#define EI_NIDENT 16
-
-#define EI_CLASS 4
-#define ELFCLASS32 1
-#define ELFCLASS64 2
-
-#define EI_DATA 5
-#define ELFDATA2LSB 1
-#define ELFDATA2MSB 2
-
-bool elf_ident_is_elf(const uint8_t* ident) {
- return ident[0] == 0x7f &&
- ident[1] == 'E' &&
- ident[2] == 'L' &&
- ident[3] == 'F';
-}
-
-bool elf_ident_is_big_endian(const uint8_t* ident) {
- return ident[EI_DATA] == ELFDATA2MSB;
-}
-
-bool elf_ident_is_64bits(const uint8_t* ident) {
- return ident[EI_CLASS] == ELFCLASS64;
-}
-
-#define SHT_STRTAB 3
-#define SHT_DYNAMIC 6
-
-// 32-bit ELF definitions.
-
-class Elf32 {
-public:
- typedef uint16_t Half;
- typedef uint32_t Word;
- typedef uint32_t Off;
- typedef uint32_t Addr;
- typedef int32_t Sword;
-
- struct Header {
- uint8_t e_ident[EI_NIDENT];
- Half e_type;
- Half e_machine;
- Word e_version;
- Addr e_entry;
- Off e_phoff;
- Off e_shoff;
- Word e_flags;
- Half e_shsize;
- Half e_phentsize;
- Half e_phnum;
- Half e_shentsize;
- Half e_shnum;
- Half e_shstrndx;
- };
-
- struct Shdr {
- Word sh_name;
- Word sh_type;
- Word sh_flags;
- Addr sh_addr;
- Off sh_offset;
- Word sh_size;
- Word sh_link;
- Word sh_info;
- Word sh_addralign;
- Word sh_entsize;
- };
-};
-
-class Elf64 {
-public:
- typedef uint16_t Half;
- typedef uint64_t Off;
- typedef uint64_t Addr;
- typedef int32_t Sword;
- typedef uint32_t Word;
- typedef uint64_t Xword;
- typedef int64_t Sxword;
-
- struct Header {
- uint8_t e_ident[EI_NIDENT];
- Half e_type;
- Half e_machine;
- Word e_version;
- Addr e_entry;
- Off e_phoff;
- Off e_shoff;
- Word e_flags;
- Half e_shsize;
- Half e_phentsize;
- Half e_phnum;
- Half e_shentsize;
- Half e_shnum;
- Half e_shstrndx;
- };
-
- struct Shdr {
- Word sh_name;
- Word sh_type;
- Xword sh_flags;
- Addr sh_addr;
- Off sh_offset;
- Xword sh_size;
- Word sh_link;
- Word sh_info;
- Xword sh_addralign;
- Xword sh_entsize;
- };
-};
-
-template <class ELF>
-class ElfParser {
-public:
- ElfParser(Reader& reader) : reader_(reader) {}
-
- typedef typename ELF::Word Word;
- typedef typename ELF::Sword Sword;
- typedef typename ELF::Addr Addr;
- typedef typename ELF::Off Off;
- typedef typename ELF::Half Half;
- typedef typename ELF::Header ElfHeader;
- typedef typename ELF::Shdr Shdr;
-
- // Read an ELF::Word at a given position.
- Word GetWordAt(off_t pos) {
- return reader_.GetU32At(pos);
- }
-
- // Read an ELF::Half at a given position.
- Half GetHalfAt(off_t pos) {
- return reader_.GetU16At(pos);
- }
-
- // Read an ELF::Sword at a given position.
- Sword GetSwordAt(off_t pos) {
- return static_cast<Sword>(GetWordAt(pos));
- }
-
- // Read an ELF::Addr at a given position.
- Addr GetAddrAt(off_t pos);
-
- // Read an ELF::Off at a given position.
- Off GetOffAt(off_t pos) {
- return static_cast<Off>(GetAddrAt(pos));
- }
-
- // Helper class to iterate over the section table.
- class SectionIterator {
- public:
- explicit SectionIterator(ElfParser& parser)
- : parser_(parser) {
- table_offset_ = parser_.GetOffAt(offsetof(ElfHeader, e_shoff));
- table_count_ = parser_.GetHalfAt(offsetof(ElfHeader, e_shnum));
- table_entry_size_ = parser_.GetHalfAt(offsetof(ElfHeader, e_shentsize));
- if (table_entry_size_ < static_cast<Half>(sizeof(Shdr))) {
- // malformed binary. Ignore all sections.
- table_count_ = 0;
- }
- }
-
- Off NextOffset() {
- if (table_count_ == 0)
- return 0;
- Off result = table_offset_;
- table_offset_ += table_entry_size_;
- table_count_ -= 1;
- return result;
- }
-
- void Skip(int count) {
- while (count > 0 && table_count_ > 0) {
- table_offset_ += table_entry_size_;
- table_count_--;
- count--;
- }
- }
-
- private:
- ElfParser& parser_;
- Off table_offset_;
- Half table_count_;
- Half table_entry_size_;
- };
-
- // Return the offset of the first section of a given type, or 0 if not
- // found. |*size| will be set to the section size in bytes in case of
- // success.
- Off GetSectionOffsetByType(int table_type, Off* size) {
- SectionIterator iter(*this);
- for (;;) {
- Off table_offset = iter.NextOffset();
- if (table_offset == 0)
- break;
- Word sh_type = GetWordAt(table_offset + offsetof(Shdr, sh_type));
- if (sh_type == static_cast<Word>(table_type)) {
- *size = GetOffAt(table_offset + offsetof(Shdr, sh_size));
- return GetOffAt(table_offset + offsetof(Shdr, sh_offset));
- }
- }
- return 0;
- }
-
- // Return the index of the string table for the dynamic section
- // in this ELF binary. Or 0 if not found.
- int GetDynamicStringTableIndex() {
- SectionIterator iter(*this);
- for (;;) {
- Off table_offset = iter.NextOffset();
- if (table_offset == 0)
- break;
- Word sh_type = GetWordAt(table_offset + offsetof(Shdr, sh_type));
- if (sh_type == SHT_DYNAMIC)
- return GetWordAt(table_offset + offsetof(Shdr, sh_link));
- }
- return 0;
- }
-
- // Return the offset of a section identified by its index, or 0 in case
- // of error (bad index).
- Off GetSectionOffsetByIndex(int sec_index, Off* size) {
- SectionIterator iter(*this);
- iter.Skip(sec_index);
- Off table_offset = iter.NextOffset();
- if (table_offset != 0) {
- *size = GetOffAt(table_offset + offsetof(Shdr, sh_size));
- return GetOffAt(table_offset + offsetof(Shdr, sh_offset));
- }
- return 0;
- }
-
- // Return a string identified by its index and its string table
- // Address. Returns an empty string in case of error.
- String GetStringByIndex(Off str_index, int str_table_index) {
- String result;
-
- if (str_table_index != 0) {
- Off str_table_size = 0;
- Off str_table = GetSectionOffsetByIndex(str_table_index,
- &str_table_size);
- if (str_table != 0 && str_index < str_table_size) {
- str_table += str_index;
- str_table_size -= str_index;
- while (str_table_size > 0) {
- const uint8_t* p = reader_.GetBytesAt(str_table, 1);
- if (p == NULL || *p == '\0')
- break;
- result.append(1, static_cast<const char>(*p));
- str_table += 1;
- str_table_size -= 1;
- }
- }
- }
- return result;
- }
-
-private:
- Reader& reader_;
-};
-
-template <>
-Elf32::Addr ElfParser<Elf32>::GetAddrAt(off_t pos) {
- return reader_.GetU32At(pos);
-}
-
-template <>
-Elf64::Addr ElfParser<Elf64>::GetAddrAt(off_t pos) {
- return reader_.GetU64At(pos);
-}
-
-// Helper class to iterate over items of a given type in the dynamic
-// section. A type of 0 (SHT_NULL) means iterate over all items.
-//
-// Examples:
-// // Iterate over all entries in the table, find the SHT_NEEDED ones.
-// DynamicIterator<Elf32> iter(parser);
-// while (iter.GetNext()) {
-// if (iter.GetTag() == SHT_NEEDED) {
-// Elf32::Off value = iter.GetValue();
-// ...
-// }
-// }
-template <class ELF>
-class DynamicIterator {
-public:
- explicit DynamicIterator(ElfParser<ELF>& parser)
- : parser_(parser),
- dyn_size_(0),
- dyn_offset_(0),
- started_(false) {
- dyn_offset_ = parser_.GetSectionOffsetByType(SHT_DYNAMIC, &dyn_size_);
- started_ = (dyn_size_ < kEntrySize);
- }
-
- bool GetNext() {
- if (!started_)
- started_ = true;
- else {
- if (dyn_size_ < kEntrySize)
- return false;
- dyn_offset_ += kEntrySize;
- dyn_size_ -= kEntrySize;
- }
- return true;
- }
-
- typename ELF::Off GetTag() {
- return parser_.GetOffAt(dyn_offset_);
- }
-
- typename ELF::Off GetValue() {
- return parser_.GetOffAt(dyn_offset_ + kTagSize);
- }
-
-private:
- typedef typename ELF::Off Off;
- static const Off kTagSize = static_cast<Off>(sizeof(Off));
- static const Off kValueSize = kTagSize;
- static const Off kEntrySize = kTagSize + kValueSize;
-
- ElfParser<ELF>& parser_;
- Off dyn_size_;
- Off dyn_offset_;
- bool started_;
-};
-
-#define DT_NEEDED 1
-#define DT_SONAME 14
-
-template <class ELF>
-String GetLibNameT(Reader& reader) {
- ElfParser<ELF> parser(reader);
- int str_table_index = parser.GetDynamicStringTableIndex();
- DynamicIterator<ELF> iter(parser);
- while (iter.GetNext()) {
- if (iter.GetTag() == DT_SONAME) {
- typename ELF::Off str_index = iter.GetValue();
- return parser.GetStringByIndex(str_index, str_table_index);
- }
- }
- return String();
-}
-
-template <class ELF>
-int GetNeededLibsT(Reader& reader, std::vector<String>* result) {
- ElfParser<ELF> parser(reader);
- int str_table_index = parser.GetDynamicStringTableIndex();
- DynamicIterator<ELF> iter(parser);
- int count = 0;
- while (iter.GetNext()) {
- if (iter.GetTag() == DT_NEEDED) {
- typename ELF::Off str_index = iter.GetValue();
- String lib_name = parser.GetStringByIndex(str_index, str_table_index);
- if (!lib_name.empty()) {
- result->push_back(lib_name);
- count++;
- }
- }
- }
- return count;
-}
-
-class ElfFile {
-public:
- ElfFile()
- : file_(NULL), big_endian_(false), is_64bits_(false), reader_(NULL) {}
-
- virtual ~ElfFile() {
- delete reader_;
- Close();
- }
-
- bool Open(const TCHAR* path, String* error) {
- Close();
- file_ = _tfopen(path, _T("rb"));
- if (file_ == NULL) {
- error->assign(TO_STRING(strerror(errno)));
- return false;
- }
- uint8_t ident[EI_NIDENT];
- if (fread(ident, sizeof(ident), 1, file_) != 1) {
- error->assign(TO_STRING(strerror(errno)));
- Close();
- return false;
- }
- if (!elf_ident_is_elf(ident)) {
- *error = _T("Not an ELF binary file");
- Close();
- return false;
- }
- big_endian_ = elf_ident_is_big_endian(ident);
- is_64bits_ = elf_ident_is_64bits(ident);
-
- if (big_endian_) {
- reader_ = new FileBigEndianReader(file_);
- } else {
- reader_ = new FileLittleEndianReader(file_);
- }
- return true;
- }
-
- bool IsOk() { return file_ != NULL; }
-
- bool IsBigEndian() { return big_endian_; }
-
- const Reader& GetReader() { return *reader_; };
-
- // Returns the embedded library name, extracted from the dynamic table.
- String GetLibName() {
- if (is_64bits_)
- return GetLibNameT<Elf64>(*reader_);
- else
- return GetLibNameT<Elf32>(*reader_);
- }
-
- // Gets the list of needed libraries and appends them to |result|.
- // Returns the number of library names appended.
- int GetNeededLibs(std::vector<String>* result) {
- if (is_64bits_)
- return GetNeededLibsT<Elf64>(*reader_, result);
- else
- return GetNeededLibsT<Elf32>(*reader_, result);
- }
-
-protected:
- void Close() {
- if (file_ != NULL) {
- fclose(file_);
- file_ = NULL;
- }
- }
-
- FILE* file_;
- bool big_endian_;
- bool is_64bits_;
- Reader* reader_;
-};
-
-#ifdef __linux__
-static bool IsLdSoConfSeparator(char ch) {
- // The ldconfig manpage indicates that /etc/ld.so.conf contains a list
- // of colon, space, tab newline or comma separated directories.
- return (ch == ' ' || ch == '\t' || ch == '\r' ||
- ch == '\n' || ch == ',' || ch == ':');
-}
-
-// Parse the content of /etc/ld.so.conf, it contains according to the
-// documentation a 'comma, space, newline, tab separated list of
-// directories'. In practice, it can also include comments, and an
-// include directive and glob patterns, as in:
-// 'include /etc/ld.so.conf.d/*.conf'
-void AddHostLdSoConfPaths(const char* ld_so_conf_path,
- std::vector<String>* lib_search_path) {
- FILE* file = fopen(ld_so_conf_path, "rb");
- if (!file)
- return;
-
- char line[1024];
- while (fgets(line, sizeof(line), file) != NULL) {
- const char* begin = line;
- const char* end = line + strlen(line);
- while (end > begin && end[-1] == '\n')
- end--;
-
- bool prev_is_include = false;
- while (begin < end) {
- // Skip over separators
- while (begin < end && IsLdSoConfSeparator(*begin))
- begin++;
-
- if (begin == end || begin[0] == '#') {
- // Skip empty lines and comments.
- break;
- }
- // Find end of current item
- const char* next_pos = begin;
- while (next_pos < end &&
- !IsLdSoConfSeparator(*next_pos) &&
- *next_pos != '#')
- next_pos++;
-
- size_t len = static_cast<size_t>(next_pos - begin);
- if (prev_is_include) {
- // If previous token was an 'include', treat this as a glob
- // pattern and try to process all matching files.
- prev_is_include = false;
- if (len == 0) {
- // Ignore stand-alone 'include' in a single line.
- break;
- }
- String pattern(begin, len);
- DLOG("%s: processing include '%s'\n",
- __FUNCTION__,
- pattern.c_str());
-
- glob_t the_glob;
- memset(&the_glob, 0, sizeof(the_glob));
- int ret = ::glob(pattern.c_str(), 0, NULL, &the_glob);
- if (ret == 0) {
- // Iterate / include all matching files.
- String filepath;
- for (size_t n = 0; n < the_glob.gl_pathc; ++n) {
- filepath.assign(the_glob.gl_pathv[n]);
- DLOG("%s: Including %s\n", __FUNCTION__, filepath.c_str());
- AddHostLdSoConfPaths(filepath.c_str(), lib_search_path);
- }
- }
- globfree(&the_glob);
- } else {
- // The previous token was not an 'include'. But is the current one?
- static const char kInclude[] = "include";
- const size_t kIncludeLen = sizeof(kInclude) - 1;
- if (len == kIncludeLen && !memcmp(begin, kInclude, len)) {
- prev_is_include = true;
- } else if (len > 0) {
- // No, it must be a directory name.
- String dirpath(begin, len);
- struct stat st;
- if (::stat(dirpath.c_str(), &st) != 0) {
- LOG("Could not stat(): %s: %s\n", dirpath.c_str(), strerror(errno));
- } else if (!S_ISDIR(st.st_mode)) {
- LOG("Not a directory: %s\n", dirpath.c_str());
- } else {
- DLOG("%s: Adding %s\n", __FUNCTION__, dirpath.c_str());
- lib_search_path->push_back(dirpath);
- }
- }
- }
- // switch to next item in line.
- begin = next_pos;
- }
- }
- fclose(file);
-}
-#endif // __linux__
-
-// Add host shared library search path to |lib_search_path|
-void AddHostLibraryPaths(std::vector<String>* lib_search_path) {
- // Only add libraries form LD_LIBRARY_PATH on ELF-based systems.
-#if defined(__ELF__)
- // If LD_LIBRARY_PATH is defined, process it
- const TCHAR* env = _tgetenv(_T("LD_LIBRARY_PATH"));
- if (env != NULL) {
- const TCHAR* pos = env;
- while (*pos) {
- size_t path_len;
- const TCHAR* next_pos = _tcschr(pos, ':');
- if (next_pos == NULL) {
- path_len = _tcslen(pos);
- next_pos = pos + path_len;
- } else {
- path_len = next_pos - pos;
- next_pos += 1;
- }
-
- if (path_len == 0) {
- // Per POSIX convention, an empty path item means "current path".
- // Not that this is generally a very bad idea, security-wise.
- lib_search_path->push_back(_T("."));
- } else {
- lib_search_path->push_back(String(pos, path_len));
- }
-
- pos = next_pos;
- }
- }
-#ifdef __linux__
- AddHostLdSoConfPaths("/etc/ld.so.conf", lib_search_path);
-#endif
- // TODO(digit): What about BSD systems?
-#endif
-}
-
-// Returns true if |libname| is the name of an Android system library.
-bool IsAndroidSystemLib(const String& libname) {
- static const TCHAR* const kAndroidSystemLibs[] = {
- _T("libc.so"),
- _T("libdl.so"),
- _T("liblog.so"),
- _T("libm.so"),
- _T("libstdc++.so"),
- _T("libz.so"),
- _T("libandroid.so"),
- _T("libjnigraphics.so"),
- _T("libEGL.so"),
- _T("libGLESv1_CM.so"),
- _T("libGLESv2.so"),
- _T("libOpenSLES.so"),
- _T("libOpenMAXAL.so"),
- NULL
- };
- for (size_t n = 0; kAndroidSystemLibs[n] != NULL; ++n) {
- if (!libname.compare(kAndroidSystemLibs[n]))
- return true;
- }
- return false;
-}
-
-// Returns true if |libname| is the name of an NDK-compatible shared
-// library. This means its must begin with "lib" and end with "so"
-// (without any version numbers).
-bool IsAndroidNdkCompatibleLib(const String& libname) {
- return libname.size() > 6 &&
- !libname.compare(0, 3, _T("lib")) &&
- !libname.compare(libname.size() - 3, 3, _T(".so"));
-}
-
-// Try to find a library named |libname| in |search_paths|
-// Returns true on success, and sets |result| to the full library path,
-// false otherwise.
-bool FindLibraryPath(const String& libname,
- const std::vector<String>& search_paths,
- String* result) {
- // Check in the search paths.
- LOG2(_T(" looking for library: %s\n"), libname.c_str());
- for (size_t n = 0; n < search_paths.size(); ++n) {
- String file_path = search_paths[n];
- if (file_path.empty())
- continue;
- if (file_path[file_path.size() - 1] != _T('/') &&
- file_path[file_path.size() - 1] != _T('\\')) {
- file_path.append(_T("/"));
- }
- file_path.append(libname);
-
- LOG2(_T(" in %s: "), file_path.c_str());
- struct _stat st;
- if (_tstat(file_path.c_str(), &st) < 0) {
- LOG2(_T("%s\n"), TO_CONST_TCHAR(strerror(errno)));
- continue;
- }
- if (!S_ISREG(st.st_mode)) {
- LOG2(_T("Not a regular file!\n"));
- continue;
- }
- // Found the library file.
- LOG2(_T("OK\n"));
- result->assign(file_path);
- return true;
- }
-
- return false;
-}
-
-// Recursive support
-
-struct LibNode {
- // An enumeration listing possible node types, which are:
- enum Type {
- NODE_NONE, // No type yet.
- NODE_PATH, // Valid ELF library, |value| is file path.
- NODE_ERROR, // Invalid library name, |value| is error string.
- NODE_SYSTEM, // Android system library, |value| is library name.
- };
-
- Type type;
- String value;
- std::vector<String> needed_libs;
-
- LibNode() : type(NODE_NONE), value(), needed_libs() {}
-
- explicit LibNode(const String& path)
- : type(NODE_PATH), value(path), needed_libs() {}
-
- void Set(Type type_p, const String& value_p) {
- type = type_p;
- value = value_p;
- }
-};
-
-typedef std::map<String, LibNode> DependencyGraph;
-typedef std::list<String> WorkQueue;
-
-// Used internally by BuildDependencyGraph().
-void UpdateDependencies(
- const String& libname,
- const String& libpath,
- DependencyGraph& deps,
- WorkQueue& queue) {
- DLOG(_T("UPDATE libname=%s path=%s\n"), libname.c_str(), libpath.c_str());
- // Sanity check: find if the library is already in the graph.
- if (!libname.empty() && deps.find(libname) != deps.end()) {
- // Should not happen.
- panic(_T("INTERNAL: Library already in graph: %s"), libname.c_str());
- }
-
- LibNode node;
- ElfFile libfile;
- String error;
- String soname = libname;
- if (!libfile.Open(libpath.c_str(), &error)) {
- node.Set(LibNode::NODE_ERROR, error);
- } else {
- String soname = libfile.GetLibName();
- if (soname.empty())
- soname = libname;
- else if (soname != libname) {
- _ftprintf(stderr,
- _T("WARNING: Library has invalid soname ('%s'): %s\n"),
- soname.c_str(),
- libpath.c_str());
- }
- // Discovered a new library, get its dependent libraries.
- node.Set(LibNode::NODE_PATH, libpath);
- libfile.GetNeededLibs(&node.needed_libs);
-
- LOG(_T("%s depends on:"), soname.c_str());
-
- // Add them to the work queue.
- for (size_t n = 0; n < node.needed_libs.size(); ++n) {
- LOG(_T(" %s"), node.needed_libs[n].c_str());
- queue.push_back(node.needed_libs[n]);
- }
- LOG(_T("\n"));
- }
- deps[soname] = node;
-}
-
-// Build the full dependency graph.
-// |root_libpath| is the path of the root library.
-// |lib_search_path| is the list of library search paths.
-// Returns a new dependency graph object.
-DependencyGraph BuildDependencyGraph(
- const String& root_libpath,
- const std::vector<String>& lib_search_path) {
- DependencyGraph deps;
- std::list<String> queue;
-
- // As a first step, build the full dependency graph, starting with the
- // root library. This records errors in the graph too.
- UpdateDependencies(path_basename(root_libpath),
- root_libpath,
- deps, queue);
-
- while (!queue.empty()) {
- // Pop first item from queue.
- String libname = queue.front();
- queue.pop_front();
-
- // Is the library already in the graph?
- DependencyGraph::iterator iter = deps.find(libname);
- if (iter != deps.end()) {
- // Library already found, skip it.
- continue;
- }
-
- // Find the library in the current search path.
- String libpath;
- if (FindLibraryPath(libname, lib_search_path, &libpath)) {
- UpdateDependencies(libname, libpath, deps, queue);
- continue;
- }
-
- if (IsAndroidSystemLib(libname)) {
- LOG(_T("Android system library: %s\n"), libname.c_str());
- LibNode node;
- node.Set(LibNode::NODE_SYSTEM, libname);
- deps[libname] = node;
- continue;
- }
-
- _ftprintf(stderr,
- _T("WARNING: Could not find library: %s\n"),
- libname.c_str());
- LibNode node;
- node.Set(LibNode::NODE_ERROR, _T("Could not find library"));
- deps[libname] = node;
- }
-
- return deps;
-}
-
-// Print the dependency graph in a human-readable format to stdout.
-void DumpDependencyGraph(const DependencyGraph& deps) {
- _tprintf(_T("Dependency graph:\n"));
- DependencyGraph::const_iterator iter = deps.begin();
- for ( ; iter != deps.end(); ++iter ) {
- const String& libname = iter->first;
- const LibNode& node = iter->second;
- String node_type;
- switch (node.type) {
- case LibNode::NODE_NONE: // should not happen.
- node_type = _T("NONE??");
- break;
- case LibNode::NODE_PATH:
- node_type = _T("PATH");
- break;
- case LibNode::NODE_ERROR:
- node_type = _T("ERROR");
- break;
- case LibNode::NODE_SYSTEM:
- node_type = _T("SYSTEM");
- }
- _tprintf(
- _T("[%s] %s %s\n"),
- libname.c_str(),
- node_type.c_str(),
- node.value.c_str());
-
- if (node.type == LibNode::NODE_PATH) {
- for (size_t n = 0; n < node.needed_libs.size(); ++n) {
- _tprintf(_T(" %s\n"), node.needed_libs[n].c_str());
- }
- }
- }
-}
-
-// Return the sorted list of libraries from a dependency graph.
-// They are topologically ordered, i.e. a library appears always
-// before any other library it depends on.
-void GetTopologicalSortedLibraries(
- DependencyGraph& deps,
- std::vector<String>* result) {
- result->clear();
- // First: Compute the number of visitors per library in the graph.
- typedef std::map<String, int> VisitorMap;
- VisitorMap visitors;
- for (DependencyGraph::const_iterator iter = deps.begin();
- iter != deps.end();
- ++iter) {
- if (visitors.find(iter->first) == visitors.end()) {
- visitors[iter->first] = 0;
- }
-
- const std::vector<String>& needed_libs = iter->second.needed_libs;
- for (size_t n = 0; n < needed_libs.size(); ++n) {
- const String& libname = needed_libs[n];
- VisitorMap::iterator lib_iter = visitors.find(libname);
- if (lib_iter != visitors.end())
- lib_iter->second += 1;
- else
- visitors[libname] = 1;
- }
- }
-
-#if DEBUG
- {
- VisitorMap::const_iterator iter_end = visitors.end();
- VisitorMap::const_iterator iter = visitors.begin();
- for ( ; iter != iter_end; ++iter ) {
- _tprintf(_T("-- %s %d\n"), iter->first.c_str(), iter->second);
- }
- }
-#endif
-
- while (!visitors.empty()) {
- // Find the library with the smallest number of visitors.
- // The value should be 0, unless there are circular dependencies.
- VisitorMap::const_iterator iter_end = visitors.end();
- VisitorMap::const_iterator iter;
- int min_visitors = INT_MAX;
- String min_libname;
- for (iter = visitors.begin(); iter != iter_end; ++iter) {
- // Note: Uses <= instead of < to ensure better diagnostics in
- // case of circular dependencies. This shall return the latest
- // node in the cycle, i.e. the first one where a 'back' edge
- // exists.
- if (iter->second <= min_visitors) {
- min_libname = iter->first;
- min_visitors = iter->second;
- }
- }
-
- if (min_visitors == INT_MAX) {
- // Should not happen.
- panic(_T("INTERNAL: Could not find minimum visited node!"));
- }
-
- // min_visitors should be 0, unless there are circular dependencies.
- if (min_visitors != 0) {
- // Warn about circular dependencies
- _ftprintf(stderr,
- _T("WARNING: Circular dependency found from: %s\n"),
- min_libname.c_str());
- }
-
- // Remove minimum node from the graph, and decrement the visitor
- // count of all its needed libraries. This also breaks dependency
- // cycles.
- result->push_back(min_libname);
- const LibNode& node = deps[min_libname];
- const std::vector<String> needed_libs = node.needed_libs;
- visitors.erase(min_libname);
-
- for (size_t n = 0; n < needed_libs.size(); ++n)
- visitors[needed_libs[n]]--;
- }
-}
-
-// Main function
-
-#define PROGNAME "ndk-depends"
-
-void print_usage(int exit_code) {
- printf(
- "Usage: %s [options] <elf-file>\n\n"
-
- "This program is used to print the dependencies of a given ELF\n"
- "binary (shared library or executable). It supports any architecture,\n"
- "endianess and bitness.\n\n"
-
- "By default, all dependencies are printed in topological order,\n"
- "which means that each item always appear before other items\n"
- "it depends on. Except in case of circular dependencies, which will\n"
- "print a warning to stderr.\n\n"
-
- "The tool will try to find other libraries in the same directory\n"
- "as the input ELF file. It is possible however to provide\n"
- "additional search paths with the -L<path>, which adds an explicit path\n"
- "or --host-libs which adds host-specific library paths, on ELF-based systems\n"
- "only.\n\n"
-
- "Use --print-paths to print the path of each ELF binary.\n\n"
-
- "Use --print-direct to only print the direct dependencies\n"
- "of the input ELF binary. All other options except --verbose will be ignored.\n\n"
-
- "Use --print-java to print a Java source fragment that loads the\n"
- "libraries with System.loadLibrary() in the correct order. This can\n"
- "be useful when copied into your application's Java source code.\n\n"
-
- "Use --print-dot to print the dependency graph as a .dot file that can be\n"
- "parsed by the GraphViz tool. For example, to generate a PNG image of the\n"
- "graph, use something like:\n\n"
-
- " ndk-depends /path/to/libfoo.so --print-dot | dot -Tpng -o /tmp/graph.png\n\n"
-
- "The --verbose option prints debugging information, which can be useful\n"
- "to diagnose problems with malformed ELF binaries.\n\n"
-
- "Valid options:\n"
- " --help|-h|-? Print this message.\n"
- " --verbose Increase verbosity.\n"
- " --print-direct Only print direct dependencies.\n"
- " -L<path> Append <path> to the library search path.\n"
- " --host-libs Append host library search path.\n"
- " --print-paths Print full paths of all libraries.\n"
- " --print-java Print Java library load sequence.\n"
- " --print-dot Print the dependency graph as a Graphviz .dot file.\n"
- "\n", PROGNAME);
-
- exit(exit_code);
-}
-
-} // namespace
-
-
-#ifdef _WIN32
-int main(void) {
- int argc = 0;
- TCHAR** argv = CommandLineToArgvW(GetCommandLine(), &argc);
-#else
-int main(int argc, const char** argv) {
-#endif
-
- enum PrintFormat {
- PRINT_DEFAULT = 0,
- PRINT_DIRECT,
- PRINT_PATHS,
- PRINT_JAVA,
- PRINT_DOT_FILE,
- };
-
- bool do_help = false;
- PrintFormat print_format = PRINT_DEFAULT;
- std::vector<String> lib_search_path;
- std::vector<String> params;
-
- // Process options.
- while (argc > 1) {
- if (argv[1][0] == _T('-')) {
- const TCHAR* arg = argv[1];
- if (!_tcscmp(arg, _T("--help")) ||
- !_tcscmp(arg, _T("-h")) ||
- !_tcscmp(arg, _T("-?")))
- do_help = true;
- else if (!_tcscmp(arg, _T("--print-direct"))) {
- print_format = PRINT_DIRECT;
- } else if (!_tcscmp(arg, _T("-L"))) {
- if (argc < 3)
- panic(_T("Option -L requires an argument."));
- lib_search_path.push_back(String(argv[2]));
- argc--;
- argv++;
- } else if (!_tcsncmp(arg, _T("-L"), 2)) {
- lib_search_path.push_back(String(arg+2));
- } else if (!_tcscmp(arg, _T("--host-libs"))) {
- AddHostLibraryPaths(&lib_search_path);
- } else if (!_tcscmp(arg, _T("--print-java"))) {
- print_format = PRINT_JAVA;
- } else if (!_tcscmp(arg, _T("--print-paths"))) {
- print_format = PRINT_PATHS;
- } else if (!_tcscmp(arg, _T("--print-dot"))) {
- print_format = PRINT_DOT_FILE;
- } else if (!_tcscmp(arg, _T("--verbose"))) {
- g_verbose++;
- } else {
- panic(_T("Unsupported option '%s', see --help."), arg);
- }
- } else {
- params.push_back(String(argv[1]));
- }
- argc--;
- argv++;
- }
-
- if (do_help)
- print_usage(0);
-
- if (params.empty())
- panic(_T("Please provide the path of an ELF shared library or executable."
- "\nSee --help for usage details."));
-
- // Insert ELF file directory at the head of the search path.
- lib_search_path.insert(lib_search_path.begin(), path_dirname(params[0]));
-
- if (g_verbose >= 1) {
- _tprintf(_T("Current library search path:\n"));
- for (size_t n = 0; n < lib_search_path.size(); ++n)
- _tprintf(_T(" %s\n"), lib_search_path[n].c_str());
- _tprintf(_T("\n"));
- }
-
- // Open main input file.
- const TCHAR* libfile_path = params[0].c_str();
-
- ElfFile libfile;
- String error;
- if (!libfile.Open(libfile_path, &error)) {
- panic(_T("Could not open file '%s': %s"), libfile_path, error.c_str());
- }
-
- if (print_format == PRINT_DIRECT) {
- // Simple dump, one line per dependency. No frills, no recursion.
- std::vector<String> needed_libs;
- libfile.GetNeededLibs(&needed_libs);
-
- for (size_t i = 0; i < needed_libs.size(); ++i)
- _tprintf(_T("%s\n"), needed_libs[i].c_str());
-
- return 0;
- }
-
- // Topological sort of all dependencies.
- LOG(_T("Building dependency graph...\n"));
- DependencyGraph deps = BuildDependencyGraph(
- libfile_path, lib_search_path);
-
- if (g_verbose >= 2)
- DumpDependencyGraph(deps);
-
- LOG(_T("Building sorted list of binaries:\n"));
- std::vector<String> needed_libs;
- GetTopologicalSortedLibraries(deps, &needed_libs);
-
- if (print_format == PRINT_JAVA) {
- // Print Java libraries in reverse order.
- std::reverse(needed_libs.begin(), needed_libs.end());
- for (size_t i = 0; i < needed_libs.size(); ++i) {
- const String& lib = needed_libs[i];
- if (IsAndroidSystemLib(lib)) {
- // Skip system libraries.
- continue;
- }
- if (!IsAndroidNdkCompatibleLib(lib)) {
- _ftprintf(
- stderr,
- _T("WARNING: Non-compatible library name ignored: %s\n"),
- lib.c_str());
- continue;
- }
- _tprintf(_T("System.loadLibrary(%s);\n"),
- lib.substr(3, lib.size() - 6).c_str());
- }
- return 0;
- }
-
- if (print_format == PRINT_DOT_FILE) {
- // Using the topological order helps generates a more human-friendly
- // directed graph.
- _tprintf(_T("digraph {\n"));
- for (size_t i = 0; i < needed_libs.size(); ++i) {
- const String& libname = needed_libs[i];
- const std::vector<String>& libdeps = deps[libname].needed_libs;
- for (size_t n = 0; n < libdeps.size(); ++n) {
- // Note: Use quoting to deal with special characters like -
- // which are not normally part of DOT 'id' tokens.
- _tprintf(_T(" \"%s\" -> \"%s\"\n"), libname.c_str(), libdeps[n].c_str());
- }
- }
- _tprintf(_T("}\n"));
- return 0;
- }
-
- if (print_format == PRINT_PATHS) {
- // Print libraries with path.
- for (size_t i = 0; i < needed_libs.size(); ++i) {
- const String& lib = needed_libs[i];
- LibNode& node = deps[lib];
- const TCHAR* format;
- switch (node.type) {
- case LibNode::NODE_PATH:
- format = _T("%s -> %s\n");
- break;
- case LibNode::NODE_SYSTEM:
- format = _T("%s -> $ /system/lib/%s\n");
- break;
- default:
- format = _T("%s -> !! %s\n");
- }
- _tprintf(format, lib.c_str(), deps[lib].value.c_str());
- }
- return 0;
- }
-
- // Print simple library names.
- for (size_t i = 0; i < needed_libs.size(); ++i) {
- const String& lib = needed_libs[i];
- _tprintf(_T("%s\n"), lib.c_str());
- }
- return 0;
-}