From 418115b3467e12e606e6e48ac3532f583e9f09ac Mon Sep 17 00:00:00 2001 From: Simon Baldwin Date: Thu, 26 Mar 2015 16:47:26 +0000 Subject: [PATCH] Port Android relocation packer to chromium build - Copy Android relocation packer source from AOSP: bionic/tools/relocation_packer/ - Remove golden test data generation scripts (not needed here) - Add a local ScopedFd.h, to avoid any dependency on base - Add gyp and gn build files for chromium - Update third_party/android_platform/README.chromium BUG=385553 R=rmcilroy@chromium.org Review URL: https://codereview.chromium.org/1027823002 Cr-Commit-Position: refs/heads/master@{#322401} --- third_party/android_platform/BUILD.gn | 31 + third_party/android_platform/README.chromium | 10 + .../bionic/tools/relocation_packer/Android.mk | 96 ++ .../bionic/tools/relocation_packer/LICENSE | 27 + .../bionic/tools/relocation_packer/src/debug.cc | 55 ++ .../bionic/tools/relocation_packer/src/debug.h | 115 +++ .../tools/relocation_packer/src/debug_unittest.cc | 122 +++ .../tools/relocation_packer/src/delta_encoder.cc | 307 ++++++ .../tools/relocation_packer/src/delta_encoder.h | 132 +++ .../src/delta_encoder_unittest.cc | 223 +++++ .../bionic/tools/relocation_packer/src/elf_file.cc | 882 +++++++++++++++++ .../bionic/tools/relocation_packer/src/elf_file.h | 155 +++ .../relocation_packer/src/elf_file_unittest.cc | 188 ++++ .../tools/relocation_packer/src/elf_traits.h | 64 ++ .../bionic/tools/relocation_packer/src/leb128.cc | 87 ++ .../bionic/tools/relocation_packer/src/leb128.h | 76 ++ .../tools/relocation_packer/src/leb128_unittest.cc | 111 +++ .../bionic/tools/relocation_packer/src/main.cc | 153 +++ .../relocation_packer/src/nativehelper/ScopedFd.h | 62 ++ .../bionic/tools/relocation_packer/src/packer.cc | 88 ++ .../bionic/tools/relocation_packer/src/packer.h | 74 ++ .../tools/relocation_packer/src/packer_unittest.cc | 292 ++++++ .../relocation_packer/src/run_all_unittests.cc | 10 + .../bionic/tools/relocation_packer/src/sleb128.cc | 131 +++ .../bionic/tools/relocation_packer/src/sleb128.h | 77 ++ .../relocation_packer/src/sleb128_unittest.cc | 166 ++++ .../test_data/elf_file_unittest_relocs.cc | 1014 ++++++++++++++++++++ .../test_data/elf_file_unittest_relocs_arm32.so | Bin 0 -> 93210 bytes .../elf_file_unittest_relocs_arm32_packed.so | Bin 0 -> 89114 bytes .../test_data/elf_file_unittest_relocs_arm64.so | Bin 0 -> 134131 bytes .../elf_file_unittest_relocs_arm64_packed.so | Bin 0 -> 113651 bytes third_party/android_platform/config.gni | 9 + third_party/android_platform/relocation_packer.gyp | 83 ++ 33 files changed, 4840 insertions(+) create mode 100644 third_party/android_platform/BUILD.gn create mode 100644 third_party/android_platform/bionic/tools/relocation_packer/Android.mk create mode 100644 third_party/android_platform/bionic/tools/relocation_packer/LICENSE create mode 100644 third_party/android_platform/bionic/tools/relocation_packer/src/debug.cc create mode 100644 third_party/android_platform/bionic/tools/relocation_packer/src/debug.h create mode 100644 third_party/android_platform/bionic/tools/relocation_packer/src/debug_unittest.cc create mode 100644 third_party/android_platform/bionic/tools/relocation_packer/src/delta_encoder.cc create mode 100644 third_party/android_platform/bionic/tools/relocation_packer/src/delta_encoder.h create mode 100644 third_party/android_platform/bionic/tools/relocation_packer/src/delta_encoder_unittest.cc create mode 100644 third_party/android_platform/bionic/tools/relocation_packer/src/elf_file.cc create mode 100644 third_party/android_platform/bionic/tools/relocation_packer/src/elf_file.h create mode 100644 third_party/android_platform/bionic/tools/relocation_packer/src/elf_file_unittest.cc create mode 100644 third_party/android_platform/bionic/tools/relocation_packer/src/elf_traits.h create mode 100644 third_party/android_platform/bionic/tools/relocation_packer/src/leb128.cc create mode 100644 third_party/android_platform/bionic/tools/relocation_packer/src/leb128.h create mode 100644 third_party/android_platform/bionic/tools/relocation_packer/src/leb128_unittest.cc create mode 100644 third_party/android_platform/bionic/tools/relocation_packer/src/main.cc create mode 100644 third_party/android_platform/bionic/tools/relocation_packer/src/nativehelper/ScopedFd.h create mode 100644 third_party/android_platform/bionic/tools/relocation_packer/src/packer.cc create mode 100644 third_party/android_platform/bionic/tools/relocation_packer/src/packer.h create mode 100644 third_party/android_platform/bionic/tools/relocation_packer/src/packer_unittest.cc create mode 100644 third_party/android_platform/bionic/tools/relocation_packer/src/run_all_unittests.cc create mode 100644 third_party/android_platform/bionic/tools/relocation_packer/src/sleb128.cc create mode 100644 third_party/android_platform/bionic/tools/relocation_packer/src/sleb128.h create mode 100644 third_party/android_platform/bionic/tools/relocation_packer/src/sleb128_unittest.cc create mode 100644 third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc create mode 100755 third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so create mode 100755 third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so create mode 100755 third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so create mode 100755 third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so create mode 100644 third_party/android_platform/config.gni create mode 100644 third_party/android_platform/relocation_packer.gyp diff --git a/third_party/android_platform/BUILD.gn b/third_party/android_platform/BUILD.gn new file mode 100644 index 000000000000..6cc665107429 --- /dev/null +++ b/third_party/android_platform/BUILD.gn @@ -0,0 +1,31 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("config.gni") + +gypi_values = exec_script("//build/gypi_to_gn.py", + [ rebase_path("relocation_packer.gyp") ], + "scope", + [ "relocation_packer.gyp" ]) + +if (current_toolchain == host_toolchain) { + # GYP: //third_party/android_platform/relocation_packer.gyp:android_lib_relocation_packer + source_set("android_lib_relocation_packer") { + deps = [ + "//third_party/elfutils:libelf", + ] + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + sources = gypi_values.relocation_packer_sources + } + + # GYP: //third_party/android_platform/relocation_packer.gyp:android_relocation_packer + executable("android_relocation_packer") { + deps = [ + ":android_lib_relocation_packer", + "//third_party/elfutils:libelf", + ] + sources = gypi_values.relocation_packer_main_source + } +} diff --git a/third_party/android_platform/README.chromium b/third_party/android_platform/README.chromium index f6645b6026c7..53f242992c0a 100644 --- a/third_party/android_platform/README.chromium +++ b/third_party/android_platform/README.chromium @@ -15,6 +15,9 @@ glue layer against. The AOSP framework is built from the Android release tag after which it is named, and the jar can be built by invoking make on the android_system_stubs target. +Also includes a ported copy of the Android relocation packing tool source, +along with the files required to build it in the chromium tree. + Local Modifications: Only picked the few scripts needed by chrome. Updated output directories to use environment variable. @@ -24,3 +27,10 @@ Added support for parsing LOG(FATAL) and DCHECK errors and their Added support for finding symbols when library is loaded directly from the APK. Changed the toolchain to remove references to 4.6 toolchains. Added support for arch=x64 as an alias to arch=x86_64 + +Android relocation packing tool details: + Copy sources from AOSP bionic/tools/relocation_packer + Remove scripts that regenerate golden test data (not relevant here) + Create a nativehelper/ScopedFd.h to satisfy inclusion from main.cc + Create gyp build + Create gn build (currently packer only; no unit tests) diff --git a/third_party/android_platform/bionic/tools/relocation_packer/Android.mk b/third_party/android_platform/bionic/tools/relocation_packer/Android.mk new file mode 100644 index 000000000000..99a39c02b72a --- /dev/null +++ b/third_party/android_platform/bionic/tools/relocation_packer/Android.mk @@ -0,0 +1,96 @@ +# +# 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. +# + +common_cppflags := -Wall -Wextra -Wunused -Werror -Wold-style-cast + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_CPP_EXTENSION := .cc + +LOCAL_SRC_FILES := \ + src/debug.cc \ + src/delta_encoder.cc \ + src/elf_file.cc \ + src/leb128.cc \ + src/packer.cc \ + src/sleb128.cc \ + +LOCAL_STATIC_LIBRARIES := libelf +LOCAL_C_INCLUDES := external/elfutils/src/libelf +LOCAL_MODULE := lib_relocation_packer + +LOCAL_CPPFLAGS := $(common_cppflags) + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk + +include $(BUILD_HOST_STATIC_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_CPP_EXTENSION := .cc + +LOCAL_SRC_FILES := src/main.cc +LOCAL_STATIC_LIBRARIES := lib_relocation_packer libelf +LOCAL_C_INCLUDES := external/elfutils/src/libelf libnativehelper/include + +LOCAL_MODULE := relocation_packer + +LOCAL_CPPFLAGS := $(common_cppflags) + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk + +include $(BUILD_HOST_EXECUTABLE) + +include $(CLEAR_VARS) + +LOCAL_CPP_EXTENSION := .cc + +LOCAL_SRC_FILES := \ + src/debug_unittest.cc \ + src/delta_encoder_unittest.cc \ + src/elf_file_unittest.cc \ + src/leb128_unittest.cc \ + src/sleb128_unittest.cc \ + src/packer_unittest.cc \ + +LOCAL_STATIC_LIBRARIES := lib_relocation_packer libelf +LOCAL_C_INCLUDES := external/elfutils/src/libelf + +LOCAL_CPPFLAGS := $(common_cppflags) + +LOCAL_MODULE := relocation_packer_unit_tests +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk + +include $(BUILD_HOST_NATIVE_TEST) + +# $(1) library name +define copy-test-library +include $(CLEAR_VARS) +LOCAL_IS_HOST_MODULE := true +LOCAL_MODULE := $(1) +LOCAL_MODULE_CLASS := SHARED_LIBRARIES +LOCAL_MODULE_PATH := $(HOST_OUT_EXECUTABLES) +LOCAL_STRIP_MODULE := false +LOCAL_SRC_FILES := test_data/$(1) +include $(BUILD_PREBUILT) +endef + +$(eval $(call copy-test-library,elf_file_unittest_relocs_arm32.so)) +$(eval $(call copy-test-library,elf_file_unittest_relocs_arm32_packed.so)) +$(eval $(call copy-test-library,elf_file_unittest_relocs_arm64.so)) +$(eval $(call copy-test-library,elf_file_unittest_relocs_arm64_packed.so)) diff --git a/third_party/android_platform/bionic/tools/relocation_packer/LICENSE b/third_party/android_platform/bionic/tools/relocation_packer/LICENSE new file mode 100644 index 000000000000..972bb2edb099 --- /dev/null +++ b/third_party/android_platform/bionic/tools/relocation_packer/LICENSE @@ -0,0 +1,27 @@ +// Copyright 2014 The Chromium Authors. 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. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// 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. diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/debug.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/debug.cc new file mode 100644 index 000000000000..29d7ab067445 --- /dev/null +++ b/third_party/android_platform/bionic/tools/relocation_packer/src/debug.cc @@ -0,0 +1,55 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "debug.h" + +#include +#include +#include + +namespace relocation_packer { + +// Construct a new message logger. Prints if level is less than or equal to +// the level set with SetVerbose() and predicate is true. +Logger::Logger(Severity severity, int level, bool predicate) { + severity_ = severity; + level_ = level; + predicate_ = predicate; +} + +// On destruction, flush and print the strings accumulated. Abort if FATAL. +Logger::~Logger() { + if (predicate_) { + if (level_ <= max_level_) { + std::ostream* log = severity_ == INFO ? info_stream_ : error_stream_; + std::string tag; + switch (severity_) { + case INFO: tag = "INFO"; break; + case WARNING: tag = "WARNING"; break; + case ERROR: tag = "ERROR"; break; + case FATAL: tag = "FATAL"; break; + } + stream_.flush(); + *log << tag << ": " << stream_.str() << std::endl; + } + if (severity_ == FATAL) + abort(); + } +} + +// Reset to initial state. +void Logger::Reset() { + max_level_ = -1; + info_stream_ = &std::cout; + error_stream_ = &std::cerr; +} + +// Verbosity. Not thread-safe. +int Logger::max_level_ = -1; + +// Logging streams. Not thread-safe. +std::ostream* Logger::info_stream_ = &std::cout; +std::ostream* Logger::error_stream_ = &std::cerr; + +} // namespace relocation_packer diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/debug.h b/third_party/android_platform/bionic/tools/relocation_packer/src/debug.h new file mode 100644 index 000000000000..48be6c19b02b --- /dev/null +++ b/third_party/android_platform/bionic/tools/relocation_packer/src/debug.h @@ -0,0 +1,115 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Logging and checks. Avoids a dependency on base. +// +// LOG(tag) prints messages. Tags are INFO, WARNING, ERROR and FATAL. +// INFO prints to stdout, the others to stderr. FATAL aborts after printing. +// +// LOG_IF(tag, predicate) logs if predicate evaluates to true, else silent. +// +// VLOG(level) logs INFO messages where level is less than or equal to the +// verbosity level set with SetVerbose(). +// +// VLOG_IF(level, predicate) logs INFO if predicate evaluates to true, +// else silent. +// +// CHECK(predicate) logs a FATAL error if predicate is false. +// NOTREACHED() always aborts. +// Log streams can be changed with SetStreams(). Logging is not thread-safe. +// + +#ifndef TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_ +#define TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_ + +#include +#include +#include + +namespace relocation_packer { + +class Logger { + public: + enum Severity {INFO = 0, WARNING, ERROR, FATAL}; + + // Construct a new message logger. Prints if level is less than or + // equal to the level set with SetVerbose() and predicate is true. + // |severity| is an enumerated severity. + // |level| is the verbosity level. + // |predicate| controls if the logger prints or is silent. + Logger(Severity severity, int level, bool predicate); + + // On destruction, flush and print the strings accumulated in stream_. + ~Logger(); + + // Return the stream for this logger. + std::ostream& GetStream() { return stream_; } + + // Set verbosity level. Messages with a level less than or equal to + // this level are printed, others are discarded. Static, not thread-safe. + static void SetVerbose(int level) { max_level_ = level; } + + // Set info and error logging streams. Static, not thread-safe. + static void SetStreams(std::ostream* info_stream, + std::ostream* error_stream) { + info_stream_ = info_stream; + error_stream_ = error_stream; + } + + // Reset to initial state. + static void Reset(); + + private: + // Message severity, verbosity level, and predicate. + Severity severity_; + int level_; + bool predicate_; + + // String stream, accumulates message text. + std::ostringstream stream_; + + // Verbosity for INFO messages. Not thread-safe. + static int max_level_; + + // Logging streams. Not thread-safe. + static std::ostream* info_stream_; + static std::ostream* error_stream_; +}; + +} // namespace relocation_packer + +// Make logging severities visible globally. +typedef relocation_packer::Logger::Severity LogSeverity; +using LogSeverity::INFO; +using LogSeverity::WARNING; +using LogSeverity::ERROR; +using LogSeverity::FATAL; + +// LOG(severity) prints a message with the given severity, and aborts if +// severity is FATAL. LOG_IF(severity, predicate) does the same but only if +// predicate is true. INT_MIN is guaranteed to be less than or equal to +// any verbosity level. +#define LOG(severity) \ + (relocation_packer::Logger(severity, INT_MIN, true).GetStream()) +#define LOG_IF(severity, predicate) \ + (relocation_packer::Logger(severity, INT_MIN, (predicate)).GetStream()) + +// VLOG(level) prints its message as INFO if level is less than or equal to +// the current verbosity level. +#define VLOG(level) \ + (relocation_packer::Logger(INFO, (level), true).GetStream()) +#define VLOG_IF(level, predicate) \ + (relocation_packer::Logger(INFO, (level), (predicate)).GetStream()) + +// CHECK(predicate) fails with a FATAL log message if predicate is false. +#define CHECK(predicate) (LOG_IF(FATAL, !(predicate)) \ + << __FILE__ << ":" << __LINE__ << ": " \ + << __FUNCTION__ << ": CHECK '" #predicate "' failed") + +// NOTREACHED() always fails with a FATAL log message. +#define NOTREACHED(_) (LOG(FATAL) \ + << __FILE__ << ":" << __LINE__ << ": " \ + << __FUNCTION__ << ": NOTREACHED() hit") + +#endif // TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_ diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/debug_unittest.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/debug_unittest.cc new file mode 100644 index 000000000000..b31e2ae7eeb0 --- /dev/null +++ b/third_party/android_platform/bionic/tools/relocation_packer/src/debug_unittest.cc @@ -0,0 +1,122 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "debug.h" + +#include +#include "gtest/gtest.h" + +namespace relocation_packer { + +TEST(Debug, Log) { + Logger::Reset(); + std::ostringstream info; + std::ostringstream error; + Logger::SetStreams(&info, &error); + + LOG(INFO) << "INFO log message"; + LOG(WARNING) << "WARNING log message"; + LOG(ERROR) << "ERROR log message"; + + EXPECT_EQ("INFO: INFO log message\n", info.str()); + EXPECT_EQ("WARNING: WARNING log message\n" + "ERROR: ERROR log message\n", error.str()); + Logger::Reset(); +} + +TEST(Debug, LogIf) { + Logger::Reset(); + std::ostringstream info; + std::ostringstream error; + Logger::SetStreams(&info, &error); + + LOG_IF(INFO, true) << "INFO log message"; + LOG_IF(INFO, false) << "INFO log message, SHOULD NOT PRINT"; + LOG_IF(WARNING, true) << "WARNING log message"; + LOG_IF(WARNING, false) << "WARNING log message, SHOULD NOT PRINT"; + LOG_IF(ERROR, true) << "ERROR log message"; + LOG_IF(ERROR, false) << "ERROR log message, SHOULD NOT PRINT"; + LOG_IF(FATAL, false) << "FATAL log message, SHOULD NOT PRINT"; + + EXPECT_EQ("INFO: INFO log message\n", info.str()); + EXPECT_EQ("WARNING: WARNING log message\n" + "ERROR: ERROR log message\n", error.str()); + Logger::Reset(); +} + +TEST(Debug, Vlog) { + Logger::Reset(); + std::ostringstream info; + std::ostringstream error; + Logger::SetStreams(&info, &error); + + VLOG(0) << "VLOG 0 INFO log message, SHOULD NOT PRINT"; + VLOG(1) << "VLOG 1 INFO log message, SHOULD NOT PRINT"; + VLOG(2) << "VLOG 2 INFO log message, SHOULD NOT PRINT"; + + EXPECT_EQ("", info.str()); + EXPECT_EQ("", error.str()); + + Logger::SetVerbose(1); + + VLOG(0) << "VLOG 0 INFO log message"; + VLOG(1) << "VLOG 1 INFO log message"; + VLOG(2) << "VLOG 2 INFO log message, SHOULD NOT PRINT"; + + EXPECT_EQ("INFO: VLOG 0 INFO log message\n" + "INFO: VLOG 1 INFO log message\n", info.str()); + EXPECT_EQ("", error.str()); + Logger::Reset(); +} + +TEST(Debug, VlogIf) { + Logger::Reset(); + std::ostringstream info; + std::ostringstream error; + Logger::SetStreams(&info, &error); + + VLOG_IF(0, true) << "VLOG 0 INFO log message, SHOULD NOT PRINT"; + VLOG_IF(1, true) << "VLOG 1 INFO log message, SHOULD NOT PRINT"; + VLOG_IF(2, true) << "VLOG 2 INFO log message, SHOULD NOT PRINT"; + + EXPECT_EQ("", info.str()); + EXPECT_EQ("", error.str()); + + Logger::SetVerbose(1); + + VLOG_IF(0, true) << "VLOG 0 INFO log message"; + VLOG_IF(0, false) << "VLOG 0 INFO log message, SHOULD NOT PRINT"; + VLOG_IF(1, true) << "VLOG 1 INFO log message"; + VLOG_IF(1, false) << "VLOG 1 INFO log message, SHOULD NOT PRINT"; + VLOG_IF(2, true) << "VLOG 2 INFO log message, SHOULD NOT PRINT"; + VLOG_IF(2, false) << "VLOG 2 INFO log message, SHOULD NOT PRINT"; + + EXPECT_EQ("INFO: VLOG 0 INFO log message\n" + "INFO: VLOG 1 INFO log message\n", info.str()); + EXPECT_EQ("", error.str()); + Logger::Reset(); +} + +TEST(DebugDeathTest, Fatal) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + Logger::Reset(); + EXPECT_DEATH(LOG(FATAL) << "FATAL log message", "FATAL: FATAL log message"); + EXPECT_DEATH( + LOG_IF(FATAL, true) << "FATAL log message", "FATAL: FATAL log message"); +} + +TEST(DebugDeathTest, Check) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + Logger::Reset(); + CHECK(0 == 0); + EXPECT_DEATH(CHECK(0 == 1), "FATAL: .*:.*: .*: CHECK '0 == 1' failed"); +} + +TEST(DebugDeathTest, NotReached) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + Logger::Reset(); + EXPECT_DEATH(NOTREACHED(), "FATAL: .*:.*: .*: NOTREACHED\\(\\) hit"); +} + +} // namespace relocation_packer diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/delta_encoder.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/delta_encoder.cc new file mode 100644 index 000000000000..8349d7c64e55 --- /dev/null +++ b/third_party/android_platform/bionic/tools/relocation_packer/src/delta_encoder.cc @@ -0,0 +1,307 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "delta_encoder.h" + +#include + +#include "debug.h" + +static constexpr uint32_t RELOCATION_GROUPED_BY_INFO_FLAG = 1; +static constexpr uint32_t RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG = 2; +static constexpr uint32_t RELOCATION_GROUPED_BY_ADDEND_FLAG = 4; +static constexpr uint32_t RELOCATION_GROUP_HAS_ADDEND_FLAG = 8; + +static bool is_relocation_grouped_by_info(uint64_t flags) { + return (flags & RELOCATION_GROUPED_BY_INFO_FLAG) != 0; +} + +static bool is_relocation_grouped_by_offset_delta(uint64_t flags) { + return (flags & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) != 0; +} + +static bool is_relocation_grouped_by_addend(uint64_t flags) { + return (flags & RELOCATION_GROUPED_BY_ADDEND_FLAG) != 0; +} + +static bool is_relocation_group_has_addend(uint64_t flags) { + return (flags & RELOCATION_GROUP_HAS_ADDEND_FLAG) != 0; +} + +namespace relocation_packer { + +// Encode relocations into a delta encoded (packed) representation. +template +void RelocationDeltaCodec::Encode(const std::vector& relocations, + std::vector* packed) { + if (relocations.size() == 0) + return; + + // Start with the relocation count, then append groups + // TODO(dimitry): we might want to move it to DT_ANDROID_RELCOUNT section + packed->push_back(static_cast(relocations.size())); + + // lets write starting offset (offset of the first reloc - first delta) + ElfAddr start_offset = relocations.size() > 1 ? + relocations[0].r_offset - (relocations[1].r_offset - relocations[0].r_offset) : + relocations[0].r_offset; + + packed->push_back(start_offset); + + // this one is used to calculate delta + ElfAddr previous_addend = 0; + ElfAddr previous_offset = start_offset; + + for (size_t group_start = 0; group_start < relocations.size(); ) { + ElfAddr group_flags = 0; + ElfAddr group_offset_delta = 0; + ElfAddr group_addend = 0; + ElfAddr group_info = 0; + + ElfAddr group_size = 0; + + DetectGroup(relocations, group_start, previous_offset, &group_size, &group_flags, + &group_offset_delta, &group_info, &group_addend); + + // write the group header + packed->push_back(group_size); + packed->push_back(group_flags); + + if (is_relocation_grouped_by_offset_delta(group_flags)) { + packed->push_back(group_offset_delta); + } + + if (is_relocation_grouped_by_info(group_flags)) { + packed->push_back(group_info); + } + + if (is_relocation_group_has_addend(group_flags) && + is_relocation_grouped_by_addend(group_flags)) { + packed->push_back(group_addend - previous_addend); + previous_addend = group_addend; + } + + for (size_t i = 0; i < static_cast(group_size); ++i) { + CHECK((group_start + i) < relocations.size()); + const ElfRela* relocation = &relocations[group_start + i]; + + if (!is_relocation_grouped_by_offset_delta(group_flags)) { + packed->push_back(relocation->r_offset - previous_offset); + } + previous_offset = relocation->r_offset; + + if (!is_relocation_grouped_by_info(group_flags)) { + packed->push_back(relocation->r_info); + } + + if (is_relocation_group_has_addend(group_flags) && + !is_relocation_grouped_by_addend(group_flags)) { + packed->push_back(relocation->r_addend - previous_addend); + previous_addend = relocation->r_addend; + } + } + + // If the relocation group does not have an addend - reset it to 0 + // to simplify addend computation for the group following this one. + if (!is_relocation_group_has_addend(group_flags)) { + previous_addend = 0; + } + + group_start += group_size; + } +} + +// Decode relocations from a delta encoded (packed) representation. +template +void RelocationDeltaCodec::Decode(const std::vector& packed, + std::vector* relocations) { + if (packed.size() < 5) { + return; + } + + size_t ndx = 0; + ElfAddr current_count = 0; + ElfAddr total_count = packed[ndx++]; + + ElfAddr offset = packed[ndx++]; + ElfAddr info = 0; + ElfAddr addend = 0; + + while(current_count < total_count) { + // read group + ElfAddr group_size = packed[ndx++]; + ElfAddr group_flags = packed[ndx++]; + ElfAddr group_offset_delta = 0; + + if (is_relocation_grouped_by_offset_delta(group_flags)) { + group_offset_delta = packed[ndx++]; + } + + if (is_relocation_grouped_by_info(group_flags)) { + info = packed[ndx++]; + } + + if (is_relocation_group_has_addend(group_flags) && + is_relocation_grouped_by_addend(group_flags)) { + addend += packed[ndx++]; + } + + // now read not grouped info + for (ElfAddr i = 0; ipush_back(reloc); + } + + if (!is_relocation_group_has_addend(group_flags)) { + addend = 0; + } + + current_count += group_size; + } +} + +// This function detects a way to group reloc_one and reloc_two, sets up group_flags +// and initializes values for corresponding group_ fields. For example if relocations +// can be grouped by r_info the function will set group_info variable. +template +void RelocationDeltaCodec::DetectGroupFields(const ElfRela& reloc_one, + const ElfRela& reloc_two, + ElfAddr current_offset_delta, + ElfAddr* group_flags, + ElfAddr* group_offset_delta, + ElfAddr* group_info, + ElfAddr* group_addend) { + *group_flags = 0; + + const ElfAddr offset_delta = static_cast(reloc_two.r_offset) - + static_cast(reloc_one.r_offset); + + if (offset_delta == current_offset_delta) { + *group_flags |= RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG; + if (group_offset_delta != nullptr) { + *group_offset_delta = current_offset_delta; + } + } + + if (reloc_one.r_info == reloc_two.r_info) { + *group_flags |= RELOCATION_GROUPED_BY_INFO_FLAG; + if (group_info != nullptr) { + *group_info = reloc_one.r_info; + } + } + + if (reloc_one.r_addend != 0 || reloc_two.r_addend != 0) { + *group_flags |= RELOCATION_GROUP_HAS_ADDEND_FLAG; + if (reloc_one.r_addend == reloc_two.r_addend) { + *group_flags |= RELOCATION_GROUPED_BY_ADDEND_FLAG; + if (group_addend != nullptr) { + *group_addend = reloc_one.r_addend; + } + } + } +} + +// This function is used to detect if there is better group available +// during RelocationDeltaCodec::DetectGroup processing. +// Current implementation prefers having groups without addend (== zero addend) +// to any other groups field with the ratio 3:1. This is because addend tends +// to be more unevenly distributed than other fields. +static uint32_t group_weight(uint64_t flags) { + uint32_t weight = 0; + if (!is_relocation_group_has_addend(flags)) { + weight += 3; + } else if (is_relocation_grouped_by_addend(flags)) { + weight += 1; + } + + if (is_relocation_grouped_by_offset_delta(flags)) { + weight += 1; + } + + if (is_relocation_grouped_by_info(flags)) { + weight += 1; + } + + return weight; +} + +template +void RelocationDeltaCodec::DetectGroup(const std::vector& relocations, + size_t group_starts_with, ElfAddr previous_offset, + ElfAddr* group_size, ElfAddr* group_flags, + ElfAddr* group_offset_delta, ElfAddr* group_info, + ElfAddr* group_addend) { + CHECK(group_starts_with < relocations.size()); + CHECK(group_flags != nullptr); + + const ElfRela& reloc_one = relocations[group_starts_with++]; + if (group_starts_with == relocations.size()) { + *group_flags = reloc_one.r_addend == 0 ? 0 : RELOCATION_GROUP_HAS_ADDEND_FLAG; + *group_size = 1; + return; + } + + const ElfAddr offset_delta = reloc_one.r_offset - previous_offset; + + // detect group_flags + DetectGroupFields(reloc_one, relocations[group_starts_with], offset_delta, group_flags, + group_offset_delta, group_info, group_addend); + + if (group_starts_with + 1 == relocations.size()) { + *group_size = 2; + return; + } + + ElfAddr cnt = 1; + for (size_t i = group_starts_with; i < relocations.size() - 1; ++i) { + ElfAddr candidate_flags; + // check if next group (reloc_current; reloc_next) has better grouped_by flags + DetectGroupFields(relocations[i], relocations[i+1], offset_delta, &candidate_flags, + nullptr, nullptr, nullptr); + + if (group_weight(*group_flags) < group_weight(candidate_flags)) { + break; + } + cnt++; + + if (candidate_flags != *group_flags) { + break; + } + + if (i + 1 == relocations.size() - 1) { // last one + cnt++; + } + } + + // if as a result of checking candidates we ended up with cnt == 1 + // reset flags to the default state + if (cnt == 1) { + *group_flags = reloc_one.r_addend == 0 ? 0 : RELOCATION_GROUP_HAS_ADDEND_FLAG; + } + + *group_size = cnt; +} + +template class RelocationDeltaCodec; +template class RelocationDeltaCodec; + +} // namespace relocation_packer diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/delta_encoder.h b/third_party/android_platform/bionic/tools/relocation_packer/src/delta_encoder.h new file mode 100644 index 000000000000..46c324c49059 --- /dev/null +++ b/third_party/android_platform/bionic/tools/relocation_packer/src/delta_encoder.h @@ -0,0 +1,132 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Delta encode and decode REL/RELA section of elf file. +// +// The encoded data format is sequence of elements of ElfAddr type (unsigned long): +// +// [00] relocation_count - the total count of relocations +// [01] initial r_offset - this is initial r_offset for the +// relocation table. +// followed by group structures: +// [02] group +// ... +// [nn] group + +// the generalized format of the group is (! - always present ? - depends on group_flags): +// -------------- +// ! group_size +// ! group_flags +// ? group_r_offset_delta when RELOCATION_GROUPED_BY_OFFSET_DELTA flag is set +// ? group_r_info when RELOCATION_GROUPED_BY_INFO flag is set +// ? group_r_addend_group_delta when RELOCATION_GROUP_HAS_ADDEND and RELOCATION_GROUPED_BY_ADDEND +// flag is set +// +// The group description is followed by individual relocations. +// please note that there is a case when individual relocation +// section could be empty - that is if every field ends up grouped. +// +// The format for individual relocations section is: +// ? r_offset_delta - when RELOCATION_GROUPED_BY_OFFSET_DELTA is not set +// ? r_info - when RELOCATION_GROUPED_BY_INFO flag is not set +// ? r_addend_delta - RELOCATION_GROUP_HAS_ADDEND is set and RELOCATION_GROUPED_BY_ADDEND is not set +// +// For example lets pack the following relocations: +// +// Relocation section '.rela.dyn' at offset 0xbf58 contains 939 entries: +// Offset Info Type Symbol's Value Symbol's Name + Addend +// 00000000000a2178 0000000000000403 R_AARCH64_RELATIVE 177a8 +// 00000000000a2180 0000000000000403 R_AARCH64_RELATIVE 177cc +// 00000000000a2188 0000000000000403 R_AARCH64_RELATIVE 177e0 +// 00000000000a2190 0000000000000403 R_AARCH64_RELATIVE 177f4 +// 00000000000a2198 0000000000000403 R_AARCH64_RELATIVE 17804 +// 00000000000a21a0 0000000000000403 R_AARCH64_RELATIVE 17818 +// 00000000000a21a8 0000000000000403 R_AARCH64_RELATIVE 1782c +// 00000000000a21b0 0000000000000403 R_AARCH64_RELATIVE 17840 +// 00000000000a21b8 0000000000000403 R_AARCH64_RELATIVE 17854 +// 00000000000a21c0 0000000000000403 R_AARCH64_RELATIVE 17868 +// 00000000000a21c8 0000000000000403 R_AARCH64_RELATIVE 1787c +// 00000000000a21d0 0000000000000403 R_AARCH64_RELATIVE 17890 +// 00000000000a21d8 0000000000000403 R_AARCH64_RELATIVE 178a4 +// 00000000000a21e8 0000000000000403 R_AARCH64_RELATIVE 178b8 +// +// The header is going to be +// [00] 14 <- count +// [01] 0x00000000000a2170 <- initial relocation (first relocation - delta, +// the delta is 8 in this case) +// -- starting the first and only group +// [03] 14 <- group size +// [03] 0xb <- flags RELOCATION_GROUP_HAS_ADDEND | RELOCATION_GROUPED_BY_OFFSET_DELTA +// | RELOCATION_GROUPED_BY_INFO +// [04] 8 <- offset delta +// [05] 0x403 <- r_info +// -- end of group definition, starting list of r_addend deltas +// [06] 0x177a8 +// [07] 0x24 = 177cc - 177a8 +// [08] 0x14 = 177e0 - 177cc +// [09] 0x14 = 177f4 - 177e0 +// [10] 0x10 = 17804 - 177f4 +// [11] 0x14 = 17818 - 17804 +// [12] 0x14 = 1782c - 17818 +// [13] 0x14 = 17840 - 1782c +// [14] 0x14 = 17854 - 17840 +// [15] 0x14 = 17868 - 17854 +// [16] 0x14 = 1787c - 17868 +// [17] 0x14 = 17890 - 1787c +// [18] 0x14 = 178a4 - 17890 +// [19] 0x14 = 178b8 - 178a4 +// -- the end. + +// TODO (dimitry): consider using r_addend_group_delta in the way we use group offset delta, it can +// save us more bytes... + +// The input ends when sum(group_size) == relocation_count + +#ifndef TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_ +#define TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_ + +#include + +#include "elf.h" +#include "elf_traits.h" + +namespace relocation_packer { + +// A RelocationDeltaCodec packs vectors of relative relocations with +// addends into more compact forms, and unpacks them to reproduce the +// pre-packed data. +template +class RelocationDeltaCodec { + public: + typedef typename ELF::Addr ElfAddr; + typedef typename ELF::Rela ElfRela; + + // Encode relocations with addends into a more compact form. + // |relocations| is a vector of relative relocation with addend structs. + // |packed| is the vector of packed words into which relocations are packed. + static void Encode(const std::vector& relocations, + std::vector* packed); + + // Decode relative relocations with addends from their more compact form. + // |packed| is the vector of packed relocations. + // |relocations| is a vector of unpacked relative relocations. + static void Decode(const std::vector& packed, + std::vector* relocations); + + private: + static void DetectGroup(const std::vector& relocations, + size_t group_starts_with, ElfAddr previous_offset, + ElfAddr* group_size, ElfAddr* group_flags, + ElfAddr* group_offset_delta, ElfAddr* group_info, + ElfAddr* group_addend); + + static void DetectGroupFields(const ElfRela& reloc_one, const ElfRela& reloc_two, + ElfAddr current_offset_delta, ElfAddr* group_flags, + ElfAddr* group_offset_delta, ElfAddr* group_info, + ElfAddr* group_addend); +}; + +} // namespace relocation_packer + +#endif // TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_ diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/delta_encoder_unittest.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/delta_encoder_unittest.cc new file mode 100644 index 000000000000..06d9c967314f --- /dev/null +++ b/third_party/android_platform/bionic/tools/relocation_packer/src/delta_encoder_unittest.cc @@ -0,0 +1,223 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "delta_encoder.h" + +#include +#include "elf.h" +#include "gtest/gtest.h" + +namespace { + +template +void AddRelocation(uint32_t addr, + uint32_t info, + int32_t addend, + std::vector* relocations) { + T relocation; + relocation.r_offset = addr; + relocation.r_info = info; + relocation.r_addend = addend; + relocations->push_back(relocation); +} + +template +bool CheckRelocation(uint32_t addr, + uint32_t info, + int32_t addend, + const T& relocation) { + return relocation.r_offset == addr && + relocation.r_info == info && + relocation.r_addend == addend; +} + +} // namespace + +namespace relocation_packer { + +template +static void encode() { + std::vector relocations; + std::vector packed; + + RelocationDeltaCodec codec; + + codec.Encode(relocations, &packed); + + ASSERT_EQ(0U, packed.size()); + + // Initial relocation. + AddRelocation(0xf00d0000, 11U, 10000, &relocations); + + codec.Encode(relocations, &packed); + + // size of reloc table, size of group, flags, 3 fields, zero + EXPECT_EQ(7U, packed.size()); + // One pair present. + size_t ndx = 0; + EXPECT_EQ(1U, packed[ndx++]); + EXPECT_EQ(0xf00d0000, packed[ndx++]); + EXPECT_EQ(1U, packed[ndx++]); // group_size + EXPECT_EQ(8U, packed[ndx++]); // flags + // Delta from the neutral element is zero + EXPECT_EQ(0U, packed[ndx++]); // offset_delta + EXPECT_EQ(11U, packed[ndx++]); // info + EXPECT_EQ(10000U, packed[ndx++]); // addend_delta + + // Add a second relocation, 4 byte offset delta, 12 byte addend delta. + // same info + AddRelocation(0xf00d0004, 11U, 10012, &relocations); + + packed.clear(); + codec.Encode(relocations, &packed); + + ndx = 0; + EXPECT_EQ(8U, packed.size()); + + EXPECT_EQ(2U, packed[ndx++]); // relocs count + EXPECT_EQ(0xf00cfffc, packed[ndx++]); // initial offset + EXPECT_EQ(2U, packed[ndx++]); // group count + EXPECT_EQ(11U, packed[ndx++]); // flags + EXPECT_EQ(4U, packed[ndx++]); // group offset delta + EXPECT_EQ(11U, packed[ndx++]); // info + + EXPECT_EQ(10000U, packed[ndx++]); // addend delta + EXPECT_EQ(12U, packed[ndx++]); // addend delta + + // Add a third relocation, 4 byte offset delta, 12 byte addend delta. + // different info + AddRelocation(0xf00d0008, 41U, 10024, &relocations); + + // Add three more relocations, 8 byte offset deltas, -24 byte addend deltas. + AddRelocation(0xf00d0010, 42U, 10000, &relocations); + AddRelocation(0xf00d0018, 42U, 9976, &relocations); + AddRelocation(0xf00d0020, 42U, 9952, &relocations); + + AddRelocation(0xf00d2028, 1042U, 0, &relocations); + AddRelocation(0xf00d2030, 3442U, 0, &relocations); + + packed.clear(); + codec.Encode(relocations, &packed); + + ndx = 0; + EXPECT_EQ(26U, packed.size()); + // Total number of relocs + EXPECT_EQ(8U, packed[ndx++]); + EXPECT_EQ(0xf00cfffc, packed[ndx++]); + // 2 in first group + EXPECT_EQ(2U, packed[ndx++]); + EXPECT_EQ(11U, packed[ndx++]); //flags + EXPECT_EQ(4U, packed[ndx++]); // group offset delta + EXPECT_EQ(11U, packed[ndx++]); // info + + // Initial relocation. + EXPECT_EQ(10000U, packed[ndx++]); // addend delta + // Two relocations, 4 byte offset deltas, 12 byte addend deltas. + EXPECT_EQ(12U, packed[ndx++]); // addend delta + + // second group has only one reloc + EXPECT_EQ(1U, packed[ndx++]); // count + EXPECT_EQ(8U, packed[ndx++]); // flags + + EXPECT_EQ(4U, packed[ndx++]); // offset delta + EXPECT_EQ(41U, packed[ndx++]); // info + EXPECT_EQ(12U, packed[ndx++]); // addend delta + + // next - 3 relocs grouped by info + EXPECT_EQ(3U, packed[ndx++]); // count + EXPECT_EQ(11U, packed[ndx++]); // flags + EXPECT_EQ(8U, packed[ndx++]); // group offset delta + EXPECT_EQ(42U, packed[ndx++]); // info + // Three relocations, 8 byte offset deltas, -24 byte addend deltas. + EXPECT_EQ(static_cast(-24), packed[ndx++]); + EXPECT_EQ(static_cast(-24), packed[ndx++]); + EXPECT_EQ(static_cast(-24), packed[ndx++]); + + // and last - 2 relocations without addend + EXPECT_EQ(2U, packed[ndx++]); + EXPECT_EQ(0U, packed[ndx++]); // flags + // offset_deltas and r_infos for next 2 relocations + EXPECT_EQ(0x2008U, packed[ndx++]); // offset delta + EXPECT_EQ(1042U, packed[ndx++]); // r_info + EXPECT_EQ(0x8U, packed[ndx++]); // offset delta + EXPECT_EQ(3442U, packed[ndx++]); // r_info + + EXPECT_EQ(packed.size(), ndx); +} + +TEST(Delta, Encode32) { + encode(); +} + +TEST(Delta, Encode64) { + encode(); +} + +template +static void decode() { + std::vector packed; + std::vector relocations; + + RelocationDeltaCodec codec; + codec.Decode(packed, &relocations); + + EXPECT_EQ(0U, relocations.size()); + + // Six pairs. + packed.push_back(6U); // count + packed.push_back(0xc0ddfffc); // base offset + packed.push_back(3U); // group count + packed.push_back(11U); // flags + packed.push_back(4U); // offset delta + packed.push_back(11U); // info + // Initial relocation. + packed.push_back(10000U); + // Two relocations, 4 byte offset deltas, 12 byte addend deltas. + packed.push_back(12U); // addend + packed.push_back(12U); // addend + + // Three relocations, 8 byte offset deltas, -24 byte addend deltas. + packed.push_back(1U); // group count + packed.push_back(9U); // flags + packed.push_back(11U); // info + + packed.push_back(8U); + packed.push_back(static_cast(-24)); + // next group with 2 relocs + packed.push_back(2U); // group count + packed.push_back(11U); // flags + packed.push_back(8U); // offset + packed.push_back(42U); // info + + packed.push_back(static_cast(-24)); // addend + packed.push_back(static_cast(-24)); // addend + + relocations.clear(); + codec.Decode(packed, &relocations); + + EXPECT_EQ(6U, relocations.size()); + // Initial relocation. + EXPECT_TRUE(CheckRelocation(0xc0de0000, 11U, 10000, relocations[0])); + // Two relocations, 4 byte offset deltas, 12 byte addend deltas. + EXPECT_TRUE(CheckRelocation(0xc0de0004, 11U, 10012, relocations[1])); + EXPECT_TRUE(CheckRelocation(0xc0de0008, 11U, 10024, relocations[2])); + // Three relocations, 8 byte offset deltas, -24 byte addend deltas. + EXPECT_TRUE(CheckRelocation(0xc0de0010, 11U, 10000, relocations[3])); + EXPECT_TRUE(CheckRelocation(0xc0de0018, 42U, 9976, relocations[4])); + EXPECT_TRUE(CheckRelocation(0xc0de0020, 42U, 9952, relocations[5])); +} + +TEST(Delta, Decode32) { + decode(); +} + +TEST(Delta, Decode64) { + decode(); +} + +// TODO (dimitry): add more tests (fix by 19 January 2038 03:14:07 UTC) +// TODO (dimtiry): 1. Incorrect packed array for decode +// TODO (dimtiry): 2. Try to catch situation where it is likely to get series of groups with size 1 + +} // namespace relocation_packer diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/elf_file.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/elf_file.cc new file mode 100644 index 000000000000..20b25ef829d3 --- /dev/null +++ b/third_party/android_platform/bionic/tools/relocation_packer/src/elf_file.cc @@ -0,0 +1,882 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Implementation notes: +// +// We need to remove a piece from the ELF shared library. However, we also +// want to avoid fixing DWARF cfi data and relative relocation addresses. +// So after packing we shift offets and starting address of the RX segment +// while preserving code/data vaddrs location. +// This requires some fixups for symtab/hash/gnu_hash dynamic section addresses. + +#include "elf_file.h" + +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "elf_traits.h" +#include "libelf.h" +#include "packer.h" + +namespace relocation_packer { + +// Out-of-band dynamic tags used to indicate the offset and size of the +// android packed relocations section. +static constexpr int32_t DT_ANDROID_REL = DT_LOOS + 2; +static constexpr int32_t DT_ANDROID_RELSZ = DT_LOOS + 3; + +static constexpr int32_t DT_ANDROID_RELA = DT_LOOS + 4; +static constexpr int32_t DT_ANDROID_RELASZ = DT_LOOS + 5; + +static constexpr uint32_t SHT_ANDROID_REL = SHT_LOOS + 1; +static constexpr uint32_t SHT_ANDROID_RELA = SHT_LOOS + 2; + +// Alignment to preserve, in bytes. This must be at least as large as the +// largest d_align and sh_addralign values found in the loaded file. +// Out of caution for RELRO page alignment, we preserve to a complete target +// page. See http://www.airs.com/blog/archives/189. +static constexpr size_t kPreserveAlignment = 4096; + +// Get section data. Checks that the section has exactly one data entry, +// so that the section size and the data size are the same. True in +// practice for all sections we resize when packing or unpacking. Done +// by ensuring that a call to elf_getdata(section, data) returns NULL as +// the next data entry. +static Elf_Data* GetSectionData(Elf_Scn* section) { + Elf_Data* data = elf_getdata(section, NULL); + CHECK(data && elf_getdata(section, data) == NULL); + return data; +} + +// Rewrite section data. Allocates new data and makes it the data element's +// buffer. Relies on program exit to free allocated data. +static void RewriteSectionData(Elf_Scn* section, + const void* section_data, + size_t size) { + Elf_Data* data = GetSectionData(section); + CHECK(size == data->d_size); + uint8_t* area = new uint8_t[size]; + memcpy(area, section_data, size); + data->d_buf = area; +} + +// Verbose ELF header logging. +template +static void VerboseLogElfHeader(const Ehdr* elf_header) { + VLOG(1) << "e_phoff = " << elf_header->e_phoff; + VLOG(1) << "e_shoff = " << elf_header->e_shoff; + VLOG(1) << "e_ehsize = " << elf_header->e_ehsize; + VLOG(1) << "e_phentsize = " << elf_header->e_phentsize; + VLOG(1) << "e_phnum = " << elf_header->e_phnum; + VLOG(1) << "e_shnum = " << elf_header->e_shnum; + VLOG(1) << "e_shstrndx = " << elf_header->e_shstrndx; +} + +// Verbose ELF program header logging. +template +static void VerboseLogProgramHeader(size_t program_header_index, + const Phdr* program_header) { + std::string type; + switch (program_header->p_type) { + case PT_NULL: type = "NULL"; break; + case PT_LOAD: type = "LOAD"; break; + case PT_DYNAMIC: type = "DYNAMIC"; break; + case PT_INTERP: type = "INTERP"; break; + case PT_PHDR: type = "PHDR"; break; + case PT_GNU_RELRO: type = "GNU_RELRO"; break; + case PT_GNU_STACK: type = "GNU_STACK"; break; + case PT_ARM_EXIDX: type = "EXIDX"; break; + default: type = "(OTHER)"; break; + } + VLOG(1) << "phdr[" << program_header_index << "] : " << type; + VLOG(1) << " p_offset = " << program_header->p_offset; + VLOG(1) << " p_vaddr = " << program_header->p_vaddr; + VLOG(1) << " p_paddr = " << program_header->p_paddr; + VLOG(1) << " p_filesz = " << program_header->p_filesz; + VLOG(1) << " p_memsz = " << program_header->p_memsz; + VLOG(1) << " p_flags = " << program_header->p_flags; + VLOG(1) << " p_align = " << program_header->p_align; +} + +// Verbose ELF section header logging. +template +static void VerboseLogSectionHeader(const std::string& section_name, + const Shdr* section_header) { + VLOG(1) << "section " << section_name; + VLOG(1) << " sh_addr = " << section_header->sh_addr; + VLOG(1) << " sh_offset = " << section_header->sh_offset; + VLOG(1) << " sh_size = " << section_header->sh_size; + VLOG(1) << " sh_entsize = " << section_header->sh_entsize; + VLOG(1) << " sh_addralign = " << section_header->sh_addralign; +} + +// Verbose ELF section data logging. +static void VerboseLogSectionData(const Elf_Data* data) { + VLOG(1) << " data"; + VLOG(1) << " d_buf = " << data->d_buf; + VLOG(1) << " d_off = " << data->d_off; + VLOG(1) << " d_size = " << data->d_size; + VLOG(1) << " d_align = " << data->d_align; +} + +// Load the complete ELF file into a memory image in libelf, and identify +// the .rel.dyn or .rela.dyn, .dynamic, and .android.rel.dyn or +// .android.rela.dyn sections. No-op if the ELF file has already been loaded. +template +bool ElfFile::Load() { + if (elf_) + return true; + + Elf* elf = elf_begin(fd_, ELF_C_RDWR, NULL); + CHECK(elf); + + if (elf_kind(elf) != ELF_K_ELF) { + LOG(ERROR) << "File not in ELF format"; + return false; + } + + auto elf_header = ELF::getehdr(elf); + if (!elf_header) { + LOG(ERROR) << "Failed to load ELF header: " << elf_errmsg(elf_errno()); + return false; + } + + if (elf_header->e_type != ET_DYN) { + LOG(ERROR) << "ELF file is not a shared object"; + return false; + } + + // Require that our endianness matches that of the target, and that both + // are little-endian. Safe for all current build/target combinations. + const int endian = elf_header->e_ident[EI_DATA]; + CHECK(endian == ELFDATA2LSB); + CHECK(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__); + + const int file_class = elf_header->e_ident[EI_CLASS]; + VLOG(1) << "endian = " << endian << ", file class = " << file_class; + VerboseLogElfHeader(elf_header); + + auto elf_program_header = ELF::getphdr(elf); + CHECK(elf_program_header != nullptr); + + const typename ELF::Phdr* dynamic_program_header = NULL; + for (size_t i = 0; i < elf_header->e_phnum; ++i) { + auto program_header = &elf_program_header[i]; + VerboseLogProgramHeader(i, program_header); + + if (program_header->p_type == PT_DYNAMIC) { + CHECK(dynamic_program_header == NULL); + dynamic_program_header = program_header; + } + } + CHECK(dynamic_program_header != nullptr); + + size_t string_index; + elf_getshdrstrndx(elf, &string_index); + + // Notes of the dynamic relocations, packed relocations, and .dynamic + // sections. Found while iterating sections, and later stored in class + // attributes. + Elf_Scn* found_relocations_section = nullptr; + Elf_Scn* found_dynamic_section = nullptr; + + // Notes of relocation section types seen. We require one or the other of + // these; both is unsupported. + bool has_rel_relocations = false; + bool has_rela_relocations = false; + + Elf_Scn* section = NULL; + while ((section = elf_nextscn(elf, section)) != nullptr) { + auto section_header = ELF::getshdr(section); + std::string name = elf_strptr(elf, string_index, section_header->sh_name); + VerboseLogSectionHeader(name, section_header); + + // Note relocation section types. + if (section_header->sh_type == SHT_REL || section_header->sh_type == SHT_ANDROID_REL) { + has_rel_relocations = true; + } + if (section_header->sh_type == SHT_RELA || section_header->sh_type == SHT_ANDROID_RELA) { + has_rela_relocations = true; + } + + // Note special sections as we encounter them. + if ((name == ".rel.dyn" || name == ".rela.dyn") && + section_header->sh_size > 0) { + found_relocations_section = section; + } + + if (section_header->sh_offset == dynamic_program_header->p_offset) { + found_dynamic_section = section; + } + + // Ensure we preserve alignment, repeated later for the data block(s). + CHECK(section_header->sh_addralign <= kPreserveAlignment); + + Elf_Data* data = NULL; + while ((data = elf_getdata(section, data)) != NULL) { + CHECK(data->d_align <= kPreserveAlignment); + VerboseLogSectionData(data); + } + } + + // Loading failed if we did not find the required special sections. + if (!found_relocations_section) { + LOG(ERROR) << "Missing or empty .rel.dyn or .rela.dyn section"; + return false; + } + if (!found_dynamic_section) { + LOG(ERROR) << "Missing .dynamic section"; + return false; + } + + // Loading failed if we could not identify the relocations type. + if (!has_rel_relocations && !has_rela_relocations) { + LOG(ERROR) << "No relocations sections found"; + return false; + } + if (has_rel_relocations && has_rela_relocations) { + LOG(ERROR) << "Multiple relocations sections with different types found, " + << "not currently supported"; + return false; + } + + elf_ = elf; + relocations_section_ = found_relocations_section; + dynamic_section_ = found_dynamic_section; + relocations_type_ = has_rel_relocations ? REL : RELA; + return true; +} + +// Helper for ResizeSection(). Adjust the main ELF header for the hole. +template +static void AdjustElfHeaderForHole(typename ELF::Ehdr* elf_header, + typename ELF::Off hole_start, + ssize_t hole_size) { + if (elf_header->e_phoff > hole_start) { + elf_header->e_phoff += hole_size; + VLOG(1) << "e_phoff adjusted to " << elf_header->e_phoff; + } + if (elf_header->e_shoff > hole_start) { + elf_header->e_shoff += hole_size; + VLOG(1) << "e_shoff adjusted to " << elf_header->e_shoff; + } +} + +// Helper for ResizeSection(). Adjust all section headers for the hole. +template +static void AdjustSectionHeadersForHole(Elf* elf, + typename ELF::Off hole_start, + ssize_t hole_size) { + size_t string_index; + elf_getshdrstrndx(elf, &string_index); + + Elf_Scn* section = NULL; + while ((section = elf_nextscn(elf, section)) != NULL) { + auto section_header = ELF::getshdr(section); + std::string name = elf_strptr(elf, string_index, section_header->sh_name); + + if (section_header->sh_offset > hole_start) { + section_header->sh_offset += hole_size; + VLOG(1) << "section " << name + << " sh_offset adjusted to " << section_header->sh_offset; + } else { + section_header->sh_addr -= hole_size; + VLOG(1) << "section " << name + << " sh_addr adjusted to " << section_header->sh_addr; + } + } +} + +// Helper for ResizeSection(). Adjust the offsets of any program headers +// that have offsets currently beyond the hole start. +template +static void AdjustProgramHeaderOffsets(typename ELF::Phdr* program_headers, + size_t count, + typename ELF::Off hole_start, + ssize_t hole_size) { + for (size_t i = 0; i < count; ++i) { + typename ELF::Phdr* program_header = &program_headers[i]; + + if (program_header->p_offset > hole_start) { + // The hole start is past this segment, so adjust offset. + program_header->p_offset += hole_size; + VLOG(1) << "phdr[" << i + << "] p_offset adjusted to "<< program_header->p_offset; + } else { + program_header->p_vaddr -= hole_size; + program_header->p_paddr -= hole_size; + VLOG(1) << "phdr[" << i + << "] p_vaddr adjusted to "<< program_header->p_vaddr + << "; p_paddr adjusted to "<< program_header->p_paddr; + } + } +} + +// Helper for ResizeSection(). Find the first loadable segment in the +// file. We expect it to map from file offset zero. +template +static typename ELF::Phdr* FindLoadSegmentForHole(typename ELF::Phdr* program_headers, + size_t count, + typename ELF::Off hole_start) { + for (size_t i = 0; i < count; ++i) { + typename ELF::Phdr* program_header = &program_headers[i]; + + if (program_header->p_type == PT_LOAD && + program_header->p_offset <= hole_start && + (program_header->p_offset + program_header->p_filesz) >= hole_start ) { + return program_header; + } + } + LOG(FATAL) << "Cannot locate a LOAD segment with hole_start=0x" << std::hex << hole_start; + NOTREACHED(); + + return nullptr; +} + +// Helper for ResizeSection(). Rewrite program headers. +template +static void RewriteProgramHeadersForHole(Elf* elf, + typename ELF::Off hole_start, + ssize_t hole_size) { + const typename ELF::Ehdr* elf_header = ELF::getehdr(elf); + CHECK(elf_header); + + typename ELF::Phdr* elf_program_header = ELF::getphdr(elf); + CHECK(elf_program_header); + + const size_t program_header_count = elf_header->e_phnum; + + // Locate the segment that we can overwrite to form the new LOAD entry, + // and the segment that we are going to split into two parts. + typename ELF::Phdr* target_load_header = + FindLoadSegmentForHole(elf_program_header, program_header_count, hole_start); + + VLOG(1) << "phdr[" << target_load_header - elf_program_header << "] adjust"; + // Adjust PT_LOAD program header memsz and filesz + target_load_header->p_filesz += hole_size; + target_load_header->p_memsz += hole_size; + + // Adjust the offsets and p_vaddrs + AdjustProgramHeaderOffsets(elf_program_header, + program_header_count, + hole_start, + hole_size); +} + +// Helper for ResizeSection(). Locate and return the dynamic section. +template +static Elf_Scn* GetDynamicSection(Elf* elf) { + const typename ELF::Ehdr* elf_header = ELF::getehdr(elf); + CHECK(elf_header); + + const typename ELF::Phdr* elf_program_header = ELF::getphdr(elf); + CHECK(elf_program_header); + + // Find the program header that describes the dynamic section. + const typename ELF::Phdr* dynamic_program_header = NULL; + for (size_t i = 0; i < elf_header->e_phnum; ++i) { + const typename ELF::Phdr* program_header = &elf_program_header[i]; + + if (program_header->p_type == PT_DYNAMIC) { + dynamic_program_header = program_header; + } + } + CHECK(dynamic_program_header); + + // Now find the section with the same offset as this program header. + Elf_Scn* dynamic_section = NULL; + Elf_Scn* section = NULL; + while ((section = elf_nextscn(elf, section)) != NULL) { + typename ELF::Shdr* section_header = ELF::getshdr(section); + + if (section_header->sh_offset == dynamic_program_header->p_offset) { + dynamic_section = section; + } + } + CHECK(dynamic_section != NULL); + + return dynamic_section; +} + +// Helper for ResizeSection(). Adjust the .dynamic section for the hole. +template +void ElfFile::AdjustDynamicSectionForHole(Elf_Scn* dynamic_section, + typename ELF::Off hole_start, + ssize_t hole_size, + relocations_type_t relocations_type) { + CHECK(relocations_type != NONE); + Elf_Data* data = GetSectionData(dynamic_section); + + auto dynamic_base = reinterpret_cast(data->d_buf); + std::vector dynamics( + dynamic_base, + dynamic_base + data->d_size / sizeof(dynamics[0])); + + if (hole_size > 0) { // expanding + hole_start += hole_size; + } + + for (size_t i = 0; i < dynamics.size(); ++i) { + typename ELF::Dyn* dynamic = &dynamics[i]; + const typename ELF::Sword tag = dynamic->d_tag; + + // Any tags that hold offsets are adjustment candidates. + const bool is_adjustable = (tag == DT_PLTGOT || + tag == DT_HASH || + tag == DT_GNU_HASH || + tag == DT_STRTAB || + tag == DT_SYMTAB || + tag == DT_RELA || + tag == DT_INIT || + tag == DT_FINI || + tag == DT_REL || + tag == DT_JMPREL || + tag == DT_INIT_ARRAY || + tag == DT_FINI_ARRAY || + tag == DT_ANDROID_REL|| + tag == DT_ANDROID_RELA); + + if (is_adjustable && dynamic->d_un.d_ptr <= hole_start) { + dynamic->d_un.d_ptr -= hole_size; + VLOG(1) << "dynamic[" << i << "] " << dynamic->d_tag + << " d_ptr adjusted to " << dynamic->d_un.d_ptr; + } + + // DT_RELSZ or DT_RELASZ indicate the overall size of relocations. + // Only one will be present. Adjust by hole size. + if (tag == DT_RELSZ || tag == DT_RELASZ || tag == DT_ANDROID_RELSZ || tag == DT_ANDROID_RELASZ) { + dynamic->d_un.d_val += hole_size; + VLOG(1) << "dynamic[" << i << "] " << dynamic->d_tag + << " d_val adjusted to " << dynamic->d_un.d_val; + } + + // Ignore DT_RELCOUNT and DT_RELACOUNT: (1) nobody uses them and + // technically (2) the relative relocation count is not changed. + + // DT_RELENT and DT_RELAENT don't change, ignore them as well. + } + + void* section_data = &dynamics[0]; + size_t bytes = dynamics.size() * sizeof(dynamics[0]); + RewriteSectionData(dynamic_section, section_data, bytes); +} + +// Resize a section. If the new size is larger than the current size, open +// up a hole by increasing file offsets that come after the hole. If smaller +// than the current size, remove the hole by decreasing those offsets. +template +void ElfFile::ResizeSection(Elf* elf, Elf_Scn* section, size_t new_size, + typename ELF::Word new_sh_type, + relocations_type_t relocations_type) { + + size_t string_index; + elf_getshdrstrndx(elf, &string_index); + auto section_header = ELF::getshdr(section); + std::string name = elf_strptr(elf, string_index, section_header->sh_name); + + if (section_header->sh_size == new_size) { + return; + } + + // Require that the section size and the data size are the same. True + // in practice for all sections we resize when packing or unpacking. + Elf_Data* data = GetSectionData(section); + CHECK(data->d_off == 0 && data->d_size == section_header->sh_size); + + // Require that the section is not zero-length (that is, has allocated + // data that we can validly expand). + CHECK(data->d_size && data->d_buf); + + const auto hole_start = section_header->sh_offset; + const ssize_t hole_size = new_size - data->d_size; + + VLOG_IF(1, (hole_size > 0)) << "expand section (" << name << ") size: " << + data->d_size << " -> " << (data->d_size + hole_size); + VLOG_IF(1, (hole_size < 0)) << "shrink section (" << name << ") size: " << + data->d_size << " -> " << (data->d_size + hole_size); + + // libelf overrides sh_entsize for known sh_types, so it does not matter what we set + // for SHT_REL/SHT_RELA. + typename ELF::Xword new_entsize = + (new_sh_type == SHT_ANDROID_REL || new_sh_type == SHT_ANDROID_RELA) ? 1 : 0; + + VLOG(1) << "Update section (" << name << ") entry size: " << + section_header->sh_entsize << " -> " << new_entsize; + + // Resize the data and the section header. + data->d_size += hole_size; + section_header->sh_size += hole_size; + section_header->sh_entsize = new_entsize; + section_header->sh_type = new_sh_type; + + // Add the hole size to all offsets in the ELF file that are after the + // start of the hole. If the hole size is positive we are expanding the + // section to create a new hole; if negative, we are closing up a hole. + + // Start with the main ELF header. + typename ELF::Ehdr* elf_header = ELF::getehdr(elf); + AdjustElfHeaderForHole(elf_header, hole_start, hole_size); + + // Adjust all section headers. + AdjustSectionHeadersForHole(elf, hole_start, hole_size); + + // Rewrite the program headers to either split or coalesce segments, + // and adjust dynamic entries to match. + RewriteProgramHeadersForHole(elf, hole_start, hole_size); + + Elf_Scn* dynamic_section = GetDynamicSection(elf); + AdjustDynamicSectionForHole(dynamic_section, hole_start, hole_size, relocations_type); +} + +// Find the first slot in a dynamics array with the given tag. The array +// always ends with a free (unused) element, and which we exclude from the +// search. Returns dynamics->size() if not found. +template +static size_t FindDynamicEntry(typename ELF::Sword tag, + std::vector* dynamics) { + // Loop until the penultimate entry. We exclude the end sentinel. + for (size_t i = 0; i < dynamics->size() - 1; ++i) { + if (dynamics->at(i).d_tag == tag) { + return i; + } + } + + // The tag was not found. + return dynamics->size(); +} + +// Replace dynamic entry. +template +static void ReplaceDynamicEntry(typename ELF::Sword tag, + const typename ELF::Dyn& dyn, + std::vector* dynamics) { + const size_t slot = FindDynamicEntry(tag, dynamics); + if (slot == dynamics->size()) { + LOG(FATAL) << "Dynamic slot is not found for tag=" << tag; + } + + // Replace this entry with the one supplied. + dynamics->at(slot) = dyn; + VLOG(1) << "dynamic[" << slot << "] overwritten with " << dyn.d_tag; +} + +// Remove relative entries from dynamic relocations and write as packed +// data into android packed relocations. +template +bool ElfFile::PackRelocations() { + // Load the ELF file into libelf. + if (!Load()) { + LOG(ERROR) << "Failed to load as ELF"; + return false; + } + + // Retrieve the current dynamic relocations section data. + Elf_Data* data = GetSectionData(relocations_section_); + // we always pack rela, because packed format is pretty much the same + std::vector relocations; + + if (relocations_type_ == REL) { + // Convert data to a vector of relocations. + const typename ELF::Rel* relocations_base = reinterpret_cast(data->d_buf); + ConvertRelArrayToRelaVector(relocations_base, + data->d_size / sizeof(typename ELF::Rel), &relocations); + LOG(INFO) << "Relocations : REL"; + } else if (relocations_type_ == RELA) { + // Convert data to a vector of relocations with addends. + const typename ELF::Rela* relocations_base = reinterpret_cast(data->d_buf); + relocations = std::vector( + relocations_base, + relocations_base + data->d_size / sizeof(relocations[0])); + + LOG(INFO) << "Relocations : RELA"; + } else { + NOTREACHED(); + } + + return PackTypedRelocations(&relocations); +} + +// Helper for PackRelocations(). Rel type is one of ELF::Rel or ELF::Rela. +template +bool ElfFile::PackTypedRelocations(std::vector* relocations) { + typedef typename ELF::Rela Rela; + + // If no relocations then we have nothing packable. Perhaps + // the shared object has already been packed? + if (relocations->empty()) { + LOG(ERROR) << "No relocations found (already packed?)"; + return false; + } + + const size_t rel_size = + relocations_type_ == RELA ? sizeof(typename ELF::Rela) : sizeof(typename ELF::Rel); + const size_t initial_bytes = relocations->size() * rel_size; + + LOG(INFO) << "Unpacked : " << initial_bytes << " bytes"; + std::vector packed; + RelocationPacker packer; + + // Pack relocations: dry run to estimate memory savings. + packer.PackRelocations(*relocations, &packed); + const size_t packed_bytes_estimate = packed.size() * sizeof(packed[0]); + LOG(INFO) << "Packed (no padding): " << packed_bytes_estimate << " bytes"; + + if (packed.empty()) { + LOG(INFO) << "Too few relocations to pack"; + return false; + } + + // Pre-calculate the size of the hole we will close up when we rewrite + // dynamic relocations. We have to adjust relocation addresses to + // account for this. + typename ELF::Shdr* section_header = ELF::getshdr(relocations_section_); + ssize_t hole_size = initial_bytes - packed_bytes_estimate; + + // hole_size needs to be page_aligned. + hole_size -= hole_size % kPreserveAlignment; + + LOG(INFO) << "Compaction : " << hole_size << " bytes"; + + // Adjusting for alignment may have removed any packing benefit. + if (hole_size == 0) { + LOG(INFO) << "Too few relocations to pack after alignment"; + return false; + } + + if (hole_size <= 0) { + LOG(INFO) << "Packing relocations saves no space"; + return false; + } + + size_t data_padding_bytes = is_padding_relocations_ ? + initial_bytes - packed_bytes_estimate : + initial_bytes - hole_size - packed_bytes_estimate; + + // pad data + std::vector padding(data_padding_bytes, 0); + packed.insert(packed.end(), padding.begin(), padding.end()); + + const void* packed_data = &packed[0]; + + // Run a loopback self-test as a check that packing is lossless. + std::vector unpacked; + packer.UnpackRelocations(packed, &unpacked); + CHECK(unpacked.size() == relocations->size()); + CHECK(!memcmp(&unpacked[0], + &relocations->at(0), + unpacked.size() * sizeof(unpacked[0]))); + + // Rewrite the current dynamic relocations section with packed one then shrink it to size. + const size_t bytes = packed.size() * sizeof(packed[0]); + ResizeSection(elf_, relocations_section_, bytes, + relocations_type_ == REL ? SHT_ANDROID_REL : SHT_ANDROID_RELA, relocations_type_); + RewriteSectionData(relocations_section_, packed_data, bytes); + + // TODO (dimitry): fix string table and replace .rel.dyn/plt with .android.rel.dyn/plt + + // Rewrite .dynamic and rename relocation tags describing the packed android + // relocations. + Elf_Data* data = GetSectionData(dynamic_section_); + const typename ELF::Dyn* dynamic_base = reinterpret_cast(data->d_buf); + std::vector dynamics( + dynamic_base, + dynamic_base + data->d_size / sizeof(dynamics[0])); + section_header = ELF::getshdr(relocations_section_); + { + typename ELF::Dyn dyn; + dyn.d_tag = relocations_type_ == REL ? DT_ANDROID_REL : DT_ANDROID_RELA; + dyn.d_un.d_ptr = section_header->sh_addr; + ReplaceDynamicEntry(relocations_type_ == REL ? DT_REL : DT_RELA, dyn, &dynamics); + } + { + typename ELF::Dyn dyn; + dyn.d_tag = relocations_type_ == REL ? DT_ANDROID_RELSZ : DT_ANDROID_RELASZ; + dyn.d_un.d_val = section_header->sh_size; + ReplaceDynamicEntry(relocations_type_ == REL ? DT_RELSZ : DT_RELASZ, dyn, &dynamics); + } + + const void* dynamics_data = &dynamics[0]; + const size_t dynamics_bytes = dynamics.size() * sizeof(dynamics[0]); + RewriteSectionData(dynamic_section_, dynamics_data, dynamics_bytes); + + Flush(); + return true; +} + +// Find packed relative relocations in the packed android relocations +// section, unpack them, and rewrite the dynamic relocations section to +// contain unpacked data. +template +bool ElfFile::UnpackRelocations() { + // Load the ELF file into libelf. + if (!Load()) { + LOG(ERROR) << "Failed to load as ELF"; + return false; + } + + typename ELF::Shdr* section_header = ELF::getshdr(relocations_section_); + // Retrieve the current packed android relocations section data. + Elf_Data* data = GetSectionData(relocations_section_); + + // Convert data to a vector of bytes. + const uint8_t* packed_base = reinterpret_cast(data->d_buf); + std::vector packed( + packed_base, + packed_base + data->d_size / sizeof(packed[0])); + + if ((section_header->sh_type == SHT_ANDROID_RELA || section_header->sh_type == SHT_ANDROID_REL) && + packed.size() > 3 && + packed[0] == 'A' && + packed[1] == 'P' && + (packed[2] == 'U' || packed[2] == 'S') && + packed[3] == '2') { + LOG(INFO) << "Relocations : " << (relocations_type_ == REL ? "REL" : "RELA"); + } else { + LOG(ERROR) << "Packed relocations not found (not packed?)"; + return false; + } + + return UnpackTypedRelocations(packed); +} + +// Helper for UnpackRelocations(). Rel type is one of ELF::Rel or ELF::Rela. +template +bool ElfFile::UnpackTypedRelocations(const std::vector& packed) { + // Unpack the data to re-materialize the relative relocations. + const size_t packed_bytes = packed.size() * sizeof(packed[0]); + LOG(INFO) << "Packed : " << packed_bytes << " bytes"; + std::vector unpacked_relocations; + RelocationPacker packer; + packer.UnpackRelocations(packed, &unpacked_relocations); + + const size_t relocation_entry_size = + relocations_type_ == REL ? sizeof(typename ELF::Rel) : sizeof(typename ELF::Rela); + const size_t unpacked_bytes = unpacked_relocations.size() * relocation_entry_size; + LOG(INFO) << "Unpacked : " << unpacked_bytes << " bytes"; + + // Retrieve the current dynamic relocations section data. + Elf_Data* data = GetSectionData(relocations_section_); + + LOG(INFO) << "Relocations : " << unpacked_relocations.size() << " entries"; + + // If we found the same number of null relocation entries in the dynamic + // relocations section as we hold as unpacked relative relocations, then + // this is a padded file. + + const bool is_padded = packed_bytes == unpacked_bytes; + + // Unless padded, pre-apply relative relocations to account for the + // hole, and pre-adjust all relocation offsets accordingly. + typename ELF::Shdr* section_header = ELF::getshdr(relocations_section_); + + if (!is_padded) { + LOG(INFO) << "Expansion : " << unpacked_bytes - packed_bytes << " bytes"; + } + + // Rewrite the current dynamic relocations section with unpacked version of + // relocations. + const void* section_data = nullptr; + std::vector unpacked_rel_relocations; + if (relocations_type_ == RELA) { + section_data = &unpacked_relocations[0]; + } else if (relocations_type_ == REL) { + ConvertRelaVectorToRelVector(unpacked_relocations, &unpacked_rel_relocations); + section_data = &unpacked_rel_relocations[0]; + } else { + NOTREACHED(); + } + + ResizeSection(elf_, relocations_section_, unpacked_bytes, + relocations_type_ == REL ? SHT_REL : SHT_RELA, relocations_type_); + RewriteSectionData(relocations_section_, section_data, unpacked_bytes); + + // Rewrite .dynamic to remove two tags describing packed android relocations. + data = GetSectionData(dynamic_section_); + const typename ELF::Dyn* dynamic_base = reinterpret_cast(data->d_buf); + std::vector dynamics( + dynamic_base, + dynamic_base + data->d_size / sizeof(dynamics[0])); + { + typename ELF::Dyn dyn; + dyn.d_tag = relocations_type_ == REL ? DT_REL : DT_RELA; + dyn.d_un.d_ptr = section_header->sh_addr; + ReplaceDynamicEntry(relocations_type_ == REL ? DT_ANDROID_REL : DT_ANDROID_RELA, + dyn, &dynamics); + } + + { + typename ELF::Dyn dyn; + dyn.d_tag = relocations_type_ == REL ? DT_RELSZ : DT_RELASZ; + dyn.d_un.d_val = section_header->sh_size; + ReplaceDynamicEntry(relocations_type_ == REL ? DT_ANDROID_RELSZ : DT_ANDROID_RELASZ, + dyn, &dynamics); + } + + const void* dynamics_data = &dynamics[0]; + const size_t dynamics_bytes = dynamics.size() * sizeof(dynamics[0]); + RewriteSectionData(dynamic_section_, dynamics_data, dynamics_bytes); + + Flush(); + return true; +} + +// Flush rewritten shared object file data. +template +void ElfFile::Flush() { + // Flag all ELF data held in memory as needing to be written back to the + // file, and tell libelf that we have controlled the file layout. + elf_flagelf(elf_, ELF_C_SET, ELF_F_DIRTY); + elf_flagelf(elf_, ELF_C_SET, ELF_F_LAYOUT); + + // Write ELF data back to disk. + const off_t file_bytes = elf_update(elf_, ELF_C_WRITE); + if (file_bytes == -1) { + LOG(ERROR) << "elf_update failed: " << elf_errmsg(elf_errno()); + } + + CHECK(file_bytes > 0); + VLOG(1) << "elf_update returned: " << file_bytes; + + // Clean up libelf, and truncate the output file to the number of bytes + // written by elf_update(). + elf_end(elf_); + elf_ = NULL; + const int truncate = ftruncate(fd_, file_bytes); + CHECK(truncate == 0); +} + +template +void ElfFile::ConvertRelArrayToRelaVector(const typename ELF::Rel* rel_array, + size_t rel_array_size, + std::vector* rela_vector) { + for (size_t i = 0; ipush_back(rela); + } +} + +template +void ElfFile::ConvertRelaVectorToRelVector(const std::vector& rela_vector, + std::vector* rel_vector) { + for (auto rela : rela_vector) { + typename ELF::Rel rel; + rel.r_offset = rela.r_offset; + rel.r_info = rela.r_info; + CHECK(rela.r_addend == 0); + rel_vector->push_back(rel); + } +} + +template class ElfFile; +template class ElfFile; + +} // namespace relocation_packer diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/elf_file.h b/third_party/android_platform/bionic/tools/relocation_packer/src/elf_file.h new file mode 100644 index 000000000000..73c31923fcc8 --- /dev/null +++ b/third_party/android_platform/bionic/tools/relocation_packer/src/elf_file.h @@ -0,0 +1,155 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// ELF shared object file updates handler. +// +// Provides functions to remove relative relocations from the .rel.dyn +// or .rela.dyn sections and pack into .android.rel.dyn or .android.rela.dyn, +// and unpack to return the file to its pre-packed state. +// +// Files to be packed or unpacked must include an existing .android.rel.dyn +// or android.rela.dyn section. A standard libchrome..so will not +// contain this section, so the following can be used to add one: +// +// echo -n 'NULL' >/tmp/small +// if file libchrome..so | grep -q 'ELF 32'; then +// arm-linux-androideabi-objcopy +// --add-section .android.rel.dyn=/tmp/small +// libchrome..so libchrome..so.packed +// else +// aarch64-linux-android-objcopy +// --add-section .android.rela.dyn=/tmp/small +// libchrome..so libchrome..so.packed +// fi +// rm /tmp/small +// +// To use, open the file and pass the file descriptor to the constructor, +// then pack or unpack as desired. Packing or unpacking will flush the file +// descriptor on success. Example: +// +// int fd = open(..., O_RDWR); +// ElfFile elf_file(fd); +// bool status; +// if (is_packing) +// status = elf_file.PackRelocations(); +// else +// status = elf_file.UnpackRelocations(); +// close(fd); +// +// SetPadding() causes PackRelocations() to pad .rel.dyn or .rela.dyn with +// NONE-type entries rather than cutting a hole out of the shared object +// file. This keeps all load addresses and offsets constant, and enables +// easier debugging and testing. +// +// A packed shared object file has all of its relative relocations +// removed from .rel.dyn or .rela.dyn, and replaced as packed data in +// .android.rel.dyn or .android.rela.dyn respectively. The resulting file +// is shorter than its non-packed original. +// +// Unpacking a packed file restores the file to its non-packed state, by +// expanding the packed data in .android.rel.dyn or .android.rela.dyn, +// combining the relative relocations with the data already in .rel.dyn +// or .rela.dyn, and then writing back the now expanded section. + +#ifndef TOOLS_RELOCATION_PACKER_SRC_ELF_FILE_H_ +#define TOOLS_RELOCATION_PACKER_SRC_ELF_FILE_H_ + +#include +#include + +#include "elf.h" +#include "libelf.h" +#include "packer.h" + +namespace relocation_packer { + +// An ElfFile reads shared objects, and shuttles relative relocations +// between .rel.dyn or .rela.dyn and .android.rel.dyn or .android.rela.dyn +// sections. +template +class ElfFile { + public: + explicit ElfFile(int fd) + : fd_(fd), is_padding_relocations_(false), elf_(NULL), + relocations_section_(NULL), dynamic_section_(NULL), + relocations_type_(NONE) {} + ~ElfFile() {} + + // Set padding mode. When padding, PackRelocations() will not shrink + // the .rel.dyn or .rela.dyn section, but instead replace relative with + // NONE-type entries. + // |flag| is true to pad .rel.dyn or .rela.dyn, false to shrink it. + inline void SetPadding(bool flag) { is_padding_relocations_ = flag; } + + // Transfer relative relocations from .rel.dyn or .rela.dyn to a packed + // representation in .android.rel.dyn or .android.rela.dyn. Returns true + // on success. + bool PackRelocations(); + + // Transfer relative relocations from a packed representation in + // .android.rel.dyn or .android.rela.dyn to .rel.dyn or .rela.dyn. Returns + // true on success. + bool UnpackRelocations(); + + private: + enum relocations_type_t { + NONE = 0, REL, RELA + }; + + // Load a new ElfFile from a filedescriptor. If flushing, the file must + // be open for read/write. Returns true on successful ELF file load. + // |fd| is an open file descriptor for the shared object. + bool Load(); + + // Templated packer, helper for PackRelocations(). Rel type is one of + // ELF::Rel or ELF::Rela. + bool PackTypedRelocations(std::vector* relocations); + + // Templated unpacker, helper for UnpackRelocations(). Rel type is one of + // ELF::Rel or ELF::Rela. + bool UnpackTypedRelocations(const std::vector& packed); + + // Write ELF file changes. + void Flush(); + + void AdjustRelativeRelocationTargets(typename ELF::Off hole_start, + ssize_t hole_size, + std::vector* relocations); + + static void ResizeSection(Elf* elf, Elf_Scn* section, size_t new_size, + typename ELF::Word new_sh_type, relocations_type_t relocations_type); + + static void AdjustDynamicSectionForHole(Elf_Scn* dynamic_section, + typename ELF::Off hole_start, + ssize_t hole_size, + relocations_type_t relocations_type); + + static void ConvertRelArrayToRelaVector(const typename ELF::Rel* rel_array, size_t rel_array_size, + std::vector* rela_vector); + + static void ConvertRelaVectorToRelVector(const std::vector& rela_vector, + std::vector* rel_vector); + + + // File descriptor opened on the shared object. + int fd_; + + // If set, pad rather than shrink .rel.dyn or .rela.dyn. Primarily for + // debugging, allows packing to be checked without affecting load addresses. + bool is_padding_relocations_; + + // Libelf handle, assigned by Load(). + Elf* elf_; + + // Sections that we manipulate, assigned by Load(). + Elf_Scn* relocations_section_; + Elf_Scn* dynamic_section_; + + // Relocation type found, assigned by Load(). + relocations_type_t relocations_type_; +}; + +} // namespace relocation_packer + +#endif // TOOLS_RELOCATION_PACKER_SRC_ELF_FILE_H_ diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/elf_file_unittest.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/elf_file_unittest.cc new file mode 100644 index 000000000000..434f10102d26 --- /dev/null +++ b/third_party/android_platform/bionic/tools/relocation_packer/src/elf_file_unittest.cc @@ -0,0 +1,188 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "elf_file.h" + +#include +#include +#include +#include +#include +#include "debug.h" +#include "elf_traits.h" +#include "gtest/gtest.h" + +namespace { + +void GetDataFilePath(const char* name, std::string* path) { + std::string data_dir; + + const char* bindir = getenv("bindir"); + if (bindir) { + data_dir = std::string(bindir); + } else { + char path[PATH_MAX]; + memset(path, 0, sizeof(path)); + ASSERT_NE(-1, readlink("/proc/self/exe", path, sizeof(path) - 1)); + + data_dir = std::string(path); + size_t pos = data_dir.rfind('/'); + ASSERT_NE(std::string::npos, pos); + + data_dir.erase(pos); + } + + *path = data_dir + "/" + name; +} + +void OpenRelocsTestFile(const char* name, FILE** stream) { + std::string path; + GetDataFilePath(name, &path); + + FILE* testfile = fopen(path.c_str(), "rb"); + ASSERT_FALSE(testfile == NULL) << "Error opening '" << path << "'"; + + FILE* temporary = tmpfile(); + ASSERT_FALSE(temporary == NULL); + + static const size_t buffer_size = 4096; + unsigned char buffer[buffer_size]; + + size_t bytes; + do { + bytes = fread(buffer, 1, sizeof(buffer), testfile); + ASSERT_EQ(bytes, fwrite(buffer, 1, bytes, temporary)); + } while (bytes > 0); + + ASSERT_EQ(0, fclose(testfile)); + ASSERT_EQ(0, fseek(temporary, 0, SEEK_SET)); + ASSERT_EQ(0, lseek(fileno(temporary), 0, SEEK_SET)); + + *stream = temporary; +} + +void OpenRelocsTestFiles(const std::string& arch, FILE** relocs_so, FILE** packed_relocs_so) { + const std::string base = std::string("elf_file_unittest_relocs_") + arch; + const std::string relocs = base + ".so"; + const std::string packed_relocs = base + "_packed.so"; + + OpenRelocsTestFile(relocs.c_str(), relocs_so); + OpenRelocsTestFile(packed_relocs.c_str(), packed_relocs_so); +} + +void CloseRelocsTestFile(FILE* temporary) { + fclose(temporary); +} + +void CloseRelocsTestFiles(FILE* relocs_so, FILE* packed_relocs_so) { + CloseRelocsTestFile(relocs_so); + CloseRelocsTestFile(packed_relocs_so); +} + +void CheckFileContentsEqual(FILE* first, FILE* second) { + ASSERT_EQ(0, fseek(first, 0, SEEK_SET)); + ASSERT_EQ(0, fseek(second, 0, SEEK_SET)); + + static const size_t buffer_size = 4096; + unsigned char first_buffer[buffer_size]; + unsigned char second_buffer[buffer_size]; + + do { + size_t first_read = fread(first_buffer, 1, sizeof(first_buffer), first); + size_t second_read = fread(second_buffer, 1, sizeof(second_buffer), second); + + EXPECT_EQ(first_read, second_read); + EXPECT_EQ(0, memcmp(first_buffer, second_buffer, first_read)); + } while (!feof(first) && !feof(second)); + + EXPECT_TRUE(feof(first) && feof(second)); +} + +template +static void ProcessUnpack(FILE* relocs_so, FILE* packed_relocs_so) { + relocation_packer::ElfFile elf_file(fileno(packed_relocs_so)); + + // Ensure packing fails (already packed). + EXPECT_FALSE(elf_file.PackRelocations()); + + // Unpack golden relocations, and check files are now identical. + EXPECT_TRUE(elf_file.UnpackRelocations()); + CheckFileContentsEqual(packed_relocs_so, relocs_so); + + CloseRelocsTestFiles(relocs_so, packed_relocs_so); +} + +static void RunUnpackRelocationsTestFor(const std::string& arch) { + ASSERT_NE(static_cast(EV_NONE), elf_version(EV_CURRENT)); + + FILE* relocs_so = NULL; + FILE* packed_relocs_so = NULL; + OpenRelocsTestFiles(arch, &relocs_so, &packed_relocs_so); + + if (relocs_so != NULL && packed_relocs_so != NULL) { + // lets detect elf class + ASSERT_EQ(0, fseek(relocs_so, EI_CLASS, SEEK_SET)) + << "Invalid file length: " << strerror(errno); + uint8_t elf_class = 0; + ASSERT_EQ(1U, fread(&elf_class, 1, 1, relocs_so)); + ASSERT_EQ(0, fseek(relocs_so, 0, SEEK_SET)); + if (elf_class == ELFCLASS32) { + ProcessUnpack(relocs_so, packed_relocs_so); + } else { + ProcessUnpack(relocs_so, packed_relocs_so); + } + } +} + +template +static void ProcessPack(FILE* relocs_so, FILE* packed_relocs_so) { + relocation_packer::ElfFile elf_file(fileno(relocs_so)); + + // Ensure unpacking fails (not packed). + EXPECT_FALSE(elf_file.UnpackRelocations()); + + // Pack relocations, and check files are now identical. + EXPECT_TRUE(elf_file.PackRelocations()); + CheckFileContentsEqual(relocs_so, packed_relocs_so); + + CloseRelocsTestFiles(relocs_so, packed_relocs_so); +} + +static void RunPackRelocationsTestFor(const std::string& arch) { + ASSERT_NE(static_cast(EV_NONE), elf_version(EV_CURRENT)); + + FILE* relocs_so = NULL; + FILE* packed_relocs_so = NULL; + OpenRelocsTestFiles(arch, &relocs_so, &packed_relocs_so); + + if (relocs_so != NULL && packed_relocs_so != NULL) { + // lets detect elf class + ASSERT_EQ(0, fseek(packed_relocs_so, EI_CLASS, SEEK_SET)) + << "Invalid file length: " << strerror(errno); + uint8_t elf_class = 0; + ASSERT_EQ(1U, fread(&elf_class, 1, 1, packed_relocs_so)); + fseek(packed_relocs_so, 0, SEEK_SET); + if (elf_class == ELFCLASS32) { + ProcessPack(relocs_so, packed_relocs_so); + } else { + ProcessPack(relocs_so, packed_relocs_so); + } + } +} + +} // namespace + +namespace relocation_packer { + +TEST(ElfFile, PackRelocations) { + RunPackRelocationsTestFor("arm32"); + RunPackRelocationsTestFor("arm64"); +} + +TEST(ElfFile, UnpackRelocations) { + RunUnpackRelocationsTestFor("arm32"); + RunUnpackRelocationsTestFor("arm64"); +} + +} // namespace relocation_packer diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/elf_traits.h b/third_party/android_platform/bionic/tools/relocation_packer/src/elf_traits.h new file mode 100644 index 000000000000..41b06c8576d6 --- /dev/null +++ b/third_party/android_platform/bionic/tools/relocation_packer/src/elf_traits.h @@ -0,0 +1,64 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Target-specific ELF type traits. + +#ifndef TOOLS_RELOCATION_PACKER_SRC_ELF_TRAITS_H_ +#define TOOLS_RELOCATION_PACKER_SRC_ELF_TRAITS_H_ + +#include "elf.h" +#include "libelf.h" + +// ELF is a traits structure used to provide convenient aliases for +// 32/64 bit Elf types and functions, depending on the target file. + +struct ELF32_traits { + typedef Elf32_Addr Addr; + typedef Elf32_Dyn Dyn; + typedef Elf32_Ehdr Ehdr; + typedef Elf32_Off Off; + typedef Elf32_Phdr Phdr; + typedef Elf32_Rel Rel; + typedef Elf32_Rela Rela; + typedef Elf32_Shdr Shdr; + typedef Elf32_Sword Sword; + typedef Elf32_Sxword Sxword; + typedef Elf32_Sym Sym; + typedef Elf32_Word Word; + typedef Elf32_Xword Xword; + typedef Elf32_Half Half; + + static inline Ehdr* getehdr(Elf* elf) { return elf32_getehdr(elf); } + static inline Phdr* getphdr(Elf* elf) { return elf32_getphdr(elf); } + static inline Shdr* getshdr(Elf_Scn* scn) { return elf32_getshdr(scn); } + static inline Word elf_r_type(Word info) { return ELF32_R_TYPE(info); } + static inline int elf_st_type(uint8_t info) { return ELF32_ST_TYPE(info); } + static inline Word elf_r_sym(Word info) { return ELF32_R_SYM(info); } +}; + +struct ELF64_traits { + typedef Elf64_Addr Addr; + typedef Elf64_Dyn Dyn; + typedef Elf64_Ehdr Ehdr; + typedef Elf64_Off Off; + typedef Elf64_Phdr Phdr; + typedef Elf64_Rel Rel; + typedef Elf64_Rela Rela; + typedef Elf64_Shdr Shdr; + typedef Elf64_Sword Sword; + typedef Elf64_Sxword Sxword; + typedef Elf64_Sym Sym; + typedef Elf64_Word Word; + typedef Elf64_Xword Xword; + typedef Elf64_Half Half; + + static inline Ehdr* getehdr(Elf* elf) { return elf64_getehdr(elf); } + static inline Phdr* getphdr(Elf* elf) { return elf64_getphdr(elf); } + static inline Shdr* getshdr(Elf_Scn* scn) { return elf64_getshdr(scn); } + static inline Xword elf_r_type(Xword info) { return ELF64_R_TYPE(info); } + static inline int elf_st_type(uint8_t info) { return ELF64_ST_TYPE(info); } + static inline Word elf_r_sym(Xword info) { return ELF64_R_SYM(info); } +}; + +#endif // TOOLS_RELOCATION_PACKER_SRC_ELF_TRAITS_H_ diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/leb128.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/leb128.cc new file mode 100644 index 000000000000..101c5577863b --- /dev/null +++ b/third_party/android_platform/bionic/tools/relocation_packer/src/leb128.cc @@ -0,0 +1,87 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "leb128.h" + +#include +#include + +#include "elf_traits.h" + +namespace relocation_packer { + +// Empty constructor and destructor to silence chromium-style. +template +Leb128Encoder::Leb128Encoder() { } + +template +Leb128Encoder::~Leb128Encoder() { } + +// Add a single value to the encoding. Values are encoded with variable +// length. The least significant 7 bits of each byte hold 7 bits of data, +// and the most significant bit is set on each byte except the last. +template +void Leb128Encoder::Enqueue(uint_t value) { + uint_t uvalue = static_cast(value); + do { + const uint8_t byte = uvalue & 127; + uvalue >>= 7; + encoding_.push_back((uvalue ? 128 : 0) | byte); + } while (uvalue); +} + +// Add a vector of values to the encoding. +template +void Leb128Encoder::EnqueueAll(const std::vector& values) { + for (size_t i = 0; i < values.size(); ++i) { + Enqueue(values[i]); + } +} + +// Create a new decoder for the given encoded stream. +template +Leb128Decoder::Leb128Decoder(const std::vector& encoding, size_t start_with) { + encoding_ = encoding; + cursor_ = start_with; +} + +// Empty destructor to silence chromium-style. +template +Leb128Decoder::~Leb128Decoder() { } + +// Decode and retrieve a single value from the encoding. Read forwards until +// a byte without its most significant bit is found, then read the 7 bit +// fields of the bytes spanned to re-form the value. +template +uint_t Leb128Decoder::Dequeue() { + uint_t value = 0; + + size_t shift = 0; + uint8_t byte; + + // Loop until we reach a byte with its high order bit clear. + do { + byte = encoding_[cursor_++]; + value |= static_cast(byte & 127) << shift; + shift += 7; + } while (byte & 128); + + return value; +} + +// Decode and retrieve all remaining values from the encoding. +template +void Leb128Decoder::DequeueAll(std::vector* values) { + while (cursor_ < encoding_.size()) { + values->push_back(Dequeue()); + } +} + +template class Leb128Encoder; +template class Leb128Encoder; + +template class Leb128Decoder; +template class Leb128Decoder; + +} // namespace relocation_packer diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/leb128.h b/third_party/android_platform/bionic/tools/relocation_packer/src/leb128.h new file mode 100644 index 000000000000..2c5b5d079c0b --- /dev/null +++ b/third_party/android_platform/bionic/tools/relocation_packer/src/leb128.h @@ -0,0 +1,76 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// LEB128 encoder and decoder for packed relative relocations. +// +// Run-length encoded relative relocations consist of a large number +// of pairs of relatively small positive integer values. Encoding these as +// LEB128 saves space. +// +// For more on LEB128 see http://en.wikipedia.org/wiki/LEB128. + +#ifndef TOOLS_RELOCATION_PACKER_SRC_LEB128_H_ +#define TOOLS_RELOCATION_PACKER_SRC_LEB128_H_ + +#include +#include + +#include "elf_traits.h" + +namespace relocation_packer { + +// Encode packed words as a LEB128 byte stream. +template +class Leb128Encoder { + public: + // Explicit (but empty) constructor and destructor, for chromium-style. + Leb128Encoder(); + ~Leb128Encoder(); + + // Add a value to the encoding stream. + // |value| is the unsigned int to add. + void Enqueue(uint_t value); + + // Add a vector of values to the encoding stream. + // |values| is the vector of unsigned ints to add. + void EnqueueAll(const std::vector& values); + + // Retrieve the encoded representation of the values. + // |encoding| is the returned vector of encoded data. + void GetEncoding(std::vector* encoding) { *encoding = encoding_; } + + private: + // Growable vector holding the encoded LEB128 stream. + std::vector encoding_; +}; + +// Decode a LEB128 byte stream to produce packed words. +template +class Leb128Decoder { + public: + // Create a new decoder for the given encoded stream. + // |encoding| is the vector of encoded data. + explicit Leb128Decoder(const std::vector& encoding, size_t start_with); + + // Explicit (but empty) destructor, for chromium-style. + ~Leb128Decoder(); + + // Retrieve the next value from the encoded stream. + uint_t Dequeue(); + + // Retrieve all remaining values from the encoded stream. + // |values| is the vector of decoded data. + void DequeueAll(std::vector* values); + + private: + // Encoded LEB128 stream. + std::vector encoding_; + + // Cursor indicating the current stream retrieval point. + size_t cursor_; +}; + +} // namespace relocation_packer + +#endif // TOOLS_RELOCATION_PACKER_SRC_LEB128_H_ diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/leb128_unittest.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/leb128_unittest.cc new file mode 100644 index 000000000000..8a7028cbcb60 --- /dev/null +++ b/third_party/android_platform/bionic/tools/relocation_packer/src/leb128_unittest.cc @@ -0,0 +1,111 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "leb128.h" + +#include +#include "gtest/gtest.h" + +namespace relocation_packer { + +TEST(Leb128, Encoder64) { + std::vector values; + values.push_back(624485); + values.push_back(0); + values.push_back(1); + values.push_back(127); + values.push_back(128); + + Leb128Encoder encoder; + encoder.EnqueueAll(values); + + encoder.Enqueue(4294967295); + encoder.Enqueue(18446744073709551615ul); + + std::vector encoding; + encoder.GetEncoding(&encoding); + + EXPECT_EQ(23U, encoding.size()); + // 624485 + EXPECT_EQ(0xe5, encoding[0]); + EXPECT_EQ(0x8e, encoding[1]); + EXPECT_EQ(0x26, encoding[2]); + // 0 + EXPECT_EQ(0x00, encoding[3]); + // 1 + EXPECT_EQ(0x01, encoding[4]); + // 127 + EXPECT_EQ(0x7f, encoding[5]); + // 128 + EXPECT_EQ(0x80, encoding[6]); + EXPECT_EQ(0x01, encoding[7]); + // 4294967295 + EXPECT_EQ(0xff, encoding[8]); + EXPECT_EQ(0xff, encoding[9]); + EXPECT_EQ(0xff, encoding[10]); + EXPECT_EQ(0xff, encoding[11]); + EXPECT_EQ(0x0f, encoding[12]); + // 18446744073709551615 + EXPECT_EQ(0xff, encoding[13]); + EXPECT_EQ(0xff, encoding[14]); + EXPECT_EQ(0xff, encoding[15]); + EXPECT_EQ(0xff, encoding[16]); + EXPECT_EQ(0xff, encoding[17]); + EXPECT_EQ(0xff, encoding[18]); + EXPECT_EQ(0xff, encoding[19]); + EXPECT_EQ(0xff, encoding[20]); + EXPECT_EQ(0xff, encoding[21]); + EXPECT_EQ(0x01, encoding[22]); +} + +TEST(Leb128, Decoder64) { + std::vector encoding; + // 624485 + encoding.push_back(0xe5); + encoding.push_back(0x8e); + encoding.push_back(0x26); + // 0 + encoding.push_back(0x00); + // 1 + encoding.push_back(0x01); + // 127 + encoding.push_back(0x7f); + // 128 + encoding.push_back(0x80); + encoding.push_back(0x01); + // 4294967295 + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0x0f); + // 18446744073709551615 + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0x01); + + Leb128Decoder decoder(encoding, 0); + + EXPECT_EQ(624485U, decoder.Dequeue()); + + std::vector dequeued; + decoder.DequeueAll(&dequeued); + + EXPECT_EQ(6U, dequeued.size()); + EXPECT_EQ(0U, dequeued[0]); + EXPECT_EQ(1U, dequeued[1]); + EXPECT_EQ(127U, dequeued[2]); + EXPECT_EQ(128U, dequeued[3]); + EXPECT_EQ(4294967295U, dequeued[4]); + EXPECT_EQ(18446744073709551615UL, dequeued[5]); +} + +} // namespace relocation_packer diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/main.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/main.cc new file mode 100644 index 000000000000..3f784e4f3b3e --- /dev/null +++ b/third_party/android_platform/bionic/tools/relocation_packer/src/main.cc @@ -0,0 +1,153 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Tool to pack and unpack relative relocations in a shared library. +// +// Packing removes relative relocations from .rel.dyn and writes them +// in a more compact form to .android.rel.dyn. Unpacking does the reverse. +// +// Invoke with -v to trace actions taken when packing or unpacking. +// Invoke with -p to pad removed relocations with R_*_NONE. Suppresses +// shrinking of .rel.dyn. +// See PrintUsage() below for full usage details. +// +// NOTE: Breaks with libelf 0.152, which is buggy. libelf 0.158 works. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "elf_file.h" +#include "elf_traits.h" +#include "libelf.h" + +#include "nativehelper/ScopedFd.h" + +static void PrintUsage(const char* argv0) { + std::string temporary = argv0; + const size_t last_slash = temporary.find_last_of("/"); + if (last_slash != temporary.npos) { + temporary.erase(0, last_slash + 1); + } + const char* basename = temporary.c_str(); + + printf( + "Usage: %s [-u] [-v] [-p] file\n\n" + "Pack or unpack relative relocations in a shared library.\n\n" + " -u, --unpack unpack previously packed relative relocations\n" + " -v, --verbose trace object file modifications (for debugging)\n" + " -p, --pad do not shrink relocations, but pad (for debugging)\n\n", + basename); + + printf( + "Debug sections are not handled, so packing should not be used on\n" + "shared libraries compiled for debugging or otherwise unstripped.\n"); +} + +int main(int argc, char* argv[]) { + bool is_unpacking = false; + bool is_verbose = false; + bool is_padding = false; + + static const option options[] = { + {"unpack", 0, 0, 'u'}, {"verbose", 0, 0, 'v'}, {"pad", 0, 0, 'p'}, + {"help", 0, 0, 'h'}, {NULL, 0, 0, 0} + }; + bool has_options = true; + while (has_options) { + int c = getopt_long(argc, argv, "uvph", options, NULL); + switch (c) { + case 'u': + is_unpacking = true; + break; + case 'v': + is_verbose = true; + break; + case 'p': + is_padding = true; + break; + case 'h': + PrintUsage(argv[0]); + return 0; + case '?': + LOG(INFO) << "Try '" << argv[0] << " --help' for more information."; + return 1; + case -1: + has_options = false; + break; + default: + NOTREACHED(); + return 1; + } + } + if (optind != argc - 1) { + LOG(INFO) << "Try '" << argv[0] << " --help' for more information."; + return 1; + } + + if (elf_version(EV_CURRENT) == EV_NONE) { + LOG(WARNING) << "Elf Library is out of date!"; + } + + const char* file = argv[argc - 1]; + ScopedFd fd(open(file, O_RDWR)); + if (fd.get() == -1) { + LOG(ERROR) << file << ": " << strerror(errno); + return 1; + } + + if (is_verbose) + relocation_packer::Logger::SetVerbose(1); + + // We need to detect elf class in order to create + // correct implementation + uint8_t e_ident[EI_NIDENT]; + if (TEMP_FAILURE_RETRY(read(fd.get(), e_ident, EI_NIDENT) != EI_NIDENT)) { + LOG(ERROR) << file << ": failed to read elf header:" << strerror(errno); + return 1; + } + + if (TEMP_FAILURE_RETRY(lseek(fd.get(), 0, SEEK_SET)) != 0) { + LOG(ERROR) << file << ": lseek to 0 failed:" << strerror(errno); + return 1; + } + + bool status = false; + + if (e_ident[EI_CLASS] == ELFCLASS32) { + relocation_packer::ElfFile elf_file(fd.get()); + elf_file.SetPadding(is_padding); + + if (is_unpacking) { + status = elf_file.UnpackRelocations(); + } else { + status = elf_file.PackRelocations(); + } + } else if (e_ident[EI_CLASS] == ELFCLASS64) { + relocation_packer::ElfFile elf_file(fd.get()); + elf_file.SetPadding(is_padding); + + if (is_unpacking) { + status = elf_file.UnpackRelocations(); + } else { + status = elf_file.PackRelocations(); + } + } else { + LOG(ERROR) << file << ": unknown ELFCLASS: " << e_ident[EI_CLASS]; + return 1; + } + + if (!status) { + LOG(ERROR) << file << ": failed to pack/unpack file"; + return 1; + } + + return 0; +} diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/nativehelper/ScopedFd.h b/third_party/android_platform/bionic/tools/relocation_packer/src/nativehelper/ScopedFd.h new file mode 100644 index 000000000000..5a22652cf088 --- /dev/null +++ b/third_party/android_platform/bionic/tools/relocation_packer/src/nativehelper/ScopedFd.h @@ -0,0 +1,62 @@ +/* + * 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_included +#define SCOPED_FD_H_included + +#include + +// Local definition of DISALLOW_COPY_AND_ASSIGN, avoids depending on base. +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +// 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_ASSIGN(ScopedFd); +}; + +#endif // SCOPED_FD_H_included diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/packer.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/packer.cc new file mode 100644 index 000000000000..8e30612e5506 --- /dev/null +++ b/third_party/android_platform/bionic/tools/relocation_packer/src/packer.cc @@ -0,0 +1,88 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "packer.h" + +#include + +#include "debug.h" +#include "delta_encoder.h" +#include "elf_traits.h" +#include "leb128.h" +#include "sleb128.h" + +namespace relocation_packer { + +// Pack relocations into a group encoded packed representation. +template +void RelocationPacker::PackRelocations(const std::vector& relocations, + std::vector* packed) { + // Run-length encode. + std::vector packed_words; + RelocationDeltaCodec codec; + codec.Encode(relocations, &packed_words); + + // If insufficient data do nothing. + if (packed_words.empty()) + return; + + Sleb128Encoder sleb128_encoder; + Leb128Encoder leb128_encoder; + + std::vector leb128_packed; + std::vector sleb128_packed; + + leb128_encoder.EnqueueAll(packed_words); + leb128_encoder.GetEncoding(&leb128_packed); + + sleb128_encoder.EnqueueAll(packed_words); + sleb128_encoder.GetEncoding(&sleb128_packed); + + // TODO (simonb): Estimate savings on current android system image and consider using + // one encoder for all packed relocations to reduce complexity. + if (leb128_packed.size() <= sleb128_packed.size()) { + packed->push_back('A'); + packed->push_back('P'); + packed->push_back('U'); + packed->push_back('2'); + packed->insert(packed->end(), leb128_packed.begin(), leb128_packed.end()); + } else { + packed->push_back('A'); + packed->push_back('P'); + packed->push_back('S'); + packed->push_back('2'); + packed->insert(packed->end(), sleb128_packed.begin(), sleb128_packed.end()); + } +} + +// Unpack relative relocations from a run-length encoded packed +// representation. +template +void RelocationPacker::UnpackRelocations( + const std::vector& packed, + std::vector* relocations) { + + std::vector packed_words; + CHECK(packed.size() > 4 && + packed[0] == 'A' && + packed[1] == 'P' && + (packed[2] == 'U' || packed[2] == 'S') && + packed[3] == '2'); + + if (packed[2] == 'U') { + Leb128Decoder decoder(packed, 4); + decoder.DequeueAll(&packed_words); + } else { + Sleb128Decoder decoder(packed, 4); + decoder.DequeueAll(&packed_words); + } + + RelocationDeltaCodec codec; + codec.Decode(packed_words, relocations); +} + +template class RelocationPacker; +template class RelocationPacker; + +} // namespace relocation_packer diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/packer.h b/third_party/android_platform/bionic/tools/relocation_packer/src/packer.h new file mode 100644 index 000000000000..8a57e623b0d6 --- /dev/null +++ b/third_party/android_platform/bionic/tools/relocation_packer/src/packer.h @@ -0,0 +1,74 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Pack relative relocations into a more compact form. +// +// +// For relative relocations without addends (32 bit platforms) +// ----------------------------------------------------------- +// +// Applies two packing strategies. The first is run-length encoding, which +// turns a large set of relative relocations into a much smaller set +// of delta-count pairs, prefixed with a two-word header comprising the +// count of pairs and the initial relocation offset. The second is LEB128 +// encoding, which compresses the result of run-length encoding. +// +// Once packed, data is prefixed by an identifier that allows for any later +// versioning of packing strategies. +// +// A complete packed stream of relocations without addends might look +// something like: +// +// "APR1" pairs init_offset count1 delta1 count2 delta2 ... +// 41505231 f2b003 b08ac716 e001 04 01 10 ... +// +// +// For relative relocations with addends (64 bit platforms) +// -------------------------------------------------------- +// +// Applies two packing strategies. The first is delta encoding, which +// turns a large set of relative relocations into a smaller set +// of offset and addend delta pairs, prefixed with a header indicating the +// count of pairs. The second is signed LEB128 encoding, which compacts +// the result of delta encoding. +// +// Once packed, data is prefixed by an identifier that allows for any later +// versioning of packing strategies. +// +// A complete packed stream might look something like: +// +// "APA1" pairs offset_d1 addend_d1 offset_d2 addend_d2 ... +// 41505232 f2b018 04 28 08 9f01 ... + +#ifndef TOOLS_RELOCATION_PACKER_SRC_PACKER_H_ +#define TOOLS_RELOCATION_PACKER_SRC_PACKER_H_ + +#include +#include + +#include "elf.h" + +namespace relocation_packer { + +// A RelocationPacker packs vectors of relocations into more +// compact forms, and unpacks them to reproduce the pre-packed data. +template +class RelocationPacker { + public: + // Pack relocations into a more compact form. + // |relocations| is a vector of relocation structs. + // |packed| is the vector of packed bytes into which relocations are packed. + static void PackRelocations(const std::vector& relocations, + std::vector* packed); + + // Unpack relocations from their more compact form. + // |packed| is the vector of packed relocations. + // |relocations| is a vector of unpacked relocation structs. + static void UnpackRelocations(const std::vector& packed, + std::vector* relocations); +}; + +} // namespace relocation_packer + +#endif // TOOLS_RELOCATION_PACKER_SRC_PACKER_H_ diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/packer_unittest.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/packer_unittest.cc new file mode 100644 index 000000000000..8dddd8b412a0 --- /dev/null +++ b/third_party/android_platform/bionic/tools/relocation_packer/src/packer_unittest.cc @@ -0,0 +1,292 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "packer.h" + +#include +#include "elf.h" +#include "elf_traits.h" +#include "gtest/gtest.h" + + +template +static void AddRelocation(typename ELF::Addr addr, + typename ELF::Xword info, + typename ELF::Sxword addend, + std::vector* relocations) { + typename ELF::Rela relocation; + relocation.r_offset = addr; + relocation.r_info = info; + relocation.r_addend = addend; + + relocations->push_back(relocation); +} + +template +static bool CheckRelocation(typename ELF::Addr addr, + typename ELF::Xword info, + typename ELF::Sxword addend, + const typename ELF::Rela& relocation) { + return relocation.r_offset == addr && + relocation.r_info == info && + relocation.r_addend == addend; +} + +namespace relocation_packer { + +template +static void DoPackNoAddend() { + std::vector relocations; + std::vector packed; + // Initial relocation. + AddRelocation(0xd1ce0000, 0x11, 0, &relocations); + // Two more relocations, 4 byte deltas. + AddRelocation(0xd1ce0004, 0x11, 0, &relocations); + AddRelocation(0xd1ce0008, 0x11, 0, &relocations); + // Three more relocations, 8 byte deltas. + AddRelocation(0xd1ce0010, 0x11, 0, &relocations); + AddRelocation(0xd1ce0018, 0x11, 0, &relocations); + AddRelocation(0xd1ce0020, 0x11, 0, &relocations); + + RelocationPacker packer; + + packed.clear(); + packer.PackRelocations(relocations, &packed); + + ASSERT_EQ(18U, packed.size()); + // Identifier. + size_t ndx = 0; + EXPECT_EQ('A', packed[ndx++]); + EXPECT_EQ('P', packed[ndx++]); + EXPECT_EQ('U', packed[ndx++]); + EXPECT_EQ('2', packed[ndx++]); + // relocation count + EXPECT_EQ(6, packed[ndx++]); + // base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d + EXPECT_EQ(0xfc, packed[ndx++]); + EXPECT_EQ(0xff, packed[ndx++]); + EXPECT_EQ(0xb7, packed[ndx++]); + EXPECT_EQ(0x8e, packed[ndx++]); + EXPECT_EQ(0x0d, packed[ndx++]); + // first group + EXPECT_EQ(3, packed[ndx++]); // size + EXPECT_EQ(3, packed[ndx++]); // flags + EXPECT_EQ(4, packed[ndx++]); // r_offset_delta + EXPECT_EQ(0x11, packed[ndx++]); // r_info + // second group + EXPECT_EQ(3, packed[ndx++]); // size + EXPECT_EQ(3, packed[ndx++]); // flags + EXPECT_EQ(8, packed[ndx++]); // r_offset_delta + EXPECT_EQ(0x11, packed[ndx++]); // r_info + + EXPECT_EQ(ndx, packed.size()); +} + +TEST(Packer, PackNoAddend) { + DoPackNoAddend(); + DoPackNoAddend(); +} + +template +static void DoUnpackNoAddend() { + std::vector relocations; + std::vector packed; + packed.push_back('A'); + packed.push_back('P'); + packed.push_back('U'); + packed.push_back('2'); + // relocation count + packed.push_back(6); + // base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d + packed.push_back(0xfc); + packed.push_back(0xff); + packed.push_back(0xb7); + packed.push_back(0x8e); + packed.push_back(0x0d); + // first group + packed.push_back(3); // size + packed.push_back(3); // flags + packed.push_back(4); // r_offset_delta + packed.push_back(0x11); // r_info + // second group + packed.push_back(3); // size + packed.push_back(3); // flags + packed.push_back(8); // r_offset_delta + packed.push_back(0x11); // r_info + + RelocationPacker packer; + packer.UnpackRelocations(packed, &relocations); + + size_t ndx = 0; + EXPECT_EQ(6U, relocations.size()); + EXPECT_TRUE(CheckRelocation(0xd1ce0000, 0x11, 0, relocations[ndx++])); + EXPECT_TRUE(CheckRelocation(0xd1ce0004, 0x11, 0, relocations[ndx++])); + EXPECT_TRUE(CheckRelocation(0xd1ce0008, 0x11, 0, relocations[ndx++])); + + EXPECT_TRUE(CheckRelocation(0xd1ce0010, 0x11, 0, relocations[ndx++])); + EXPECT_TRUE(CheckRelocation(0xd1ce0018, 0x11, 0, relocations[ndx++])); + EXPECT_TRUE(CheckRelocation(0xd1ce0020, 0x11, 0, relocations[ndx++])); + + EXPECT_EQ(ndx, relocations.size()); +} + +TEST(Packer, UnpackNoAddend) { + DoUnpackNoAddend(); + DoUnpackNoAddend(); +} + +template +static void DoPackWithAddend() { + std::vector relocations; + + // Initial relocation. + AddRelocation(0xd1ce0000, 0x01, 10024, &relocations); + // Two more relocations, 4 byte offset deltas, 12 byte addend deltas. + AddRelocation(0xd1ce0004, 0x01, 10012, &relocations); + AddRelocation(0xd1ce0008, 0x01, 10024, &relocations); + // Three more relocations, 8 byte deltas, -24 byte addend deltas. + AddRelocation(0xd1ce0010, 0x01, 10000, &relocations); + AddRelocation(0xd1ce0018, 0x01, 9976, &relocations); + AddRelocation(0xd1ce0020, 0x01, 9952, &relocations); + + std::vector packed; + + RelocationPacker packer; + + packed.clear(); + packer.PackRelocations(relocations, &packed); + + EXPECT_EQ(26U, packed.size()); + size_t ndx = 0; + // Identifier. + EXPECT_EQ('A', packed[ndx++]); + EXPECT_EQ('P', packed[ndx++]); + EXPECT_EQ('S', packed[ndx++]); + EXPECT_EQ('2', packed[ndx++]); + // Relocation count + EXPECT_EQ(6U, packed[ndx++]); + // base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d/7d (depending on ELF::Addr) + EXPECT_EQ(0xfc, packed[ndx++]); + EXPECT_EQ(0xff, packed[ndx++]); + EXPECT_EQ(0xb7, packed[ndx++]); + EXPECT_EQ(0x8e, packed[ndx++]); + if (sizeof(typename ELF::Addr) == 8) { + // positive for uint64_t + EXPECT_EQ(0x0d, packed[ndx++]); + } else { + // negative for uint32_t + EXPECT_EQ(0x7d, packed[ndx++]); + } + // group 1 + EXPECT_EQ(0x03, packed[ndx++]); // size + EXPECT_EQ(0x0b, packed[ndx++]); // flags + EXPECT_EQ(0x04, packed[ndx++]); // r_offset_delta + EXPECT_EQ(0x01, packed[ndx++]); // r_info + // group 1 - addend 1: 10024 = 0xa8, 0xce, 0x80 + EXPECT_EQ(0xa8, packed[ndx++]); + EXPECT_EQ(0xce, packed[ndx++]); + EXPECT_EQ(0x00, packed[ndx++]); + // group 1 - addend 2: -12 = 0x74 + EXPECT_EQ(0x74, packed[ndx++]); + // group 1 - addend 3: +12 = 0x0c + EXPECT_EQ(0x0c, packed[ndx++]); + + // group 2 + EXPECT_EQ(0x03, packed[ndx++]); // size + EXPECT_EQ(0x0b, packed[ndx++]); // flags + EXPECT_EQ(0x08, packed[ndx++]); // r_offset_delta + EXPECT_EQ(0x01, packed[ndx++]); // r_info + + // group 2 - addend 1: -24 = 0x68 + EXPECT_EQ(0x68, packed[ndx++]); + // group 2 - addend 2: -24 = 0x68 + EXPECT_EQ(0x68, packed[ndx++]); + // group 2 - addend 3: -24 = 0x68 + EXPECT_EQ(0x68, packed[ndx++]); + + EXPECT_EQ(ndx, packed.size()); +} + +TEST(Packer, PackWithAddend) { + DoPackWithAddend(); + DoPackWithAddend(); +} + +template +static void DoUnpackWithAddend() { + std::vector packed; + // Identifier. + packed.push_back('A'); + packed.push_back('P'); + packed.push_back('S'); + packed.push_back('2'); + // Relocation count + packed.push_back(6U); + // base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d + packed.push_back(0xfc); + packed.push_back(0xff); + packed.push_back(0xb7); + packed.push_back(0x8e); + if (sizeof(typename ELF::Addr) == 8) { + // positive for uint64_t + packed.push_back(0x0d); + } else { + // negative for uint32_t + packed.push_back(0x7d); + } + // group 1 + packed.push_back(0x03); // size + packed.push_back(0x0b); // flags + packed.push_back(0x04); // r_offset_delta + packed.push_back(0x01); // r_info + // group 1 - addend 1: 10024 = 0xa8, 0xce, 0x80 + packed.push_back(0xa8); + packed.push_back(0xce); + packed.push_back(0x00); + // group 1 - addend 2: -12 = 0x74 + packed.push_back(0x74); + // group 1 - addend 3: +12 = 0x0c + packed.push_back(0x0c); + + // group 2 + packed.push_back(0x03); // size + packed.push_back(0x0b); // flags + packed.push_back(0x08); // r_offset_delta + packed.push_back(0x01); // r_info + + // group 2 - addend 1: -24 = 0x68 + packed.push_back(0x68); + // group 2 - addend 2: -24 = 0x68 + packed.push_back(0x68); + // group 2 - addend 3: -24 = 0x68 + packed.push_back(0x68); + + std::vector relocations; + + RelocationPacker packer; + + relocations.clear(); + packer.UnpackRelocations(packed, &relocations); + + EXPECT_EQ(6U, relocations.size()); + size_t ndx = 0; + // Initial relocation. + EXPECT_TRUE(CheckRelocation(0xd1ce0000, 0x01, 10024, relocations[ndx++])); + // Two more relocations, 4 byte offset deltas, 12 byte addend deltas. + EXPECT_TRUE(CheckRelocation(0xd1ce0004, 0x01, 10012, relocations[ndx++])); + EXPECT_TRUE(CheckRelocation(0xd1ce0008, 0x01, 10024, relocations[ndx++])); + // Three more relocations, 8 byte offset deltas, -24 byte addend deltas. + EXPECT_TRUE(CheckRelocation(0xd1ce0010, 0x01, 10000, relocations[ndx++])); + EXPECT_TRUE(CheckRelocation(0xd1ce0018, 0x01, 9976, relocations[ndx++])); + EXPECT_TRUE(CheckRelocation(0xd1ce0020, 0x01, 9952, relocations[ndx++])); + + EXPECT_EQ(ndx, relocations.size()); +} + +TEST(Packer, UnpackWithAddend) { + DoUnpackWithAddend(); + DoUnpackWithAddend(); +} + +} // namespace relocation_packer diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/run_all_unittests.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/run_all_unittests.cc new file mode 100644 index 000000000000..4122be18cea7 --- /dev/null +++ b/third_party/android_platform/bionic/tools/relocation_packer/src/run_all_unittests.cc @@ -0,0 +1,10 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "testing/gtest/include/gtest/gtest.h" + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/sleb128.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/sleb128.cc new file mode 100644 index 000000000000..12c21e364562 --- /dev/null +++ b/third_party/android_platform/bionic/tools/relocation_packer/src/sleb128.cc @@ -0,0 +1,131 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sleb128.h" + +#include +#include +#include + +#include "elf_traits.h" + +namespace { + +template +class uint_traits {}; + +template <> +class uint_traits { + public: + typedef int64_t int_t; +}; + +template <> +class uint_traits { + public: + typedef int32_t int_t; +}; + +} + +namespace relocation_packer { + +// Empty constructor and destructor to silence chromium-style. +template +Sleb128Encoder::Sleb128Encoder() { } + +template +Sleb128Encoder::~Sleb128Encoder() { } + +// Add a single value to the encoding. Values are encoded with variable +// length. The least significant 7 bits of each byte hold 7 bits of data, +// and the most significant bit is set on each byte except the last. The +// value is sign extended up to a multiple of 7 bits (ensuring that the +// most significant bit is zero for a positive number and one for a +// negative number). +template +void Sleb128Encoder::Enqueue(uint_t value) { + typedef typename uint_traits::int_t int_t; + static const size_t size = CHAR_BIT * sizeof(value); + + bool more = true; + const bool negative = static_cast(value) < 0; + + while (more) { + uint8_t byte = value & 127; + value >>= 7; + + // Sign extend if encoding a -ve value. + if (negative) + value |= -(static_cast(1) << (size - 7)); + + // The sign bit of byte is second high order bit. + const bool sign_bit = byte & 64; + if ((value == 0 && !sign_bit) || (value == static_cast(-1) && sign_bit)) + more = false; + else + byte |= 128; + encoding_.push_back(byte); + } +} + +// Add a vector of values to the encoding. +template +void Sleb128Encoder::EnqueueAll(const std::vector& values) { + for (size_t i = 0; i < values.size(); ++i) { + Enqueue(values[i]); + } +} + +// Create a new decoder for the given encoded stream. +template +Sleb128Decoder::Sleb128Decoder(const std::vector& encoding, size_t start_with) { + encoding_ = encoding; + cursor_ = start_with; +} + +// Empty destructor to silence chromium-style. +template +Sleb128Decoder::~Sleb128Decoder() { } + +// Decode and retrieve a single value from the encoding. Consume bytes +// until one without its most significant bit is found, and re-form the +// value from the 7 bit fields of the bytes consumed. +template +uint_t Sleb128Decoder::Dequeue() { + uint_t value = 0; + static const size_t size = CHAR_BIT * sizeof(value); + + size_t shift = 0; + uint8_t byte; + + // Loop until we reach a byte with its high order bit clear. + do { + byte = encoding_[cursor_++]; + value |= (static_cast(byte & 127) << shift); + shift += 7; + } while (byte & 128); + + // The sign bit is second high order bit of the final byte decoded. + // Sign extend if value is -ve and we did not shift all of it. + if (shift < size && (byte & 64)) + value |= -(static_cast(1) << shift); + + return static_cast(value); +} + +// Decode and retrieve all remaining values from the encoding. +template +void Sleb128Decoder::DequeueAll(std::vector* values) { + while (cursor_ < encoding_.size()) { + values->push_back(Dequeue()); + } +} + +template class Sleb128Encoder; +template class Sleb128Encoder; +template class Sleb128Decoder; +template class Sleb128Decoder; + +} // namespace relocation_packer diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/sleb128.h b/third_party/android_platform/bionic/tools/relocation_packer/src/sleb128.h new file mode 100644 index 000000000000..fa0a24610335 --- /dev/null +++ b/third_party/android_platform/bionic/tools/relocation_packer/src/sleb128.h @@ -0,0 +1,77 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// SLEB128 encoder and decoder for packed relative relocations. +// +// Delta encoded relative relocations consist of a large number +// of pairs signed integer values, many with small values. Encoding these +// as signed LEB128 saves space. +// +// For more on LEB128 see http://en.wikipedia.org/wiki/LEB128. + +#ifndef TOOLS_RELOCATION_PACKER_SRC_SLEB128_H_ +#define TOOLS_RELOCATION_PACKER_SRC_SLEB128_H_ + +#include +#include +#include + +#include "elf_traits.h" + +namespace relocation_packer { + +// Encode packed words as a signed LEB128 byte stream. +template +class Sleb128Encoder { + public: + // Explicit (but empty) constructor and destructor, for chromium-style. + Sleb128Encoder(); + ~Sleb128Encoder(); + + // Add a value to the encoding stream. + // |value| is the signed int to add. + void Enqueue(int_t value); + + // Add a vector of values to the encoding stream. + // |values| is the vector of signed ints to add. + void EnqueueAll(const std::vector& values); + + // Retrieve the encoded representation of the values. + // |encoding| is the returned vector of encoded data. + void GetEncoding(std::vector* encoding) { *encoding = encoding_; } + + private: + // Growable vector holding the encoded LEB128 stream. + std::vector encoding_; +}; + +// Decode a LEB128 byte stream to produce packed words. +template +class Sleb128Decoder { + public: + // Create a new decoder for the given encoded stream. + // |encoding| is the vector of encoded data. + explicit Sleb128Decoder(const std::vector& encoding, size_t start_with); + + // Explicit (but empty) destructor, for chromium-style. + ~Sleb128Decoder(); + + // Retrieve the next value from the encoded stream. + int_t Dequeue(); + + // Retrieve all remaining values from the encoded stream. + // |values| is the vector of decoded data. + void DequeueAll(std::vector* values); + + private: + // Encoded LEB128 stream. + std::vector encoding_; + + // Cursor indicating the current stream retrieval point. + size_t cursor_; +}; + +} // namespace relocation_packer + +#endif // TOOLS_RELOCATION_PACKER_SRC_SLEB128_H_ diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/sleb128_unittest.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/sleb128_unittest.cc new file mode 100644 index 000000000000..49a553c2fb19 --- /dev/null +++ b/third_party/android_platform/bionic/tools/relocation_packer/src/sleb128_unittest.cc @@ -0,0 +1,166 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sleb128.h" + +#include +#include "elf_traits.h" +#include "gtest/gtest.h" + +namespace relocation_packer { + +TEST(Sleb128, Encoder64) { + std::vector values; + values.push_back(624485U); + values.push_back(0U); + values.push_back(1U); + values.push_back(63U); + values.push_back(64U); + values.push_back(static_cast(-1)); + values.push_back(static_cast(-624485)); + + Sleb128Encoder encoder; + encoder.EnqueueAll(values); + + encoder.Enqueue(2147483647U); + encoder.Enqueue(static_cast(-2147483648)); + encoder.Enqueue(9223372036854775807ULL); + encoder.Enqueue(static_cast(-9223372036854775807LL - 1)); + + std::vector encoding; + encoder.GetEncoding(&encoding); + + EXPECT_EQ(42u, encoding.size()); + // 624485 + EXPECT_EQ(0xe5, encoding[0]); + EXPECT_EQ(0x8e, encoding[1]); + EXPECT_EQ(0x26, encoding[2]); + // 0 + EXPECT_EQ(0x00, encoding[3]); + // 1 + EXPECT_EQ(0x01, encoding[4]); + // 63 + EXPECT_EQ(0x3f, encoding[5]); + // 64 + EXPECT_EQ(0xc0, encoding[6]); + EXPECT_EQ(0x00, encoding[7]); + // -1 + EXPECT_EQ(0x7f, encoding[8]); + // -624485 + EXPECT_EQ(0x9b, encoding[9]); + EXPECT_EQ(0xf1, encoding[10]); + EXPECT_EQ(0x59, encoding[11]); + // 2147483647 + EXPECT_EQ(0xff, encoding[12]); + EXPECT_EQ(0xff, encoding[13]); + EXPECT_EQ(0xff, encoding[14]); + EXPECT_EQ(0xff, encoding[15]); + EXPECT_EQ(0x07, encoding[16]); + // -2147483648 + EXPECT_EQ(0x80, encoding[17]); + EXPECT_EQ(0x80, encoding[18]); + EXPECT_EQ(0x80, encoding[19]); + EXPECT_EQ(0x80, encoding[20]); + EXPECT_EQ(0x78, encoding[21]); + // 9223372036854775807 + EXPECT_EQ(0xff, encoding[22]); + EXPECT_EQ(0xff, encoding[23]); + EXPECT_EQ(0xff, encoding[24]); + EXPECT_EQ(0xff, encoding[25]); + EXPECT_EQ(0xff, encoding[26]); + EXPECT_EQ(0xff, encoding[27]); + EXPECT_EQ(0xff, encoding[28]); + EXPECT_EQ(0xff, encoding[29]); + EXPECT_EQ(0xff, encoding[30]); + EXPECT_EQ(0x00, encoding[31]); + // -9223372036854775808 + EXPECT_EQ(0x80, encoding[32]); + EXPECT_EQ(0x80, encoding[33]); + EXPECT_EQ(0x80, encoding[34]); + EXPECT_EQ(0x80, encoding[35]); + EXPECT_EQ(0x80, encoding[36]); + EXPECT_EQ(0x80, encoding[37]); + EXPECT_EQ(0x80, encoding[38]); + EXPECT_EQ(0x80, encoding[39]); + EXPECT_EQ(0x80, encoding[40]); + EXPECT_EQ(0x7f, encoding[41]); +} + +TEST(Sleb128, Decoder) { + std::vector encoding; + // 624485 + encoding.push_back(0xe5); + encoding.push_back(0x8e); + encoding.push_back(0x26); + // 0 + encoding.push_back(0x00); + // 1 + encoding.push_back(0x01); + // 63 + encoding.push_back(0x3f); + // 64 + encoding.push_back(0xc0); + encoding.push_back(0x00); + // -1 + encoding.push_back(0x7f); + // -624485 + encoding.push_back(0x9b); + encoding.push_back(0xf1); + encoding.push_back(0x59); + // 2147483647 + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0x07); + // -2147483648 + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x78); + // 9223372036854775807 + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0x00); + // -9223372036854775808 + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x7f); + + Sleb128Decoder decoder(encoding, 0); + + EXPECT_EQ(624485U, decoder.Dequeue()); + + std::vector dequeued; + decoder.DequeueAll(&dequeued); + + EXPECT_EQ(10U, dequeued.size()); + EXPECT_EQ(0U, dequeued[0]); + EXPECT_EQ(1U, dequeued[1]); + EXPECT_EQ(63U, dequeued[2]); + EXPECT_EQ(64U, dequeued[3]); + EXPECT_EQ(static_cast(-1), dequeued[4]); + EXPECT_EQ(static_cast(-624485), dequeued[5]); + EXPECT_EQ(2147483647U, dequeued[6]); + EXPECT_EQ(static_cast(-2147483648), dequeued[7]); + EXPECT_EQ(9223372036854775807ULL, dequeued[8]); + EXPECT_EQ(static_cast(-9223372036854775807LL - 1), dequeued[9]); +} + +} // namespace relocation_packer diff --git a/third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc b/third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc new file mode 100644 index 000000000000..5e1fa747e853 --- /dev/null +++ b/third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc @@ -0,0 +1,1014 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Test data for packing/unpacking. When compiled, creates a run of +// relative relocations. +// +// See generate_elf_file_unittest_relocs.sh for instructions on how to build +// unit test data from this source file. + +const int i = 0; + +// Generator: +// python -c 'for i in xrange(0,1000):print"const void* pointer_%d = &i;"%i' +const void* pointer_0 = &i; +const void* pointer_1 = &i; +const void* pointer_2 = &i; +const void* pointer_3 = &i; +const void* pointer_4 = &i; +const void* pointer_5 = &i; +const void* pointer_6 = &i; +const void* pointer_7 = &i; +const void* pointer_8 = &i; +const void* pointer_9 = &i; +const void* pointer_10 = &i; +const void* pointer_11 = &i; +const void* pointer_12 = &i; +const void* pointer_13 = &i; +const void* pointer_14 = &i; +const void* pointer_15 = &i; +const void* pointer_16 = &i; +const void* pointer_17 = &i; +const void* pointer_18 = &i; +const void* pointer_19 = &i; +const void* pointer_20 = &i; +const void* pointer_21 = &i; +const void* pointer_22 = &i; +const void* pointer_23 = &i; +const void* pointer_24 = &i; +const void* pointer_25 = &i; +const void* pointer_26 = &i; +const void* pointer_27 = &i; +const void* pointer_28 = &i; +const void* pointer_29 = &i; +const void* pointer_30 = &i; +const void* pointer_31 = &i; +const void* pointer_32 = &i; +const void* pointer_33 = &i; +const void* pointer_34 = &i; +const void* pointer_35 = &i; +const void* pointer_36 = &i; +const void* pointer_37 = &i; +const void* pointer_38 = &i; +const void* pointer_39 = &i; +const void* pointer_40 = &i; +const void* pointer_41 = &i; +const void* pointer_42 = &i; +const void* pointer_43 = &i; +const void* pointer_44 = &i; +const void* pointer_45 = &i; +const void* pointer_46 = &i; +const void* pointer_47 = &i; +const void* pointer_48 = &i; +const void* pointer_49 = &i; +const void* pointer_50 = &i; +const void* pointer_51 = &i; +const void* pointer_52 = &i; +const void* pointer_53 = &i; +const void* pointer_54 = &i; +const void* pointer_55 = &i; +const void* pointer_56 = &i; +const void* pointer_57 = &i; +const void* pointer_58 = &i; +const void* pointer_59 = &i; +const void* pointer_60 = &i; +const void* pointer_61 = &i; +const void* pointer_62 = &i; +const void* pointer_63 = &i; +const void* pointer_64 = &i; +const void* pointer_65 = &i; +const void* pointer_66 = &i; +const void* pointer_67 = &i; +const void* pointer_68 = &i; +const void* pointer_69 = &i; +const void* pointer_70 = &i; +const void* pointer_71 = &i; +const void* pointer_72 = &i; +const void* pointer_73 = &i; +const void* pointer_74 = &i; +const void* pointer_75 = &i; +const void* pointer_76 = &i; +const void* pointer_77 = &i; +const void* pointer_78 = &i; +const void* pointer_79 = &i; +const void* pointer_80 = &i; +const void* pointer_81 = &i; +const void* pointer_82 = &i; +const void* pointer_83 = &i; +const void* pointer_84 = &i; +const void* pointer_85 = &i; +const void* pointer_86 = &i; +const void* pointer_87 = &i; +const void* pointer_88 = &i; +const void* pointer_89 = &i; +const void* pointer_90 = &i; +const void* pointer_91 = &i; +const void* pointer_92 = &i; +const void* pointer_93 = &i; +const void* pointer_94 = &i; +const void* pointer_95 = &i; +const void* pointer_96 = &i; +const void* pointer_97 = &i; +const void* pointer_98 = &i; +const void* pointer_99 = &i; +const void* pointer_100 = &i; +const void* pointer_101 = &i; +const void* pointer_102 = &i; +const void* pointer_103 = &i; +const void* pointer_104 = &i; +const void* pointer_105 = &i; +const void* pointer_106 = &i; +const void* pointer_107 = &i; +const void* pointer_108 = &i; +const void* pointer_109 = &i; +const void* pointer_110 = &i; +const void* pointer_111 = &i; +const void* pointer_112 = &i; +const void* pointer_113 = &i; +const void* pointer_114 = &i; +const void* pointer_115 = &i; +const void* pointer_116 = &i; +const void* pointer_117 = &i; +const void* pointer_118 = &i; +const void* pointer_119 = &i; +const void* pointer_120 = &i; +const void* pointer_121 = &i; +const void* pointer_122 = &i; +const void* pointer_123 = &i; +const void* pointer_124 = &i; +const void* pointer_125 = &i; +const void* pointer_126 = &i; +const void* pointer_127 = &i; +const void* pointer_128 = &i; +const void* pointer_129 = &i; +const void* pointer_130 = &i; +const void* pointer_131 = &i; +const void* pointer_132 = &i; +const void* pointer_133 = &i; +const void* pointer_134 = &i; +const void* pointer_135 = &i; +const void* pointer_136 = &i; +const void* pointer_137 = &i; +const void* pointer_138 = &i; +const void* pointer_139 = &i; +const void* pointer_140 = &i; +const void* pointer_141 = &i; +const void* pointer_142 = &i; +const void* pointer_143 = &i; +const void* pointer_144 = &i; +const void* pointer_145 = &i; +const void* pointer_146 = &i; +const void* pointer_147 = &i; +const void* pointer_148 = &i; +const void* pointer_149 = &i; +const void* pointer_150 = &i; +const void* pointer_151 = &i; +const void* pointer_152 = &i; +const void* pointer_153 = &i; +const void* pointer_154 = &i; +const void* pointer_155 = &i; +const void* pointer_156 = &i; +const void* pointer_157 = &i; +const void* pointer_158 = &i; +const void* pointer_159 = &i; +const void* pointer_160 = &i; +const void* pointer_161 = &i; +const void* pointer_162 = &i; +const void* pointer_163 = &i; +const void* pointer_164 = &i; +const void* pointer_165 = &i; +const void* pointer_166 = &i; +const void* pointer_167 = &i; +const void* pointer_168 = &i; +const void* pointer_169 = &i; +const void* pointer_170 = &i; +const void* pointer_171 = &i; +const void* pointer_172 = &i; +const void* pointer_173 = &i; +const void* pointer_174 = &i; +const void* pointer_175 = &i; +const void* pointer_176 = &i; +const void* pointer_177 = &i; +const void* pointer_178 = &i; +const void* pointer_179 = &i; +const void* pointer_180 = &i; +const void* pointer_181 = &i; +const void* pointer_182 = &i; +const void* pointer_183 = &i; +const void* pointer_184 = &i; +const void* pointer_185 = &i; +const void* pointer_186 = &i; +const void* pointer_187 = &i; +const void* pointer_188 = &i; +const void* pointer_189 = &i; +const void* pointer_190 = &i; +const void* pointer_191 = &i; +const void* pointer_192 = &i; +const void* pointer_193 = &i; +const void* pointer_194 = &i; +const void* pointer_195 = &i; +const void* pointer_196 = &i; +const void* pointer_197 = &i; +const void* pointer_198 = &i; +const void* pointer_199 = &i; +const void* pointer_200 = &i; +const void* pointer_201 = &i; +const void* pointer_202 = &i; +const void* pointer_203 = &i; +const void* pointer_204 = &i; +const void* pointer_205 = &i; +const void* pointer_206 = &i; +const void* pointer_207 = &i; +const void* pointer_208 = &i; +const void* pointer_209 = &i; +const void* pointer_210 = &i; +const void* pointer_211 = &i; +const void* pointer_212 = &i; +const void* pointer_213 = &i; +const void* pointer_214 = &i; +const void* pointer_215 = &i; +const void* pointer_216 = &i; +const void* pointer_217 = &i; +const void* pointer_218 = &i; +const void* pointer_219 = &i; +const void* pointer_220 = &i; +const void* pointer_221 = &i; +const void* pointer_222 = &i; +const void* pointer_223 = &i; +const void* pointer_224 = &i; +const void* pointer_225 = &i; +const void* pointer_226 = &i; +const void* pointer_227 = &i; +const void* pointer_228 = &i; +const void* pointer_229 = &i; +const void* pointer_230 = &i; +const void* pointer_231 = &i; +const void* pointer_232 = &i; +const void* pointer_233 = &i; +const void* pointer_234 = &i; +const void* pointer_235 = &i; +const void* pointer_236 = &i; +const void* pointer_237 = &i; +const void* pointer_238 = &i; +const void* pointer_239 = &i; +const void* pointer_240 = &i; +const void* pointer_241 = &i; +const void* pointer_242 = &i; +const void* pointer_243 = &i; +const void* pointer_244 = &i; +const void* pointer_245 = &i; +const void* pointer_246 = &i; +const void* pointer_247 = &i; +const void* pointer_248 = &i; +const void* pointer_249 = &i; +const void* pointer_250 = &i; +const void* pointer_251 = &i; +const void* pointer_252 = &i; +const void* pointer_253 = &i; +const void* pointer_254 = &i; +const void* pointer_255 = &i; +const void* pointer_256 = &i; +const void* pointer_257 = &i; +const void* pointer_258 = &i; +const void* pointer_259 = &i; +const void* pointer_260 = &i; +const void* pointer_261 = &i; +const void* pointer_262 = &i; +const void* pointer_263 = &i; +const void* pointer_264 = &i; +const void* pointer_265 = &i; +const void* pointer_266 = &i; +const void* pointer_267 = &i; +const void* pointer_268 = &i; +const void* pointer_269 = &i; +const void* pointer_270 = &i; +const void* pointer_271 = &i; +const void* pointer_272 = &i; +const void* pointer_273 = &i; +const void* pointer_274 = &i; +const void* pointer_275 = &i; +const void* pointer_276 = &i; +const void* pointer_277 = &i; +const void* pointer_278 = &i; +const void* pointer_279 = &i; +const void* pointer_280 = &i; +const void* pointer_281 = &i; +const void* pointer_282 = &i; +const void* pointer_283 = &i; +const void* pointer_284 = &i; +const void* pointer_285 = &i; +const void* pointer_286 = &i; +const void* pointer_287 = &i; +const void* pointer_288 = &i; +const void* pointer_289 = &i; +const void* pointer_290 = &i; +const void* pointer_291 = &i; +const void* pointer_292 = &i; +const void* pointer_293 = &i; +const void* pointer_294 = &i; +const void* pointer_295 = &i; +const void* pointer_296 = &i; +const void* pointer_297 = &i; +const void* pointer_298 = &i; +const void* pointer_299 = &i; +const void* pointer_300 = &i; +const void* pointer_301 = &i; +const void* pointer_302 = &i; +const void* pointer_303 = &i; +const void* pointer_304 = &i; +const void* pointer_305 = &i; +const void* pointer_306 = &i; +const void* pointer_307 = &i; +const void* pointer_308 = &i; +const void* pointer_309 = &i; +const void* pointer_310 = &i; +const void* pointer_311 = &i; +const void* pointer_312 = &i; +const void* pointer_313 = &i; +const void* pointer_314 = &i; +const void* pointer_315 = &i; +const void* pointer_316 = &i; +const void* pointer_317 = &i; +const void* pointer_318 = &i; +const void* pointer_319 = &i; +const void* pointer_320 = &i; +const void* pointer_321 = &i; +const void* pointer_322 = &i; +const void* pointer_323 = &i; +const void* pointer_324 = &i; +const void* pointer_325 = &i; +const void* pointer_326 = &i; +const void* pointer_327 = &i; +const void* pointer_328 = &i; +const void* pointer_329 = &i; +const void* pointer_330 = &i; +const void* pointer_331 = &i; +const void* pointer_332 = &i; +const void* pointer_333 = &i; +const void* pointer_334 = &i; +const void* pointer_335 = &i; +const void* pointer_336 = &i; +const void* pointer_337 = &i; +const void* pointer_338 = &i; +const void* pointer_339 = &i; +const void* pointer_340 = &i; +const void* pointer_341 = &i; +const void* pointer_342 = &i; +const void* pointer_343 = &i; +const void* pointer_344 = &i; +const void* pointer_345 = &i; +const void* pointer_346 = &i; +const void* pointer_347 = &i; +const void* pointer_348 = &i; +const void* pointer_349 = &i; +const void* pointer_350 = &i; +const void* pointer_351 = &i; +const void* pointer_352 = &i; +const void* pointer_353 = &i; +const void* pointer_354 = &i; +const void* pointer_355 = &i; +const void* pointer_356 = &i; +const void* pointer_357 = &i; +const void* pointer_358 = &i; +const void* pointer_359 = &i; +const void* pointer_360 = &i; +const void* pointer_361 = &i; +const void* pointer_362 = &i; +const void* pointer_363 = &i; +const void* pointer_364 = &i; +const void* pointer_365 = &i; +const void* pointer_366 = &i; +const void* pointer_367 = &i; +const void* pointer_368 = &i; +const void* pointer_369 = &i; +const void* pointer_370 = &i; +const void* pointer_371 = &i; +const void* pointer_372 = &i; +const void* pointer_373 = &i; +const void* pointer_374 = &i; +const void* pointer_375 = &i; +const void* pointer_376 = &i; +const void* pointer_377 = &i; +const void* pointer_378 = &i; +const void* pointer_379 = &i; +const void* pointer_380 = &i; +const void* pointer_381 = &i; +const void* pointer_382 = &i; +const void* pointer_383 = &i; +const void* pointer_384 = &i; +const void* pointer_385 = &i; +const void* pointer_386 = &i; +const void* pointer_387 = &i; +const void* pointer_388 = &i; +const void* pointer_389 = &i; +const void* pointer_390 = &i; +const void* pointer_391 = &i; +const void* pointer_392 = &i; +const void* pointer_393 = &i; +const void* pointer_394 = &i; +const void* pointer_395 = &i; +const void* pointer_396 = &i; +const void* pointer_397 = &i; +const void* pointer_398 = &i; +const void* pointer_399 = &i; +const void* pointer_400 = &i; +const void* pointer_401 = &i; +const void* pointer_402 = &i; +const void* pointer_403 = &i; +const void* pointer_404 = &i; +const void* pointer_405 = &i; +const void* pointer_406 = &i; +const void* pointer_407 = &i; +const void* pointer_408 = &i; +const void* pointer_409 = &i; +const void* pointer_410 = &i; +const void* pointer_411 = &i; +const void* pointer_412 = &i; +const void* pointer_413 = &i; +const void* pointer_414 = &i; +const void* pointer_415 = &i; +const void* pointer_416 = &i; +const void* pointer_417 = &i; +const void* pointer_418 = &i; +const void* pointer_419 = &i; +const void* pointer_420 = &i; +const void* pointer_421 = &i; +const void* pointer_422 = &i; +const void* pointer_423 = &i; +const void* pointer_424 = &i; +const void* pointer_425 = &i; +const void* pointer_426 = &i; +const void* pointer_427 = &i; +const void* pointer_428 = &i; +const void* pointer_429 = &i; +const void* pointer_430 = &i; +const void* pointer_431 = &i; +const void* pointer_432 = &i; +const void* pointer_433 = &i; +const void* pointer_434 = &i; +const void* pointer_435 = &i; +const void* pointer_436 = &i; +const void* pointer_437 = &i; +const void* pointer_438 = &i; +const void* pointer_439 = &i; +const void* pointer_440 = &i; +const void* pointer_441 = &i; +const void* pointer_442 = &i; +const void* pointer_443 = &i; +const void* pointer_444 = &i; +const void* pointer_445 = &i; +const void* pointer_446 = &i; +const void* pointer_447 = &i; +const void* pointer_448 = &i; +const void* pointer_449 = &i; +const void* pointer_450 = &i; +const void* pointer_451 = &i; +const void* pointer_452 = &i; +const void* pointer_453 = &i; +const void* pointer_454 = &i; +const void* pointer_455 = &i; +const void* pointer_456 = &i; +const void* pointer_457 = &i; +const void* pointer_458 = &i; +const void* pointer_459 = &i; +const void* pointer_460 = &i; +const void* pointer_461 = &i; +const void* pointer_462 = &i; +const void* pointer_463 = &i; +const void* pointer_464 = &i; +const void* pointer_465 = &i; +const void* pointer_466 = &i; +const void* pointer_467 = &i; +const void* pointer_468 = &i; +const void* pointer_469 = &i; +const void* pointer_470 = &i; +const void* pointer_471 = &i; +const void* pointer_472 = &i; +const void* pointer_473 = &i; +const void* pointer_474 = &i; +const void* pointer_475 = &i; +const void* pointer_476 = &i; +const void* pointer_477 = &i; +const void* pointer_478 = &i; +const void* pointer_479 = &i; +const void* pointer_480 = &i; +const void* pointer_481 = &i; +const void* pointer_482 = &i; +const void* pointer_483 = &i; +const void* pointer_484 = &i; +const void* pointer_485 = &i; +const void* pointer_486 = &i; +const void* pointer_487 = &i; +const void* pointer_488 = &i; +const void* pointer_489 = &i; +const void* pointer_490 = &i; +const void* pointer_491 = &i; +const void* pointer_492 = &i; +const void* pointer_493 = &i; +const void* pointer_494 = &i; +const void* pointer_495 = &i; +const void* pointer_496 = &i; +const void* pointer_497 = &i; +const void* pointer_498 = &i; +const void* pointer_499 = &i; +const void* pointer_500 = &i; +const void* pointer_501 = &i; +const void* pointer_502 = &i; +const void* pointer_503 = &i; +const void* pointer_504 = &i; +const void* pointer_505 = &i; +const void* pointer_506 = &i; +const void* pointer_507 = &i; +const void* pointer_508 = &i; +const void* pointer_509 = &i; +const void* pointer_510 = &i; +const void* pointer_511 = &i; +const void* pointer_512 = &i; +const void* pointer_513 = &i; +const void* pointer_514 = &i; +const void* pointer_515 = &i; +const void* pointer_516 = &i; +const void* pointer_517 = &i; +const void* pointer_518 = &i; +const void* pointer_519 = &i; +const void* pointer_520 = &i; +const void* pointer_521 = &i; +const void* pointer_522 = &i; +const void* pointer_523 = &i; +const void* pointer_524 = &i; +const void* pointer_525 = &i; +const void* pointer_526 = &i; +const void* pointer_527 = &i; +const void* pointer_528 = &i; +const void* pointer_529 = &i; +const void* pointer_530 = &i; +const void* pointer_531 = &i; +const void* pointer_532 = &i; +const void* pointer_533 = &i; +const void* pointer_534 = &i; +const void* pointer_535 = &i; +const void* pointer_536 = &i; +const void* pointer_537 = &i; +const void* pointer_538 = &i; +const void* pointer_539 = &i; +const void* pointer_540 = &i; +const void* pointer_541 = &i; +const void* pointer_542 = &i; +const void* pointer_543 = &i; +const void* pointer_544 = &i; +const void* pointer_545 = &i; +const void* pointer_546 = &i; +const void* pointer_547 = &i; +const void* pointer_548 = &i; +const void* pointer_549 = &i; +const void* pointer_550 = &i; +const void* pointer_551 = &i; +const void* pointer_552 = &i; +const void* pointer_553 = &i; +const void* pointer_554 = &i; +const void* pointer_555 = &i; +const void* pointer_556 = &i; +const void* pointer_557 = &i; +const void* pointer_558 = &i; +const void* pointer_559 = &i; +const void* pointer_560 = &i; +const void* pointer_561 = &i; +const void* pointer_562 = &i; +const void* pointer_563 = &i; +const void* pointer_564 = &i; +const void* pointer_565 = &i; +const void* pointer_566 = &i; +const void* pointer_567 = &i; +const void* pointer_568 = &i; +const void* pointer_569 = &i; +const void* pointer_570 = &i; +const void* pointer_571 = &i; +const void* pointer_572 = &i; +const void* pointer_573 = &i; +const void* pointer_574 = &i; +const void* pointer_575 = &i; +const void* pointer_576 = &i; +const void* pointer_577 = &i; +const void* pointer_578 = &i; +const void* pointer_579 = &i; +const void* pointer_580 = &i; +const void* pointer_581 = &i; +const void* pointer_582 = &i; +const void* pointer_583 = &i; +const void* pointer_584 = &i; +const void* pointer_585 = &i; +const void* pointer_586 = &i; +const void* pointer_587 = &i; +const void* pointer_588 = &i; +const void* pointer_589 = &i; +const void* pointer_590 = &i; +const void* pointer_591 = &i; +const void* pointer_592 = &i; +const void* pointer_593 = &i; +const void* pointer_594 = &i; +const void* pointer_595 = &i; +const void* pointer_596 = &i; +const void* pointer_597 = &i; +const void* pointer_598 = &i; +const void* pointer_599 = &i; +const void* pointer_600 = &i; +const void* pointer_601 = &i; +const void* pointer_602 = &i; +const void* pointer_603 = &i; +const void* pointer_604 = &i; +const void* pointer_605 = &i; +const void* pointer_606 = &i; +const void* pointer_607 = &i; +const void* pointer_608 = &i; +const void* pointer_609 = &i; +const void* pointer_610 = &i; +const void* pointer_611 = &i; +const void* pointer_612 = &i; +const void* pointer_613 = &i; +const void* pointer_614 = &i; +const void* pointer_615 = &i; +const void* pointer_616 = &i; +const void* pointer_617 = &i; +const void* pointer_618 = &i; +const void* pointer_619 = &i; +const void* pointer_620 = &i; +const void* pointer_621 = &i; +const void* pointer_622 = &i; +const void* pointer_623 = &i; +const void* pointer_624 = &i; +const void* pointer_625 = &i; +const void* pointer_626 = &i; +const void* pointer_627 = &i; +const void* pointer_628 = &i; +const void* pointer_629 = &i; +const void* pointer_630 = &i; +const void* pointer_631 = &i; +const void* pointer_632 = &i; +const void* pointer_633 = &i; +const void* pointer_634 = &i; +const void* pointer_635 = &i; +const void* pointer_636 = &i; +const void* pointer_637 = &i; +const void* pointer_638 = &i; +const void* pointer_639 = &i; +const void* pointer_640 = &i; +const void* pointer_641 = &i; +const void* pointer_642 = &i; +const void* pointer_643 = &i; +const void* pointer_644 = &i; +const void* pointer_645 = &i; +const void* pointer_646 = &i; +const void* pointer_647 = &i; +const void* pointer_648 = &i; +const void* pointer_649 = &i; +const void* pointer_650 = &i; +const void* pointer_651 = &i; +const void* pointer_652 = &i; +const void* pointer_653 = &i; +const void* pointer_654 = &i; +const void* pointer_655 = &i; +const void* pointer_656 = &i; +const void* pointer_657 = &i; +const void* pointer_658 = &i; +const void* pointer_659 = &i; +const void* pointer_660 = &i; +const void* pointer_661 = &i; +const void* pointer_662 = &i; +const void* pointer_663 = &i; +const void* pointer_664 = &i; +const void* pointer_665 = &i; +const void* pointer_666 = &i; +const void* pointer_667 = &i; +const void* pointer_668 = &i; +const void* pointer_669 = &i; +const void* pointer_670 = &i; +const void* pointer_671 = &i; +const void* pointer_672 = &i; +const void* pointer_673 = &i; +const void* pointer_674 = &i; +const void* pointer_675 = &i; +const void* pointer_676 = &i; +const void* pointer_677 = &i; +const void* pointer_678 = &i; +const void* pointer_679 = &i; +const void* pointer_680 = &i; +const void* pointer_681 = &i; +const void* pointer_682 = &i; +const void* pointer_683 = &i; +const void* pointer_684 = &i; +const void* pointer_685 = &i; +const void* pointer_686 = &i; +const void* pointer_687 = &i; +const void* pointer_688 = &i; +const void* pointer_689 = &i; +const void* pointer_690 = &i; +const void* pointer_691 = &i; +const void* pointer_692 = &i; +const void* pointer_693 = &i; +const void* pointer_694 = &i; +const void* pointer_695 = &i; +const void* pointer_696 = &i; +const void* pointer_697 = &i; +const void* pointer_698 = &i; +const void* pointer_699 = &i; +const void* pointer_700 = &i; +const void* pointer_701 = &i; +const void* pointer_702 = &i; +const void* pointer_703 = &i; +const void* pointer_704 = &i; +const void* pointer_705 = &i; +const void* pointer_706 = &i; +const void* pointer_707 = &i; +const void* pointer_708 = &i; +const void* pointer_709 = &i; +const void* pointer_710 = &i; +const void* pointer_711 = &i; +const void* pointer_712 = &i; +const void* pointer_713 = &i; +const void* pointer_714 = &i; +const void* pointer_715 = &i; +const void* pointer_716 = &i; +const void* pointer_717 = &i; +const void* pointer_718 = &i; +const void* pointer_719 = &i; +const void* pointer_720 = &i; +const void* pointer_721 = &i; +const void* pointer_722 = &i; +const void* pointer_723 = &i; +const void* pointer_724 = &i; +const void* pointer_725 = &i; +const void* pointer_726 = &i; +const void* pointer_727 = &i; +const void* pointer_728 = &i; +const void* pointer_729 = &i; +const void* pointer_730 = &i; +const void* pointer_731 = &i; +const void* pointer_732 = &i; +const void* pointer_733 = &i; +const void* pointer_734 = &i; +const void* pointer_735 = &i; +const void* pointer_736 = &i; +const void* pointer_737 = &i; +const void* pointer_738 = &i; +const void* pointer_739 = &i; +const void* pointer_740 = &i; +const void* pointer_741 = &i; +const void* pointer_742 = &i; +const void* pointer_743 = &i; +const void* pointer_744 = &i; +const void* pointer_745 = &i; +const void* pointer_746 = &i; +const void* pointer_747 = &i; +const void* pointer_748 = &i; +const void* pointer_749 = &i; +const void* pointer_750 = &i; +const void* pointer_751 = &i; +const void* pointer_752 = &i; +const void* pointer_753 = &i; +const void* pointer_754 = &i; +const void* pointer_755 = &i; +const void* pointer_756 = &i; +const void* pointer_757 = &i; +const void* pointer_758 = &i; +const void* pointer_759 = &i; +const void* pointer_760 = &i; +const void* pointer_761 = &i; +const void* pointer_762 = &i; +const void* pointer_763 = &i; +const void* pointer_764 = &i; +const void* pointer_765 = &i; +const void* pointer_766 = &i; +const void* pointer_767 = &i; +const void* pointer_768 = &i; +const void* pointer_769 = &i; +const void* pointer_770 = &i; +const void* pointer_771 = &i; +const void* pointer_772 = &i; +const void* pointer_773 = &i; +const void* pointer_774 = &i; +const void* pointer_775 = &i; +const void* pointer_776 = &i; +const void* pointer_777 = &i; +const void* pointer_778 = &i; +const void* pointer_779 = &i; +const void* pointer_780 = &i; +const void* pointer_781 = &i; +const void* pointer_782 = &i; +const void* pointer_783 = &i; +const void* pointer_784 = &i; +const void* pointer_785 = &i; +const void* pointer_786 = &i; +const void* pointer_787 = &i; +const void* pointer_788 = &i; +const void* pointer_789 = &i; +const void* pointer_790 = &i; +const void* pointer_791 = &i; +const void* pointer_792 = &i; +const void* pointer_793 = &i; +const void* pointer_794 = &i; +const void* pointer_795 = &i; +const void* pointer_796 = &i; +const void* pointer_797 = &i; +const void* pointer_798 = &i; +const void* pointer_799 = &i; +const void* pointer_800 = &i; +const void* pointer_801 = &i; +const void* pointer_802 = &i; +const void* pointer_803 = &i; +const void* pointer_804 = &i; +const void* pointer_805 = &i; +const void* pointer_806 = &i; +const void* pointer_807 = &i; +const void* pointer_808 = &i; +const void* pointer_809 = &i; +const void* pointer_810 = &i; +const void* pointer_811 = &i; +const void* pointer_812 = &i; +const void* pointer_813 = &i; +const void* pointer_814 = &i; +const void* pointer_815 = &i; +const void* pointer_816 = &i; +const void* pointer_817 = &i; +const void* pointer_818 = &i; +const void* pointer_819 = &i; +const void* pointer_820 = &i; +const void* pointer_821 = &i; +const void* pointer_822 = &i; +const void* pointer_823 = &i; +const void* pointer_824 = &i; +const void* pointer_825 = &i; +const void* pointer_826 = &i; +const void* pointer_827 = &i; +const void* pointer_828 = &i; +const void* pointer_829 = &i; +const void* pointer_830 = &i; +const void* pointer_831 = &i; +const void* pointer_832 = &i; +const void* pointer_833 = &i; +const void* pointer_834 = &i; +const void* pointer_835 = &i; +const void* pointer_836 = &i; +const void* pointer_837 = &i; +const void* pointer_838 = &i; +const void* pointer_839 = &i; +const void* pointer_840 = &i; +const void* pointer_841 = &i; +const void* pointer_842 = &i; +const void* pointer_843 = &i; +const void* pointer_844 = &i; +const void* pointer_845 = &i; +const void* pointer_846 = &i; +const void* pointer_847 = &i; +const void* pointer_848 = &i; +const void* pointer_849 = &i; +const void* pointer_850 = &i; +const void* pointer_851 = &i; +const void* pointer_852 = &i; +const void* pointer_853 = &i; +const void* pointer_854 = &i; +const void* pointer_855 = &i; +const void* pointer_856 = &i; +const void* pointer_857 = &i; +const void* pointer_858 = &i; +const void* pointer_859 = &i; +const void* pointer_860 = &i; +const void* pointer_861 = &i; +const void* pointer_862 = &i; +const void* pointer_863 = &i; +const void* pointer_864 = &i; +const void* pointer_865 = &i; +const void* pointer_866 = &i; +const void* pointer_867 = &i; +const void* pointer_868 = &i; +const void* pointer_869 = &i; +const void* pointer_870 = &i; +const void* pointer_871 = &i; +const void* pointer_872 = &i; +const void* pointer_873 = &i; +const void* pointer_874 = &i; +const void* pointer_875 = &i; +const void* pointer_876 = &i; +const void* pointer_877 = &i; +const void* pointer_878 = &i; +const void* pointer_879 = &i; +const void* pointer_880 = &i; +const void* pointer_881 = &i; +const void* pointer_882 = &i; +const void* pointer_883 = &i; +const void* pointer_884 = &i; +const void* pointer_885 = &i; +const void* pointer_886 = &i; +const void* pointer_887 = &i; +const void* pointer_888 = &i; +const void* pointer_889 = &i; +const void* pointer_890 = &i; +const void* pointer_891 = &i; +const void* pointer_892 = &i; +const void* pointer_893 = &i; +const void* pointer_894 = &i; +const void* pointer_895 = &i; +const void* pointer_896 = &i; +const void* pointer_897 = &i; +const void* pointer_898 = &i; +const void* pointer_899 = &i; +const void* pointer_900 = &i; +const void* pointer_901 = &i; +const void* pointer_902 = &i; +const void* pointer_903 = &i; +const void* pointer_904 = &i; +const void* pointer_905 = &i; +const void* pointer_906 = &i; +const void* pointer_907 = &i; +const void* pointer_908 = &i; +const void* pointer_909 = &i; +const void* pointer_910 = &i; +const void* pointer_911 = &i; +const void* pointer_912 = &i; +const void* pointer_913 = &i; +const void* pointer_914 = &i; +const void* pointer_915 = &i; +const void* pointer_916 = &i; +const void* pointer_917 = &i; +const void* pointer_918 = &i; +const void* pointer_919 = &i; +const void* pointer_920 = &i; +const void* pointer_921 = &i; +const void* pointer_922 = &i; +const void* pointer_923 = &i; +const void* pointer_924 = &i; +const void* pointer_925 = &i; +const void* pointer_926 = &i; +const void* pointer_927 = &i; +const void* pointer_928 = &i; +const void* pointer_929 = &i; +const void* pointer_930 = &i; +const void* pointer_931 = &i; +const void* pointer_932 = &i; +const void* pointer_933 = &i; +const void* pointer_934 = &i; +const void* pointer_935 = &i; +const void* pointer_936 = &i; +const void* pointer_937 = &i; +const void* pointer_938 = &i; +const void* pointer_939 = &i; +const void* pointer_940 = &i; +const void* pointer_941 = &i; +const void* pointer_942 = &i; +const void* pointer_943 = &i; +const void* pointer_944 = &i; +const void* pointer_945 = &i; +const void* pointer_946 = &i; +const void* pointer_947 = &i; +const void* pointer_948 = &i; +const void* pointer_949 = &i; +const void* pointer_950 = &i; +const void* pointer_951 = &i; +const void* pointer_952 = &i; +const void* pointer_953 = &i; +const void* pointer_954 = &i; +const void* pointer_955 = &i; +const void* pointer_956 = &i; +const void* pointer_957 = &i; +const void* pointer_958 = &i; +const void* pointer_959 = &i; +const void* pointer_960 = &i; +const void* pointer_961 = &i; +const void* pointer_962 = &i; +const void* pointer_963 = &i; +const void* pointer_964 = &i; +const void* pointer_965 = &i; +const void* pointer_966 = &i; +const void* pointer_967 = &i; +const void* pointer_968 = &i; +const void* pointer_969 = &i; +const void* pointer_970 = &i; +const void* pointer_971 = &i; +const void* pointer_972 = &i; +const void* pointer_973 = &i; +const void* pointer_974 = &i; +const void* pointer_975 = &i; +const void* pointer_976 = &i; +const void* pointer_977 = &i; +const void* pointer_978 = &i; +const void* pointer_979 = &i; +const void* pointer_980 = &i; +const void* pointer_981 = &i; +const void* pointer_982 = &i; +const void* pointer_983 = &i; +const void* pointer_984 = &i; +const void* pointer_985 = &i; +const void* pointer_986 = &i; +const void* pointer_987 = &i; +const void* pointer_988 = &i; +const void* pointer_989 = &i; +const void* pointer_990 = &i; +const void* pointer_991 = &i; +const void* pointer_992 = &i; +const void* pointer_993 = &i; +const void* pointer_994 = &i; +const void* pointer_995 = &i; +const void* pointer_996 = &i; +const void* pointer_997 = &i; +const void* pointer_998 = &i; +const void* pointer_999 = &i; diff --git a/third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so b/third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so new file mode 100755 index 0000000000000000000000000000000000000000..6ce6d0cd22dba72959ffa1fb7f9abacc917c28bc GIT binary patch literal 93210 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

s5Wh=Hf^Z3{dW7o*ZlodX6${lPRwDm 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 literal 0 HcwPel00001 diff --git a/third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so b/third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so new file mode 100755 index 0000000000000000000000000000000000000000..d97ef825260d3a5dfec58d42b477693dc6220532 GIT binary patch literal 89114 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 Q@$u8sAOF7e`%ka_268n@7XSbN literal 0 HcwPel00001 diff --git a/third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so b/third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so new file mode 100755 index 0000000000000000000000000000000000000000..945b450e3ae1706dea37589b1b1708f8492994ca GIT binary patch literal 134131 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 RYJ9z5f4n>FHZOnM{0n5tBgOy# literal 0 HcwPel00001 diff --git a/third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so b/third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so new file mode 100755 index 0000000000000000000000000000000000000000..e44e4597b30e56d749a748ae7df6c8756e4cd1db GIT binary patch literal 113651 zcwX(@Yj|8`^*{Vc3++ImgmWmT0Skmf+d0o3LXCj%%<8Of=9?w!xt{;^fAhS^T*^#7 z-Lv<)*S+?A-+RwAz4(Y@kDOIe5yE43=qDlhsnAsVxki0p-P9&U3ql(g|63K>cEh$C zhv-pfp0_g3p^B;h&ME#syh};L?G@9WYjz7wd&c`_>H8x3zKCv*=Rs`>p2Pa?Far+avXE@c~gDfBxLdzDz$H;(0@N> ze~kT~JZ2YPmV9&h|$-F8Teq{p{y#e}dzC zIKIsBP(#Ctd{e`!*7l~h)(e~U$EL34d}~*zt#$c|#ht4|4b3Z?x|)iAw!E{mp|h*0 zql;fRH0V#w?JGCD*5~4r?4hH%ZS{(#uGZD<4Y{TjXEk>;bTxN&HOL-i-}*SD?&SDu zbNfa&o;7cB_NC#%FDnlC>)b8Em!eK)9y5=b$IN5qG4q&t%sgfuGmn|a%wy*9|L@26 zYQ0af(e!Kh33A*6*x;Xk_4F zN%7c0pYi$Vj`&%h7wB`P{`pS$S)XyuRDXQw8J~?#oKSkkXSgH!=lC4;i~2lIpR4p4 zpP7!}`b&R~>hm`GjB7Gn6xHeHq+5nUjq;p7vGns+`t#`KrJpy^?W@c)9`Aqj(T30b zkjH=P&#(R8ewh1z_~>q${*2GdXC5<;na9jy<}ve_dCWX!9y5=b$IN5qG4q&t%sgfu zGmn|a%wy&;^O$+eJZ2sniAQ6}Xb{gt zs&gI99nEdcO`XkqZ}_r9=?e%S|JQsEJ{ezt2v>Y{zvAmvJBLEG`b~jZt)Yr}^0kFc zW`Cd`;?GVM|Fh9X8@IN1g*Lvixc7=Jj^D6B=)%&c`*XsdDgJlv#{KeVm8Cxm>z{3W zr{I<)tOVXCxK#K;PZmpl`sIlD!6?KYk(7iRVAzi zp0J>ZJCtzpe8tZRF3{HI6oDY%O^6ZloZUA39OZwW@U znZP>*chhD9e=NAWHWN6xV-Y`5!tM(cXG_}`maqpnUvQ5S_5ybh+_Qvzz=eW)m9QVU zM6kMq1HkmWI`;~AQ_++tAD;AY-93A&Z!Nnz<0KOu) zM0*qXreIWi^Ye=D3C6THfgA5q#JKh*uu?Fgy$Re=FsZ!>+*2^6y$Os7rnNVLhwoa% zObP41pm>~MP{KywGQn&KTY$}i`Cdq7=Skm z9<0RxyhE@?ivf7cZbdvqJMJRI?+G4S!V%zof`^rG6!@^<;UydcJ}!8K_B-$of=6n< z1IGl9(tZd2O|Vw`eU0LJ!K1a`ffc(K@fhuQ;M1Qd;-|Dlz&{Bdt1SY)F8FC}5%68X zrP?B3XkihL(-r~e3D#+gfZGWkuPp-ZCU}Cj=wikF1W(i!0W*RpX^Vg#?oq^(OIQGI zvS$%b(Pjd-5&Voc6S%8jy*3lLkKkvunZT6bGHoWXM(|W^Ch${&r)i6N6i*TSoMN|P zli=yvOkibo5gSSvd{J>n!NwBS0QVGZDq$@!Cb+zWb-)7zSCp_Gc$DDE5;g))5Ns}C z3-EJ-t4f#ywg{e4!aT53u%(3Ez$^AHVrvNpFHyWs@XQhp0dE#OOM4S|hhUrbCh#7? zv$Z#YKNoD*-UJQ{uGZcJ{$4Pr9k*8TMZt3vvE^%m9VMKc+H%_>c52(tQG8vnOYthj z^@8VWLV&ZjE8=-2>;cXfJimm!zy*T&67~W26uh8>{lK{3g}Nms{euKQuZTF05&VL_ z9eBd_MZ8G+{c6S21=nc51J4w^SepqvPq15?3A{w`i`q=!HG-FDGl4e=UaD~dZx_5w zGvA4R7reZLp)SRT1g|Jz82EZs5x=Ad`ZBshutyIBoV7y{uhfJ9=L@ba;Q(-f;8i6Y z1nwz#bqR-naltQ_umC(r@EXn1dFT$oYc)&2Qv`c8cwj-6M%QT{U8DGf;Pu)^z)``k zXdeMz5&Wti9XKI)gB~6Df#8jLbYNK4U|-YJ0k;wC)6@YYf?wCv0rwNUNmB=0B}=1k zXvgUlFao?yy7*fqtOnjK*k8gZ@BzVFOBevx3Eo!1 z8sJlc-!5S-@I}Gzl&}tXfGmx^Tf$Lbt>En?90Q&tct;7xfsKL#C7b}Z34X7Hlfb;- zoh1z6EWS+e`}h6a2B}6F4VQ#QQa$z^w#-qWJ{wA~>Y^1a2lvqo3;A zI~1z~AJDf07YhDN-wuolKB(ORJV5a0+8r3IY6TzC?f{-7_^@^duu-s}-2rS9{DpQ0 zFfaH>39EsJ%F^hUx=##%O9da*eFB#WKBoHwt`c0Q`vi6f{;Gsg;2Oc<5(dDvg1;_d z4e&<6$4giXyiM>oC9DJ9E%=0Pc{$oHOQYZFKF>wl1)tO{ftLyXkM0xLD>zcZdf?51 zzbj!Qa6s^>61D*E6a0M%bHIY&(0~sikJ5h@)Cd zsL!BU)LmGsyMMv5&x$9M14LW_4#)#8{j&@e`whNpAvjS%Le$O z;G0@DQ;QS92`wAodcn7}Y=E;Q$!}}hadylX{AUUKu;l{5cQklx`GK_P-4YI7fwoJ1 zt}kIP>T^QyU%F4=2ZEC&><5PTD&oIOID~y}Blw;k2wO%3-`6dH`w4zf!UEnN2>z#p z!`Sk0!4DO&Hz9ZKfo2b_6B0e0FF5FbL4!W7>Jk@$=mFQ-w zjnIXno2#}!qoVUwbI=1sw@}SPYeg$nyP+qEZmHS>Z4})~wHF%Rx2RjAZF*xbyp8BK zXd5&lx-HrU-A{Bov<(`FZjZJ>4;QUM+n{x#JD_dQ(?l1bZO|6c9TBfyWre#$cS5|- zC5wx?GkRWc@r6Grx(j+9dX(s{=y~Xgq7l_`XoKi(suR$&M0Z!6gq|<@3DrQaIKr2T zE>x|7UMso>+6MiG=$>dB^iNBQx)=7VH~YeW6RpO6q5l%y8~cUMjuv$v>=(L)=)UM< z=#HZMp^u?^i7rAPLldHl(Z^8vPsI}SF;srDY(*5U&|h&HmMhJQnCgJu_6zrk##INQ z-w{oy4ngI&=2j$C3(yBeQ>w$zVbL_I1p2gS22}zb6Ae%$(7%gjQ6*6MO}Z8PtM=$3 zBK*?EMg62|FRHp*>h}QEKIqk=2def%`$P{ycA(!8Js8=64vN+wJJ1J34?%XI!=i^G zJJ6>^4@1x6%o-CtT(ui*dtj5I9)a3HrQRX+dnEP%y;t-o)DCpkrbVqq?LZ%vZI4Fn zKz}283}S#jEBYy99s089v8r`?w?6!)=%*1c^nKB#h!+~)yr{?FXsBQLTe1~(I2v@J z=<(=dsC?vAaRT}ndVuJO=woQD=t<~f=t-g{qmQADqNkvbp>3j{K_5f&qV?!w=%HH_ z^|PuIsNaod6?K{FB(zfWRMn8){|@gYdYWn&S}poH)kcbqixU+MRRBy zbW<6l&OzIt+lh9dZP49CJJB}iV$m+N4Z6SRxo8{oNYV4qHs~9o=c8?UX%l`=G>^7H z=gcYU1!x;|E71$lHs~&*pGVuE`-px4ZG)yoFGAa(hl;L2+n`HDFGky-%S5};Ht43s z*|6e^s=Z%Cenl@)?St+vdZ}tZbg}4VssqsdMK4z!gdQn+h3XLW1ko?47NDn#_NWd+ z&lJ55ltd*~sSJWG@-^)~Q61@@oh2AduHS8C9 zk7yri2l|lc*HJsr$3<^K?LeOq{f24_^d-@oRddicM8Bz;hrTCz3vz~W{S^81;ajLl z%;>A6e*1AW)NhCAtvDKVjp%JS8g#Abw^h5LH;R5owFi2e=ywqV^ls7HRr{b1h~A;v z4_zlZpgI6uE_dy}hknzm*zhG%zjvyRp?$*LZ1=+k!lt6CDD6SBhWQ6Gu(@cLj4{j^?M&G3VMv_k5N(3Q$+7a zA48Xm{sestZ5JIvA44w`{VDnwdWGl%=ws;hqCZ0)LvIm%5Pb~2L@M>?*e~jLPVu6l z;vwu8x|Qg|*e`S!(E|1h-AD8ns2ylp^byn!^ia`Xst!Vziax431YIWjm}&vKN_3s- zF!UfJ6gs>7q}mRzc4c{k>`gdYR(hxq0foF zsyYUJRrIf_*61vY;MSW9s2$~k1P%S_Y6@5!}7`jySZPgLzGSPn`XV6um?;vN;4$*g!Gw2%8 z^~f1?t?0jyGw6+?lc=aGRWIDSsQ*SLp;w5$hde;97kwXjfZihd0rCL7Q}jQ`1N45; z50MAxBcdN656~w?L$fpw(C0)eRL7yOiq2B4$DM|q_ABaaY>PANMXBG7RBKRCuZwQ1 zS_@q-I!Cn*I!kJDE{+DBFB(>Dgf0->M70IFr|71tIcQvTo@yR?kmzQr-O&4r|Ebtq zHG)1qa?7I5SFMJgAi9NW6neU7rD^~@Q*=vY2YR09R>%(Y649-Z9q2Wp+aNp8n?$!o zcA&S5ZinnZ7s!?6_GqzwK_`5h)Nhq)7(IWt=nkrt&<8{ps8&JOiSCG;L7x)c2|0tl zD7rIp27O(07vv1OUUXOF3_43{GJ>2zAC%8Oc2k{zzAfACt~v?*Q1lb3A>?7xZHl@O zd4O&wx(D(A-CcA~-^bT8xqy1!^O@&G+jbZ_JVdV=Ua$OH5l(S22Wpf8E;r`ijB zLv)d9AM`!Z#j5?#Ir4?8C8`6^twf`$gV0?>W2!^YeMIA`1!!6{p*jpbR5YnN0$nAV zLLcL9bBAaeJ%n**jc5iv1YIi{pogG0ie}M6(Az}!M-M^o7X2i82>O8N0q7y$8PQLp#n6{Tm!iecH$;y^WYG6S>(FB89Qg*)@n|u0E723sV(3G1ww#Eg z;ePewq9@^K(D~BhlW{a?T=Wzi4cac(yr03*pcjhPt4=_#5dEy`B=maGWvZcT&_kl9 zs)nI=ik_xg30*2P@8?t_Q&&l%r>j;&yJSplP>n(Z`F2_(`VIR%T(k-O2CWlaj!Z&N z6J3E!LR&;vB9qWA(Pm^4+AX?DwFjSz+$l5q8OS8+_fDzb7Gx57zi2BmiJpH%^h{(D z`lRSt$RzYR(Kgj8=&Pbb}taDUraQAB$soxH? z7~Ae6+KCoJ)1qByF)Hd%(R0ya=u*-1&|>H^(eu$_=qk}XS`6(Fy#OtSt`WTuEuK1> zjGmuY9mgm9_e=eL0Y^j6KO%Y&js|^FbPbLMeNOaZ91Z%aXgBf;eOvU4$S?Fm(Mwco zp_|HgJ}*_RgKj5!nQA@s9vL+*N86xROZ{GfeuMUjehK{s{f=l4`VBMWpy-w8H|T?+ zYte7eVbQCQN$As}S0j_qG0`t0lhD75UV}_R-;?i@UaOkZZ(D}v$X~7LRn0@U61`5f z8@h|=^{PG4eMG;a+6zsKepR&(dZ_3Ps{PQVqBp7zKzElO`kLw>beU}1r#b{J$oD(H zjy&Mr>l30kArH_|(Qlxlps$GDjEaIzh<+0l1^qzu7E}~8EMJ5B7Agw5jcC7WBQzp< zt7;2$KhfLJL(uh+qJA4~)9KD47=#Npq&_MKl)Gu_teCO#W z*l!nlNOTA-#{Is7M1P9?PHihKegON0#zlXoItV>T^g-1j=rN){S1mwK5q(H?7`j~a zVbu|6yJ!LXMZDwkN%1eRUwj@gDQo3NR7bJ)td#o?zr=pAZJn%E9>uoM(?lOr9fP)r zu2UU{=48LWQk{Ty%eKSF8T4w=U#o^NXZ49bt~!Zruh^}qzd;Q61myKdQJ+Aap?-H3I$TqN2Wr{i1%qBlY___6r>p{TucR z{YdukckCBBEZhD=wHo@g=o_k0=$Po6ssZ%xq7$k$&`Hs^RBNFd$#+BFR;`1s7yT!) z4xJ@E^bWEPJ-ql_@h-BCb@c+-c0IBKt&?s4h3r7%vh5_Y13gW){Wr1$Jw~>D57~j9 zBKkhE16?lq0kQ+l%eC}>RQsWqiGHX$0PPk1NOcf;vuJ2`Nr#{Vq7|wI=$&#MJ4+ZSojnQqeExrS{wdl60Iq0sU+o|TE`-*O_+6~Q!R;l(t z4-?%%wHJDv=mOO~=!f!&F|;7tegbs{eL{30`WQ!RmwO6(pcPa1&qViB?VtL@ zMszPkhSf?=RyNhBDD?3cME6#$h4zTi1aDm}&%CFB(^^hOQJ%s79e5%F&Xl0rU&9Z3@|e_K2pD z9q3l_c|Zo)f&NwIp#a%|o+sx}7Hz||^r=$6`=j4bzh{VkQgr~@DSCkFAoOC<167Bh zSBV~^T7Z5{^kCIt=(j~{R7apc6g@r{K7kH~(HSM7zqBio*!+6R??*r(z|R0(u5 z(UVXm&??cBQ6px215LffD>iJpPBL2nmrLEE7Bh_<3_(1%3NMBApmWh-}N&r%&m zP3|t*raA&$EPA%;D0F|(cI+2dfG^6YJF8J8Q`d!}In@Gmz34fJ0Xj>@)DFY|oiEyn z7@!M8yU;@zCl|{-v2#`Hu`1qQ^gPu@=#ir5qc@>%$amoKs@3=o+9JLS&a#fS`7KKF}uBeJP0D$y^Z#mLT` z@*TKKP&>G~y zZ=x^SMc1n4p%;o?g`;6K>X7fiU5(noDszqKmsQ87zA-9#jp`V-y;1a9Yzw_jv{!Wk zdbj9xs*}(MM6Xv3;a<>@a<+U0b%ws!Q|kAtXd5&xdIOGzcn=c25l4d_BleNykA$Xgtp7JH>p-Ze=pm9Lp1`uLbkmb)d#&rM$d1eelhDED)oB{st@(M zRPhJM4gkBB~~It+bM z^yjK0(C0)SLLWot$a?)@^fB~p*|vZ_hAxxO0Dpl#hHfe=-AB;J&|PG|zeFG7I%{{? z_EGc&^l{P0R2!ksh^|v@fxaaAE7ctI4bfrMJoG)$U#oUQ=g8;ik0Za(twevL+6&!9 z^a<5I=su#qRqcnSMW0k1fXJYT0_+0TvM20JDxmQ~8 z93q3t_+Ih9hzxqqmPLIYy@~I@JtX=9dK3D%=!@u0=rf{!LJZKCM8^;V^bOHJBL?Vu zqAwvk(EDW$cv*E6*XWOkzM?t?eNyx)f%+; zOwqTnU+8(FZ)3mEOGN*P{X(x1eFu4f-X!`i@&LVEbUpH*Dt~`}kNI2An>~NW`IW^b z%B*B)c6k4JpPYB}*0YM0J$uj4(Z!T(v@kSp-a+$b7uUZV7dMz++Th?FWRv3XFmIl0 zGm1l>s->&+_ri}Q&;Qi z_J&;3inE$KmUK0Db~UVQ>S_vwn%h=2tZHp*ZaBBSwQGZK*t~P`iWRb>P;PZ=dslNu zLp%uRb$04?A|9jHaeAGg*GYPvqStA9ouStOy)GXs6C~($l3u6ib$OquSd=`V*X4Z% z6n{YR2NZum@dp%tK=B6@e?ajE9Dg!SuPOdyc|X}qlwQZ^b@{#VY?NNd=rwPXpw~%y zoub!idYz%y)Yn<+>jd?6g8Di^eVw4bPEcPbsIL>$*9q$D1m%k2&rtjsia$f~XDI#* z#h;=01BySO_ydYRO}Pyy{(#~SDE@%r4>*3RnM|=xmYmz!u|&?pwk2n*UVTPe^O6;- zR~G-%*3^Dh>$zty>FijsWc9gS4NdJUJ65-@Tyl8x@^jCa+Cws4jw+Gl*VH3aU5O<1 z2vu4lNj*YUmq=2NB&kP|6jdV~H=yGNbliZB z8_;nBI&MJ64d}Q59XFukX1SJV32q?}3%}@y%6B0B-B{EbS z8LEv8?}ut5L$#5i+Q?9C#Hco6R2wm>jTqHNjA|oBwGpG*h*53C=-j0E1BySO_ydYR zp!frdKcM)t6n~cD&r!KvDi|DvnI&PMZn@w`td3(O9Vy7DBH5DBblfZ*H%rIO(s8rAwxr``>9|=sZkCRlpxmai z{F>TMYk*{$opM5ReUj$-B&}PLH18*A-cQoHB}wa+6s=oQv~Ee!x+O*HmK3cAQnc<$ z(Yhr?>#h{7Te1{?mg3J+{25-KQ2ZHQt5EzI8u!w?Zb|TK$`y^tNg9WfH1;NG4Uo+6 zdnwPf21wExAW3V0B&`8bv<67g8X!e;Yl_wYDOv-hXbq5}H9(5i04ZJrQ2benKTGjv zDgG?QpQZS-yoTZHHR|6ik3Te4Bx!s|(wLB>IXX#mbdu)iB+b!D8bgu+AB%EJb99R4 z=oHP-DVn2GG)Jdsjgg``Iz?-Y6wT3Dia$&7XDR+H#h<14vlM@p;!o3DAMhMO=R%Ul z3aa@e&6P=-E0Z)=CTXrr(p;IOxiU#}Ws2s?6wQ?>nk!RuPNitBOwnAKqPa3fb7hL= z$}Gj7rTDWHf0p9UQv6wpKTGkadAtqyHMNWHS0ve~y)-|jsQyzlKc;A2PSO0BqVpm} z^JALk!7RU*=B5}c3J}DlWn^H74#c0fo(OeLtxgbV!L5${t z7|jJSnhRny7sP0+h|ye-q~oS&E=bW_kfOOPMRP%l=CTyc1!;;uP4TBG{xrp(rufqo zf12V?Q~YU)KcM+Kp!qtW`8weFj`JMj*OYUb7h*Io#AsfK(Yz3&c_BvgLX75x7|jbQ znio5(773-IVDDON{r@|7|kg$np0vlr^IMZiP4-Aqd6r>^^l@D zB}H>eia*_;IVDANN{Z(cia$;9rz!q4#h<45(-eQ2;!jijX^KD1_hxB*70~)B$nbbl ze#u!bzoZ;fiYddCV9GD0mwJ_EgBZ>EGjz}|GjAphN&1^B6*13cS^Kp#kW7@jByQx>SCMxH#+{&p}bbUJYit9Q`uPO5sQ<`E* z(>Ro-aVSmWP@2Y}baCwY|M+q3lK;0)Yp2>kYn_1BIsxC~D{sP|>Cpz|_V5RK<<{`W zdF8h7hk4Yiw5o~GswPIOni#EWVkth|>D;4LO^jAGFuHBB+3DTXw~kfs>Y z6hoR~NK*`HiXoliWAXVzt9N=y>v&qmQ^nFEp4RZRgy$=w@=MA(txaOIHi^;NBt~nK z7_GlzwEl|Gd>fud>T8d0kO{NtH#jdyHoH7|-ss%8bz}Ge)b-7_Bm6w91UpDlp5h|_8zPRoTjtryZ1f12V?Q~YU)KTYwcDgHFYpP~3O z6n}=|&rtjsia$f~XDI#*#h;0B71MtBQ^sjmIOUhTPAtFVS*`q%&n;?b`6V@MDb_he1_5f`86}Wv;2}O zm?zfpfIRXXe5l$NEoA$Fh=X@7_F;gw62cPx;jSd>KLu7W3;Z0(^@D_i=jBJ zhT^mwiqm>1P79(qt%%~ZB#P6TC{6LFDgN}d>jF9zDgJbpPcW(!ia#^$jvaL$#h;=0 zGZcS@;?Ge08UA#RCM%k&$_JhH?Vs|?@)7wXSXzP8MI`kMEuG@Dc8b&DDNd`WI4z&z zw0?@y0xC``s5mX5;`})l9XG>k|A3v(dFlaP`}5~v{P`IjH^axJ%dQN6-dtw>B!o67 z$HKQ!%aQO+)N<ZlRVlLCcCbtt;ZRu!z&jB2G(-IIS(>w77`V>LO0di#V+>Qas$G z_%&shV#rVo8D8G-07WrmD25Ehkl}SZU-;7b6HoJN$_Y)Iahf;dG;zjh=8V(S8K=23 zPLpSxX3qqzN)xp5OVFw`L34hBzp+Deeu7q|30jpVrhPMtDxK;q!!th>U5Zzw6n}=| z4=DbC;twePfbYukC;L%;oe3Sa^yJ84`|cY_F3v`ZR~yM_5YI%aa~;jai{a*`&gQ)% zi_TtNynb$7QGC%>yijgyUew&avbCwb_;b_p)&tve>11Tl8PVeZH~jCo;;Z92Ba1q_ zRvvgp`?>q>8;eC2t!iIggpSq~U5lF9TAMms+lyPSIW)54H*Z@PeTdX>_eZyzf*3zr4mX7A8m7Ptinv0<}b+xYOoEGAu zQV5-k5bB~_Q%BR;kJ`*;V-K3m(%zZpi?+b zONKaqjhPk=aauLRY1t5`bwivM4slvJ#A)e}pe1&Kme>heRwrm#ouFlPf|k_@T2?1` zSenxXAer#=DwR-B0mM`yUJ`X?134_-gg3)he zmx3xDDfEB)*s=KkjYINRH%gECzed= z%hGdHUH*Jd=)dDnhf4dJqd#}+`@8l1HKFN`O7r~5(De5g^!){W{{f-tkBvgpABTiW z???O}==(p|^5eI+KObHE|NKy?59ASU*S|)zi~el3xA{N0`2Q_J<@Z2l;QiR|nW0il`16s~r8kW1TKXCNhey~vuMSOre<+8-Ej`ul z8k+X7@t+$iz26?E_TbX<9>y!o^P13q=TshN>-!_d?Y3Y37~A0b{=-h5?snU+e~igv z)mcEr^!^` z(_>WMKVsazpU*vh3_MYK9)Gy>ynXTi>*xOW__0aoH!n$Q0)EBO|V+ffd_rQ=&UzV&~ZM7L4st&gmiBAFX{!sHEZ z^ISUOf9I{_AD@ShJMOlQV?SZDADLe(sDE)jV*AS6$H!CUc*OB)$D@u1j@LL|>v)~x z^^P|>-r{)9@x0^Rj`uj;>v*5z{f-YfKIr(6;|0fu9UpOg)bTOL#~q(=eA4mIIUny~ z$15GLay;UAwc}C81IKF|uXVi6@p{J_9dB_w=Xl=nZpV8Z?{&P-@qWh#93OOi$nk>X z!;X(QKI-_GX!;X(QKI-_GX!;X(QKI-_GX!;X(QKI-_GSuW~%%c(vnE#{tV+$ z9j|gc;&`>=QO5(vYaFk2yw34@#~U4QaXjaE-tlh7dmQg|ywCA|#|IoAbbQG1g5$%E zk2pT+_?YA4j!!r~={Wuw7VBZhD;=+LJmPq@<59;0$7>v~b-d2;ddC|bZ*e^5c;4}D z$9o*_b-d5iC%BX}2Jnwk7<2{b|I^O4azvBar4>~^Nc){^u$449=b$rb6amObd zpL87mz>@W_V$@m|OK9Pf90!0|!Hha4|BKJ562 z?AgyWNr;~yWi9(KIa@hZn7j#oP#bv$sq#_?Lm>m09lywUL%$8(P79q)F$ z$MIgr`yB6ge8BNR$A=s*I6mz7h~uM_=Mw=j)yLC>fiB7$EzHVI9}~|)bYUa z8pmrLuXDWK@kYm69M3tPcf8y29>;qf?{mE0@d3vN9UpSM;P|lPBaV+cKIZti;}ecg zIv!f%)W74Ej#oJzalG2`sN;d-HICOhUgvndXSA8~xt@iE889iMP~((zEYQ~!=vI$q^? z#PMp!qmBoT*EnA5c%9?*jyF2q;&{&SyyM-D_c-3`c%S3_jt@9K==hN11;>XSA8~xt z@iE889iMP~(sBILtk%PhS2|whc*OB)$D@u1j@LL|>v)~x^^P|>-r{)9@x0^Rj`uj; z>v*5z{f-YfKIr(6;|0fu9UpOg)bTOL#~q(=eA4mIB~JZ2Ug>z1;}OTJ9gjL5I9}s; zt>bl$*E`?|NeAw|3$44C>b9~(K3CAZL4_)rmzvGpTS2-SW zyxQ@oy{j`up==Xk&41C9?mKIC}8@nOeD93ORj z%<*x@Cmf%2JamOq|BhEWUgdbi@oLATjt7p{I9}^`o#XY6H#*+pc+T;>&&P_>kiT$A=vsaeUPAF~`RppKyHA@z9r?`ggq2@hZn7j#oP#bv$sq#_?Lm z>m09lywUL%$8(P79q)F$$MIgr`yB6ge8BNR$A=s*I6mz7h~uM_=Mw=j)!`j z`ggq2@hZn7j#oP#bv$sq#_?Lm>m09lywUL%$8(P79q)F$$MIgr`yB6ge8BNR$A=s* zI6mz7h~uM_=Mw=j)$&v>fiB7$EzHVI9}~|)bYUa8pmrLuXDWK@kYm69M3tP zcf8y29>;qf?{mE0@d3vN9UpSM;P|lPBaV+cKIZti;}ecgIv!f<)W74Ej#oJzalG2` zsN;d-HICOhUgvndKw0kywUL%$8(P79q)F$ z$MIgr`yB6ge8BNR$A=s*I6mz7h~uM_=Mw=j)$)Pcn>>X>3Egn5yz_?k2)SW zUgLPJ<8_YLJKpGci{m-R^Nx2r-s5<$<9&|zJ3ip}pyNZ17aSjUe8lll$HyEWcYMO} zNykH9cIw~pO2?}lk2qfKc+~O0@fyc#9j|k|-tk7qTO7|ho_D<4@gB!}9q)6z-|+#* z2OS@Byx{n-<0FobIzHz3xZ@L!PdXmD#;JeDD;=+LJmPq@<59;0$7>v~b-d2;ddC|b zZ*e^5c;4}D$9o*_b-d5iC%Bk+_J5^1J_m%~ppN@F@LNOj?>v-$-#-5Rh6i+e{Qe?rzfK*0{}KL_I)3jN z{6+N%v!B=1M@+lctK;{yz-LwHe$4*otK<8n@CE84roZ-7$MEFxMCrv!psbf71|E4~zjnGmtB&uX!C%JqCU0-3=ge{6Q^();fzO$x{bKgBCEVnHC-t1ElfBe? zOg+cdCyXDYj^Fn?RS)WYCjY0XX6i#hHE>XqiazFfWD?EgCTQFFe3 zQyss*VyYh02TY!StUhk`{|ohysfXXH5=y!}NzFz5AK>IG8||3MzEo$i~= z*8Q06w^1K8<3L2c+SJc}>O*EffqK-$dANGmiBzw@KuQ4Y~P`de-{J3 z2JxHYu2ruw z>(v|0yg6$ltt+$reDz_|UklW0?7X1fW9l=m-eR^tNPWoU{}}agGwz*&{hRzR$NtUs z?doG@p1M%I*3`ol>VY{=uSXut_XT+%JwkoN)I**6 zu-X4<8~m-0?7!>OtlRgRJe-65n|Ll#?=$(oQoYvf=LWc$M{dRb&A9Xf^%~Jf8Z zKdL@t=D`uT$=mblV`lqt9M{zOJL*wWKk|?5ZE#a>o2dupyslCYn{i{IdcWC!RK3#7 z%Ll0A@AtuL)yK{DC&5kr8`b+vJZ*^IoOgM+84oX0?=kh(tKMkF=bO4Ch^%m3be^Aexy8W|y$i(>%^J#5w+wdy&upOe(9&A8gAo;UHdAx<-2=W$$fzFel> zYwD^Oc`*CA8E(eU0k}E8?n9iWUkd7#rmmh)uQz!c#rEd7uc!~2^JqeS)U@jZ^*(c+ zhBqmBx5>{o>iuT>hO*FnsZ$S`yq%^VG4;7hy~>;y9qN6i z-qxt+OuN>qkD54dL_Fp^x=nr3wD)fHF>~Ap)a%VUd7b*Osq^3AxMnyz$jq}b^={KI2dWp$_;a*+zvx7ro$4wsIQ13CveNR1N>S4}2trOE#$wN+k%$#4JSD!Td`4Wz6&ZDm&4`v+rmU_MSV`=@%yjBlaM^tfjG&D102K2nu>qsj9^^%i?w zfc=~E_W<>3ljmA>{QegBN$T}x+-^iXCO>WJA#=Xx)klqAre0_2wpV?^_|0gqiE}_b zXWDxo@?hFqK>VgYpHS~Jsv;VE(W?bD> zz0S0EU-cSOKN)rWJ|FmD>f@%pOVvls{y(c8Hse^cdZX#DbJY9I_<0fbW6q;1)pPc` zKs|5j=T`Lr<9Dg|n)BkP>LVuqk0E|@p8ig~%Ea>m;xXguIN~?s+dtI@&A9rJdbK(3 zy!m=u6aV(=b>=$y6Y62JpC#Cj>Gw~n_nC3;DD_@bhbOAnn|f|g?>2REmU`5*_k8Tf zT#sL>-eTgpR=vlx>lcE7jc^N{bBVnbN>EDebD6jS+vV+|FZh9IqsXt zvl)NhM?7YoId_Ya2j+a-QoYCc&gvs(eArvP${aVPj^AemKSVub^8ab|VYC0ws@Iu( zHmlc|@!=fxMpF+Lp}ppOyb|%7dFKZ8F>~JCiaZ#OCg@3)BOX=gSelIqr4pAyaQRt52Fd45(L{{ojXpOg$8^A9MZn z8}%A9&OEC=VDk1d@?gfRH`Vd`+2HS^U8a8KZmHXw_3hT`1rz74>UHM)+E+bi_McJj zHRtf`3P$EgpPe4eTvGX37HK4#|kbJV-daW7IIG4WriK4I4bXqV~7TagEIe%+;B zYx3|@xLH3urrv1c{GEEA+0P5=RVJR-(2r(bcvn4Q*7NcOpbdF8``=8x+U&ndyJqg>J{Wq%jn{lrV+nfH)WB+D7aG83&y-%oKXX@%^ z^_=km^}zJwed?2D`-1wAnZKSuyG)$Vs<)W&=VkSfSqHwUUTMaY_t1}Ke4Dd%$)jdG z+)91aiGu=GWAB&u2tB-spk&$eskP4h|`R# zYt>s!ecqtnZQ{8V`#0yyUD)1!UZ6f~)=Q77Pnh^e5Ra*o=hZ`Id>+Ss%z5+<@@C@s zNFBdt6+Ukp-QKLvw^#2mb@&POg7GEl{pP&*qnQbFGw+|MK4R*vLA}MCFK4L- zX8ZHit4)1gs$OsMd96BrA1wSE>SN}-xC3#T^Zj1+ag(2i)g$J5=QrvT#-BmEOufB? zQ&}^{~Y2m=Wi?AoG<6951IJCsGc+R^JQ#r=F_hu z9^>CtA2oIIBlQ|{zW-c3V#bYMV|$aIKd29yy8W|y$n@_&(2r((__unGIUhH!(*2v` zZmC{p=JB1?yY2T$)ay+@rql;a9u85Dn)Bt;>J!F4s~$GvK(l(KsgrYXTvN{%sfXk1 z)hEpMm#L4L@u3%P;<;IU(5%x2u)T@%KJ^|mE)~#Tlm91>XHyTO>Z2z9SJa10olGFl zrXJo$KF#?)cR|Vf%y_u9dZRhNc2$p>{p_orGxJMEebBV)F!eDrKOU!EYsTAC)qBl; z&VZZzbgK6ozgWG_wCgJMz??5%Qy(?`^=7pT{n@!@jBW7>P2daqpvst=g+>wD_8rXKFaaZQ~+tUhV_ z@i*%A=DdCu$2IwXS-sJW&u=1rlehQP@$Zbl=kBC=F!jH+dfu#Cc2%!46xaArGcb9#!u#bv1(hnDO>`^=dQkjH~yV^X?t=ugP<0=aP?`I^Rsa-;C!~>Vqbq z3)RD>KBMYA#t%>*G5uSs9yQ~@N$L@kp9b{_lg~5N$4vd7i}=mF@I}OF&bu$G=gm0$ zb@eJUetsA6n7j?D514*@P`%!?cUV1V;(S^?F#8`v-t7GW^={+;LY&5D?^5zcbG~n( z-fik?NA+sczk8|InSM#Aht2*E#{SKCevEp*sgsk{^Tr$13uZiTQy(#L=GF1g7n-QlOHwNJ5xc4D`lh1;B#D3pcz1M!f+ZpQhW_;^ZuQKEI z#n_Lj!>iPzX8W(H=gj&0ZS2Ri_lN3ZroVoM?M?sw3VAc}{9e7C-&*w+)4wNSdo!Lis)tS9 z+SGF<&w2HFGp=3+H{*P-`l!85R3A6_98e!J^>82D#8XgjG2_e=>ci$d9aZl$>;G5O zN6onOCfwxnJ@sBQ-_6;rVnL>SJd64!DVbje4Egel4~)@!W{^+P`<8UTxNacd6&i_CHmxG5zuw@?*bOpk8aQ z6S03&&#xgKljnEUtIYkMirq`UPve!Q($DyP8sXhb+jpCK+eSTX_7hRhnelT!^%@gT zpk8JF9*TOzj3;&Kb!MJAO?}*)FD>dVX8&Diml@x>aa_~ySEIeAp8K%9$^UoM2Ta}u z)%(o$531Li;|{A2n|gR!z1qw#W7v;r*WZyJv;8Fc%dCet`h@PsI266)pKS) zKY*L@YDm4_^w*;}uF2;J;y2qruRdbVi*dBe^!q#NL*{%8E!6y*{com@f2S2*h3$UCybxKzE~__gZQX59OR z`j}aN-m$PWt`?d~KX0afpKjl6_FusM&A9Od+GXlzRDHta?G^P(v)-6cFPOS|AN^wH zm$`drJobK(ddRGoc2%D=aqg>LY5F|_xA%?Jdrkcur(R{wuT#}?_PRiQ+_b9`?K0=@ z#p;3C|5e!DwD)W3HD*8GRv$F&y<5HC^zQ@Mj~V~hsTa)n{}lFP^7bOyYsS^rksmV; zu2+wkIA`st^=5p&`iRNr0`&>wd#Kl#>yahu<0jAhtM{1m=tvyboYyC)51DoN>DZ6y zuQL&knUBtco9oR>)O*eK%a_%oCZ4aW*P8S0yJ)YOPY2b*rhgyY;BS32xw<06kEe9| zNqar0-eShX*U*n<+V0Ot*Q7pZ@^CidvA?fOyn>e>spD=Omrao!b)ipy~vMg@6GBV zGw$7i{FpfJ!TwEMJ%o77{vXG2O+KGduQPGJq&{Z0e*^t!wtr8(+pHhv?4xyJ>S`UsM;bF|mQbC>$K8HazW-fjBpG4&oZUj0tJ&y3qIpkM6w2jC{ocadi^-d60Z zZu)(5^(vE}?bSo(`sfqtm8M-w)N^KB`Xr8P&ZDE$`^-FhqI!)vUmDa~%zn;NA2#cX z^VP#<{c|bWW$NTw^?H;4Z>U$B^ZE|7*Btj=^>H&^J&Zh<@%cCEQPaQAsz*$nzpOrD z&bv3&yG{J>qrD~%bNADJH0!0U)kDU2RnMDoW?%J!>936Xq&dG1Q|~tQcAR>j8Lv)N z&zbS$4D~8g=bh>kre7{rkJ!(N)N4&XzXmtQ{kHmu$@34@YfL;pLw-!Vex=@M>iPG` zTlaMTllrI`Xa1%>Z0hr0*pHdtXDurEkn#EIwZ<2y_n7l~5A`~8T^3W1nsM?#^>NeQ zqt!#ET_>vtX8R`fM*H_q)qCylrNMqo-CnL2jW?S_d)2`jrtIRmINIhq^ z&#L#DIFC?|n)B#*^+9tUeGdCK<5H{osM-Ep^-43}b*m4X`n*bg($x9au)Y19NWIbY z@7?OPCZ7+e=S`gJ)O*Z%_Z0GG)|oG=51BfAUA^A)%X;;QiGPa?|D6-+Zwmd_V|Hkp z|FT(2LZM1&m-&75n=irdvY-Cg$=QA%$J5S!4s*6Y&hgJUe!AnK6&+p6o6l%%Z|GdT zctvPoKD2OUsJU%b!>ZP{=7w|ITf4fNJG&Y>n%h>d=v=&FMW|ufv9Z=rZgp#WS93>0 zB0cpomQGB)PRFz5*Xi;$nKB2_Soy7q8)9MP3er4cax>v#8Tz$GwE^&@$x3w@)k+rRC(v|bUBzrs(c`NnJsS}WXk(1 zYa&rTc$#t=i*X~UamjKh$$0r?irNq_zfN)hl#3u$-c2T6-cKe?QATMS3L!(~nuwNP zrxVmS^wyxf`$Qr`Z%xrI%dgYa*%UCfJD_)_lT@Fn@{!Upu0z^BMz1+(v2tWVhWoO- z&uqHjlgX%(l_gnA^PQ!k*= zB%Y-KhpHk@)tII(NvAnyR5>x~!t&O1(#CmgrM~1blmbZd=|dx0iiYb{h6ASDQ^GUU z$+2X)uVQrERJOd&c)FbA1dYrApAJ-8S(=98G#ftknMv-iWrWG3PV|@1WVH4C> zH0lJ@{{fX?GD@|RP z%ur5}R03HllO!!mvXq}Rbw!%ar#Nk&rYVaeqsc4I>y0dBmd5r3pEZ>6I2B%;T1D%D z6xDb@>y2!jmP@?Mp@wI8j-??y&YeqRW0J<(fKJ>X#hpuo3eV5fM9K(F1Th*Z6THl! z^5U@|pmheX2kAsiCb_5RM5ijHi9Jb0MCVK*!_x^bX=v#}LsN#va2o0o(-x}KRxT$V zb!ay+8hX>bqUPSAw!ovU>@OmLl*0stpW+jgr)AnDT?)}4 z6{o7sP-!OVJWSGn9?&*%s`fNBB%S56fEvh`A#_Gick<$ihfg}2Q?we2@qk4mO)Sd! zEAJ=Zt38^@l61iqM7gVYSfw(edb#9hg8>0)E6py!5gK2tioVH2Q zNt2-Z3i#5FFa0w#^3c*gP4h@V^GlZ2Sy>)zruA0&tyyj&t!t?u`J##LAY^Em3Mgv< z4O2;43S=nb8LHD{inGZpcUn?V#*;Lx@w$y>HJT4oX}Zp!ORp?V`LTc_pon=wqCU*h z%#fnFEX6YkbqQSwrl?C&yr!hCjnk4f$+s#f%rsAIwCqUI>5-+eJx(2wqEgH9`j0vy zNym!OAWnx$&_!*AuBK;A^Xfc|kE==<1!pmy9!;cu0S}#MBVvK4aL7kfB!Hmy2n#bv_0nMjeZL|c)&;*&G zAu&NiWtv8Go=50Z$xu(HX_pBKI7tZ$Xw4N+^a)-q)4-OXb1OrqZju+6G~lJFqq3Cx zB*mZN+hBYhPoq=7x6r8g;?vf2v@)X@VzgSyQpyuNY*CysYHz^v1P!!Fn$zR7dQ0;F zLOD;<*^s7vrupU#uaWqs5lu3&6c3jaC#R4?;1QVSk1XGvY$80THK@ zEXIT0v}HY&11)3UIo&;k>pD)T2lqo^enAH>usJU6bCB!2#zqhP(UZ!|4L3Nbk8=ZXh zL2LOex15JCe$BN>LwlUAdQ#k>w0%Hp{49@8d<&kokMXQSS2_v4hh2_bLZ?exrKfsh`&~)DcvG)9#njl+DFOmpNSbyg1-tJkE<1 zUe!@TxUqZ-p617ZN+;k;cD{J#J9$|yh4Op(BO{utX*`ef>+%jWl#eujWJIy^?J2rU zNYGk5LFaow!#4G0oT5q7;y1%r7)fpi^*1$&KeV8id~rlmEZ-L7i**{ylhoFLrlm}Z z`;xOtQx^rsLog42l!F8fqY27LT2{vBkSQ9*W3=l8jok^l zg_PkEqCqA}Ctiw&LR!BhxOezMgfGgdzf&}U1$?Q%x4LK@7i2hoPBATbco9OL;Ey`^ z6r-49JOE9*rlN%t-zTSIrKjD;rn;v~izHQ7mTy2&H3rld0WI*7G%qG;Qcv(lmTAs8 zz2@_c@|>kU&G6@H6elm=Da<&hikIERlcu3zb$i3gwiRuwJIkM0B&XdHp?agiJwx+I zhCe2zv4GN(FOK4a9qiD{SqJhal1gGN)n9XahQ zK6EJBeTqLL% z#X}889edoNhaB5*+>u9~c*IE!CmnL=u}3tNcS0vHe{GAip5eiT77f$BUPB8lN@GBI zNl|gfcyU7~YMhES%_k&Zkn$Hu_@+1Clc4TP^KGAL3nqF^tB)kDNRyQBZ z3=NpZRG;zk1+XbXjIf0S{!6}sGzszAEU&2fLuLv; z&Z*+h?8@()cEwMFA|*3L2~6=SeD{#Q;K%nPrd?)JK6x2Q zm6YXW2`?3RuB8L8lKh1U@(i^%!#7BI5zH5SR2eDm3A%Dk(&{P2`=kz{^Ngo?I?ebB zk|xa-H_B|eq@wEmnBtM1zAG-JDaR2}S literal 0 HcwPel00001 diff --git a/third_party/android_platform/config.gni b/third_party/android_platform/config.gni new file mode 100644 index 000000000000..88f6362e6a99 --- /dev/null +++ b/third_party/android_platform/config.gni @@ -0,0 +1,9 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +relocation_packer_target = + "//third_party/android_platform/relocation_packer($host_toolchain)" +relocation_packer_dir = + get_label_info("$relocation_packer_target", "root_out_dir") +relocation_packer_exe = "${relocation_packer_dir}/android_relocation_packer" diff --git a/third_party/android_platform/relocation_packer.gyp b/third_party/android_platform/relocation_packer.gyp new file mode 100644 index 000000000000..69136f5340be --- /dev/null +++ b/third_party/android_platform/relocation_packer.gyp @@ -0,0 +1,83 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'variables': { + # These files lists are shared with the GN build. + 'relocation_packer_sources': [ + 'bionic/tools/relocation_packer/src/debug.cc', + 'bionic/tools/relocation_packer/src/delta_encoder.cc', + 'bionic/tools/relocation_packer/src/elf_file.cc', + 'bionic/tools/relocation_packer/src/leb128.cc', + 'bionic/tools/relocation_packer/src/packer.cc', + 'bionic/tools/relocation_packer/src/sleb128.cc', + ], + 'relocation_packer_main_source': [ + 'bionic/tools/relocation_packer/src/main.cc', + ], + 'relocation_packer_test_sources': [ + 'bionic/tools/relocation_packer/src/debug_unittest.cc', + 'bionic/tools/relocation_packer/src/delta_encoder_unittest.cc', + 'bionic/tools/relocation_packer/src/elf_file_unittest.cc', + 'bionic/tools/relocation_packer/src/leb128_unittest.cc', + 'bionic/tools/relocation_packer/src/packer_unittest.cc', + 'bionic/tools/relocation_packer/src/sleb128_unittest.cc', + 'bionic/tools/relocation_packer/src/run_all_unittests.cc', + ], + }, + 'targets': [ + { + # GN: //third_party/android_platform:android_lib_relocation_packer + 'target_name': 'android_lib_relocation_packer', + 'toolsets': ['host'], + 'type': 'static_library', + 'dependencies': [ + '../../third_party/elfutils/elfutils.gyp:libelf', + ], + 'sources': [ + '<@(relocation_packer_sources)' + ], + }, + { + # GN: //third_party/android_platform:android_relocation_packer + 'target_name': 'android_relocation_packer', + 'toolsets': ['host'], + 'type': 'executable', + 'dependencies': [ + '../../third_party/elfutils/elfutils.gyp:libelf', + 'android_lib_relocation_packer', + ], + 'sources': [ + '<@(relocation_packer_main_source)' + ], + }, + { + # TODO(GN) + 'target_name': 'android_relocation_packer_unittests', + 'toolsets': ['host'], + 'type': 'executable', + 'dependencies': [ + '../../testing/gtest.gyp:gtest', + 'android_lib_relocation_packer', + ], + 'include_dirs': [ + '../..', + ], + 'sources': [ + '<@(relocation_packer_test_sources)' + ], + 'copies': [ + { + 'destination': '<(PRODUCT_DIR)', + 'files': [ + 'bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so', + 'bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so', + 'bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so', + 'bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so', + ], + }, + ], + }, + ], +} -- 2.11.4.GIT