Merge "Update OWNERS based on Trusty team membership"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index cc1978d..375207b 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -10,7 +10,7 @@
"name": "debuggerd_test"
},
{
- "name": "fs_mgr_unit_test"
+ "name": "CtsFsMgrTestCases"
},
{
"name": "fs_mgr_vendor_overlay_test"
diff --git a/adb/Android.bp b/adb/Android.bp
index 2f9c8fc..d14fe56 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -422,33 +422,16 @@
"liblog",
],
- product_variables: {
- debuggable: {
- required: [
- "remount",
- ],
- },
- },
-
target: {
android: {
srcs: [
"daemon/abb_service.cpp",
"daemon/framebuffer_service.cpp",
"daemon/mdns.cpp",
- "daemon/reboot_service.cpp",
- "daemon/remount_service.cpp",
"daemon/restart_service.cpp",
- "daemon/set_verity_enable_state_service.cpp",
- ],
- static_libs: [
- "libavb_user",
],
shared_libs: [
- "libbootloader_message",
"libmdnssd",
- "libfec",
- "libfs_mgr",
"libselinux",
],
},
@@ -522,6 +505,22 @@
],
}
+phony {
+ name: "adbd_system_binaries",
+ required: [
+ "abb",
+ "reboot",
+ "set-verity-state",
+ ]
+}
+
+phony {
+ name: "adbd_system_binaries_recovery",
+ required: [
+ "reboot.recovery",
+ ],
+}
+
cc_binary {
name: "static_adbd",
defaults: ["adbd_defaults", "host_adbd_supported"],
@@ -617,7 +616,6 @@
static_libs: [
"libadbd",
"libbase",
- "libbootloader_message",
"libcutils",
"libcrypto_utils",
"libcrypto_static",
diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp
index ed6a9a8..e8be784 100644
--- a/adb/client/auth.cpp
+++ b/adb/client/auth.cpp
@@ -173,7 +173,8 @@
RSA* key = RSA_new();
if (!PEM_read_RSAPrivateKey(fp.get(), &key, nullptr, nullptr)) {
- LOG(ERROR) << "Failed to read key";
+ LOG(ERROR) << "Failed to read key from '" << file << "'";
+ ERR_print_errors_fp(stderr);
RSA_free(key);
return nullptr;
}
@@ -249,7 +250,7 @@
return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adbkey";
}
-static bool generate_userkey() {
+static bool load_userkey() {
std::string path = get_user_key_path();
if (path.empty()) {
PLOG(ERROR) << "Error getting user key filename";
@@ -435,8 +436,8 @@
void adb_auth_init() {
LOG(INFO) << "adb_auth_init...";
- if (!generate_userkey()) {
- LOG(ERROR) << "Failed to generate user key";
+ if (!load_userkey()) {
+ LOG(ERROR) << "Failed to load (or generate) user key";
return;
}
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index e5a4917..9ebab74 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -248,6 +248,12 @@
prop_port = android::base::GetProperty("persist.adb.tcp.port", "");
}
+#if !defined(__ANDROID__)
+ if (prop_port.empty() && getenv("ADBD_PORT")) {
+ prop_port = getenv("ADBD_PORT");
+ }
+#endif
+
int port;
if (sscanf(prop_port.c_str(), "%d", &port) == 1 && port > 0) {
D("using port=%d", port);
diff --git a/adb/daemon/reboot_service.cpp b/adb/daemon/reboot_service.cpp
deleted file mode 100644
index 13398af..0000000
--- a/adb/daemon/reboot_service.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#define TRACE_TAG SERVICES
-
-#include "sysdeps.h"
-
-#include <stdlib.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-#include <string>
-
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-#include <bootloader_message/bootloader_message.h>
-#include <cutils/android_reboot.h>
-
-#include "adb_io.h"
-#include "adb_unique_fd.h"
-
-void reboot_service(unique_fd fd, const std::string& arg) {
- std::string reboot_arg = arg;
- sync();
-
- if (reboot_arg.empty()) reboot_arg = "adb";
- std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg.c_str());
-
- if (reboot_arg == "fastboot" &&
- android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) &&
- access("/dev/socket/recovery", F_OK) == 0) {
- LOG(INFO) << "Recovery specific reboot fastboot";
- /*
- * The socket is created to allow switching between recovery and
- * fastboot.
- */
- android::base::unique_fd sock(socket(AF_UNIX, SOCK_STREAM, 0));
- if (sock < 0) {
- WriteFdFmt(fd, "reboot (%s) create\n", strerror(errno));
- PLOG(ERROR) << "Creating recovery socket failed";
- return;
- }
-
- sockaddr_un addr = {.sun_family = AF_UNIX};
- strncpy(addr.sun_path, "/dev/socket/recovery", sizeof(addr.sun_path) - 1);
- if (connect(sock.get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1) {
- WriteFdFmt(fd, "reboot (%s) connect\n", strerror(errno));
- PLOG(ERROR) << "Couldn't connect to recovery socket";
- return;
- }
- const char msg_switch_to_fastboot = 'f';
- auto ret = adb_write(sock, &msg_switch_to_fastboot, sizeof(msg_switch_to_fastboot));
- if (ret != sizeof(msg_switch_to_fastboot)) {
- WriteFdFmt(fd, "reboot (%s) write\n", strerror(errno));
- PLOG(ERROR) << "Couldn't write message to recovery socket to switch to fastboot";
- return;
- }
- } else {
- if (!android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_string)) {
- WriteFdFmt(fd.get(), "reboot (%s) failed\n", reboot_string.c_str());
- return;
- }
- }
- // Don't return early. Give the reboot command time to take effect
- // to avoid messing up scripts which do "adb reboot && adb wait-for-device"
- while (true) {
- pause();
- }
-}
diff --git a/adb/daemon/reboot_service.h b/adb/daemon/reboot_service.h
deleted file mode 100644
index f68913e..0000000
--- a/adb/daemon/reboot_service.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#pragma once
-
-#include <string>
-
-#include "adb_unique_fd.h"
-
-#if defined(__ANDROID__)
-void reboot_service(unique_fd fd, const std::string& arg);
-#endif
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
deleted file mode 100644
index 6bd7855..0000000
--- a/adb/daemon/remount_service.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include <string>
-
-#include "adb.h"
-#include "adb_io.h"
-#include "adb_unique_fd.h"
-
-static constexpr char kRemountCmd[] = "/system/bin/remount";
-
-static bool do_remount(int fd, const std::string& cmd) {
- if (getuid() != 0) {
- WriteFdExactly(fd, "Not running as root. Try \"adb root\" first.\n");
- return false;
- }
-
- auto pid = fork();
- if (pid < 0) {
- WriteFdFmt(fd, "Failed to fork to %s: %s\n", kRemountCmd, strerror(errno));
- return false;
- }
-
- if (pid == 0) {
- // child side of the fork
- dup2(fd, STDIN_FILENO);
- dup2(fd, STDOUT_FILENO);
- dup2(fd, STDERR_FILENO);
-
- execl(kRemountCmd, kRemountCmd, cmd.empty() ? nullptr : cmd.c_str(), nullptr);
- const char* msg = "failed to exec remount\n";
- write(STDERR_FILENO, msg, strlen(msg));
- _exit(errno);
- }
-
- int wstatus = 0;
- auto ret = waitpid(pid, &wstatus, 0);
-
- if (ret == -1) {
- WriteFdFmt(fd, "Failed to wait for %s: %s\n", kRemountCmd, strerror(errno));
- return false;
- } else if (ret != pid) {
- WriteFdFmt(fd, "pid %d and waitpid return %d do not match for %s\n",
- static_cast<int>(pid), static_cast<int>(ret), kRemountCmd);
- return false;
- }
-
- if (WIFSIGNALED(wstatus)) {
- WriteFdFmt(fd, "%s terminated with signal %s\n", kRemountCmd,
- strsignal(WTERMSIG(wstatus)));
- return false;
- }
-
- if (!WIFEXITED(wstatus)) {
- WriteFdFmt(fd, "%s stopped with status 0x%x\n", kRemountCmd, wstatus);
- return false;
- }
-
- if (WEXITSTATUS(wstatus)) {
- WriteFdFmt(fd, "%s exited with status %d\n", kRemountCmd, WEXITSTATUS(wstatus));
- return false;
- }
-
- return true;
-}
-
-void remount_service(unique_fd fd, const std::string& cmd) {
- do_remount(fd.get(), cmd);
- // The remount command will print success or failure for us.
-}
diff --git a/adb/daemon/remount_service.h b/adb/daemon/remount_service.h
deleted file mode 100644
index 522a5da..0000000
--- a/adb/daemon/remount_service.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include <string>
-
-#include "adb_unique_fd.h"
-
-#if defined(__ANDROID__)
-void remount_service(unique_fd, const std::string&);
-#endif
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index e6f4499..a44c10b 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -53,10 +53,7 @@
#include "daemon/file_sync_service.h"
#include "daemon/framebuffer_service.h"
-#include "daemon/reboot_service.h"
-#include "daemon/remount_service.h"
#include "daemon/restart_service.h"
-#include "daemon/set_verity_enable_state_service.h"
#include "daemon/shell_service.h"
@@ -251,13 +248,13 @@
if (name.starts_with("framebuffer:")) {
return create_service_thread("fb", framebuffer_service);
} else if (android::base::ConsumePrefix(&name, "remount:")) {
- std::string arg(name);
- return create_service_thread("remount",
- std::bind(remount_service, std::placeholders::_1, arg));
+ std::string cmd = "/system/bin/remount ";
+ cmd += name;
+ return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
} else if (android::base::ConsumePrefix(&name, "reboot:")) {
- std::string arg(name);
- return create_service_thread("reboot",
- std::bind(reboot_service, std::placeholders::_1, arg));
+ std::string cmd = "/system/bin/reboot ";
+ cmd += name;
+ return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
} else if (name.starts_with("root:")) {
return create_service_thread("root", restart_root_service);
} else if (name.starts_with("unroot:")) {
@@ -270,11 +267,11 @@
return StartSubprocess("/system/bin/bu restore", nullptr, SubprocessType::kRaw,
SubprocessProtocol::kNone);
} else if (name.starts_with("disable-verity:")) {
- return create_service_thread("verity-on", std::bind(set_verity_enabled_state_service,
- std::placeholders::_1, false));
+ return StartSubprocess("/system/bin/disable-verity", nullptr, SubprocessType::kRaw,
+ SubprocessProtocol::kNone);
} else if (name.starts_with("enable-verity:")) {
- return create_service_thread("verity-off", std::bind(set_verity_enabled_state_service,
- std::placeholders::_1, true));
+ return StartSubprocess("/system/bin/enable-verity", nullptr, SubprocessType::kRaw,
+ SubprocessProtocol::kNone);
} else if (android::base::ConsumePrefix(&name, "tcpip:")) {
std::string str(name);
diff --git a/adb/daemon/set_verity_enable_state_service.cpp b/adb/daemon/set_verity_enable_state_service.cpp
deleted file mode 100644
index 4fbccdb..0000000
--- a/adb/daemon/set_verity_enable_state_service.cpp
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-#define TRACE_TAG ADB
-
-#include "set_verity_enable_state_service.h"
-#include "sysdeps.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <libavb_user/libavb_user.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-
-#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-#include <fs_mgr.h>
-#include <fs_mgr_overlayfs.h>
-#include <fstab/fstab.h>
-#include <log/log_properties.h>
-
-#include "adb.h"
-#include "adb_io.h"
-#include "adb_unique_fd.h"
-
-#include "fec/io.h"
-
-#ifdef ALLOW_ADBD_DISABLE_VERITY
-static const bool kAllowDisableVerity = true;
-#else
-static const bool kAllowDisableVerity = false;
-#endif
-
-void suggest_run_adb_root(int fd) {
- if (getuid() != 0) WriteFdExactly(fd, "Maybe run adb root?\n");
-}
-
-static bool make_block_device_writable(const std::string& dev) {
- unique_fd fd(unix_open(dev, O_RDONLY | O_CLOEXEC));
- if (fd == -1) {
- return false;
- }
-
- int OFF = 0;
- bool result = (ioctl(fd.get(), BLKROSET, &OFF) != -1);
- return result;
-}
-
-/* Turn verity on/off */
-static bool set_verity_enabled_state(int fd, const char* block_device, const char* mount_point,
- bool enable) {
- if (!make_block_device_writable(block_device)) {
- WriteFdFmt(fd, "Could not make block device %s writable (%s).\n",
- block_device, strerror(errno));
- return false;
- }
-
- fec::io fh(block_device, O_RDWR);
-
- if (!fh) {
- WriteFdFmt(fd, "Could not open block device %s (%s).\n", block_device, strerror(errno));
- suggest_run_adb_root(fd);
- return false;
- }
-
- fec_verity_metadata metadata;
-
- if (!fh.get_verity_metadata(metadata)) {
- WriteFdExactly(fd, "Couldn't find verity metadata!\n");
- return false;
- }
-
- if (!enable && metadata.disabled) {
- WriteFdFmt(fd, "Verity already disabled on %s\n", mount_point);
- return false;
- }
-
- if (enable && !metadata.disabled) {
- WriteFdFmt(fd, "Verity already enabled on %s\n", mount_point);
- return false;
- }
-
- if (!fh.set_verity_status(enable)) {
- WriteFdFmt(fd, "Could not set verity %s flag on device %s with error %s\n",
- enable ? "enabled" : "disabled",
- block_device, strerror(errno));
- return false;
- }
-
- auto change = false;
- errno = 0;
- if (enable ? fs_mgr_overlayfs_teardown(mount_point, &change)
- : fs_mgr_overlayfs_setup(nullptr, mount_point, &change)) {
- if (change) {
- WriteFdFmt(fd, "%s overlayfs for %s\n", enable ? "disabling" : "using", mount_point);
- }
- } else if (errno) {
- int expected_errno = enable ? EBUSY : ENOENT;
- if (errno != expected_errno) {
- WriteFdFmt(fd, "Overlayfs %s for %s failed with error %s\n",
- enable ? "teardown" : "setup", mount_point, strerror(errno));
- }
- }
- WriteFdFmt(fd, "Verity %s on %s\n", enable ? "enabled" : "disabled", mount_point);
- return true;
-}
-
-/* Helper function to get A/B suffix, if any. If the device isn't
- * using A/B the empty string is returned. Otherwise either "_a",
- * "_b", ... is returned.
- */
-static std::string get_ab_suffix() {
- return android::base::GetProperty("ro.boot.slot_suffix", "");
-}
-
-static bool is_avb_device_locked() {
- return android::base::GetProperty("ro.boot.vbmeta.device_state", "") == "locked";
-}
-
-static bool overlayfs_setup(int fd, bool enable) {
- auto change = false;
- errno = 0;
- if (enable ? fs_mgr_overlayfs_teardown(nullptr, &change)
- : fs_mgr_overlayfs_setup(nullptr, nullptr, &change)) {
- if (change) {
- WriteFdFmt(fd, "%s overlayfs\n", enable ? "disabling" : "using");
- }
- } else if (errno) {
- WriteFdFmt(fd, "Overlayfs %s failed with error %s\n", enable ? "teardown" : "setup",
- strerror(errno));
- suggest_run_adb_root(fd);
- }
- return change;
-}
-
-/* Use AVB to turn verity on/off */
-static bool set_avb_verity_enabled_state(int fd, AvbOps* ops, bool enable_verity) {
- std::string ab_suffix = get_ab_suffix();
- bool verity_enabled;
-
- if (is_avb_device_locked()) {
- WriteFdExactly(fd, "Device is locked. Please unlock the device first\n");
- return false;
- }
-
- if (!avb_user_verity_get(ops, ab_suffix.c_str(), &verity_enabled)) {
- WriteFdExactly(fd, "Error getting verity state. Try adb root first?\n");
- return false;
- }
-
- if ((verity_enabled && enable_verity) || (!verity_enabled && !enable_verity)) {
- WriteFdFmt(fd, "verity is already %s\n", verity_enabled ? "enabled" : "disabled");
- return false;
- }
-
- if (!avb_user_verity_set(ops, ab_suffix.c_str(), enable_verity)) {
- WriteFdExactly(fd, "Error setting verity\n");
- return false;
- }
-
- overlayfs_setup(fd, enable_verity);
- WriteFdFmt(fd, "Successfully %s verity\n", enable_verity ? "enabled" : "disabled");
- return true;
-}
-
-void set_verity_enabled_state_service(unique_fd fd, bool enable) {
- bool any_changed = false;
-
- // Figure out if we're using VB1.0 or VB2.0 (aka AVB) - by
- // contract, androidboot.vbmeta.digest is set by the bootloader
- // when using AVB).
- bool using_avb = !android::base::GetProperty("ro.boot.vbmeta.digest", "").empty();
-
- // If using AVB, dm-verity is used on any build so we want it to
- // be possible to disable/enable on any build (except USER). For
- // VB1.0 dm-verity is only enabled on certain builds.
- if (!using_avb) {
- if (!kAllowDisableVerity) {
- WriteFdFmt(fd.get(), "%s-verity only works for userdebug builds\n",
- enable ? "enable" : "disable");
- }
-
- if (!android::base::GetBoolProperty("ro.secure", false)) {
- overlayfs_setup(fd.get(), enable);
- WriteFdExactly(fd.get(), "verity not enabled - ENG build\n");
- return;
- }
- }
-
- // Should never be possible to disable dm-verity on a USER build
- // regardless of using AVB or VB1.0.
- if (!__android_log_is_debuggable()) {
- WriteFdExactly(fd.get(), "verity cannot be disabled/enabled - USER build\n");
- return;
- }
-
- if (using_avb) {
- // Yep, the system is using AVB.
- AvbOps* ops = avb_ops_user_new();
- if (ops == nullptr) {
- WriteFdExactly(fd.get(), "Error getting AVB ops\n");
- return;
- }
- if (set_avb_verity_enabled_state(fd.get(), ops, enable)) {
- any_changed = true;
- }
- avb_ops_user_free(ops);
- } else {
- // Not using AVB - assume VB1.0.
-
- // read all fstab entries at once from all sources
- android::fs_mgr::Fstab fstab;
- if (!android::fs_mgr::ReadDefaultFstab(&fstab)) {
- WriteFdExactly(fd.get(), "Failed to read fstab\n");
- suggest_run_adb_root(fd.get());
- return;
- }
-
- // Loop through entries looking for ones that verity manages.
- for (const auto& entry : fstab) {
- if (entry.fs_mgr_flags.verify) {
- if (set_verity_enabled_state(fd.get(), entry.blk_device.c_str(),
- entry.mount_point.c_str(), enable)) {
- any_changed = true;
- }
- }
- }
- }
- if (!any_changed) any_changed = overlayfs_setup(fd.get(), enable);
-
- if (any_changed) {
- WriteFdExactly(fd.get(), "Now reboot your device for settings to take effect\n");
- }
-}
diff --git a/adb/daemon/set_verity_enable_state_service.h b/adb/daemon/set_verity_enable_state_service.h
deleted file mode 100644
index c0ed98e..0000000
--- a/adb/daemon/set_verity_enable_state_service.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#pragma once
-
-#include "adb_unique_fd.h"
-
-#if defined(__ANDROID__)
-void set_verity_enabled_state_service(unique_fd fd, bool enable);
-#endif
diff --git a/adb/fastdeploy/Android.bp b/adb/fastdeploy/Android.bp
index 245d52a..f5893aa 100644
--- a/adb/fastdeploy/Android.bp
+++ b/adb/fastdeploy/Android.bp
@@ -27,6 +27,7 @@
java_binary {
name: "deployagent",
+ sdk_version: "24",
static_libs: [
"deployagent_lib",
],
diff --git a/base/Android.bp b/base/Android.bp
index f5000c1..25ae535 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -61,7 +61,6 @@
"parsenetaddress.cpp",
"process.cpp",
"properties.cpp",
- "quick_exit.cpp",
"stringprintf.cpp",
"strings.cpp",
"threads.cpp",
@@ -154,7 +153,6 @@
"parsenetaddress_test.cpp",
"process_test.cpp",
"properties_test.cpp",
- "quick_exit_test.cpp",
"result_test.cpp",
"scopeguard_test.cpp",
"stringprintf_test.cpp",
diff --git a/base/include/android-base/quick_exit.h b/base/include/android-base/quick_exit.h
deleted file mode 100644
index a03b14f..0000000
--- a/base/include/android-base/quick_exit.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdlib.h>
-
-// Provide emulation for at_quick_exit/quick_exit on platforms that don't have it.
-namespace android {
-namespace base {
-
-// Bionic and glibc have quick_exit, Darwin and Windows don't.
-#if !defined(__linux__)
- void quick_exit(int exit_code) __attribute__((noreturn));
- int at_quick_exit(void (*func)());
-#else
- using ::at_quick_exit;
- using ::quick_exit;
-#endif
-}
-}
diff --git a/base/quick_exit.cpp b/base/quick_exit.cpp
deleted file mode 100644
index e4dd62b..0000000
--- a/base/quick_exit.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/quick_exit.h"
-
-#if !defined(__linux__)
-
-#include <mutex>
-#include <vector>
-
-namespace android {
-namespace base {
-
-static auto& quick_exit_mutex = *new std::mutex();
-static auto& quick_exit_handlers = *new std::vector<void (*)()>();
-
-void quick_exit(int exit_code) {
- std::lock_guard<std::mutex> lock(quick_exit_mutex);
- for (auto it = quick_exit_handlers.rbegin(); it != quick_exit_handlers.rend(); ++it) {
- (*it)();
- }
- _Exit(exit_code);
-}
-
-int at_quick_exit(void (*func)()) {
- std::lock_guard<std::mutex> lock(quick_exit_mutex);
- quick_exit_handlers.push_back(func);
- return 0;
-}
-
-} // namespace base
-} // namespace android
-#endif
diff --git a/base/quick_exit_test.cpp b/base/quick_exit_test.cpp
deleted file mode 100644
index 7ca8156..0000000
--- a/base/quick_exit_test.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/quick_exit.h"
-
-#include <gtest/gtest.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include <string>
-
-#include "android-base/test_utils.h"
-
-// These tests are a bit sketchy, since each test run adds global state that affects subsequent
-// tests (including ones not in this file!). Exit with 0 in Exiter and stick the at_quick_exit test
-// at the end to hack around this.
-struct Exiter {
- ~Exiter() {
- _Exit(0);
- }
-};
-
-TEST(quick_exit, smoke) {
- ASSERT_EXIT(android::base::quick_exit(123), testing::ExitedWithCode(123), "");
-}
-
-TEST(quick_exit, skip_static_destructors) {
- static Exiter exiter;
- ASSERT_EXIT(android::base::quick_exit(123), testing::ExitedWithCode(123), "");
-}
-
-TEST(quick_exit, at_quick_exit) {
- static int counter = 4;
- // "Functions passed to at_quick_exit are called in reverse order of their registration."
- ASSERT_EQ(0, android::base::at_quick_exit([]() { _exit(counter); }));
- ASSERT_EQ(0, android::base::at_quick_exit([]() { counter += 2; }));
- ASSERT_EQ(0, android::base::at_quick_exit([]() { counter *= 10; }));
- ASSERT_EXIT(android::base::quick_exit(123), testing::ExitedWithCode(42), "");
-}
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 1a5b435..7e35a2f 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -195,7 +195,10 @@
return false;
}
- InterceptRequest req = {.pid = pid, .dump_type = dump_type};
+ InterceptRequest req = {
+ .dump_type = dump_type,
+ .pid = pid,
+ };
if (!set_timeout(sockfd)) {
PLOG(ERROR) << "libdebugger_client: failed to set timeout";
return false;
diff --git a/debuggerd/client/debuggerd_client_test.cpp b/debuggerd/client/debuggerd_client_test.cpp
index 9c2f0d6..2545cd6 100644
--- a/debuggerd/client/debuggerd_client_test.cpp
+++ b/debuggerd/client/debuggerd_client_test.cpp
@@ -73,15 +73,15 @@
unique_fd pipe_read, pipe_write;
ASSERT_TRUE(Pipe(&pipe_read, &pipe_write));
- // 64 kB should be enough for everyone.
+ // 64 MiB should be enough for everyone.
constexpr int PIPE_SIZE = 64 * 1024 * 1024;
ASSERT_EQ(PIPE_SIZE, fcntl(pipe_read.get(), F_SETPIPE_SZ, PIPE_SIZE));
// Wait for a bit to let the child spawn all of its threads.
- std::this_thread::sleep_for(250ms);
+ std::this_thread::sleep_for(1s);
ASSERT_TRUE(
- debuggerd_trigger_dump(forkpid, kDebuggerdNativeBacktrace, 10000, std::move(pipe_write)));
+ debuggerd_trigger_dump(forkpid, kDebuggerdNativeBacktrace, 60000, std::move(pipe_write)));
// Immediately kill the forked child, to make sure that the dump didn't return early.
ASSERT_EQ(0, kill(forkpid, SIGKILL)) << strerror(errno);
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index c9a193c..99729dc 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -101,7 +101,10 @@
FAIL() << "failed to contact tombstoned: " << strerror(errno);
}
- InterceptRequest req = {.pid = target_pid, .dump_type = intercept_type};
+ InterceptRequest req = {
+ .dump_type = intercept_type,
+ .pid = target_pid,
+ };
unique_fd output_pipe_write;
if (!Pipe(output_fd, &output_pipe_write)) {
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 598ea85..b90ca80 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -525,8 +525,8 @@
log_signal_summary(info);
debugger_thread_info thread_info = {
- .pseudothread_tid = -1,
.crashing_tid = __gettid(),
+ .pseudothread_tid = -1,
.siginfo = info,
.ucontext = context,
.abort_msg = reinterpret_cast<uintptr_t>(abort_message),
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index 88c206f..9dea7ac 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -345,9 +345,9 @@
TEST_F(TombstoneTest, dump_thread_info_uid) {
dump_thread_info(&log_, ThreadInfo{.uid = 1,
- .pid = 2,
.tid = 3,
.thread_name = "some_thread",
+ .pid = 2,
.process_name = "some_process"});
std::string expected = "pid: 2, tid: 3, name: some_thread >>> some_process <<<\nuid: 1\n";
ASSERT_STREQ(expected.c_str(), amfd_data_.c_str());
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index da2ba58..1993840 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -595,7 +595,7 @@
}
AndroidLogEntry e;
char buf[512];
- if (android_log_processBinaryLogBuffer(&log_entry.entry_v1, &e, g_eventTagMap, buf,
+ if (android_log_processBinaryLogBuffer(&log_entry.entry, &e, g_eventTagMap, buf,
sizeof(buf)) == 0) {
_LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8.*s: %s\n", timeBuf,
log_entry.entry.nsec / 1000000, log_entry.entry.pid, log_entry.entry.tid, 'I',
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index f452a64..02a887e 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -122,6 +122,7 @@
shared_libs: [
"android.hardware.boot@1.0",
+ "android.hardware.boot@1.1",
"android.hardware.fastboot@1.0",
"android.hardware.health@2.0",
"libadbd",
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 8a72627..7fba67c 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -34,6 +34,7 @@
#define FB_CMD_UPDATE_SUPER "update-super"
#define FB_CMD_OEM "oem"
#define FB_CMD_GSI "gsi"
+#define FB_CMD_SNAPSHOT_UPDATE "snapshot-update"
#define RESPONSE_OKAY "OKAY"
#define RESPONSE_FAIL "FAIL"
@@ -66,3 +67,4 @@
#define FB_VAR_BATTERY_VOLTAGE "battery-voltage"
#define FB_VAR_BATTERY_SOC_OK "battery-soc-ok"
#define FB_VAR_SUPER_PARTITION_NAME "super-partition-name"
+#define FB_VAR_SNAPSHOT_UPDATE_STATUS "snapshot-update-status"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 4c77c75..dfd5690 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -25,6 +25,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <android/hardware/boot/1.1/IBootControl.h>
#include <cutils/android_reboot.h>
#include <ext4_utils/wipe.h>
#include <fs_mgr.h>
@@ -44,8 +45,10 @@
using ::android::hardware::boot::V1_0::BoolResult;
using ::android::hardware::boot::V1_0::CommandResult;
using ::android::hardware::boot::V1_0::Slot;
+using ::android::hardware::boot::V1_1::MergeStatus;
using ::android::hardware::fastboot::V1_0::Result;
using ::android::hardware::fastboot::V1_0::Status;
+using IBootControl1_1 = ::android::hardware::boot::V1_1::IBootControl;
struct VariableHandlers {
// Callback to retrieve the value of a single variable.
@@ -101,7 +104,8 @@
{FB_VAR_BATTERY_VOLTAGE, {GetBatteryVoltage, nullptr}},
{FB_VAR_BATTERY_SOC_OK, {GetBatterySoCOk, nullptr}},
{FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}},
- {FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}}};
+ {FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}},
+ {FB_VAR_SNAPSHOT_UPDATE_STATUS, {GetSnapshotUpdateStatus, nullptr}}};
if (args.size() < 2) {
return device->WriteFail("Missing argument");
@@ -547,3 +551,40 @@
}
return device->WriteStatus(FastbootResult::OKAY, "Success");
}
+
+bool SnapshotUpdateHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+ // Note that we use the HAL rather than mounting /metadata, since we want
+ // our results to match the bootloader.
+ auto hal = device->boot_control_hal();
+ if (!hal) return device->WriteFail("Not supported");
+
+ android::sp<IBootControl1_1> hal11 = IBootControl1_1::castFrom(hal);
+ if (!hal11) return device->WriteFail("Not supported");
+
+ // If no arguments, return the same thing as a getvar. Note that we get the
+ // HAL first so we can return "not supported" before we return the less
+ // specific error message below.
+ if (args.size() < 2 || args[1].empty()) {
+ std::string message;
+ if (!GetSnapshotUpdateStatus(device, {}, &message)) {
+ return device->WriteFail("Could not determine update status");
+ }
+ device->WriteInfo(message);
+ return device->WriteOkay("");
+ }
+
+ if (args.size() != 2 || args[1] != "cancel") {
+ return device->WriteFail("Invalid arguments");
+ }
+
+ MergeStatus status = hal11->getSnapshotMergeStatus();
+ switch (status) {
+ case MergeStatus::SNAPSHOTTED:
+ case MergeStatus::MERGING:
+ hal11->setSnapshotMergeStatus(MergeStatus::CANCELLED);
+ break;
+ default:
+ break;
+ }
+ return device->WriteStatus(FastbootResult::OKAY, "Success");
+}
diff --git a/fastboot/device/commands.h b/fastboot/device/commands.h
index afd6d08..c1324bc 100644
--- a/fastboot/device/commands.h
+++ b/fastboot/device/commands.h
@@ -19,7 +19,7 @@
#include <string>
#include <vector>
-constexpr unsigned int kMaxDownloadSizeDefault = 0x20000000;
+constexpr unsigned int kMaxDownloadSizeDefault = 0x10000000;
class FastbootDevice;
@@ -49,3 +49,4 @@
bool UpdateSuperHandler(FastbootDevice* device, const std::vector<std::string>& args);
bool OemCmdHandler(FastbootDevice* device, const std::vector<std::string>& args);
bool GsiHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool SnapshotUpdateHandler(FastbootDevice* device, const std::vector<std::string>& args);
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index 56fafab..d3c2bda 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -54,6 +54,7 @@
{FB_CMD_UPDATE_SUPER, UpdateSuperHandler},
{FB_CMD_OEM, OemCmdHandler},
{FB_CMD_GSI, GsiHandler},
+ {FB_CMD_SNAPSHOT_UPDATE, SnapshotUpdateHandler},
}),
transport_(std::make_unique<ClientUsbTransport>()),
boot_control_hal_(IBootControl::getService()),
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index 130a3cf..6e613d6 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -23,6 +23,7 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android/hardware/boot/1.1/IBootControl.h>
#include <ext4_utils/ext4_utils.h>
#include <fs_mgr.h>
#include <healthhalutils/HealthHalUtils.h>
@@ -34,9 +35,11 @@
using ::android::hardware::boot::V1_0::BoolResult;
using ::android::hardware::boot::V1_0::Slot;
+using ::android::hardware::boot::V1_1::MergeStatus;
using ::android::hardware::fastboot::V1_0::FileSystemType;
using ::android::hardware::fastboot::V1_0::Result;
using ::android::hardware::fastboot::V1_0::Status;
+using IBootControl1_1 = ::android::hardware::boot::V1_1::IBootControl;
using namespace android::fs_mgr;
constexpr char kFastbootProtocolVersion[] = "0.4";
@@ -424,3 +427,34 @@
*message = fs_mgr_get_super_partition_name(slot_number);
return true;
}
+
+bool GetSnapshotUpdateStatus(FastbootDevice* device, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ // Note that we use the HAL rather than mounting /metadata, since we want
+ // our results to match the bootloader.
+ auto hal = device->boot_control_hal();
+ if (!hal) {
+ *message = "not supported";
+ return false;
+ }
+
+ android::sp<IBootControl1_1> hal11 = IBootControl1_1::castFrom(hal);
+ if (!hal11) {
+ *message = "not supported";
+ return false;
+ }
+
+ MergeStatus status = hal11->getSnapshotMergeStatus();
+ switch (status) {
+ case MergeStatus::SNAPSHOTTED:
+ *message = "snapshotted";
+ break;
+ case MergeStatus::MERGING:
+ *message = "merging";
+ break;
+ default:
+ *message = "none";
+ break;
+ }
+ return true;
+}
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
index 015a4c5..4dec10f 100644
--- a/fastboot/device/variables.h
+++ b/fastboot/device/variables.h
@@ -61,6 +61,8 @@
std::string* message);
bool GetSuperPartitionName(FastbootDevice* device, const std::vector<std::string>& args,
std::string* message);
+bool GetSnapshotUpdateStatus(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
// Helpers for getvar all.
std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device);
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 2fe3b1a..7ce7c7c 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -396,6 +396,9 @@
" gsi wipe|disable Wipe or disable a GSI installation (fastbootd only).\n"
" wipe-super [SUPER_EMPTY] Wipe the super partition. This will reset it to\n"
" contain an empty set of default dynamic partitions.\n"
+ " snapshot-update cancel On devices that support snapshot-based updates, cancel\n"
+ " an in-progress update. This may make the device\n"
+ " unbootable until it is reflashed.\n"
"\n"
"boot image:\n"
" boot KERNEL [RAMDISK [SECOND]]\n"
@@ -1216,6 +1219,14 @@
target_sparse_limit = -1;
}
+static void CancelSnapshotIfNeeded() {
+ std::string merge_status = "none";
+ if (fb->GetVar(FB_VAR_SNAPSHOT_UPDATE_STATUS, &merge_status) == fastboot::SUCCESS &&
+ merge_status != "none") {
+ fb->SnapshotUpdateCommand("Cancel");
+ }
+}
+
class ImageSource {
public:
virtual bool ReadFile(const std::string& name, std::vector<char>* out) const = 0;
@@ -1268,6 +1279,8 @@
DetermineSecondarySlot();
CollectImages();
+ CancelSnapshotIfNeeded();
+
// First flash boot partitions. We allow this to happen either in userspace
// or in bootloader fastboot.
FlashImages(boot_images_);
@@ -2071,12 +2084,24 @@
image = next_arg(&args);
}
do_wipe_super(image, slot_override);
+ } else if (command == "snapshot-update") {
+ std::string arg;
+ if (!args.empty()) {
+ arg = next_arg(&args);
+ }
+ if (!arg.empty() && arg != "cancel") {
+ syntax_error("expected: snapshot-update [cancel]");
+ }
+ fb->SnapshotUpdateCommand(arg);
} else {
syntax_error("unknown command %s", command.c_str());
}
}
if (wants_wipe) {
+ if (force_flash) {
+ CancelSnapshotIfNeeded();
+ }
std::vector<std::string> partitions = { "userdata", "cache", "metadata" };
for (const auto& partition : partitions) {
std::string partition_type;
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index b897182..6a5ad20 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -122,6 +122,12 @@
response, info);
}
+RetCode FastBootDriver::SnapshotUpdateCommand(const std::string& command, std::string* response,
+ std::vector<std::string>* info) {
+ std::string raw = FB_CMD_SNAPSHOT_UPDATE ":" + command;
+ return RawCommand(raw, response, info);
+}
+
RetCode FastBootDriver::FlashPartition(const std::string& partition,
const std::vector<char>& data) {
RetCode ret;
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index af02637..7265632 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -104,6 +104,8 @@
std::vector<std::string>* info = nullptr);
RetCode Upload(const std::string& outfile, std::string* response = nullptr,
std::vector<std::string>* info = nullptr);
+ RetCode SnapshotUpdateCommand(const std::string& command, std::string* response = nullptr,
+ std::vector<std::string>* info = nullptr);
/* HIGHER LEVEL COMMANDS -- Composed of the commands above */
RetCode FlashPartition(const std::string& partition, const std::vector<char>& data);
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 65f0eff..eb737bb 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -74,6 +74,7 @@
"liblogwrap",
"libdm",
"libext2_uuid",
+ "libfscrypt",
"libfstab",
],
cppflags: [
diff --git a/fs_mgr/README.overlayfs.md b/fs_mgr/README.overlayfs.md
index bb63df8..f579078 100644
--- a/fs_mgr/README.overlayfs.md
+++ b/fs_mgr/README.overlayfs.md
@@ -32,9 +32,9 @@
Then enter one of the following sequences:
- $ adb stop
+ $ adb shell stop
$ adb sync
- $ adb start
+ $ adb shell start
$ adb reboot
*or*
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 4ba1c49..a8059b7 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -58,6 +58,7 @@
#include <fs_avb/fs_avb.h>
#include <fs_mgr/file_wait.h>
#include <fs_mgr_overlayfs.h>
+#include <fscrypt/fscrypt.h>
#include <libdm/dm.h>
#include <liblp/metadata_format.h>
#include <linux/fs.h>
@@ -84,6 +85,9 @@
#define SYSFS_EXT4_VERITY "/sys/fs/ext4/features/verity"
+// FIXME: this should be in system/extras
+#define EXT4_FEATURE_COMPAT_STABLE_INODES 0x0800
+
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
using android::base::Basename;
@@ -412,25 +416,43 @@
// Enable file-based encryption if needed.
static void tune_encrypt(const std::string& blk_device, const FstabEntry& entry,
const struct ext4_super_block* sb, int* fs_stat) {
- bool has_encrypt = (sb->s_feature_incompat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_ENCRYPT)) != 0;
- bool want_encrypt = entry.fs_mgr_flags.file_encryption;
-
- if (has_encrypt || !want_encrypt) {
+ if (!entry.fs_mgr_flags.file_encryption) {
+ return; // Nothing needs done.
+ }
+ std::vector<std::string> features_needed;
+ if ((sb->s_feature_incompat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_ENCRYPT)) == 0) {
+ features_needed.emplace_back("encrypt");
+ }
+ android::fscrypt::EncryptionOptions options;
+ if (!android::fscrypt::ParseOptions(entry.encryption_options, &options)) {
+ LERROR << "Unable to parse encryption options on " << blk_device << ": "
+ << entry.encryption_options;
return;
}
-
+ if ((options.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) != 0) {
+ // We can only use this policy on ext4 if the "stable_inodes" feature
+ // is set on the filesystem, otherwise shrinking will break encrypted files.
+ if ((sb->s_feature_compat & cpu_to_le32(EXT4_FEATURE_COMPAT_STABLE_INODES)) == 0) {
+ features_needed.emplace_back("stable_inodes");
+ }
+ }
+ if (features_needed.size() == 0) {
+ return;
+ }
if (!tune2fs_available()) {
LERROR << "Unable to enable ext4 encryption on " << blk_device
<< " because " TUNE2FS_BIN " is missing";
return;
}
- const char* argv[] = {TUNE2FS_BIN, "-Oencrypt", blk_device.c_str()};
+ auto flags = android::base::Join(features_needed, ',');
+ auto flag_arg = "-O"s + flags;
+ const char* argv[] = {TUNE2FS_BIN, flag_arg.c_str(), blk_device.c_str()};
- LINFO << "Enabling ext4 encryption on " << blk_device;
+ LINFO << "Enabling ext4 flags " << flags << " on " << blk_device;
if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
LERROR << "Failed to run " TUNE2FS_BIN " to enable "
- << "ext4 encryption on " << blk_device;
+ << "ext4 flags " << flags << " on " << blk_device;
*fs_stat |= FS_STAT_ENABLE_ENCRYPTION_FAILED;
}
}
@@ -888,6 +910,17 @@
public:
CheckpointManager(int needs_checkpoint = -1) : needs_checkpoint_(needs_checkpoint) {}
+ bool NeedsCheckpoint() {
+ if (needs_checkpoint_ != UNKNOWN) {
+ return needs_checkpoint_ == YES;
+ }
+ if (!call_vdc({"checkpoint", "needsCheckpoint"}, &needs_checkpoint_)) {
+ LERROR << "Failed to find if checkpointing is needed. Assuming no.";
+ needs_checkpoint_ = NO;
+ }
+ return needs_checkpoint_ == YES;
+ }
+
bool Update(FstabEntry* entry, const std::string& block_device = std::string()) {
if (!entry->fs_mgr_flags.checkpoint_blk && !entry->fs_mgr_flags.checkpoint_fs) {
return true;
@@ -897,13 +930,7 @@
call_vdc({"checkpoint", "restoreCheckpoint", entry->blk_device}, nullptr);
}
- if (needs_checkpoint_ == UNKNOWN &&
- !call_vdc({"checkpoint", "needsCheckpoint"}, &needs_checkpoint_)) {
- LERROR << "Failed to find if checkpointing is needed. Assuming no.";
- needs_checkpoint_ = NO;
- }
-
- if (needs_checkpoint_ != YES) {
+ if (!NeedsCheckpoint()) {
return true;
}
@@ -1324,6 +1351,69 @@
return ret;
}
+static std::string GetUserdataBlockDevice() {
+ Fstab fstab;
+ if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
+ LERROR << "Failed to read /proc/mounts";
+ return "";
+ }
+ auto entry = GetEntryForMountPoint(&fstab, "/data");
+ if (entry == nullptr) {
+ LERROR << "Didn't find /data mount point in /proc/mounts";
+ return "";
+ }
+ return entry->blk_device;
+}
+
+int fs_mgr_remount_userdata_into_checkpointing(Fstab* fstab) {
+ const std::string& block_device = GetUserdataBlockDevice();
+ LINFO << "Userdata is mounted on " << block_device;
+ auto entry = std::find_if(fstab->begin(), fstab->end(), [&block_device](const FstabEntry& e) {
+ if (e.mount_point != "/data") {
+ return false;
+ }
+ if (e.blk_device == block_device) {
+ return true;
+ }
+ DeviceMapper& dm = DeviceMapper::Instance();
+ std::string path;
+ if (!dm.GetDmDevicePathByName("userdata", &path)) {
+ return false;
+ }
+ return path == block_device;
+ });
+ if (entry == fstab->end()) {
+ LERROR << "Can't find /data in fstab";
+ return -1;
+ }
+ if (!entry->fs_mgr_flags.checkpoint_blk && !entry->fs_mgr_flags.checkpoint_fs) {
+ LINFO << "Userdata doesn't support checkpointing. Nothing to do";
+ return 0;
+ }
+ CheckpointManager checkpoint_manager;
+ if (!checkpoint_manager.NeedsCheckpoint()) {
+ LINFO << "Checkpointing not needed. Don't remount";
+ return 0;
+ }
+ if (entry->fs_mgr_flags.checkpoint_fs) {
+ // Userdata is f2fs, simply remount it.
+ if (!checkpoint_manager.Update(&(*entry))) {
+ LERROR << "Failed to remount userdata in checkpointing mode";
+ return -1;
+ }
+ if (mount(entry->blk_device.c_str(), entry->mount_point.c_str(), "none",
+ MS_REMOUNT | entry->flags, entry->fs_options.c_str()) != 0) {
+ LERROR << "Failed to remount userdata in checkpointing mode";
+ return -1;
+ }
+ } else {
+ // TODO(b/135984674): support remounting for ext4.
+ LERROR << "Remounting in checkpointing mode is not yet supported for ext4";
+ return -1;
+ }
+ return 0;
+}
+
// wrapper to __mount() and expects a fully prepared fstab_rec,
// unlike fs_mgr_do_mount which does more things with avb / verity etc.
int fs_mgr_do_mount_one(const FstabEntry& entry, const std::string& mount_point) {
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index c1a8dae..d216458 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -99,71 +99,9 @@
return false;
}
-const std::array<const char*, 3> kFileContentsEncryptionMode = {
- "aes-256-xts",
- "adiantum",
- "ice",
-};
-
-const std::array<const char*, 3> kFileNamesEncryptionMode = {
- "aes-256-cts",
- "aes-256-heh",
- "adiantum",
-};
-
void ParseFileEncryption(const std::string& arg, FstabEntry* entry) {
- // The fileencryption flag is followed by an = and 1 to 3 colon-separated fields:
- //
- // 1. Contents encryption mode
- // 2. Filenames encryption mode (defaults to "aes-256-cts" or "adiantum"
- // depending on the contents encryption mode)
- // 3. Encryption policy version (defaults to "v1". Use "v2" on new devices.)
entry->fs_mgr_flags.file_encryption = true;
-
- auto parts = Split(arg, ":");
- if (parts.empty() || parts.size() > 3) {
- LWARNING << "Warning: fileencryption= flag malformed: " << arg;
- return;
- }
-
- // Alias for backwards compatibility.
- if (parts[0] == "software") {
- parts[0] = "aes-256-xts";
- }
-
- if (std::find(kFileContentsEncryptionMode.begin(), kFileContentsEncryptionMode.end(),
- parts[0]) == kFileContentsEncryptionMode.end()) {
- LWARNING << "fileencryption= flag malformed, file contents encryption mode not found: "
- << arg;
- return;
- }
-
- entry->file_contents_mode = parts[0];
-
- if (parts.size() >= 2) {
- if (std::find(kFileNamesEncryptionMode.begin(), kFileNamesEncryptionMode.end(), parts[1]) ==
- kFileNamesEncryptionMode.end()) {
- LWARNING << "fileencryption= flag malformed, file names encryption mode not found: "
- << arg;
- return;
- }
-
- entry->file_names_mode = parts[1];
- } else if (entry->file_contents_mode == "adiantum") {
- entry->file_names_mode = "adiantum";
- } else {
- entry->file_names_mode = "aes-256-cts";
- }
-
- if (parts.size() >= 3) {
- if (!android::base::StartsWith(parts[2], 'v') ||
- !android::base::ParseInt(&parts[2][1], &entry->file_policy_version)) {
- LWARNING << "fileencryption= flag malformed, unknown options: " << arg;
- return;
- }
- } else {
- entry->file_policy_version = 1;
- }
+ entry->encryption_options = arg;
}
bool SetMountFlag(const std::string& flag, FstabEntry* entry) {
@@ -299,9 +237,7 @@
// return it.
entry->fs_mgr_flags.force_fde_or_fbe = true;
entry->key_loc = arg;
- entry->file_contents_mode = "aes-256-xts";
- entry->file_names_mode = "aes-256-cts";
- entry->file_policy_version = 1;
+ entry->encryption_options = "aes-256-xts:aes-256-cts";
} else if (StartsWith(flag, "max_comp_streams=")) {
if (!ParseInt(arg, &entry->max_comp_streams)) {
LWARNING << "Warning: max_comp_streams= flag malformed: " << arg;
@@ -686,13 +622,14 @@
if (entries.empty()) {
FstabEntry entry = {
.blk_device = partition,
+ // .logical_partition_name is required to look up AVB Hashtree descriptors.
+ .logical_partition_name = "system",
.mount_point = mount_point,
.fs_type = "ext4",
.flags = MS_RDONLY,
.fs_options = "barrier=1",
.avb_keys = kGsiKeys,
- // .logical_partition_name is required to look up AVB Hashtree descriptors.
- .logical_partition_name = "system"};
+ };
entry.fs_mgr_flags.wait = true;
entry.fs_mgr_flags.logical = true;
entry.fs_mgr_flags.first_stage_mount = true;
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index bdec7be..ca67f37 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -105,6 +105,8 @@
// it destroys verity devices from device mapper after the device is unmounted.
int fs_mgr_umount_all(android::fs_mgr::Fstab* fstab);
+int fs_mgr_remount_userdata_into_checkpointing(android::fs_mgr::Fstab* fstab);
+
// Finds the dm_bow device on which this block device is stacked, or returns
// empty string
std::string fs_mgr_find_bow_device(const std::string& block_device);
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 3c517dc..c6a16e3 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -45,9 +45,7 @@
int max_comp_streams = 0;
off64_t zram_size = 0;
off64_t reserved_size = 0;
- std::string file_contents_mode;
- std::string file_names_mode;
- int file_policy_version = 0;
+ std::string encryption_options;
off64_t erase_blk_size = 0;
off64_t logical_blk_size = 0;
std::string sysfs_path;
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index dd95a5e..8a924d5 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -79,3 +79,19 @@
require_root: true,
test_min_api_level: 29,
}
+
+cc_fuzz {
+ name: "dm_linear_table_fuzzer",
+ defaults: ["fs_mgr_defaults"],
+ srcs: [
+ "dm_linear_fuzzer.cpp",
+ "test_util.cpp",
+ ],
+ static_libs: [
+ "libdm",
+ "libbase",
+ "libext2_uuid",
+ "libfs_mgr",
+ "liblog",
+ ],
+}
diff --git a/fs_mgr/libdm/dm_linear_fuzzer.cpp b/fs_mgr/libdm/dm_linear_fuzzer.cpp
new file mode 100644
index 0000000..8462901
--- /dev/null
+++ b/fs_mgr/libdm/dm_linear_fuzzer.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <chrono>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+#include <libdm/dm_table.h>
+#include <libdm/loop_control.h>
+
+#include "test_util.h"
+
+using namespace android;
+using namespace android::base;
+using namespace android::dm;
+using namespace std;
+using namespace std::chrono_literals;
+
+/*
+ * This test aims at making the library crash, so these functions are not
+ * really useful.
+ * Keeping them here for future use.
+ */
+template <class T, class C>
+void ASSERT_EQ(const T& /*a*/, const C& /*b*/) {
+ // if (a != b) {}
+}
+
+template <class T>
+void ASSERT_FALSE(const T& /*a*/) {
+ // if (a) {}
+}
+
+template <class T, class C>
+void ASSERT_GE(const T& /*a*/, const C& /*b*/) {
+ // if (a < b) {}
+}
+
+template <class T, class C>
+void ASSERT_NE(const T& /*a*/, const C& /*b*/) {
+ // if (a == b) {}
+}
+
+template <class T>
+void ASSERT_TRUE(const T& /*a*/) {
+ // if (!a) {}
+}
+
+template <class T, class C>
+void EXPECT_EQ(const T& a, const C& b) {
+ ASSERT_EQ(a, b);
+}
+
+template <class T>
+void EXPECT_TRUE(const T& a) {
+ ASSERT_TRUE(a);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ uint64_t val[6];
+
+ if (size != sizeof(val)) {
+ return 0;
+ }
+
+ memcpy(&val, &data[0], sizeof(*val));
+
+ unique_fd tmp1(CreateTempFile("file_1", 4096));
+ ASSERT_GE(tmp1, 0);
+ unique_fd tmp2(CreateTempFile("file_2", 4096));
+ ASSERT_GE(tmp2, 0);
+
+ LoopDevice loop_a(tmp1, 10s);
+ ASSERT_TRUE(loop_a.valid());
+ LoopDevice loop_b(tmp2, 10s);
+ ASSERT_TRUE(loop_b.valid());
+
+ // Define a 2-sector device, with each sector mapping to the first sector
+ // of one of our loop devices.
+ DmTable table;
+ ASSERT_TRUE(table.Emplace<DmTargetLinear>(val[0], val[1], loop_a.device(), val[2]));
+ ASSERT_TRUE(table.Emplace<DmTargetLinear>(val[3], val[4], loop_b.device(), val[5]));
+ ASSERT_TRUE(table.valid());
+ ASSERT_EQ(2u, table.num_sectors());
+
+ TempDevice dev("libdm-test-dm-linear", table);
+ ASSERT_TRUE(dev.valid());
+ ASSERT_FALSE(dev.path().empty());
+
+ auto& dm = DeviceMapper::Instance();
+
+ dev_t dev_number;
+ ASSERT_TRUE(dm.GetDeviceNumber(dev.name(), &dev_number));
+ ASSERT_NE(dev_number, 0);
+
+ std::string dev_string;
+ ASSERT_TRUE(dm.GetDeviceString(dev.name(), &dev_string));
+ ASSERT_FALSE(dev_string.empty());
+
+ // Test GetTableStatus.
+ vector<DeviceMapper::TargetInfo> targets;
+ ASSERT_TRUE(dm.GetTableStatus(dev.name(), &targets));
+ ASSERT_EQ(targets.size(), 2);
+ EXPECT_EQ(strcmp(targets[0].spec.target_type, "linear"), 0);
+ EXPECT_TRUE(targets[0].data.empty());
+ EXPECT_EQ(targets[0].spec.sector_start, 0);
+ EXPECT_EQ(targets[0].spec.length, 1);
+ EXPECT_EQ(strcmp(targets[1].spec.target_type, "linear"), 0);
+ EXPECT_TRUE(targets[1].data.empty());
+ EXPECT_EQ(targets[1].spec.sector_start, 1);
+ EXPECT_EQ(targets[1].spec.length, 1);
+
+ // Test GetTargetType().
+ EXPECT_EQ(DeviceMapper::GetTargetType(targets[0].spec), std::string{"linear"});
+ EXPECT_EQ(DeviceMapper::GetTargetType(targets[1].spec), std::string{"linear"});
+
+ // Normally the TestDevice destructor would delete this, but at least one
+ // test should ensure that device deletion works.
+ ASSERT_TRUE(dev.Destroy());
+
+ return 0;
+}
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index 39c908d..ed2fa83 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -47,50 +47,6 @@
ASSERT_TRUE(dm.GetTargetByName("linear", &info));
}
-// Helper to ensure that device mapper devices are released.
-class TempDevice {
- public:
- TempDevice(const std::string& name, const DmTable& table)
- : dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
- valid_ = dm_.CreateDevice(name, table, &path_, 5s);
- }
- TempDevice(TempDevice&& other) noexcept
- : dm_(other.dm_), name_(other.name_), path_(other.path_), valid_(other.valid_) {
- other.valid_ = false;
- }
- ~TempDevice() {
- if (valid_) {
- dm_.DeleteDevice(name_);
- }
- }
- bool Destroy() {
- if (!valid_) {
- return false;
- }
- valid_ = false;
- return dm_.DeleteDevice(name_);
- }
- std::string path() const { return path_; }
- const std::string& name() const { return name_; }
- bool valid() const { return valid_; }
-
- TempDevice(const TempDevice&) = delete;
- TempDevice& operator=(const TempDevice&) = delete;
-
- TempDevice& operator=(TempDevice&& other) noexcept {
- name_ = other.name_;
- valid_ = other.valid_;
- other.valid_ = false;
- return *this;
- }
-
- private:
- DeviceMapper& dm_;
- std::string name_;
- std::string path_;
- bool valid_;
-};
-
TEST(libdm, DmLinear) {
unique_fd tmp1(CreateTempFile("file_1", 4096));
ASSERT_GE(tmp1, 0);
diff --git a/fs_mgr/libdm/test_util.h b/fs_mgr/libdm/test_util.h
index 96b051c..6671364 100644
--- a/fs_mgr/libdm/test_util.h
+++ b/fs_mgr/libdm/test_util.h
@@ -20,8 +20,12 @@
#include <android-base/unique_fd.h>
#include <stddef.h>
+#include <chrono>
#include <string>
+#include <libdm/dm.h>
+#include <libdm/dm_table.h>
+
namespace android {
namespace dm {
@@ -29,6 +33,50 @@
// created with a fixed size.
android::base::unique_fd CreateTempFile(const std::string& name, size_t size);
+// Helper to ensure that device mapper devices are released.
+class TempDevice {
+ public:
+ TempDevice(const std::string& name, const DmTable& table)
+ : dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
+ valid_ = dm_.CreateDevice(name, table, &path_, std::chrono::seconds(5));
+ }
+ TempDevice(TempDevice&& other) noexcept
+ : dm_(other.dm_), name_(other.name_), path_(other.path_), valid_(other.valid_) {
+ other.valid_ = false;
+ }
+ ~TempDevice() {
+ if (valid_) {
+ dm_.DeleteDevice(name_);
+ }
+ }
+ bool Destroy() {
+ if (!valid_) {
+ return false;
+ }
+ valid_ = false;
+ return dm_.DeleteDevice(name_);
+ }
+ std::string path() const { return path_; }
+ const std::string& name() const { return name_; }
+ bool valid() const { return valid_; }
+
+ TempDevice(const TempDevice&) = delete;
+ TempDevice& operator=(const TempDevice&) = delete;
+
+ TempDevice& operator=(TempDevice&& other) noexcept {
+ name_ = other.name_;
+ valid_ = other.valid_;
+ other.valid_ = false;
+ return *this;
+ }
+
+ private:
+ DeviceMapper& dm_;
+ std::string name_;
+ std::string path_;
+ bool valid_;
+};
+
} // namespace dm
} // namespace android
diff --git a/fs_mgr/libfs_avb/tests/avb_util_test.cpp b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
index 0d342d3..784eb9c 100644
--- a/fs_mgr/libfs_avb/tests/avb_util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
@@ -101,10 +101,10 @@
TEST_F(AvbUtilTest, DeriveAvbPartitionName) {
// The fstab_entry to test.
FstabEntry fstab_entry = {
- .blk_device = "/dev/block/dm-1", // a dm-linear device (logical)
- .mount_point = "/system",
- .fs_type = "ext4",
- .logical_partition_name = "system",
+ .blk_device = "/dev/block/dm-1", // a dm-linear device (logical)
+ .logical_partition_name = "system",
+ .mount_point = "/system",
+ .fs_type = "ext4",
};
// Logical partitions.
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index a7c77b8..ea0fca8 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -100,6 +100,7 @@
test_suites: ["vts-core"],
auto_gen_config: true,
test_min_api_level: 29,
+ require_root: true,
}
cc_test {
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 7405039..54350a5 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -171,7 +171,8 @@
std::unique_ptr<MetadataBuilder> MetadataBuilder::NewForUpdate(const IPartitionOpener& opener,
const std::string& source_partition,
uint32_t source_slot_number,
- uint32_t target_slot_number) {
+ uint32_t target_slot_number,
+ bool always_keep_source_slot) {
auto metadata = ReadMetadata(opener, source_partition, source_slot_number);
if (!metadata) {
return nullptr;
@@ -189,7 +190,8 @@
}
}
- if (IPropertyFetcher::GetInstance()->GetBoolProperty("ro.virtual_ab.enabled", false)) {
+ if (IPropertyFetcher::GetInstance()->GetBoolProperty("ro.virtual_ab.enabled", false) &&
+ !always_keep_source_slot) {
if (!UpdateMetadataForInPlaceSnapshot(metadata.get(), source_slot_number,
target_slot_number)) {
return nullptr;
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index b43ccf0..1e9d636 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -209,10 +209,13 @@
// metadata may not have the target slot's devices listed yet, in which
// case, it is automatically upgraded to include all available block
// devices.
+ // If |always_keep_source_slot| is set, on a Virtual A/B device, source slot
+ // partitions are kept. This is useful when applying a downgrade package.
static std::unique_ptr<MetadataBuilder> NewForUpdate(const IPartitionOpener& opener,
const std::string& source_partition,
uint32_t source_slot_number,
- uint32_t target_slot_number);
+ uint32_t target_slot_number,
+ bool always_keep_source_slot = false);
// Import an existing table for modification. If the table is not valid, for
// example it contains duplicate partition names, then nullptr is returned.
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 8cf0f3b..9256a16 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -28,6 +28,7 @@
"liblp",
],
static_libs: [
+ "libcutils",
"libdm",
"libfs_mgr",
"libfstab",
@@ -56,6 +57,17 @@
},
}
+cc_defaults {
+ name: "libsnapshot_hal_deps",
+ cflags: [
+ "-DLIBSNAPSHOT_USE_HAL",
+ ],
+ shared_libs: [
+ "android.hardware.boot@1.0",
+ "android.hardware.boot@1.1",
+ ],
+}
+
filegroup {
name: "libsnapshot_sources",
srcs: [
@@ -75,7 +87,10 @@
cc_library_static {
name: "libsnapshot",
- defaults: ["libsnapshot_defaults"],
+ defaults: [
+ "libsnapshot_defaults",
+ "libsnapshot_hal_deps",
+ ],
srcs: [":libsnapshot_sources"],
whole_static_libs: [
"libfiemap_binder",
@@ -83,7 +98,7 @@
}
cc_library_static {
- name: "libsnapshot_nobinder",
+ name: "libsnapshot_init",
defaults: ["libsnapshot_defaults"],
srcs: [":libsnapshot_sources"],
recovery_available: true,
@@ -92,6 +107,19 @@
],
}
+cc_library_static {
+ name: "libsnapshot_nobinder",
+ defaults: [
+ "libsnapshot_defaults",
+ "libsnapshot_hal_deps",
+ ],
+ srcs: [":libsnapshot_sources"],
+ recovery_available: true,
+ whole_static_libs: [
+ "libfiemap_passthrough",
+ ],
+}
+
cc_test {
name: "libsnapshot_test",
defaults: ["libsnapshot_defaults"],
@@ -103,12 +131,14 @@
],
shared_libs: [
"libbinder",
+ "libcrypto",
+ "libhidlbase",
"libprotobuf-cpp-lite",
"libutils",
],
static_libs: [
- "libcutils",
- "libcrypto_static",
+ "android.hardware.boot@1.0",
+ "android.hardware.boot@1.1",
"libfs_mgr",
"libgmock",
"liblp",
@@ -134,10 +164,14 @@
"libsnapshot",
],
shared_libs: [
+ "android.hardware.boot@1.0",
+ "android.hardware.boot@1.1",
"libbase",
"libbinder",
+ "libbinderthreadstate",
"libext4_utils",
"libfs_mgr",
+ "libhidlbase",
"liblog",
"liblp",
"libprotobuf-cpp-lite",
diff --git a/fs_mgr/libsnapshot/dm_snapshot_internals.h b/fs_mgr/libsnapshot/dm_snapshot_internals.h
new file mode 100644
index 0000000..fef256d
--- /dev/null
+++ b/fs_mgr/libsnapshot/dm_snapshot_internals.h
@@ -0,0 +1,135 @@
+// Copyright (C) 2019 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.
+
+#pragma once
+
+#include <stdint.h>
+
+#include <vector>
+
+namespace android {
+namespace snapshot {
+
+class DmSnapCowSizeCalculator {
+ public:
+ DmSnapCowSizeCalculator(unsigned int sector_bytes, unsigned int chunk_sectors)
+ : sector_bytes_(sector_bytes),
+ chunk_sectors_(chunk_sectors),
+ exceptions_per_chunk(chunk_sectors_ * sector_bytes_ / (64 * 2 / 8)) {}
+
+ void WriteByte(uint64_t address) { WriteSector(address / sector_bytes_); }
+ void WriteSector(uint64_t sector) { WriteChunk(sector / chunk_sectors_); }
+ void WriteChunk(uint64_t chunk_id) {
+ if (modified_chunks_.size() <= chunk_id) {
+ modified_chunks_.resize(chunk_id + 1, false);
+ }
+ modified_chunks_[chunk_id] = true;
+ }
+
+ uint64_t cow_size_bytes() const { return cow_size_sectors() * sector_bytes_; }
+ uint64_t cow_size_sectors() const { return cow_size_chunks() * chunk_sectors_; }
+
+ /*
+ * The COW device has a precise internal structure as follows:
+ *
+ * - header (1 chunk)
+ * - #0 map and chunks
+ * - map (1 chunk)
+ * - chunks addressable by previous map (exceptions_per_chunk)
+ * - #1 map and chunks
+ * - map (1 chunk)
+ * - chunks addressable by previous map (exceptions_per_chunk)
+ * ...
+ * - #n: map and chunks
+ * - map (1 chunk)
+ * - chunks addressable by previous map (exceptions_per_chunk)
+ * - 1 extra chunk
+ */
+ uint64_t cow_size_chunks() const {
+ uint64_t modified_chunks_count = 0;
+ uint64_t cow_chunks = 0;
+
+ for (const auto& c : modified_chunks_) {
+ if (c) {
+ ++modified_chunks_count;
+ }
+ }
+
+ /* disk header + padding = 1 chunk */
+ cow_chunks += 1;
+
+ /* snapshot modified chunks */
+ cow_chunks += modified_chunks_count;
+
+ /* snapshot chunks index metadata */
+ cow_chunks += 1 + modified_chunks_count / exceptions_per_chunk;
+
+ return cow_chunks;
+ }
+
+ private:
+ /*
+ * Size of each sector in bytes.
+ */
+ const uint64_t sector_bytes_;
+
+ /*
+ * Size of each chunk in sectors.
+ */
+ const uint64_t chunk_sectors_;
+
+ /*
+ * The COW device stores tables to map the modified chunks. Each table
+ * has the size of exactly 1 chunk.
+ * Each row of the table (also called exception in the kernel) contains two
+ * 64 bit indices to identify the corresponding chunk, and this 128 bit row
+ * size is a constant.
+ * The number of exceptions that each table can contain determines the
+ * number of data chunks that separate two consecutive tables. This value
+ * is then fundamental to compute the space overhead introduced by the
+ * tables in COW devices.
+ */
+ const uint64_t exceptions_per_chunk;
+
+ /*
+ * |modified_chunks_| is a container that keeps trace of the modified
+ * chunks.
+ * Multiple options were considered when choosing the most appropriate data
+ * structure for this container. Here follows a summary of why vector<bool>
+ * has been chosen, taking as a reference a snapshot partition of 4 GiB and
+ * chunk size of 4 KiB.
+ * - std::set<uint64_t> is very space-efficient for a small number of
+ * operations, but if the whole snapshot is changed, it would need to
+ * store
+ * 4 GiB / 4 KiB * (64 bit / 8) = 8 MiB
+ * just for the data, plus the additional data overhead for the red-black
+ * tree used for data sorting (if each rb-tree element stores 3 address
+ * and the word-aligne color, the total size grows to 32 MiB).
+ * - std::bitset<N> is not a good fit because requires a priori knowledge,
+ * at compile time, of the bitset size.
+ * - std::vector<bool> is a special case of vector, which uses a data
+ * compression that allows reducing the space utilization of each element
+ * to 1 bit. In detail, this data structure is composed of a resizable
+ * array of words, each of them representing a bitmap. On a 64 bit
+ * device, modifying the whole 4 GiB snapshot grows this container up to
+ * 4 * GiB / 4 KiB / 64 = 64 KiB
+ * that, even if is the same space requirement to change a single byte at
+ * the highest address of the snapshot, is a very affordable space
+ * requirement.
+ */
+ std::vector<bool> modified_chunks_;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/auto_device.h b/fs_mgr/libsnapshot/include/libsnapshot/auto_device.h
new file mode 100644
index 0000000..d5ceb0e
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/auto_device.h
@@ -0,0 +1,40 @@
+// Copyright (C) 2019 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.
+
+#pragma once
+
+#include <string>
+
+#include <android-base/macros.h>
+
+namespace android {
+namespace snapshot {
+
+// An abstract "device" that will be cleaned up (unmapped, unmounted, etc.) upon
+// destruction.
+struct AutoDevice {
+ virtual ~AutoDevice(){};
+ void Release();
+
+ protected:
+ AutoDevice(const std::string& name) : name_(name) {}
+ std::string name_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AutoDevice);
+ AutoDevice(AutoDevice&& other) = delete;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 69f2895..431fea1 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -19,6 +19,7 @@
#include <chrono>
#include <map>
#include <memory>
+#include <optional>
#include <ostream>
#include <string>
#include <string_view>
@@ -32,6 +33,8 @@
#include <liblp/liblp.h>
#include <update_engine/update_metadata.pb.h>
+#include <libsnapshot/auto_device.h>
+
#ifndef FRIEND_TEST
#define FRIEND_TEST(test_set_name, individual_test) \
friend class test_set_name##_##individual_test##_Test
@@ -49,6 +52,16 @@
class IPartitionOpener;
} // namespace fs_mgr
+// Forward declare IBootControl types since we cannot include only the headers
+// with Soong. Note: keep the enum width in sync.
+namespace hardware {
+namespace boot {
+namespace V1_1 {
+enum class MergeStatus : int32_t;
+} // namespace V1_1
+} // namespace boot
+} // namespace hardware
+
namespace snapshot {
struct AutoDeleteCowImage;
@@ -94,6 +107,7 @@
using LpMetadata = android::fs_mgr::LpMetadata;
using MetadataBuilder = android::fs_mgr::MetadataBuilder;
using DeltaArchiveManifest = chromeos_update_engine::DeltaArchiveManifest;
+ using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
public:
// Dependency injection for testing.
@@ -107,6 +121,8 @@
virtual std::string GetSuperDevice(uint32_t slot) const = 0;
virtual const IPartitionOpener& GetPartitionOpener() const = 0;
virtual bool IsOverlayfsSetup() const = 0;
+ virtual bool SetBootControlMergeStatus(MergeStatus status) = 0;
+ virtual bool IsRecovery() const = 0;
};
~SnapshotManager();
@@ -196,6 +212,22 @@
// Dump debug information.
bool Dump(std::ostream& os);
+ // Ensure metadata directory is mounted in recovery. When the returned
+ // AutoDevice is destroyed, the metadata directory is automatically
+ // unmounted.
+ // Return nullptr if any failure.
+ // In Android mode, Return an AutoDevice that does nothing
+ // In recovery, return an AutoDevice that does nothing if metadata entry
+ // is not found in fstab.
+ // Note: if this function is called the second time before the AutoDevice returned from the
+ // first call is destroyed, the device will be unmounted when any of these AutoDevices is
+ // destroyed. FOr example:
+ // auto a = mgr->EnsureMetadataMounted(); // mounts
+ // auto b = mgr->EnsureMetadataMounted(); // does nothing
+ // b.reset() // unmounts
+ // a.reset() // does nothing
+ std::unique_ptr<AutoDevice> EnsureMetadataMounted();
+
private:
FRIEND_TEST(SnapshotTest, CleanFirstStageMount);
FRIEND_TEST(SnapshotTest, CreateSnapshot);
@@ -208,6 +240,7 @@
FRIEND_TEST(SnapshotTest, Merge);
FRIEND_TEST(SnapshotTest, MergeCannotRemoveCow);
FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
+ FRIEND_TEST(SnapshotTest, UpdateBootControlHal);
FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow);
friend class SnapshotTest;
friend class SnapshotUpdateTest;
@@ -285,7 +318,8 @@
std::string* dev_path);
// Map a COW image that was previous created with CreateCowImage.
- bool MapCowImage(const std::string& name, const std::chrono::milliseconds& timeout_ms);
+ std::optional<std::string> MapCowImage(const std::string& name,
+ const std::chrono::milliseconds& timeout_ms);
// Remove the backing copy-on-write image and snapshot states for the named snapshot. The
// caller is responsible for ensuring that the snapshot is unmapped.
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.cpp b/fs_mgr/libsnapshot/partition_cow_creator.cpp
index eedc1cd..77315b4 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator.cpp
@@ -17,8 +17,9 @@
#include <math.h>
#include <android-base/logging.h>
-
#include <android/snapshot/snapshot.pb.h>
+
+#include "dm_snapshot_internals.h"
#include "utility.h"
using android::dm::kSectorSize;
@@ -33,13 +34,6 @@
namespace android {
namespace snapshot {
-// Round |d| up to a multiple of |block_size|.
-static uint64_t RoundUp(double d, uint64_t block_size) {
- uint64_t ret = ((uint64_t)ceil(d) + block_size - 1) / block_size * block_size;
- CHECK(ret >= d) << "Can't round " << d << " up to a multiple of " << block_size;
- return ret;
-}
-
// Intersect two linear extents. If no intersection, return an extent with length 0.
static std::unique_ptr<Extent> Intersect(Extent* target_extent, Extent* existing_extent) {
// Convert target_extent and existing_extent to linear extents. Zero extents
@@ -68,33 +62,58 @@
return false;
}
-std::optional<uint64_t> PartitionCowCreator::GetCowSize(uint64_t snapshot_size) {
- // TODO: Use |operations|. to determine a minimum COW size.
- // kCowEstimateFactor is good for prototyping but we can't use that in production.
- static constexpr double kCowEstimateFactor = 1.05;
- auto cow_size = RoundUp(snapshot_size * kCowEstimateFactor, kDefaultBlockSize);
- return cow_size;
+uint64_t PartitionCowCreator::GetCowSize() {
+ // WARNING: The origin partition should be READ-ONLY
+ const uint64_t logical_block_size = current_metadata->logical_block_size();
+ const unsigned int sectors_per_block = logical_block_size / kSectorSize;
+ DmSnapCowSizeCalculator sc(kSectorSize, kSnapshotChunkSize);
+
+ if (operations == nullptr) return sc.cow_size_bytes();
+
+ for (const auto& iop : *operations) {
+ for (const auto& de : iop.dst_extents()) {
+ // Skip if no blocks are written
+ if (de.num_blocks() == 0) continue;
+
+ // Flag all the blocks that were written
+ const auto block_boundary = de.start_block() + de.num_blocks();
+ for (auto b = de.start_block(); b < block_boundary; ++b) {
+ for (unsigned int s = 0; s < sectors_per_block; ++s) {
+ const auto sector_id = b * sectors_per_block + s;
+ sc.WriteSector(sector_id);
+ }
+ }
+ }
+ }
+
+ return sc.cow_size_bytes();
}
std::optional<PartitionCowCreator::Return> PartitionCowCreator::Run() {
CHECK(current_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME &&
target_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME);
- uint64_t logical_block_size = current_metadata->logical_block_size();
+ const uint64_t logical_block_size = current_metadata->logical_block_size();
CHECK(logical_block_size != 0 && !(logical_block_size & (logical_block_size - 1)))
<< "logical_block_size is not power of 2";
Return ret;
ret.snapshot_status.set_name(target_partition->name());
ret.snapshot_status.set_device_size(target_partition->size());
-
- // TODO(b/141889746): Optimize by using a smaller snapshot. Some ranges in target_partition
- // may be written directly.
ret.snapshot_status.set_snapshot_size(target_partition->size());
- auto cow_size = GetCowSize(ret.snapshot_status.snapshot_size());
- if (!cow_size.has_value()) return std::nullopt;
-
+ // Being the COW partition virtual, its size doesn't affect the storage
+ // memory that will be occupied by the target.
+ // The actual storage space is affected by the COW file, whose size depends
+ // on the chunks that diverged between |current| and |target|.
+ // If the |target| partition is bigger than |current|, the data that is
+ // modified outside of |current| can be written directly to |current|.
+ // This because the data that will be written outside of |current| would
+ // not invalidate any useful information of |current|, thus:
+ // - if the snapshot is accepted for merge, this data would be already at
+ // the right place and should not be copied;
+ // - in the unfortunate case of the snapshot to be discarded, the regions
+ // modified by this data can be set as free regions and reused.
// Compute regions that are free in both current and target metadata. These are the regions
// we can use for COW partition.
auto target_free_regions = target_metadata->GetFreeRegions();
@@ -102,13 +121,15 @@
auto free_regions = Interval::Intersect(target_free_regions, current_free_regions);
uint64_t free_region_length = 0;
for (const auto& interval : free_regions) {
- free_region_length += interval.length() * kSectorSize;
+ free_region_length += interval.length();
}
+ free_region_length *= kSectorSize;
LOG(INFO) << "Remaining free space for COW: " << free_region_length << " bytes";
+ auto cow_size = GetCowSize();
// Compute the COW partition size.
- uint64_t cow_partition_size = std::min(*cow_size, free_region_length);
+ uint64_t cow_partition_size = std::min(cow_size, free_region_length);
// Round it down to the nearest logical block. Logical partitions must be a multiple
// of logical blocks.
cow_partition_size &= ~(logical_block_size - 1);
@@ -116,8 +137,7 @@
// Assign cow_partition_usable_regions to indicate what regions should the COW partition uses.
ret.cow_partition_usable_regions = std::move(free_regions);
- // The rest of the COW space is allocated on ImageManager.
- uint64_t cow_file_size = (*cow_size) - ret.snapshot_status.cow_partition_size();
+ auto cow_file_size = cow_size - cow_partition_size;
// Round it up to the nearest sector.
cow_file_size += kSectorSize - 1;
cow_file_size &= ~(kSectorSize - 1);
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.h b/fs_mgr/libsnapshot/partition_cow_creator.h
index 8888f78..d3d186b 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.h
+++ b/fs_mgr/libsnapshot/partition_cow_creator.h
@@ -60,7 +60,7 @@
private:
bool HasExtent(Partition* p, Extent* e);
- std::optional<uint64_t> GetCowSize(uint64_t snapshot_size);
+ uint64_t GetCowSize();
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
index feb3c2d..eae6c35 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -14,11 +14,14 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <libdm/dm.h>
#include <liblp/builder.h>
#include <liblp/property_fetcher.h>
+#include "dm_snapshot_internals.h"
#include "partition_cow_creator.h"
#include "test_helpers.h"
+#include "utility.h"
using namespace android::fs_mgr;
@@ -32,17 +35,20 @@
};
TEST_F(PartitionCowCreatorTest, IntersectSelf) {
- auto builder_a = MetadataBuilder::New(1024 * 1024, 1024, 2);
+ constexpr uint64_t initial_size = 1_MiB;
+ constexpr uint64_t final_size = 40_KiB;
+
+ auto builder_a = MetadataBuilder::New(initial_size, 1_KiB, 2);
ASSERT_NE(builder_a, nullptr);
auto system_a = builder_a->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system_a, nullptr);
- ASSERT_TRUE(builder_a->ResizePartition(system_a, 40 * 1024));
+ ASSERT_TRUE(builder_a->ResizePartition(system_a, final_size));
- auto builder_b = MetadataBuilder::New(1024 * 1024, 1024, 2);
+ auto builder_b = MetadataBuilder::New(initial_size, 1_KiB, 2);
ASSERT_NE(builder_b, nullptr);
auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system_b, nullptr);
- ASSERT_TRUE(builder_b->ResizePartition(system_b, 40 * 1024));
+ ASSERT_TRUE(builder_b->ResizePartition(system_b, final_size));
PartitionCowCreator creator{.target_metadata = builder_b.get(),
.target_suffix = "_b",
@@ -51,8 +57,8 @@
.current_suffix = "_a"};
auto ret = creator.Run();
ASSERT_TRUE(ret.has_value());
- ASSERT_EQ(40 * 1024, ret->snapshot_status.device_size());
- ASSERT_EQ(40 * 1024, ret->snapshot_status.snapshot_size());
+ ASSERT_EQ(final_size, ret->snapshot_status.device_size());
+ ASSERT_EQ(final_size, ret->snapshot_status.snapshot_size());
}
TEST_F(PartitionCowCreatorTest, Holes) {
@@ -64,7 +70,7 @@
BlockDeviceInfo super_device("super", kSuperSize, 0, 0, 4_KiB);
std::vector<BlockDeviceInfo> devices = {super_device};
- auto source = MetadataBuilder::New(devices, "super", 1024, 2);
+ auto source = MetadataBuilder::New(devices, "super", 1_KiB, 2);
auto system = source->AddPartition("system_a", 0);
ASSERT_NE(nullptr, system);
ASSERT_TRUE(source->ResizePartition(system, big_size));
@@ -96,5 +102,115 @@
ASSERT_TRUE(ret.has_value());
}
+TEST_F(PartitionCowCreatorTest, CowSize) {
+ using InstallOperation = chromeos_update_engine::InstallOperation;
+ using RepeatedInstallOperationPtr = google::protobuf::RepeatedPtrField<InstallOperation>;
+ using Extent = chromeos_update_engine::Extent;
+
+ constexpr uint64_t initial_size = 50_MiB;
+ constexpr uint64_t final_size = 40_MiB;
+
+ auto builder_a = MetadataBuilder::New(initial_size, 1_KiB, 2);
+ ASSERT_NE(builder_a, nullptr);
+ auto system_a = builder_a->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system_a, nullptr);
+ ASSERT_TRUE(builder_a->ResizePartition(system_a, final_size));
+
+ auto builder_b = MetadataBuilder::New(initial_size, 1_KiB, 2);
+ ASSERT_NE(builder_b, nullptr);
+ auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system_b, nullptr);
+ ASSERT_TRUE(builder_b->ResizePartition(system_b, final_size));
+
+ const uint64_t block_size = builder_b->logical_block_size();
+ const uint64_t chunk_size = kSnapshotChunkSize * dm::kSectorSize;
+ ASSERT_EQ(chunk_size, block_size);
+
+ auto cow_device_size = [](const std::vector<InstallOperation>& iopv, MetadataBuilder* builder_a,
+ MetadataBuilder* builder_b, Partition* system_b) {
+ RepeatedInstallOperationPtr riop(iopv.begin(), iopv.end());
+ PartitionCowCreator creator{.target_metadata = builder_b,
+ .target_suffix = "_b",
+ .target_partition = system_b,
+ .current_metadata = builder_a,
+ .current_suffix = "_a",
+ .operations = &riop};
+
+ auto ret = creator.Run();
+
+ if (ret.has_value()) {
+ return ret->snapshot_status.cow_file_size() + ret->snapshot_status.cow_partition_size();
+ }
+ return std::numeric_limits<uint64_t>::max();
+ };
+
+ std::vector<InstallOperation> iopv;
+ InstallOperation iop;
+ Extent* e;
+
+ // No data written, no operations performed
+ ASSERT_EQ(2 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));
+
+ // No data written
+ e = iop.add_dst_extents();
+ e->set_start_block(0);
+ e->set_num_blocks(0);
+ iopv.push_back(iop);
+ ASSERT_EQ(2 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));
+
+ e = iop.add_dst_extents();
+ e->set_start_block(1);
+ e->set_num_blocks(0);
+ iopv.push_back(iop);
+ ASSERT_EQ(2 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));
+
+ // Fill the first block
+ e = iop.add_dst_extents();
+ e->set_start_block(0);
+ e->set_num_blocks(1);
+ iopv.push_back(iop);
+ ASSERT_EQ(3 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));
+
+ // Fill the second block
+ e = iop.add_dst_extents();
+ e->set_start_block(1);
+ e->set_num_blocks(1);
+ iopv.push_back(iop);
+ ASSERT_EQ(4 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));
+
+ // Jump to 5th block and write 2
+ e = iop.add_dst_extents();
+ e->set_start_block(5);
+ e->set_num_blocks(2);
+ iopv.push_back(iop);
+ ASSERT_EQ(6 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));
+}
+
+TEST(DmSnapshotInternals, CowSizeCalculator) {
+ DmSnapCowSizeCalculator cc(512, 8);
+ unsigned long int b;
+
+ // Empty COW
+ ASSERT_EQ(cc.cow_size_sectors(), 16);
+
+ // First chunk written
+ for (b = 0; b < 4_KiB; ++b) {
+ cc.WriteByte(b);
+ ASSERT_EQ(cc.cow_size_sectors(), 24);
+ }
+
+ // Second chunk written
+ for (b = 4_KiB; b < 8_KiB; ++b) {
+ cc.WriteByte(b);
+ ASSERT_EQ(cc.cow_size_sectors(), 32);
+ }
+
+ // Leave a hole and write 5th chunk
+ for (b = 16_KiB; b < 20_KiB; ++b) {
+ cc.WriteByte(b);
+ ASSERT_EQ(cc.cow_size_sectors(), 40);
+ }
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 5b758c9..63d97d0 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -29,6 +29,9 @@
#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#ifdef LIBSNAPSHOT_USE_HAL
+#include <android/hardware/boot/1.1/IBootControl.h>
+#endif
#include <ext4_utils/ext4_utils.h>
#include <fs_mgr.h>
#include <fs_mgr_dm_linear.h>
@@ -63,6 +66,7 @@
using android::fs_mgr::LpMetadata;
using android::fs_mgr::MetadataBuilder;
using android::fs_mgr::SlotNumberForSlotSuffix;
+using android::hardware::boot::V1_1::MergeStatus;
using chromeos_update_engine::DeltaArchiveManifest;
using chromeos_update_engine::InstallOperation;
template <typename T>
@@ -71,10 +75,14 @@
using namespace std::chrono_literals;
using namespace std::string_literals;
-// Unit is sectors, this is a 4K chunk.
-static constexpr uint32_t kSnapshotChunkSize = 8;
static constexpr char kBootIndicatorPath[] = "/metadata/ota/snapshot-boot";
+#ifdef __ANDROID_RECOVERY__
+constexpr bool kIsRecovery = true;
+#else
+constexpr bool kIsRecovery = false;
+#endif
+
class DeviceInfo final : public SnapshotManager::IDeviceInfo {
public:
std::string GetGsidDir() const override { return "ota"s; }
@@ -86,11 +94,40 @@
return fs_mgr_get_super_partition_name(slot);
}
bool IsOverlayfsSetup() const override { return fs_mgr_overlayfs_is_setup(); }
+ bool SetBootControlMergeStatus(MergeStatus status) override;
+ bool IsRecovery() const override { return kIsRecovery; }
private:
android::fs_mgr::PartitionOpener opener_;
+#ifdef LIBSNAPSHOT_USE_HAL
+ android::sp<android::hardware::boot::V1_1::IBootControl> boot_control_;
+#endif
};
+bool DeviceInfo::SetBootControlMergeStatus([[maybe_unused]] MergeStatus status) {
+#ifdef LIBSNAPSHOT_USE_HAL
+ if (!boot_control_) {
+ auto hal = android::hardware::boot::V1_0::IBootControl::getService();
+ if (!hal) {
+ LOG(ERROR) << "Could not find IBootControl HAL";
+ return false;
+ }
+ boot_control_ = android::hardware::boot::V1_1::IBootControl::castFrom(hal);
+ if (!boot_control_) {
+ LOG(ERROR) << "Could not find IBootControl 1.1 HAL";
+ return false;
+ }
+ }
+ if (!boot_control_->setSnapshotMergeStatus(status)) {
+ LOG(ERROR) << "Unable to set the snapshot merge status";
+ return false;
+ }
+ return true;
+#else
+ return false;
+#endif
+}
+
// Note: IImageManager is an incomplete type in the header, so the default
// destructor doesn't work.
SnapshotManager::~SnapshotManager() {}
@@ -312,7 +349,6 @@
const std::chrono::milliseconds& timeout_ms,
std::string* dev_path) {
CHECK(lock);
- if (!EnsureImageManager()) return false;
SnapshotStatus status;
if (!ReadSnapshotStatus(lock, name, &status)) {
@@ -420,9 +456,9 @@
return true;
}
-bool SnapshotManager::MapCowImage(const std::string& name,
- const std::chrono::milliseconds& timeout_ms) {
- if (!EnsureImageManager()) return false;
+std::optional<std::string> SnapshotManager::MapCowImage(
+ const std::string& name, const std::chrono::milliseconds& timeout_ms) {
+ if (!EnsureImageManager()) return std::nullopt;
auto cow_image_name = GetCowImageDeviceName(name);
bool ok;
@@ -438,10 +474,10 @@
if (ok) {
LOG(INFO) << "Mapped " << cow_image_name << " to " << cow_dev;
- } else {
- LOG(ERROR) << "Could not map image device: " << cow_image_name;
+ return cow_dev;
}
- return ok;
+ LOG(ERROR) << "Could not map image device: " << cow_image_name;
+ return std::nullopt;
}
bool SnapshotManager::UnmapSnapshot(LockedFile* lock, const std::string& name) {
@@ -1398,7 +1434,6 @@
const SnapshotStatus& snapshot_status,
AutoDeviceList* created_devices, std::string* cow_name) {
CHECK(lock);
- if (!EnsureImageManager()) return false;
CHECK(snapshot_status.cow_partition_size() + snapshot_status.cow_file_size() > 0);
auto begin = std::chrono::steady_clock::now();
@@ -1410,10 +1445,11 @@
// Map COW image if necessary.
if (snapshot_status.cow_file_size() > 0) {
+ if (!EnsureImageManager()) return false;
auto remaining_time = GetRemainingTime(params.timeout_ms, begin);
if (remaining_time.count() < 0) return false;
- if (!MapCowImage(partition_name, remaining_time)) {
+ if (!MapCowImage(partition_name, remaining_time).has_value()) {
LOG(ERROR) << "Could not map cow image for partition: " << partition_name;
return false;
}
@@ -1592,6 +1628,35 @@
PLOG(ERROR) << "Could not write to state file";
return false;
}
+
+#ifdef LIBSNAPSHOT_USE_HAL
+ auto merge_status = MergeStatus::UNKNOWN;
+ switch (state) {
+ // The needs-reboot and completed cases imply that /data and /metadata
+ // can be safely wiped, so we don't report a merge status.
+ case UpdateState::None:
+ case UpdateState::MergeNeedsReboot:
+ case UpdateState::MergeCompleted:
+ merge_status = MergeStatus::NONE;
+ break;
+ case UpdateState::Initiated:
+ case UpdateState::Unverified:
+ merge_status = MergeStatus::SNAPSHOTTED;
+ break;
+ case UpdateState::Merging:
+ case UpdateState::MergeFailed:
+ merge_status = MergeStatus::MERGING;
+ break;
+ default:
+ // Note that Cancelled flows to here - it is never written, since
+ // it only communicates a transient state to the caller.
+ LOG(ERROR) << "Unexpected update status: " << state;
+ break;
+ }
+ if (!device_->SetBootControlMergeStatus(merge_status)) {
+ return false;
+ }
+#endif
return true;
}
@@ -1728,6 +1793,14 @@
auto target_metadata =
MetadataBuilder::NewForUpdate(opener, current_super, current_slot, target_slot);
+ // Delete partitions with target suffix in |current_metadata|. Otherwise,
+ // partition_cow_creator recognizes these left-over partitions as used space.
+ for (const auto& group_name : current_metadata->ListGroups()) {
+ if (android::base::EndsWith(group_name, target_suffix)) {
+ current_metadata->RemoveGroupAndPartitions(group_name);
+ }
+ }
+
SnapshotMetadataUpdater metadata_updater(target_metadata.get(), target_slot, manifest);
if (!metadata_updater.Update()) {
LOG(ERROR) << "Cannot calculate new metadata.";
@@ -2007,5 +2080,14 @@
return ok;
}
+std::unique_ptr<AutoDevice> SnapshotManager::EnsureMetadataMounted() {
+ if (!device_->IsRecovery()) {
+ // No need to mount anything in recovery.
+ LOG(INFO) << "EnsureMetadataMounted does nothing in Android mode.";
+ return std::unique_ptr<AutoUnmountDevice>(new AutoUnmountDevice());
+ }
+ return AutoUnmountDevice::New(device_->GetMetadataDir());
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index fd7754e..5728582 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -26,6 +26,7 @@
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <fs_mgr/roots.h>
#include <fs_mgr_dm_linear.h>
#include <gtest/gtest.h>
#include <libdm/dm.h>
@@ -47,12 +48,16 @@
using android::fs_mgr::BlockDeviceInfo;
using android::fs_mgr::CreateLogicalPartitionParams;
using android::fs_mgr::DestroyLogicalPartition;
+using android::fs_mgr::EnsurePathMounted;
+using android::fs_mgr::EnsurePathUnmounted;
using android::fs_mgr::Extent;
+using android::fs_mgr::Fstab;
using android::fs_mgr::GetPartitionGroupName;
using android::fs_mgr::GetPartitionName;
using android::fs_mgr::Interval;
using android::fs_mgr::MetadataBuilder;
using chromeos_update_engine::DeltaArchiveManifest;
+using chromeos_update_engine::DynamicPartitionGroup;
using chromeos_update_engine::PartitionUpdate;
using namespace ::testing;
using namespace android::storage_literals;
@@ -203,9 +208,9 @@
.block_device = fake_super,
.metadata = metadata.get(),
.partition = &partition,
- .device_name = GetPartitionName(partition) + "-base",
.force_writable = true,
.timeout_ms = 10s,
+ .device_name = GetPartitionName(partition) + "-base",
};
std::string ignore_path;
if (!CreateLogicalPartition(params, &ignore_path)) {
@@ -254,12 +259,11 @@
AssertionResult MapCowImage(const std::string& name,
const std::chrono::milliseconds& timeout_ms, std::string* path) {
- if (!sm->MapCowImage(name, timeout_ms)) {
+ auto cow_image_path = sm->MapCowImage(name, timeout_ms);
+ if (!cow_image_path.has_value()) {
return AssertionFailure() << "Cannot map cow image " << name;
}
- if (!dm_.GetDmDevicePathByName(name + "-cow-img"s, path)) {
- return AssertionFailure() << "No path for " << name << "-cow-img";
- }
+ *path = *cow_image_path;
return AssertionSuccess();
}
@@ -617,6 +621,31 @@
ASSERT_EQ(sm->GetUpdateState(), UpdateState::None);
}
+TEST_F(SnapshotTest, UpdateBootControlHal) {
+ ASSERT_TRUE(AcquireLock());
+
+ ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::None));
+ ASSERT_EQ(test_device->merge_status(), MergeStatus::NONE);
+
+ ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::Initiated));
+ ASSERT_EQ(test_device->merge_status(), MergeStatus::SNAPSHOTTED);
+
+ ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::Unverified));
+ ASSERT_EQ(test_device->merge_status(), MergeStatus::SNAPSHOTTED);
+
+ ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::Merging));
+ ASSERT_EQ(test_device->merge_status(), MergeStatus::MERGING);
+
+ ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::MergeNeedsReboot));
+ ASSERT_EQ(test_device->merge_status(), MergeStatus::NONE);
+
+ ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::MergeCompleted));
+ ASSERT_EQ(test_device->merge_status(), MergeStatus::NONE);
+
+ ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::MergeFailed));
+ ASSERT_EQ(test_device->merge_status(), MergeStatus::MERGING);
+}
+
class SnapshotUpdateTest : public SnapshotTest {
public:
void SetUp() override {
@@ -632,12 +661,12 @@
// Not using full name "system", "vendor", "product" because these names collide with the
// mapped partitions on the running device.
// Each test modifies manifest_ slightly to indicate changes to the partition layout.
- auto group = manifest_.mutable_dynamic_partition_metadata()->add_groups();
- group->set_name("group");
- group->set_size(kGroupSize);
- group->add_partition_names("sys");
- group->add_partition_names("vnd");
- group->add_partition_names("prd");
+ group_ = manifest_.mutable_dynamic_partition_metadata()->add_groups();
+ group_->set_name("group");
+ group_->set_size(kGroupSize);
+ group_->add_partition_names("sys");
+ group_->add_partition_names("vnd");
+ group_->add_partition_names("prd");
sys_ = manifest_.add_partitions();
sys_->set_partition_name("sys");
SetSize(sys_, 3_MiB);
@@ -741,6 +770,7 @@
PartitionUpdate* sys_ = nullptr;
PartitionUpdate* vnd_ = nullptr;
PartitionUpdate* prd_ = nullptr;
+ DynamicPartitionGroup* group_ = nullptr;
};
// Test full update flow executed by update_engine. Some partitions uses super empty space,
@@ -754,9 +784,17 @@
}
// Grow all partitions.
- SetSize(sys_, 3788_KiB);
- SetSize(vnd_, 3788_KiB);
- SetSize(prd_, 3788_KiB);
+ constexpr uint64_t partition_size = 3788_KiB;
+ SetSize(sys_, partition_size);
+ SetSize(vnd_, partition_size);
+ SetSize(prd_, partition_size);
+
+ // Create fake install operations to grow the COW device size.
+ for (auto& partition : {sys_, vnd_, prd_}) {
+ auto e = partition->add_operations()->add_dst_extents();
+ e->set_start_block(0);
+ e->set_num_blocks(GetSize(partition) / manifest_.block_size());
+ }
// Execute the update.
ASSERT_TRUE(sm->BeginUpdate());
@@ -919,6 +957,13 @@
ASSERT_TRUE(sm->BeginUpdate());
ASSERT_TRUE(sm->UnmapUpdateSnapshot("sys_b"));
+ // Create fake install operations to grow the COW device size.
+ for (auto& partition : {sys_, vnd_, prd_}) {
+ auto e = partition->add_operations()->add_dst_extents();
+ e->set_start_block(0);
+ e->set_num_blocks(GetSize(partition) / manifest_.block_size());
+ }
+
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
// Write some data to target partitions.
@@ -1032,6 +1077,149 @@
}
}
+TEST_F(SnapshotUpdateTest, RetrofitAfterRegularAb) {
+ constexpr auto kRetrofitGroupSize = kGroupSize / 2;
+
+ // Initialize device-mapper / disk
+ ASSERT_TRUE(UnmapAll());
+ FormatFakeSuper();
+
+ // Setup source partition metadata to have both _a and _b partitions.
+ src_ = MetadataBuilder::New(*opener_, "super", 0);
+ ASSERT_NE(nullptr, src_);
+ for (const auto& suffix : {"_a"s, "_b"s}) {
+ ASSERT_TRUE(src_->AddGroup(group_->name() + suffix, kRetrofitGroupSize));
+ for (const auto& name : {"sys"s, "vnd"s, "prd"s}) {
+ auto partition = src_->AddPartition(name + suffix, group_->name() + suffix, 0);
+ ASSERT_NE(nullptr, partition);
+ ASSERT_TRUE(src_->ResizePartition(partition, 2_MiB));
+ }
+ }
+ auto metadata = src_->Export();
+ ASSERT_NE(nullptr, metadata);
+ ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *metadata.get(), 0));
+
+ // Flash source partitions
+ std::string path;
+ for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
+ ASSERT_TRUE(CreateLogicalPartition(
+ CreateLogicalPartitionParams{
+ .block_device = fake_super,
+ .metadata_slot = 0,
+ .partition_name = name,
+ .timeout_ms = 1s,
+ .partition_opener = opener_.get(),
+ },
+ &path));
+ ASSERT_TRUE(WriteRandomData(path));
+ auto hash = GetHash(path);
+ ASSERT_TRUE(hash.has_value());
+ hashes_[name] = *hash;
+ }
+
+ // Setup manifest.
+ group_->set_size(kRetrofitGroupSize);
+ for (auto* partition : {sys_, vnd_, prd_}) {
+ SetSize(partition, 2_MiB);
+ auto* e = partition->add_operations()->add_dst_extents();
+ e->set_start_block(0);
+ e->set_num_blocks(2_MiB / manifest_.block_size());
+ }
+
+ ASSERT_TRUE(sm->BeginUpdate());
+ ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+
+ // Test that COW image should not be created for retrofit devices; super
+ // should be big enough.
+ ASSERT_FALSE(image_manager_->BackingImageExists("sys_b-cow-img"));
+ ASSERT_FALSE(image_manager_->BackingImageExists("vnd_b-cow-img"));
+ ASSERT_FALSE(image_manager_->BackingImageExists("prd_b-cow-img"));
+
+ // Write some data to target partitions.
+ for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+ std::string path;
+ ASSERT_TRUE(sm->MapUpdateSnapshot(
+ CreateLogicalPartitionParams{
+ .block_device = fake_super,
+ .metadata_slot = 1,
+ .partition_name = name,
+ .timeout_ms = 10s,
+ .partition_opener = opener_.get(),
+ },
+ &path))
+ << name;
+ ASSERT_TRUE(WriteRandomData(path));
+ auto hash = GetHash(path);
+ ASSERT_TRUE(hash.has_value());
+ hashes_[name] = *hash;
+ }
+
+ // Assert that source partitions aren't affected.
+ for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
+ ASSERT_TRUE(IsPartitionUnchanged(name));
+ }
+
+ ASSERT_TRUE(sm->FinishedSnapshotWrites());
+}
+
+class MetadataMountedTest : public SnapshotUpdateTest {
+ public:
+ void SetUp() override {
+ metadata_dir_ = test_device->GetMetadataDir();
+ ASSERT_TRUE(ReadDefaultFstab(&fstab_));
+ }
+ void TearDown() override {
+ SetUp();
+ // Remount /metadata
+ test_device->set_recovery(false);
+ EXPECT_TRUE(android::fs_mgr::EnsurePathMounted(&fstab_, metadata_dir_));
+ }
+ AssertionResult IsMetadataMounted() {
+ Fstab mounted_fstab;
+ if (!ReadFstabFromFile("/proc/mounts", &mounted_fstab)) {
+ ADD_FAILURE() << "Failed to scan mounted volumes";
+ return AssertionFailure() << "Failed to scan mounted volumes";
+ }
+
+ auto entry = GetEntryForPath(&fstab_, metadata_dir_);
+ if (entry == nullptr) {
+ return AssertionFailure() << "No mount point found in fstab for path " << metadata_dir_;
+ }
+
+ auto mv = GetEntryForMountPoint(&mounted_fstab, entry->mount_point);
+ if (mv == nullptr) {
+ return AssertionFailure() << metadata_dir_ << " is not mounted";
+ }
+ return AssertionSuccess() << metadata_dir_ << " is mounted";
+ }
+ std::string metadata_dir_;
+ Fstab fstab_;
+};
+
+TEST_F(MetadataMountedTest, Android) {
+ auto device = sm->EnsureMetadataMounted();
+ EXPECT_NE(nullptr, device);
+ device.reset();
+
+ EXPECT_TRUE(IsMetadataMounted());
+ EXPECT_TRUE(sm->CancelUpdate()) << "Metadata dir should never be unmounted in Android mode";
+}
+
+TEST_F(MetadataMountedTest, Recovery) {
+ test_device->set_recovery(true);
+ metadata_dir_ = test_device->GetMetadataDir();
+
+ EXPECT_TRUE(android::fs_mgr::EnsurePathUnmounted(&fstab_, metadata_dir_));
+ EXPECT_FALSE(IsMetadataMounted());
+
+ auto device = sm->EnsureMetadataMounted();
+ EXPECT_NE(nullptr, device);
+ EXPECT_TRUE(IsMetadataMounted());
+
+ device.reset();
+ EXPECT_FALSE(IsMetadataMounted());
+}
+
} // namespace snapshot
} // namespace android
@@ -1073,6 +1261,7 @@
}
// Clean up previous run.
+ MetadataMountedTest().TearDown();
SnapshotUpdateTest().Cleanup();
SnapshotTest().Cleanup();
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index 1a6a593..312fa3e 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -92,21 +92,14 @@
}
std::optional<std::string> GetHash(const std::string& path) {
- unique_fd fd(open(path.c_str(), O_RDONLY));
- char buf[4096];
+ std::string content;
+ if (!android::base::ReadFileToString(path, &content, true)) {
+ PLOG(ERROR) << "Cannot access " << path;
+ return std::nullopt;
+ }
SHA256_CTX ctx;
SHA256_Init(&ctx);
- while (true) {
- ssize_t n = TEMP_FAILURE_RETRY(read(fd.get(), buf, sizeof(buf)));
- if (n < 0) {
- PLOG(ERROR) << "Cannot read " << path;
- return std::nullopt;
- }
- if (n == 0) {
- break;
- }
- SHA256_Update(&ctx, buf, n);
- }
+ SHA256_Update(&ctx, content.c_str(), content.size());
uint8_t out[32];
SHA256_Final(out, &ctx);
return ToHexString(out, sizeof(out));
@@ -147,5 +140,9 @@
partition_update->mutable_new_partition_info()->set_size(size);
}
+uint64_t GetSize(PartitionUpdate* partition_update) {
+ return partition_update->mutable_new_partition_info()->size();
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/test_helpers.h
index 769d21e..0f70afe 100644
--- a/fs_mgr/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/test_helpers.h
@@ -17,6 +17,7 @@
#include <optional>
#include <string>
+#include <android/hardware/boot/1.1/IBootControl.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <libfiemap/image_manager.h>
@@ -32,6 +33,7 @@
using android::fs_mgr::IPropertyFetcher;
using android::fs_mgr::MetadataBuilder;
using android::fs_mgr::testing::MockPropertyFetcher;
+using android::hardware::boot::V1_1::MergeStatus;
using chromeos_update_engine::DeltaArchiveManifest;
using chromeos_update_engine::PartitionUpdate;
using testing::_;
@@ -81,16 +83,25 @@
const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const override {
return *opener_.get();
}
+ bool SetBootControlMergeStatus(MergeStatus status) override {
+ merge_status_ = status;
+ return true;
+ }
bool IsOverlayfsSetup() const override { return false; }
+ bool IsRecovery() const override { return recovery_; }
void set_slot_suffix(const std::string& suffix) { slot_suffix_ = suffix; }
void set_fake_super(const std::string& path) {
opener_ = std::make_unique<TestPartitionOpener>(path);
}
+ void set_recovery(bool value) { recovery_ = value; }
+ MergeStatus merge_status() const { return merge_status_; }
private:
std::string slot_suffix_ = "_a";
std::unique_ptr<TestPartitionOpener> opener_;
+ MergeStatus merge_status_;
+ bool recovery_ = false;
};
class SnapshotTestPropertyFetcher : public android::fs_mgr::testing::MockPropertyFetcher {
@@ -130,5 +141,8 @@
// In the update package metadata, set a partition with the given size.
void SetSize(PartitionUpdate* partition_update, uint64_t size);
+// Get partition size from update package metadata.
+uint64_t GetSize(PartitionUpdate* partition_update);
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index 66629e8..1b2f528 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -17,9 +17,16 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
+#include <fs_mgr/roots.h>
+using android::dm::kSectorSize;
+using android::fs_mgr::EnsurePathMounted;
+using android::fs_mgr::EnsurePathUnmounted;
+using android::fs_mgr::Fstab;
+using android::fs_mgr::GetEntryForPath;
using android::fs_mgr::MetadataBuilder;
using android::fs_mgr::Partition;
+using android::fs_mgr::ReadDefaultFstab;
namespace android {
namespace snapshot {
@@ -88,13 +95,11 @@
// so it can be used to resume the last state of a snapshot device;
// - an _INVALID_ snapshot otherwise.
// To avoid zero-filling the whole CoW file when a new dm-snapshot is
- // created, here we zero-fill only the first 32 bits. This is a temporary
- // workaround that will be discussed again when the kernel API gets
- // consolidated.
- // TODO(b/139202197): Remove this hack once the kernel API is consolidated.
- constexpr ssize_t kDmSnapZeroFillSize = 4; // 32-bit
+ // created, here we zero-fill only the first chunk to be compliant with
+ // lvm.
+ constexpr ssize_t kDmSnapZeroFillSize = kSectorSize * kSnapshotChunkSize;
- char zeros[kDmSnapZeroFillSize] = {0};
+ std::vector<uint8_t> zeros(kDmSnapZeroFillSize, 0);
android::base::unique_fd fd(open(device.c_str(), O_WRONLY | O_BINARY));
if (fd < 0) {
PLOG(ERROR) << "Can't open COW device: " << device;
@@ -102,12 +107,38 @@
}
LOG(INFO) << "Zero-filling COW device: " << device;
- if (!android::base::WriteFully(fd, zeros, kDmSnapZeroFillSize)) {
+ if (!android::base::WriteFully(fd, zeros.data(), kDmSnapZeroFillSize)) {
PLOG(ERROR) << "Can't zero-fill COW device for " << device;
return false;
}
return true;
}
+std::unique_ptr<AutoUnmountDevice> AutoUnmountDevice::New(const std::string& path) {
+ Fstab fstab;
+ if (!ReadDefaultFstab(&fstab)) {
+ LOG(ERROR) << "Cannot read default fstab";
+ return nullptr;
+ }
+
+ if (GetEntryForPath(&fstab, path) == nullptr) {
+ LOG(INFO) << "EnsureMetadataMounted can't find entry for " << path << ", skipping";
+ return std::unique_ptr<AutoUnmountDevice>(new AutoUnmountDevice("", {}));
+ }
+
+ if (!EnsurePathMounted(&fstab, path)) {
+ LOG(ERROR) << "Cannot mount " << path;
+ return nullptr;
+ }
+ return std::unique_ptr<AutoUnmountDevice>(new AutoUnmountDevice(path, std::move(fstab)));
+}
+
+AutoUnmountDevice::~AutoUnmountDevice() {
+ if (name_.empty()) return;
+ if (!EnsurePathUnmounted(&fstab_, name_)) {
+ LOG(ERROR) << "Cannot unmount " << name_;
+ }
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 75c694c..0c08ed2 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -18,27 +18,20 @@
#include <string>
#include <android-base/macros.h>
+#include <fstab/fstab.h>
#include <libdm/dm.h>
#include <libfiemap/image_manager.h>
#include <liblp/builder.h>
#include <libsnapshot/snapshot.h>
#include <update_engine/update_metadata.pb.h>
+#include <libsnapshot/auto_device.h>
+
namespace android {
namespace snapshot {
-struct AutoDevice {
- virtual ~AutoDevice(){};
- void Release();
-
- protected:
- AutoDevice(const std::string& name) : name_(name) {}
- std::string name_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(AutoDevice);
- AutoDevice(AutoDevice&& other) = delete;
-};
+// Unit is sectors, this is a 4K chunk.
+static constexpr uint32_t kSnapshotChunkSize = 8;
// A list of devices we created along the way.
// - Whenever a device is created that is subject to GC'ed at the end of
@@ -100,6 +93,18 @@
SnapshotManager::LockedFile* lock_ = nullptr;
};
+struct AutoUnmountDevice : AutoDevice {
+ // Empty object that does nothing.
+ AutoUnmountDevice() : AutoDevice("") {}
+ static std::unique_ptr<AutoUnmountDevice> New(const std::string& path);
+ ~AutoUnmountDevice();
+
+ private:
+ AutoUnmountDevice(const std::string& path, android::fs_mgr::Fstab&& fstab)
+ : AutoDevice(path), fstab_(std::move(fstab)) {}
+ android::fs_mgr::Fstab fstab_;
+};
+
// Return a list of partitions in |builder| with the name ending in |suffix|.
std::vector<android::fs_mgr::Partition*> ListPartitionsWithSuffix(
android::fs_mgr::MetadataBuilder* builder, const std::string& suffix);
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index 83668e9..4f6ec5a 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -13,8 +13,21 @@
// limitations under the License.
cc_test {
- name: "fs_mgr_unit_test",
- test_suites: ["device-tests"],
+ name: "CtsFsMgrTestCases",
+ test_suites: [
+ "cts",
+ "device-tests",
+ "vts",
+ ],
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
shared_libs: [
"libbase",
diff --git a/fs_mgr/tests/AndroidTest.xml b/fs_mgr/tests/AndroidTest.xml
new file mode 100644
index 0000000..91c3fb9
--- /dev/null
+++ b/fs_mgr/tests/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+<configuration description="Config for CTS fs_mgr test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="systems" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="CtsFsMgrTestCases->/data/local/tmp/CtsFsMgrTestCases" />
+ <option name="append-bitness" value="true" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="CtsFsMgrTestCases" />
+ <option name="runtime-hint" value="65s" />
+ </test>
+</configuration>
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index a7ea817..1cbaf45 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -179,6 +179,7 @@
{"nodiratime", MS_NODIRATIME},
{"ro", MS_RDONLY},
{"rw", 0},
+ {"sync", MS_SYNCHRONOUS},
{"remount", MS_REMOUNT},
{"bind", MS_BIND},
{"rec", MS_REC},
@@ -197,7 +198,7 @@
if (!(entry.flags & MS_RDONLY)) {
fs_options.emplace("rw");
}
- EXPECT_EQ(mnt_opts, fs_options);
+ EXPECT_EQ(mnt_opts, fs_options) << "At line " << i;
++i;
}
EXPECT_EQ(i, fstab.size());
@@ -420,8 +421,7 @@
EXPECT_EQ(0, entry->max_comp_streams);
EXPECT_EQ(0, entry->zram_size);
EXPECT_EQ(0, entry->reserved_size);
- EXPECT_EQ("", entry->file_contents_mode);
- EXPECT_EQ("", entry->file_names_mode);
+ EXPECT_EQ("", entry->encryption_options);
EXPECT_EQ(0, entry->erase_blk_size);
EXPECT_EQ(0, entry->logical_blk_size);
EXPECT_EQ("", entry->sysfs_path);
@@ -448,8 +448,7 @@
EXPECT_EQ(0, entry->max_comp_streams);
EXPECT_EQ(0, entry->zram_size);
EXPECT_EQ(0, entry->reserved_size);
- EXPECT_EQ("", entry->file_contents_mode);
- EXPECT_EQ("", entry->file_names_mode);
+ EXPECT_EQ("", entry->encryption_options);
EXPECT_EQ(0, entry->erase_blk_size);
EXPECT_EQ(0, entry->logical_blk_size);
EXPECT_EQ("", entry->sysfs_path);
@@ -458,16 +457,14 @@
EXPECT_EQ("", entry->zram_backing_dev_path);
entry++;
- // forcefdeorfbe sets file_contents_mode and file_names_mode by default, so test it separately.
+ // forcefdeorfbe has its own encryption_options defaults, so test it separately.
EXPECT_EQ("none2", entry->mount_point);
{
FstabEntry::FsMgrFlags flags = {};
flags.force_fde_or_fbe = true;
EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
}
- EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
- EXPECT_EQ("aes-256-cts", entry->file_names_mode);
- EXPECT_EQ(1, entry->file_policy_version);
+ EXPECT_EQ("aes-256-xts:aes-256-cts", entry->encryption_options);
EXPECT_EQ("", entry->key_loc);
}
@@ -681,37 +678,21 @@
EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
EXPECT_EQ("/dir/key", entry->key_loc);
- EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
- EXPECT_EQ("aes-256-cts", entry->file_names_mode);
- EXPECT_EQ(1, entry->file_policy_version);
+ EXPECT_EQ("aes-256-xts:aes-256-cts", entry->encryption_options);
}
TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_FileEncryption) {
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
std::string fstab_contents = R"fs(
-source none0 swap defaults fileencryption=blah
-source none1 swap defaults fileencryption=software
-source none2 swap defaults fileencryption=aes-256-xts
-source none3 swap defaults fileencryption=adiantum
-source none4 swap defaults fileencryption=adiantum:aes-256-heh
-source none5 swap defaults fileencryption=ice
-source none6 swap defaults fileencryption=ice:blah
-source none7 swap defaults fileencryption=ice:aes-256-cts
-source none8 swap defaults fileencryption=ice:aes-256-heh
-source none9 swap defaults fileencryption=ice:adiantum
-source none10 swap defaults fileencryption=aes-256-xts:aes-256-cts:v1
-source none11 swap defaults fileencryption=aes-256-xts:aes-256-cts:v2
-source none12 swap defaults fileencryption=aes-256-xts:aes-256-cts:v2:
-source none13 swap defaults fileencryption=aes-256-xts:aes-256-cts:blah
-source none14 swap defaults fileencryption=aes-256-xts:aes-256-cts:vblah
+source none0 swap defaults fileencryption=aes-256-xts:aes-256-cts:v1
)fs";
ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_EQ(15U, fstab.size());
+ ASSERT_EQ(1U, fstab.size());
FstabEntry::FsMgrFlags flags = {};
flags.file_encryption = true;
@@ -719,107 +700,7 @@
auto entry = fstab.begin();
EXPECT_EQ("none0", entry->mount_point);
EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("", entry->file_contents_mode);
- EXPECT_EQ("", entry->file_names_mode);
- EXPECT_EQ(0, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none1", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
- EXPECT_EQ("aes-256-cts", entry->file_names_mode);
- EXPECT_EQ(1, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none2", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
- EXPECT_EQ("aes-256-cts", entry->file_names_mode);
- EXPECT_EQ(1, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none3", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("adiantum", entry->file_contents_mode);
- EXPECT_EQ("adiantum", entry->file_names_mode);
- EXPECT_EQ(1, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none4", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("adiantum", entry->file_contents_mode);
- EXPECT_EQ("aes-256-heh", entry->file_names_mode);
- EXPECT_EQ(1, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none5", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("ice", entry->file_contents_mode);
- EXPECT_EQ("aes-256-cts", entry->file_names_mode);
- EXPECT_EQ(1, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none6", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("ice", entry->file_contents_mode);
- EXPECT_EQ("", entry->file_names_mode);
- EXPECT_EQ(0, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none7", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("ice", entry->file_contents_mode);
- EXPECT_EQ("aes-256-cts", entry->file_names_mode);
- EXPECT_EQ(1, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none8", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("ice", entry->file_contents_mode);
- EXPECT_EQ("aes-256-heh", entry->file_names_mode);
- EXPECT_EQ(1, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none9", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("ice", entry->file_contents_mode);
- EXPECT_EQ("adiantum", entry->file_names_mode);
- EXPECT_EQ(1, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none10", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
- EXPECT_EQ("aes-256-cts", entry->file_names_mode);
- EXPECT_EQ(1, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none11", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
- EXPECT_EQ("aes-256-cts", entry->file_names_mode);
- EXPECT_EQ(2, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none12", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("", entry->file_contents_mode);
- EXPECT_EQ("", entry->file_names_mode);
- EXPECT_EQ(0, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none13", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
- EXPECT_EQ("aes-256-cts", entry->file_names_mode);
- EXPECT_EQ(0, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none14", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
- EXPECT_EQ("aes-256-cts", entry->file_names_mode);
- EXPECT_EQ(0, entry->file_policy_version);
+ EXPECT_EQ("aes-256-xts:aes-256-cts:v1", entry->encryption_options);
}
TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_MaxCompStreams) {
@@ -1081,3 +962,10 @@
EXPECT_EQ("none5", entry->mount_point);
EXPECT_EQ("/dev/path2", entry->zram_backing_dev_path);
}
+
+TEST(fs_mgr, DefaultFstabContainsUserdata) {
+ Fstab fstab;
+ ASSERT_TRUE(ReadDefaultFstab(&fstab)) << "Failed to read default fstab";
+ ASSERT_NE(nullptr, GetEntryForMountPoint(&fstab, "/data"))
+ << "Default fstab doesn't contain /data entry";
+}
diff --git a/healthd/Android.bp b/healthd/Android.bp
index 4f89bfb..e04f70f 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -17,6 +17,10 @@
shared_libs: [
"libutils",
"libbase",
+
+ // Need latest HealthInfo definition from headers of this shared
+ // library. Clients don't need to link to this.
+ "android.hardware.health@2.1",
],
header_libs: ["libhealthd_headers"],
export_header_lib_headers: ["libhealthd_headers"],
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 06c8176..9e168e9 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -29,10 +29,12 @@
#include <algorithm>
#include <memory>
+#include <optional>
#include <android-base/file.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
+#include <android/hardware/health/2.1/types.h>
#include <batteryservice/BatteryService.h>
#include <cutils/klog.h>
#include <cutils/properties.h>
@@ -47,97 +49,93 @@
#define MILLION 1.0e6
#define DEFAULT_VBUS_VOLTAGE 5000000
+using HealthInfo_1_0 = android::hardware::health::V1_0::HealthInfo;
+using HealthInfo_2_0 = android::hardware::health::V2_0::HealthInfo;
+using HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo;
+using android::hardware::health::V1_0::BatteryHealth;
+using android::hardware::health::V1_0::BatteryStatus;
+using android::hardware::health::V2_1::BatteryCapacityLevel;
+
namespace android {
-struct sysfsStringEnumMap {
+template <typename T>
+struct SysfsStringEnumMap {
const char* s;
- int val;
+ T val;
};
-static int mapSysfsString(const char* str,
- struct sysfsStringEnumMap map[]) {
+template <typename T>
+static std::optional<T> mapSysfsString(const char* str, SysfsStringEnumMap<T> map[]) {
for (int i = 0; map[i].s; i++)
if (!strcmp(str, map[i].s))
return map[i].val;
- return -1;
-}
-
-static void initBatteryProperties(BatteryProperties* props) {
- props->chargerAcOnline = false;
- props->chargerUsbOnline = false;
- props->chargerWirelessOnline = false;
- props->maxChargingCurrent = 0;
- props->maxChargingVoltage = 0;
- props->batteryStatus = BATTERY_STATUS_UNKNOWN;
- props->batteryHealth = BATTERY_HEALTH_UNKNOWN;
- props->batteryPresent = false;
- props->batteryLevel = 0;
- props->batteryVoltage = 0;
- props->batteryTemperature = 0;
- props->batteryCurrent = 0;
- props->batteryCycleCount = 0;
- props->batteryFullCharge = 0;
- props->batteryChargeCounter = 0;
- props->batteryTechnology.clear();
+ return std::nullopt;
}
BatteryMonitor::BatteryMonitor()
: mHealthdConfig(nullptr),
mBatteryDevicePresent(false),
mBatteryFixedCapacity(0),
- mBatteryFixedTemperature(0) {
- initBatteryProperties(&props);
+ mBatteryFixedTemperature(0),
+ mHealthInfo(std::make_unique<HealthInfo_2_1>()) {}
+
+BatteryMonitor::~BatteryMonitor() {}
+
+const HealthInfo_1_0& BatteryMonitor::getHealthInfo_1_0() const {
+ return getHealthInfo_2_0().legacy;
}
-struct BatteryProperties getBatteryProperties(BatteryMonitor* batteryMonitor) {
- return batteryMonitor->props;
+const HealthInfo_2_0& BatteryMonitor::getHealthInfo_2_0() const {
+ return getHealthInfo_2_1().legacy;
}
-int BatteryMonitor::getBatteryStatus(const char* status) {
- int ret;
- struct sysfsStringEnumMap batteryStatusMap[] = {
- { "Unknown", BATTERY_STATUS_UNKNOWN },
- { "Charging", BATTERY_STATUS_CHARGING },
- { "Discharging", BATTERY_STATUS_DISCHARGING },
- { "Not charging", BATTERY_STATUS_NOT_CHARGING },
- { "Full", BATTERY_STATUS_FULL },
- { NULL, 0 },
+const HealthInfo_2_1& BatteryMonitor::getHealthInfo_2_1() const {
+ return *mHealthInfo;
+}
+
+BatteryStatus getBatteryStatus(const char* status) {
+ static SysfsStringEnumMap<BatteryStatus> batteryStatusMap[] = {
+ {"Unknown", BatteryStatus::UNKNOWN},
+ {"Charging", BatteryStatus::CHARGING},
+ {"Discharging", BatteryStatus::DISCHARGING},
+ {"Not charging", BatteryStatus::NOT_CHARGING},
+ {"Full", BatteryStatus::FULL},
+ {NULL, BatteryStatus::UNKNOWN},
};
- ret = mapSysfsString(status, batteryStatusMap);
- if (ret < 0) {
+ auto ret = mapSysfsString(status, batteryStatusMap);
+ if (!ret) {
KLOG_WARNING(LOG_TAG, "Unknown battery status '%s'\n", status);
- ret = BATTERY_STATUS_UNKNOWN;
+ *ret = BatteryStatus::UNKNOWN;
}
- return ret;
+ return *ret;
}
-int BatteryMonitor::getBatteryHealth(const char* status) {
- int ret;
- struct sysfsStringEnumMap batteryHealthMap[] = {
- { "Unknown", BATTERY_HEALTH_UNKNOWN },
- { "Good", BATTERY_HEALTH_GOOD },
- { "Overheat", BATTERY_HEALTH_OVERHEAT },
- { "Dead", BATTERY_HEALTH_DEAD },
- { "Over voltage", BATTERY_HEALTH_OVER_VOLTAGE },
- { "Unspecified failure", BATTERY_HEALTH_UNSPECIFIED_FAILURE },
- { "Cold", BATTERY_HEALTH_COLD },
- // battery health values from JEITA spec
- { "Warm", BATTERY_HEALTH_GOOD },
- { "Cool", BATTERY_HEALTH_GOOD },
- { "Hot", BATTERY_HEALTH_OVERHEAT },
- { NULL, 0 },
+BatteryHealth getBatteryHealth(const char* status) {
+ static SysfsStringEnumMap<BatteryHealth> batteryHealthMap[] = {
+ {"Unknown", BatteryHealth::UNKNOWN},
+ {"Good", BatteryHealth::GOOD},
+ {"Overheat", BatteryHealth::OVERHEAT},
+ {"Dead", BatteryHealth::DEAD},
+ {"Over voltage", BatteryHealth::OVER_VOLTAGE},
+ {"Unspecified failure", BatteryHealth::UNSPECIFIED_FAILURE},
+ {"Cold", BatteryHealth::COLD},
+ // battery health values from JEITA spec
+ {"Warm", BatteryHealth::GOOD},
+ {"Cool", BatteryHealth::GOOD},
+ {"Hot", BatteryHealth::OVERHEAT},
+ {NULL, BatteryHealth::UNKNOWN},
};
- ret = mapSysfsString(status, batteryHealthMap);
- if (ret < 0) {
+ auto ret = mapSysfsString(status, batteryHealthMap);
+ if (!ret) {
KLOG_WARNING(LOG_TAG, "Unknown battery health '%s'\n", status);
- ret = BATTERY_HEALTH_UNKNOWN;
+ *ret = BatteryHealth::UNKNOWN;
}
- return ret;
+ return *ret;
}
int BatteryMonitor::readFromFile(const String8& path, std::string* buf) {
@@ -148,35 +146,34 @@
}
BatteryMonitor::PowerSupplyType BatteryMonitor::readPowerSupplyType(const String8& path) {
- std::string buf;
- int ret;
- struct sysfsStringEnumMap supplyTypeMap[] = {
- { "Unknown", ANDROID_POWER_SUPPLY_TYPE_UNKNOWN },
- { "Battery", ANDROID_POWER_SUPPLY_TYPE_BATTERY },
- { "UPS", ANDROID_POWER_SUPPLY_TYPE_AC },
- { "Mains", ANDROID_POWER_SUPPLY_TYPE_AC },
- { "USB", ANDROID_POWER_SUPPLY_TYPE_USB },
- { "USB_DCP", ANDROID_POWER_SUPPLY_TYPE_AC },
- { "USB_HVDCP", ANDROID_POWER_SUPPLY_TYPE_AC },
- { "USB_CDP", ANDROID_POWER_SUPPLY_TYPE_AC },
- { "USB_ACA", ANDROID_POWER_SUPPLY_TYPE_AC },
- { "USB_C", ANDROID_POWER_SUPPLY_TYPE_AC },
- { "USB_PD", ANDROID_POWER_SUPPLY_TYPE_AC },
- { "USB_PD_DRP", ANDROID_POWER_SUPPLY_TYPE_USB },
- { "Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS },
- { NULL, 0 },
+ static SysfsStringEnumMap<int> supplyTypeMap[] = {
+ {"Unknown", ANDROID_POWER_SUPPLY_TYPE_UNKNOWN},
+ {"Battery", ANDROID_POWER_SUPPLY_TYPE_BATTERY},
+ {"UPS", ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"Mains", ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB", ANDROID_POWER_SUPPLY_TYPE_USB},
+ {"USB_DCP", ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_HVDCP", ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_CDP", ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_ACA", ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_C", ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_PD", ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_PD_DRP", ANDROID_POWER_SUPPLY_TYPE_USB},
+ {"Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS},
+ {NULL, 0},
};
+ std::string buf;
if (readFromFile(path, &buf) <= 0)
return ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
- ret = mapSysfsString(buf.c_str(), supplyTypeMap);
+ auto ret = mapSysfsString(buf.c_str(), supplyTypeMap);
if (ret < 0) {
KLOG_WARNING(LOG_TAG, "Unknown power supply type '%s'\n", buf.c_str());
- ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
+ *ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
}
- return static_cast<BatteryMonitor::PowerSupplyType>(ret);
+ return static_cast<BatteryMonitor::PowerSupplyType>(*ret);
}
bool BatteryMonitor::getBooleanField(const String8& path) {
@@ -200,10 +197,10 @@
return value;
}
-bool BatteryMonitor::update(void) {
- bool logthis;
+void BatteryMonitor::updateValues(void) {
+ *mHealthInfo = HealthInfo_2_1{};
- initBatteryProperties(&props);
+ HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
if (!mHealthdConfig->batteryPresentPath.isEmpty())
props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
@@ -227,6 +224,15 @@
if (!mHealthdConfig->batteryChargeCounterPath.isEmpty())
props.batteryChargeCounter = getIntField(mHealthdConfig->batteryChargeCounterPath);
+ if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty())
+ mHealthInfo->legacy.batteryCurrentAverage =
+ getIntField(mHealthdConfig->batteryCurrentAvgPath);
+
+ // TODO(b/142260281): Retrieve these values correctly.
+ mHealthInfo->batteryCapacityLevel = BatteryCapacityLevel::UNKNOWN;
+ mHealthInfo->batteryChargeTimeToFullNowSeconds = 0;
+ mHealthInfo->batteryFullCapacityUah = props.batteryFullCharge;
+
props.batteryTemperature = mBatteryFixedTemperature ?
mBatteryFixedTemperature :
getIntField(mHealthdConfig->batteryTemperaturePath);
@@ -289,62 +295,58 @@
}
}
}
+}
- logthis = !healthd_board_battery_update(&props);
+void BatteryMonitor::logValues(void) {
+ char dmesgline[256];
+ size_t len;
+ const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
+ if (props.batteryPresent) {
+ snprintf(dmesgline, sizeof(dmesgline), "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
+ props.batteryLevel, props.batteryVoltage, props.batteryTemperature < 0 ? "-" : "",
+ abs(props.batteryTemperature / 10), abs(props.batteryTemperature % 10),
+ props.batteryHealth, props.batteryStatus);
- if (logthis) {
- char dmesgline[256];
- size_t len;
- if (props.batteryPresent) {
- snprintf(dmesgline, sizeof(dmesgline),
- "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
- props.batteryLevel, props.batteryVoltage,
- props.batteryTemperature < 0 ? "-" : "",
- abs(props.batteryTemperature / 10),
- abs(props.batteryTemperature % 10), props.batteryHealth,
- props.batteryStatus);
-
- len = strlen(dmesgline);
- if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
- len += snprintf(dmesgline + len, sizeof(dmesgline) - len,
- " c=%d", props.batteryCurrent);
- }
-
- if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
- len += snprintf(dmesgline + len, sizeof(dmesgline) - len,
- " fc=%d", props.batteryFullCharge);
- }
-
- if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
- len += snprintf(dmesgline + len, sizeof(dmesgline) - len,
- " cc=%d", props.batteryCycleCount);
- }
- } else {
- len = snprintf(dmesgline, sizeof(dmesgline),
- "battery none");
+ len = strlen(dmesgline);
+ if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " c=%d",
+ props.batteryCurrent);
}
- snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s",
- props.chargerAcOnline ? "a" : "",
- props.chargerUsbOnline ? "u" : "",
- props.chargerWirelessOnline ? "w" : "");
+ if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
+ len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " fc=%d",
+ props.batteryFullCharge);
+ }
- KLOG_WARNING(LOG_TAG, "%s\n", dmesgline);
+ if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+ len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " cc=%d",
+ props.batteryCycleCount);
+ }
+ } else {
+ len = snprintf(dmesgline, sizeof(dmesgline), "battery none");
}
- healthd_mode_ops->battery_update(&props);
+ snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s",
+ props.chargerAcOnline ? "a" : "", props.chargerUsbOnline ? "u" : "",
+ props.chargerWirelessOnline ? "w" : "");
+
+ KLOG_WARNING(LOG_TAG, "%s\n", dmesgline);
+}
+
+bool BatteryMonitor::isChargerOnline() {
+ const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
return props.chargerAcOnline | props.chargerUsbOnline |
props.chargerWirelessOnline;
}
int BatteryMonitor::getChargeStatus() {
- int result = BATTERY_STATUS_UNKNOWN;
+ BatteryStatus result = BatteryStatus::UNKNOWN;
if (!mHealthdConfig->batteryStatusPath.isEmpty()) {
std::string buf;
if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
result = getBatteryStatus(buf.c_str());
}
- return result;
+ return static_cast<int>(result);
}
status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {
@@ -417,6 +419,7 @@
void BatteryMonitor::dumpState(int fd) {
int v;
char vs[128];
+ const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d current_max: %d voltage_max: %d\n",
props.chargerAcOnline, props.chargerUsbOnline,
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index 4d1d53f..d41a374 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -17,6 +17,8 @@
#ifndef HEALTHD_BATTERYMONITOR_H
#define HEALTHD_BATTERYMONITOR_H
+#include <memory>
+
#include <batteryservice/BatteryService.h>
#include <utils/String8.h>
#include <utils/Vector.h>
@@ -24,6 +26,19 @@
#include <healthd/healthd.h>
namespace android {
+namespace hardware {
+namespace health {
+namespace V1_0 {
+struct HealthInfo;
+} // namespace V1_0
+namespace V2_0 {
+struct HealthInfo;
+} // namespace V2_0
+namespace V2_1 {
+struct HealthInfo;
+} // namespace V2_1
+} // namespace health
+} // namespace hardware
class BatteryMonitor {
public:
@@ -37,12 +52,19 @@
};
BatteryMonitor();
+ ~BatteryMonitor();
void init(struct healthd_config *hc);
- bool update(void);
int getChargeStatus();
status_t getProperty(int id, struct BatteryProperty *val);
void dumpState(int fd);
- friend struct BatteryProperties getBatteryProperties(BatteryMonitor* batteryMonitor);
+
+ const android::hardware::health::V1_0::HealthInfo& getHealthInfo_1_0() const;
+ const android::hardware::health::V2_0::HealthInfo& getHealthInfo_2_0() const;
+ const android::hardware::health::V2_1::HealthInfo& getHealthInfo_2_1() const;
+
+ void updateValues(void);
+ void logValues(void);
+ bool isChargerOnline();
private:
struct healthd_config *mHealthdConfig;
@@ -50,10 +72,8 @@
bool mBatteryDevicePresent;
int mBatteryFixedCapacity;
int mBatteryFixedTemperature;
- struct BatteryProperties props;
+ std::unique_ptr<android::hardware::health::V2_1::HealthInfo> mHealthInfo;
- int getBatteryStatus(const char* status);
- int getBatteryHealth(const char* status);
int readFromFile(const String8& path, std::string* buf);
PowerSupplyType readPowerSupplyType(const String8& path);
bool getBooleanField(const String8& path);
diff --git a/init/Android.bp b/init/Android.bp
index b601075..776a3a6 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -69,21 +69,18 @@
"libprotobuf-cpp-lite",
"libpropertyinfoserializer",
"libpropertyinfoparser",
- "libsnapshot_nobinder",
+ "libsnapshot_init",
],
shared_libs: [
"libbacktrace",
"libbase",
"libbootloader_message",
"libcutils",
- "libcrypto",
"libdl",
"libext4_utils",
"libfs_mgr",
- "libfscrypt",
"libgsi",
"libhidl-gen-utils",
- "libjsoncpp",
"libkeyutils",
"liblog",
"liblogwrap",
@@ -287,13 +284,11 @@
shared_libs: [
"libcutils",
"libhidl-gen-utils",
+ "libhidlmetadata",
"liblog",
"libprocessgroup",
"libprotobuf-cpp-lite",
],
- header_libs: [
- "libjsoncpp_headers",
- ],
srcs: [
"action.cpp",
"action_manager.cpp",
diff --git a/init/Android.mk b/init/Android.mk
index 8fc44da..997b2bc 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -91,7 +91,6 @@
libsquashfs_utils \
liblogwrap \
libext4_utils \
- libfscrypt \
libcrypto_utils \
libsparse \
libavb \
@@ -114,7 +113,7 @@
libmodprobe \
libext2_uuid \
libprotobuf-cpp-lite \
- libsnapshot_nobinder \
+ libsnapshot_init \
LOCAL_SANITIZE := signed-integer-overflow
# First stage init is weird: it may start without stdout/stderr, and no /proc.
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
index 9736824..a8e1e09 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -16,11 +16,14 @@
#include "action_parser.h"
+#include <ctype.h>
+
#include <android-base/properties.h>
#include <android-base/strings.h>
#if defined(__ANDROID__)
#include "property_service.h"
+#include "selinux.h"
#else
#include "host_init_stubs.h"
#endif
@@ -77,6 +80,17 @@
return {};
}
+Result<void> ValidateEventTrigger(const std::string& event_trigger) {
+ if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {
+ for (const char& c : event_trigger) {
+ if (c != '_' && c != '-' && !std::isalnum(c)) {
+ return Error() << "Illegal character '" << c << "' in '" << event_trigger << "'";
+ }
+ }
+ }
+ return {};
+}
+
Result<void> ParseTriggers(const std::vector<std::string>& args, Subcontext* subcontext,
std::string* event_trigger,
std::map<std::string, std::string>* property_triggers) {
@@ -103,6 +117,9 @@
if (!event_trigger->empty()) {
return Error() << "multiple event triggers are not allowed";
}
+ if (auto result = ValidateEventTrigger(args[i]); !result) {
+ return result;
+ }
*event_trigger = args[i];
}
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 42211b2..b2c6461 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -42,6 +42,8 @@
#include <sys/wait.h>
#include <unistd.h>
+#include <memory>
+
#include <ApexProperties.sysprop.h>
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
@@ -138,7 +140,14 @@
if (!write_bootloader_message(options, &err)) {
return Error() << "Failed to set bootloader message: " << err;
}
- property_set("sys.powerctl", "reboot,recovery");
+ // This function should only be reached from init and not from vendor_init, and we want to
+ // immediately trigger reboot instead of relaying through property_service. Older devices may
+ // still have paths that reach here from vendor_init, so we keep the property_set as a fallback.
+ if (getpid() == 1) {
+ TriggerShutdown("reboot,recovery");
+ } else {
+ property_set("sys.powerctl", "reboot,recovery");
+ }
return {};
}
@@ -620,6 +629,8 @@
return Error() << "Invalid code: " << code;
}
+static int initial_mount_fstab_return_code = -1;
+
/* mount_all <fstab> [ <path> ]* [--<options>]*
*
* This function might request a reboot, in which case it will
@@ -655,6 +666,7 @@
if (!ReadFstabFromFile(fstab_file, &fstab)) {
return Error() << "Could not read fstab";
}
+
auto mount_fstab_return_code = fs_mgr_mount_all(&fstab, mount_mode);
property_set(prop_name, std::to_string(t.duration().count()));
@@ -666,6 +678,7 @@
if (queue_event) {
/* queue_fs_event will queue event based on mount_fstab return code
* and return processed return code*/
+ initial_mount_fstab_return_code = mount_fstab_return_code;
auto queue_fs_result = queue_fs_event(mount_fstab_return_code);
if (!queue_fs_result) {
return Error() << "queue_fs_event() failed: " << queue_fs_result.error();
@@ -1125,6 +1138,25 @@
return ExecWithFunctionOnFailure(args, reboot);
}
+static Result<void> do_remount_userdata(const BuiltinArguments& args) {
+ if (initial_mount_fstab_return_code == -1) {
+ return Error() << "Calling remount_userdata too early";
+ }
+ Fstab fstab;
+ if (!ReadDefaultFstab(&fstab)) {
+ // TODO(b/135984674): should we reboot here?
+ return Error() << "Failed to read fstab";
+ }
+ // TODO(b/135984674): check that fstab contains /data.
+ if (auto rc = fs_mgr_remount_userdata_into_checkpointing(&fstab); rc < 0) {
+ TriggerShutdown("reboot,mount-userdata-failed");
+ }
+ if (auto result = queue_fs_event(initial_mount_fstab_return_code); !result) {
+ return Error() << "queue_fs_event() failed: " << result.error();
+ }
+ return {};
+}
+
static Result<void> do_installkey(const BuiltinArguments& args) {
if (!is_file_crypto()) return {};
@@ -1236,6 +1268,7 @@
{"umount", {1, 1, {false, do_umount}}},
{"umount_all", {1, 1, {false, do_umount_all}}},
{"readahead", {1, 2, {true, do_readahead}}},
+ {"remount_userdata", {0, 0, {false, do_remount_userdata}}},
{"restart", {1, 1, {false, do_restart}}},
{"restorecon", {1, kMax, {true, do_restorecon}}},
{"restorecon_recursive", {1, kMax, {true, do_restorecon_recursive}}},
diff --git a/init/fscrypt_init_extensions.cpp b/init/fscrypt_init_extensions.cpp
index bbebbe8..5fa07dd 100644
--- a/init/fscrypt_init_extensions.cpp
+++ b/init/fscrypt_init_extensions.cpp
@@ -39,6 +39,8 @@
#define TAG "fscrypt"
+using namespace android::fscrypt;
+
static int set_policy_on(const std::string& ref_basename, const std::string& dir);
int fscrypt_install_keyring() {
@@ -164,32 +166,12 @@
return err;
}
-static int parse_encryption_options_string(const std::string& options_string,
- std::string* contents_mode_ret,
- std::string* filenames_mode_ret,
- int* policy_version_ret) {
- auto parts = android::base::Split(options_string, ":");
-
- if (parts.size() != 3) {
- return -1;
- }
-
- *contents_mode_ret = parts[0];
- *filenames_mode_ret = parts[1];
- if (!android::base::StartsWith(parts[2], 'v') ||
- !android::base::ParseInt(&parts[2][1], policy_version_ret)) {
- return -1;
- }
-
- return 0;
-}
-
// Set an encryption policy on the given directory. The policy (key reference
// and encryption options) to use is read from files that were written by vold.
static int set_policy_on(const std::string& ref_basename, const std::string& dir) {
+ EncryptionPolicy policy;
std::string ref_filename = std::string("/data") + ref_basename;
- std::string key_ref;
- if (!android::base::ReadFileToString(ref_filename, &key_ref)) {
+ if (!android::base::ReadFileToString(ref_filename, &policy.key_raw_ref)) {
LOG(ERROR) << "Unable to read system policy to set on " << dir;
return -1;
}
@@ -200,24 +182,15 @@
LOG(ERROR) << "Cannot read encryption options string";
return -1;
}
-
- std::string contents_mode;
- std::string filenames_mode;
- int policy_version = 0;
-
- if (parse_encryption_options_string(options_string, &contents_mode, &filenames_mode,
- &policy_version)) {
+ if (!ParseOptions(options_string, &policy.options)) {
LOG(ERROR) << "Invalid encryption options string: " << options_string;
return -1;
}
- int result =
- fscrypt_policy_ensure(dir.c_str(), key_ref.c_str(), key_ref.length(),
- contents_mode.c_str(), filenames_mode.c_str(), policy_version);
- if (result) {
- LOG(ERROR) << android::base::StringPrintf("Setting %02x%02x%02x%02x policy on %s failed!",
- key_ref[0], key_ref[1], key_ref[2], key_ref[3],
- dir.c_str());
+ if (!EnsurePolicy(policy, dir)) {
+ std::string ref_hex;
+ BytesToHex(policy.key_raw_ref, &ref_hex);
+ LOG(ERROR) << "Setting " << ref_hex << " policy on " << dir << " failed!";
return -1;
}
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index e3068b2..5dd5cf1 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -35,7 +35,7 @@
namespace init {
// init.h
-inline void EnterShutdown(const std::string&) {
+inline void TriggerShutdown(const std::string&) {
abort();
}
@@ -55,7 +55,7 @@
// reboot_utils.h
inline void SetFatalRebootTarget() {}
-inline void __attribute__((noreturn)) InitFatalReboot() {
+inline void __attribute__((noreturn)) InitFatalReboot(int signal_number) {
abort();
}
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index b2402b3..522709e 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -30,6 +30,7 @@
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
+#include <hidl/metadata.h>
#include "action.h"
#include "action_manager.h"
@@ -142,28 +143,46 @@
#include "generated_stub_builtin_function_map.h"
void PrintUsage() {
- std::cout << "usage: host_init_verifier [-p FILE] -i FILE <init rc file>\n"
+ std::cout << "usage: host_init_verifier [-p FILE] <init rc file>\n"
"\n"
"Tests an init script for correctness\n"
"\n"
"-p FILE\tSearch this passwd file for users and groups\n"
- "-i FILE\tParse this JSON file for the HIDL interface inheritance hierarchy\n"
<< std::endl;
}
+Result<InterfaceInheritanceHierarchyMap> ReadInterfaceInheritanceHierarchy() {
+ InterfaceInheritanceHierarchyMap result;
+ for (const HidlInterfaceMetadata& iface : HidlInterfaceMetadata::all()) {
+ std::set<FQName> inherited_interfaces;
+ for (const std::string& intf : iface.inherited) {
+ FQName fqname;
+ if (!fqname.setTo(intf)) {
+ return Error() << "Unable to parse interface '" << intf << "'";
+ }
+ inherited_interfaces.insert(fqname);
+ }
+ FQName fqname;
+ if (!fqname.setTo(iface.name)) {
+ return Error() << "Unable to parse interface '" << iface.name << "'";
+ }
+ result[fqname] = inherited_interfaces;
+ }
+
+ return result;
+}
+
int main(int argc, char** argv) {
android::base::InitLogging(argv, &android::base::StdioLogger);
android::base::SetMinimumLogSeverity(android::base::ERROR);
- std::string interface_inheritance_hierarchy_file;
-
while (true) {
static const struct option long_options[] = {
{"help", no_argument, nullptr, 'h'},
{nullptr, 0, nullptr, 0},
};
- int arg = getopt_long(argc, argv, "p:i:", long_options, nullptr);
+ int arg = getopt_long(argc, argv, "p:", long_options, nullptr);
if (arg == -1) {
break;
@@ -176,9 +195,6 @@
case 'p':
passwd_files.emplace_back(optarg);
break;
- case 'i':
- interface_inheritance_hierarchy_file = optarg;
- break;
default:
std::cerr << "getprop: getopt returned invalid result: " << arg << std::endl;
return EXIT_FAILURE;
@@ -188,13 +204,12 @@
argc -= optind;
argv += optind;
- if (argc != 1 || interface_inheritance_hierarchy_file.empty()) {
+ if (argc != 1) {
PrintUsage();
return EXIT_FAILURE;
}
- auto interface_inheritance_hierarchy_map =
- ReadInterfaceInheritanceHierarchy(interface_inheritance_hierarchy_file);
+ auto interface_inheritance_hierarchy_map = ReadInterfaceInheritanceHierarchy();
if (!interface_inheritance_hierarchy_map) {
LOG(ERROR) << interface_inheritance_hierarchy_map.error();
return EXIT_FAILURE;
diff --git a/init/init.cpp b/init/init.cpp
index ad31fa0..f775d8f 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -98,7 +98,6 @@
static std::unique_ptr<Timer> waiting_for_prop(nullptr);
static std::string wait_prop_name;
static std::string wait_prop_value;
-static bool shutting_down;
static std::string shutdown_command;
static bool do_shutdown = false;
static bool load_debug_prop = false;
@@ -180,7 +179,7 @@
waiting_for_prop.reset();
}
-void EnterShutdown(const std::string& command) {
+void TriggerShutdown(const std::string& command) {
// We can't call HandlePowerctlMessage() directly in this function,
// because it modifies the contents of the action queue, which can cause the action queue
// to get into a bad state if this function is called from a command being executed by the
@@ -198,7 +197,7 @@
// In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
// commands to be executed.
if (name == "sys.powerctl") {
- EnterShutdown(value);
+ TriggerShutdown(value);
}
if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
@@ -624,7 +623,15 @@
auto init_message = InitMessage{};
init_message.set_stop_sending_messages(true);
if (auto result = SendMessage(property_fd, init_message); !result) {
- LOG(ERROR) << "Failed to send load persistent properties message: " << result.error();
+ LOG(ERROR) << "Failed to send 'stop sending messages' message: " << result.error();
+ }
+}
+
+void SendStartSendingMessagesMessage() {
+ auto init_message = InitMessage{};
+ init_message.set_start_sending_messages(true);
+ if (auto result = SendMessage(property_fd, init_message); !result) {
+ LOG(ERROR) << "Failed to send 'start sending messages' message: " << result.error();
}
}
@@ -811,18 +818,16 @@
// By default, sleep until something happens.
auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
- if (do_shutdown && !shutting_down) {
+ if (do_shutdown && !IsShuttingDown()) {
do_shutdown = false;
- if (HandlePowerctlMessage(shutdown_command)) {
- shutting_down = true;
- }
+ HandlePowerctlMessage(shutdown_command);
}
if (!(waiting_for_prop || Service::is_exec_service_running())) {
am.ExecuteOneCommand();
}
if (!(waiting_for_prop || Service::is_exec_service_running())) {
- if (!shutting_down) {
+ if (!IsShuttingDown()) {
auto next_process_action_time = HandleProcessActions();
// If there's a process that needs restarting, wake up in time for that.
diff --git a/init/init.h b/init/init.h
index 61fb110..d884a94 100644
--- a/init/init.h
+++ b/init/init.h
@@ -31,7 +31,7 @@
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
Parser CreateServiceOnlyParser(ServiceList& service_list);
-void EnterShutdown(const std::string& command);
+void TriggerShutdown(const std::string& command);
bool start_waiting_for_property(const char *name, const char *value);
@@ -41,6 +41,7 @@
void SendLoadPersistentPropertiesMessage();
void SendStopSendingMessagesMessage();
+void SendStartSendingMessagesMessage();
int SecondStageMain(int argc, char** argv);
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 315d584..9f63e4f 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -93,6 +93,26 @@
EXPECT_TRUE(expect_true);
}
+TEST(init, WrongEventTrigger) {
+ std::string init_script =
+ R"init(
+on boot:
+pass_test
+)init";
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));
+
+ ActionManager am;
+
+ Parser parser;
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
+
+ ASSERT_TRUE(parser.ParseConfig(tf.path));
+ ASSERT_EQ(1u, parser.parse_error_count());
+}
+
TEST(init, EventTriggerOrder) {
std::string init_script =
R"init(
diff --git a/init/interface_utils.cpp b/init/interface_utils.cpp
index ddbacd7..1b76bba 100644
--- a/init/interface_utils.cpp
+++ b/init/interface_utils.cpp
@@ -21,7 +21,6 @@
#include <android-base/strings.h>
#include <hidl-util/FqInstance.h>
-#include <json/json.h>
using android::FqInstance;
using android::FQName;
@@ -42,37 +41,6 @@
} // namespace
-Result<InterfaceInheritanceHierarchyMap> ReadInterfaceInheritanceHierarchy(
- const std::string& path) {
- Json::Value root;
- Json::Reader reader;
- std::ifstream stream(path);
- if (!reader.parse(stream, root)) {
- return Error() << "Failed to read interface inheritance hierarchy file: " << path << "\n"
- << reader.getFormattedErrorMessages();
- }
-
- InterfaceInheritanceHierarchyMap result;
- for (const Json::Value& entry : root) {
- std::set<FQName> inherited_interfaces;
- for (const Json::Value& intf : entry["inheritedInterfaces"]) {
- FQName fqname;
- if (!fqname.setTo(intf.asString())) {
- return Error() << "Unable to parse interface '" << intf.asString() << "'";
- }
- inherited_interfaces.insert(fqname);
- }
- std::string intf_string = entry["interface"].asString();
- FQName fqname;
- if (!fqname.setTo(intf_string)) {
- return Error() << "Unable to parse interface '" << intf_string << "'";
- }
- result[fqname] = inherited_interfaces;
- }
-
- return result;
-}
-
Result<void> CheckInterfaceInheritanceHierarchy(const std::set<std::string>& instances,
const InterfaceInheritanceHierarchyMap& hierarchy) {
std::set<FQName> interface_fqnames;
diff --git a/init/interface_utils.h b/init/interface_utils.h
index bd0c104..4ca377f 100644
--- a/init/interface_utils.h
+++ b/init/interface_utils.h
@@ -29,9 +29,6 @@
using InterfaceInheritanceHierarchyMap = std::map<android::FQName, std::set<android::FQName>>;
-// Reads the HIDL interface inheritance hierarchy JSON file at the given path.
-Result<InterfaceInheritanceHierarchyMap> ReadInterfaceInheritanceHierarchy(const std::string& path);
-
// For the given set of interfaces / interface instances, checks that each
// interface's hierarchy of inherited interfaces is also included in the given
// interface set. Uses the provided hierarchy data.
diff --git a/init/property_service.cpp b/init/property_service.cpp
index d7e4021..3baaf7c 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -93,6 +93,7 @@
static int property_set_fd = -1;
static int init_socket = -1;
+static bool accept_messages = false;
static PropertyInfoAreaFile property_info_area;
@@ -211,7 +212,7 @@
}
// If init hasn't started its main loop, then it won't be handling property changed messages
// anyway, so there's no need to try to send them.
- if (init_socket != -1) {
+ if (accept_messages) {
SendPropertyChanged(name, value);
}
return PROP_SUCCESS;
@@ -389,7 +390,7 @@
static uint32_t SendControlMessage(const std::string& msg, const std::string& name, pid_t pid,
SocketConnection* socket, std::string* error) {
- if (init_socket == -1) {
+ if (!accept_messages) {
*error = "Received control message after shutdown, ignoring";
return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
}
@@ -403,7 +404,7 @@
// We must release the fd before sending it to init, otherwise there will be a race with init.
// If init calls close() before Release(), then fdsan will see the wrong tag and abort().
int fd = -1;
- if (socket != nullptr) {
+ if (socket != nullptr && SelinuxGetVendorAndroidVersion() > __ANDROID_API_Q__) {
fd = socket->Release();
control_message->set_fd(fd);
}
@@ -1035,7 +1036,11 @@
break;
}
case InitMessage::kStopSendingMessages: {
- init_socket = -1;
+ accept_messages = false;
+ break;
+ }
+ case InitMessage::kStartSendingMessages: {
+ accept_messages = true;
break;
}
default:
@@ -1078,6 +1083,7 @@
}
*epoll_socket = sockets[0];
init_socket = sockets[1];
+ accept_messages = true;
if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
false, 0666, 0, 0, {})) {
diff --git a/init/property_service.proto b/init/property_service.proto
index ea454d4..08268d9 100644
--- a/init/property_service.proto
+++ b/init/property_service.proto
@@ -40,5 +40,6 @@
oneof msg {
bool load_persistent_properties = 1;
bool stop_sending_messages = 2;
+ bool start_sending_messages = 3;
};
}
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 30836d2..fc18ecb 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -22,6 +22,7 @@
#include <linux/loop.h>
#include <mntent.h>
#include <semaphore.h>
+#include <stdlib.h>
#include <sys/cdefs.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
@@ -31,6 +32,7 @@
#include <sys/types.h>
#include <sys/wait.h>
+#include <chrono>
#include <memory>
#include <set>
#include <thread>
@@ -41,6 +43,7 @@
#include <android-base/logging.h>
#include <android-base/macros.h>
#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <bootloader_message/bootloader_message.h>
@@ -50,13 +53,16 @@
#include <private/android_filesystem_config.h>
#include <selinux/selinux.h>
+#include "action.h"
#include "action_manager.h"
+#include "builtin_arguments.h"
#include "init.h"
#include "property_service.h"
#include "reboot_utils.h"
#include "service.h"
#include "service_list.h"
#include "sigchld_handler.h"
+#include "util.h"
#define PROC_SYSRQ "/proc/sysrq-trigger"
@@ -71,6 +77,21 @@
namespace android {
namespace init {
+static bool shutting_down = false;
+
+static const std::set<std::string> kDebuggingServices{"tombstoned", "logd", "adbd", "console"};
+
+static std::vector<Service*> GetDebuggingServices(bool only_post_data) {
+ std::vector<Service*> ret;
+ ret.reserve(kDebuggingServices.size());
+ for (const auto& s : ServiceList::GetInstance()) {
+ if (kDebuggingServices.count(s->name()) && (!only_post_data || s->is_post_data())) {
+ ret.push_back(s.get());
+ }
+ }
+ return ret;
+}
+
// represents umount status during reboot / shutdown.
enum UmountStat {
/* umount succeeded. */
@@ -160,10 +181,17 @@
}
}
-static void ShutdownVold() {
+static Result<void> ShutdownVold() {
const char* vdc_argv[] = {"/system/bin/vdc", "volume", "shutdown"};
int status;
- logwrap_fork_execvp(arraysize(vdc_argv), vdc_argv, &status, false, LOG_KLOG, true, nullptr);
+ if (logwrap_fork_execvp(arraysize(vdc_argv), vdc_argv, &status, false, LOG_KLOG, true,
+ nullptr) != 0) {
+ return ErrnoError() << "Failed to call 'vdc volume shutdown'";
+ }
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+ return {};
+ }
+ return Error() << "'vdc volume shutdown' failed : " << status;
}
static void LogShutdownTime(UmountStat stat, Timer* t) {
@@ -188,8 +216,8 @@
// Find all read+write block devices and emulated devices in /proc/mounts and add them to
// the correpsponding list.
-static bool FindPartitionsToUmount(std::vector<MountEntry>* blockDevPartitions,
- std::vector<MountEntry>* emulatedPartitions, bool dump) {
+static bool FindPartitionsToUmount(std::vector<MountEntry>* block_dev_partitions,
+ std::vector<MountEntry>* emulated_partitions, bool dump) {
std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent("/proc/mounts", "re"), endmntent);
if (fp == nullptr) {
PLOG(ERROR) << "Failed to open /proc/mounts";
@@ -206,10 +234,10 @@
// Do not umount them as shutdown critical services may rely on them.
if (mount_dir != "/" && mount_dir != "/system" && mount_dir != "/vendor" &&
mount_dir != "/oem") {
- blockDevPartitions->emplace(blockDevPartitions->begin(), *mentry);
+ block_dev_partitions->emplace(block_dev_partitions->begin(), *mentry);
}
} else if (MountEntry::IsEmulatedDevice(*mentry)) {
- emulatedPartitions->emplace(emulatedPartitions->begin(), *mentry);
+ emulated_partitions->emplace(emulated_partitions->begin(), *mentry);
}
}
return true;
@@ -271,8 +299,9 @@
}
// Create reboot/shutdwon monitor thread
-void RebootMonitorThread(unsigned int cmd, const std::string& rebootTarget, sem_t* reboot_semaphore,
- std::chrono::milliseconds shutdown_timeout, bool* reboot_monitor_run) {
+void RebootMonitorThread(unsigned int cmd, const std::string& reboot_target,
+ sem_t* reboot_semaphore, std::chrono::milliseconds shutdown_timeout,
+ bool* reboot_monitor_run) {
unsigned int remaining_shutdown_time = 0;
// 30 seconds more than the timeout passed to the thread as there is a final Umount pass
@@ -334,7 +363,7 @@
WriteStringToFile("u", PROC_SYSRQ);
- RebootSystem(cmd, rebootTarget);
+ RebootSystem(cmd, reboot_target);
}
LOG(ERROR) << "Trigger crash at last!";
@@ -364,13 +393,13 @@
*
* return true when umount was successful. false when timed out.
*/
-static UmountStat TryUmountAndFsck(unsigned int cmd, const std::string& rebootTarget, bool runFsck,
+static UmountStat TryUmountAndFsck(unsigned int cmd, bool run_fsck,
std::chrono::milliseconds timeout, sem_t* reboot_semaphore) {
Timer t;
std::vector<MountEntry> block_devices;
std::vector<MountEntry> emulated_devices;
- if (runFsck && !FindPartitionsToUmount(&block_devices, &emulated_devices, false)) {
+ if (run_fsck && !FindPartitionsToUmount(&block_devices, &emulated_devices, false)) {
return UMOUNT_STAT_ERROR;
}
@@ -384,7 +413,7 @@
if ((st != UMOUNT_STAT_SUCCESS) && DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo();
}
- if (stat == UMOUNT_STAT_SUCCESS && runFsck) {
+ if (stat == UMOUNT_STAT_SUCCESS && run_fsck) {
LOG(INFO) << "Pause reboot monitor thread before fsck";
sem_post(reboot_semaphore);
@@ -405,11 +434,11 @@
#define ZRAM_DEVICE "/dev/block/zram0"
#define ZRAM_RESET "/sys/block/zram0/reset"
#define ZRAM_BACK_DEV "/sys/block/zram0/backing_dev"
-static void KillZramBackingDevice() {
+static Result<void> KillZramBackingDevice() {
std::string backing_dev;
- if (!android::base::ReadFileToString(ZRAM_BACK_DEV, &backing_dev)) return;
+ if (!android::base::ReadFileToString(ZRAM_BACK_DEV, &backing_dev)) return {};
- if (!android::base::StartsWith(backing_dev, "/dev/block/loop")) return;
+ if (!android::base::StartsWith(backing_dev, "/dev/block/loop")) return {};
// cut the last "\n"
backing_dev.erase(backing_dev.length() - 1);
@@ -418,47 +447,90 @@
Timer swap_timer;
LOG(INFO) << "swapoff() start...";
if (swapoff(ZRAM_DEVICE) == -1) {
- LOG(ERROR) << "zram_backing_dev: swapoff (" << backing_dev << ")" << " failed";
- return;
+ return ErrnoError() << "zram_backing_dev: swapoff (" << backing_dev << ")"
+ << " failed";
}
LOG(INFO) << "swapoff() took " << swap_timer;;
if (!WriteStringToFile("1", ZRAM_RESET)) {
- LOG(ERROR) << "zram_backing_dev: reset (" << backing_dev << ")" << " failed";
- return;
+ return Error() << "zram_backing_dev: reset (" << backing_dev << ")"
+ << " failed";
}
// clear loopback device
unique_fd loop(TEMP_FAILURE_RETRY(open(backing_dev.c_str(), O_RDWR | O_CLOEXEC)));
if (loop.get() < 0) {
- LOG(ERROR) << "zram_backing_dev: open(" << backing_dev << ")" << " failed";
- return;
+ return ErrnoError() << "zram_backing_dev: open(" << backing_dev << ")"
+ << " failed";
}
if (ioctl(loop.get(), LOOP_CLR_FD, 0) < 0) {
- LOG(ERROR) << "zram_backing_dev: loop_clear (" << backing_dev << ")" << " failed";
- return;
+ return ErrnoError() << "zram_backing_dev: loop_clear (" << backing_dev << ")"
+ << " failed";
}
LOG(INFO) << "zram_backing_dev: `" << backing_dev << "` is cleared successfully.";
+ return {};
+}
+
+// Stops given services, waits for them to be stopped for |timeout| ms.
+// If terminate is true, then SIGTERM is sent to services, otherwise SIGKILL is sent.
+static void StopServices(const std::vector<Service*>& services, std::chrono::milliseconds timeout,
+ bool terminate) {
+ LOG(INFO) << "Stopping " << services.size() << " services by sending "
+ << (terminate ? "SIGTERM" : "SIGKILL");
+ std::vector<pid_t> pids;
+ pids.reserve(services.size());
+ for (const auto& s : services) {
+ if (s->pid() > 0) {
+ pids.push_back(s->pid());
+ }
+ if (terminate) {
+ s->Terminate();
+ } else {
+ s->Stop();
+ }
+ }
+ if (timeout > 0ms) {
+ WaitToBeReaped(pids, timeout);
+ } else {
+ // Even if we don't to wait for services to stop, we still optimistically reap zombies.
+ ReapAnyOutstandingChildren();
+ }
+}
+
+// Like StopServices, but also logs all the services that failed to stop after the provided timeout.
+// Returns number of violators.
+static int StopServicesAndLogViolations(const std::vector<Service*>& services,
+ std::chrono::milliseconds timeout, bool terminate) {
+ StopServices(services, timeout, terminate);
+ int still_running = 0;
+ for (const auto& s : services) {
+ if (s->IsRunning()) {
+ LOG(ERROR) << "[service-misbehaving] : service '" << s->name() << "' is still running "
+ << timeout.count() << "ms after receiving "
+ << (terminate ? "SIGTERM" : "SIGKILL");
+ still_running++;
+ }
+ }
+ return still_running;
}
//* Reboot / shutdown the system.
// cmd ANDROID_RB_* as defined in android_reboot.h
// reason Reason string like "reboot", "shutdown,userrequested"
-// rebootTarget Reboot target string like "bootloader". Otherwise, it should be an
-// empty string.
-// runFsck Whether to run fsck after umount is done.
+// reboot_target Reboot target string like "bootloader". Otherwise, it should be an empty string.
+// run_fsck Whether to run fsck after umount is done.
//
-static void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
- bool runFsck) {
+static void DoReboot(unsigned int cmd, const std::string& reason, const std::string& reboot_target,
+ bool run_fsck) {
Timer t;
- LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
+ LOG(INFO) << "Reboot start, reason: " << reason << ", reboot_target: " << reboot_target;
// If /data isn't mounted then we can skip the extra reboot steps below, since we don't need to
// worry about unmounting it.
if (!IsDataMounted()) {
sync();
- RebootSystem(cmd, rebootTarget);
+ RebootSystem(cmd, reboot_target);
abort();
}
@@ -493,25 +565,26 @@
if (sem_init(&reboot_semaphore, false, 0) == -1) {
// These should never fail, but if they do, skip the graceful reboot and reboot immediately.
LOG(ERROR) << "sem_init() fail and RebootSystem() return!";
- RebootSystem(cmd, rebootTarget);
+ RebootSystem(cmd, reboot_target);
}
// Start a thread to monitor init shutdown process
LOG(INFO) << "Create reboot monitor thread.";
bool reboot_monitor_run = true;
- std::thread reboot_monitor_thread(&RebootMonitorThread, cmd, rebootTarget, &reboot_semaphore,
+ std::thread reboot_monitor_thread(&RebootMonitorThread, cmd, reboot_target, &reboot_semaphore,
shutdown_timeout, &reboot_monitor_run);
reboot_monitor_thread.detach();
// Start reboot monitor thread
sem_post(&reboot_semaphore);
- // keep debugging tools until non critical ones are all gone.
- const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"};
// watchdogd is a vendor specific component but should be alive to complete shutdown safely.
const std::set<std::string> to_starts{"watchdogd"};
+ std::vector<Service*> stop_first;
+ stop_first.reserve(ServiceList::GetInstance().services().size());
for (const auto& s : ServiceList::GetInstance()) {
- if (kill_after_apps.count(s->name())) {
+ if (kDebuggingServices.count(s->name())) {
+ // keep debugging tools until non critical ones are all gone.
s->SetShutdownCritical();
} else if (to_starts.count(s->name())) {
if (auto result = s->Start(); !result) {
@@ -525,6 +598,8 @@
LOG(ERROR) << "Could not start shutdown critical service '" << s->name()
<< "': " << result.error();
}
+ } else {
+ stop_first.push_back(s.get());
}
}
@@ -533,16 +608,16 @@
TurnOffBacklight();
}
- Service* bootAnim = ServiceList::GetInstance().FindService("bootanim");
- Service* surfaceFlinger = ServiceList::GetInstance().FindService("surfaceflinger");
- if (bootAnim != nullptr && surfaceFlinger != nullptr && surfaceFlinger->IsRunning()) {
+ Service* boot_anim = ServiceList::GetInstance().FindService("bootanim");
+ Service* surface_flinger = ServiceList::GetInstance().FindService("surfaceflinger");
+ if (boot_anim != nullptr && surface_flinger != nullptr && surface_flinger->IsRunning()) {
bool do_shutdown_animation = GetBoolProperty("ro.init.shutdown_animation", false);
if (do_shutdown_animation) {
property_set("service.bootanim.exit", "0");
// Could be in the middle of animation. Stop and start so that it can pick
// up the right mode.
- bootAnim->Stop();
+ boot_anim->Stop();
}
for (const auto& service : ServiceList::GetInstance()) {
@@ -558,72 +633,33 @@
}
if (do_shutdown_animation) {
- bootAnim->Start();
- surfaceFlinger->SetShutdownCritical();
- bootAnim->SetShutdownCritical();
+ boot_anim->Start();
+ surface_flinger->SetShutdownCritical();
+ boot_anim->SetShutdownCritical();
}
}
// optional shutdown step
// 1. terminate all services except shutdown critical ones. wait for delay to finish
if (shutdown_timeout > 0ms) {
- LOG(INFO) << "terminating init services";
-
- // Ask all services to terminate except shutdown critical ones.
- for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
- if (!s->IsShutdownCritical()) s->Terminate();
- }
-
- int service_count = 0;
- // Only wait up to half of timeout here
- auto termination_wait_timeout = shutdown_timeout / 2;
- while (t.duration() < termination_wait_timeout) {
- ReapAnyOutstandingChildren();
-
- service_count = 0;
- for (const auto& s : ServiceList::GetInstance()) {
- // Count the number of services running except shutdown critical.
- // Exclude the console as it will ignore the SIGTERM signal
- // and not exit.
- // Note: SVC_CONSOLE actually means "requires console" but
- // it is only used by the shell.
- if (!s->IsShutdownCritical() && s->pid() != 0 && (s->flags() & SVC_CONSOLE) == 0) {
- service_count++;
- }
- }
-
- if (service_count == 0) {
- // All terminable services terminated. We can exit early.
- break;
- }
-
- // Wait a bit before recounting the number or running services.
- std::this_thread::sleep_for(50ms);
- }
- LOG(INFO) << "Terminating running services took " << t
- << " with remaining services:" << service_count;
+ StopServicesAndLogViolations(stop_first, shutdown_timeout / 2, true /* SIGTERM */);
}
-
- // minimum safety steps before restarting
- // 2. kill all services except ones that are necessary for the shutdown sequence.
- for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
- if (!s->IsShutdownCritical()) s->Stop();
- }
+ // Send SIGKILL to ones that didn't terminate cleanly.
+ StopServicesAndLogViolations(stop_first, 0ms, false /* SIGKILL */);
SubcontextTerminate();
+ // Reap subcontext pids.
ReapAnyOutstandingChildren();
// 3. send volume shutdown to vold
- Service* voldService = ServiceList::GetInstance().FindService("vold");
- if (voldService != nullptr && voldService->IsRunning()) {
+ Service* vold_service = ServiceList::GetInstance().FindService("vold");
+ if (vold_service != nullptr && vold_service->IsRunning()) {
ShutdownVold();
- voldService->Stop();
+ vold_service->Stop();
} else {
LOG(INFO) << "vold not running, skipping vold shutdown";
}
// logcat stopped here
- for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
- if (kill_after_apps.count(s->name())) s->Stop();
- }
+ StopServices(GetDebuggingServices(false /* only_post_data */), 0ms, false /* SIGKILL */);
// 4. sync, try umount, and optionally run fsck for user shutdown
{
Timer sync_timer;
@@ -634,8 +670,8 @@
// 5. drop caches and disable zram backing device, if exist
KillZramBackingDevice();
- UmountStat stat = TryUmountAndFsck(cmd, rebootTarget, runFsck, shutdown_timeout - t.duration(),
- &reboot_semaphore);
+ UmountStat stat =
+ TryUmountAndFsck(cmd, run_fsck, shutdown_timeout - t.duration(), &reboot_semaphore);
// Follow what linux shutdown is doing: one more sync with little bit delay
{
Timer sync_timer;
@@ -651,16 +687,116 @@
sem_post(&reboot_semaphore);
// Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
- RebootSystem(cmd, rebootTarget);
+ RebootSystem(cmd, reboot_target);
abort();
}
-bool HandlePowerctlMessage(const std::string& command) {
+static void EnterShutdown() {
+ LOG(INFO) << "Entering shutdown mode";
+ shutting_down = true;
+ // Skip wait for prop if it is in progress
+ ResetWaitForProp();
+ // Clear EXEC flag if there is one pending
+ for (const auto& s : ServiceList::GetInstance()) {
+ s->UnSetExec();
+ }
+ // We no longer process messages about properties changing coming from property service, so we
+ // need to tell property service to stop sending us these messages, otherwise it'll fill the
+ // buffers and block indefinitely, causing future property sets, including those that init makes
+ // during shutdown in Service::NotifyStateChange() to also block indefinitely.
+ SendStopSendingMessagesMessage();
+}
+
+static void LeaveShutdown() {
+ LOG(INFO) << "Leaving shutdown mode";
+ shutting_down = false;
+ SendStartSendingMessagesMessage();
+}
+
+static Result<void> DoUserspaceReboot() {
+ LOG(INFO) << "Userspace reboot initiated";
+ auto guard = android::base::make_scope_guard([] {
+ // Leave shutdown so that we can handle a full reboot.
+ LeaveShutdown();
+ TriggerShutdown("reboot,abort-userspace-reboot");
+ });
+ // Triggering userspace-reboot-requested will result in a bunch of set_prop
+ // actions. We should make sure, that all of them are propagated before
+ // proceeding with userspace reboot.
+ // TODO(b/135984674): implement proper synchronization logic.
+ std::this_thread::sleep_for(500ms);
+ EnterShutdown();
+ std::vector<Service*> stop_first;
+ // Remember the services that were enabled. We will need to manually enable them again otherwise
+ // triggers like class_start won't restart them.
+ std::vector<Service*> were_enabled;
+ stop_first.reserve(ServiceList::GetInstance().services().size());
+ for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
+ if (s->is_post_data() && !kDebuggingServices.count(s->name())) {
+ stop_first.push_back(s);
+ }
+ if (s->is_post_data() && s->IsEnabled()) {
+ were_enabled.push_back(s);
+ }
+ }
+ // TODO(b/135984674): do we need shutdown animation for userspace reboot?
+ // TODO(b/135984674): control userspace timeout via read-only property?
+ StopServicesAndLogViolations(stop_first, 10s, true /* SIGTERM */);
+ if (int r = StopServicesAndLogViolations(stop_first, 20s, false /* SIGKILL */); r > 0) {
+ // TODO(b/135984674): store information about offending services for debugging.
+ return Error() << r << " post-data services are still running";
+ }
+ // We only really need to restart vold if userdata is ext4 filesystem.
+ // TODO(b/135984674): get userdata fs type here, and do nothing in case of f2fs.
+ // First shutdown volumes managed by vold. They will be recreated by
+ // system_server.
+ Service* vold_service = ServiceList::GetInstance().FindService("vold");
+ if (vold_service != nullptr && vold_service->IsRunning()) {
+ if (auto result = ShutdownVold(); !result) {
+ return result;
+ }
+ LOG(INFO) << "Restarting vold";
+ vold_service->Restart();
+ }
+ // Again, we only need to kill zram backing device in case of ext4 userdata.
+ // TODO(b/135984674): get userdata fs type here, and do nothing in case of f2fs.
+ if (auto result = KillZramBackingDevice(); !result) {
+ return result;
+ }
+ if (int r = StopServicesAndLogViolations(GetDebuggingServices(true /* only_post_data */), 5s,
+ false /* SIGKILL */);
+ r > 0) {
+ // TODO(b/135984674): store information about offending services for debugging.
+ return Error() << r << " debugging services are still running";
+ }
+ // TODO(b/135984674): deactivate APEX modules and switch back to bootstrap namespace.
+ // Re-enable services
+ for (const auto& s : were_enabled) {
+ LOG(INFO) << "Re-enabling service '" << s->name() << "'";
+ s->Enable();
+ }
+ LeaveShutdown();
+ ActionManager::GetInstance().QueueEventTrigger("userspace-reboot-resume");
+ guard.Disable(); // Go on with userspace reboot.
+ return {};
+}
+
+static void HandleUserspaceReboot() {
+ LOG(INFO) << "Clearing queue and starting userspace-reboot-requested trigger";
+ auto& am = ActionManager::GetInstance();
+ am.ClearQueue();
+ am.QueueEventTrigger("userspace-reboot-requested");
+ auto handler = [](const BuiltinArguments&) { return DoUserspaceReboot(); };
+ am.QueueBuiltinAction(handler, "userspace-reboot");
+}
+
+void HandlePowerctlMessage(const std::string& command) {
unsigned int cmd = 0;
std::vector<std::string> cmd_params = Split(command, ",");
std::string reboot_target = "";
bool run_fsck = false;
bool command_invalid = false;
+ bool userspace_reboot = false;
if (cmd_params[0] == "shutdown") {
cmd = ANDROID_RB_POWEROFF;
@@ -680,6 +816,10 @@
cmd = ANDROID_RB_RESTART2;
if (cmd_params.size() >= 2) {
reboot_target = cmd_params[1];
+ if (reboot_target == "userspace") {
+ LOG(INFO) << "Userspace reboot requested";
+ userspace_reboot = true;
+ }
// adb reboot fastboot should boot into bootloader for devices not
// supporting logical partitions.
if (reboot_target == "fastboot" &&
@@ -706,7 +846,7 @@
strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
if (std::string err; !write_bootloader_message(boot, &err)) {
LOG(ERROR) << "Failed to set bootloader message: " << err;
- return false;
+ return;
}
}
} else if (reboot_target == "sideload" || reboot_target == "sideload-auto-reboot" ||
@@ -719,7 +859,7 @@
std::string err;
if (!write_bootloader_message(options, &err)) {
LOG(ERROR) << "Failed to set bootloader message: " << err;
- return false;
+ return;
}
reboot_target = "recovery";
}
@@ -734,7 +874,12 @@
}
if (command_invalid) {
LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
- return false;
+ return;
+ }
+
+ if (userspace_reboot) {
+ HandleUserspaceReboot();
+ return;
}
LOG(INFO) << "Clear action queue and start shutdown trigger";
@@ -748,21 +893,11 @@
};
ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done");
- // Skip wait for prop if it is in progress
- ResetWaitForProp();
+ EnterShutdown();
+}
- // Clear EXEC flag if there is one pending
- for (const auto& s : ServiceList::GetInstance()) {
- s->UnSetExec();
- }
-
- // We no longer process messages about properties changing coming from property service, so we
- // need to tell property service to stop sending us these messages, otherwise it'll fill the
- // buffers and block indefinitely, causing future property sets, including those that init makes
- // during shutdown in Service::NotifyStateChange() to also block indefinitely.
- SendStopSendingMessagesMessage();
-
- return true;
+bool IsShuttingDown() {
+ return shutting_down;
}
} // namespace init
diff --git a/init/reboot.h b/init/reboot.h
index 07dcb6e..81c3edc 100644
--- a/init/reboot.h
+++ b/init/reboot.h
@@ -23,8 +23,9 @@
namespace init {
// Parses and handles a setprop sys.powerctl message.
-bool HandlePowerctlMessage(const std::string& command);
+void HandlePowerctlMessage(const std::string& command);
+bool IsShuttingDown();
} // namespace init
} // namespace android
diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp
index de085cc..dac0cf4 100644
--- a/init/reboot_utils.cpp
+++ b/init/reboot_utils.cpp
@@ -109,7 +109,7 @@
abort();
}
-void __attribute__((noreturn)) InitFatalReboot() {
+void __attribute__((noreturn)) InitFatalReboot(int signal_number) {
auto pid = fork();
if (pid == -1) {
@@ -124,6 +124,7 @@
}
// In the parent, let's try to get a backtrace then shutdown.
+ LOG(ERROR) << __FUNCTION__ << ": signal " << signal_number;
std::unique_ptr<Backtrace> backtrace(
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
if (!backtrace->Unwind(0)) {
@@ -154,7 +155,7 @@
// RebootSystem uses syscall() which isn't actually async-signal-safe, but our only option
// and probably good enough given this is already an error case and only enabled for
// development builds.
- InitFatalReboot();
+ InitFatalReboot(signal);
};
action.sa_flags = SA_RESTART;
sigaction(SIGABRT, &action, nullptr);
diff --git a/init/reboot_utils.h b/init/reboot_utils.h
index 3fd969e..878ad96 100644
--- a/init/reboot_utils.h
+++ b/init/reboot_utils.h
@@ -27,7 +27,7 @@
bool IsRebootCapable();
// This is a wrapper around the actual reboot calls.
void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& reboot_target);
-void __attribute__((noreturn)) InitFatalReboot();
+void __attribute__((noreturn)) InitFatalReboot(int signal_number);
void InstallRebootSignalHandlers();
} // namespace init
diff --git a/init/service.cpp b/init/service.cpp
index a2db070..c8568a0 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -255,7 +255,7 @@
if ((siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) && on_failure_reboot_target_) {
LOG(ERROR) << "Service with 'reboot_on_failure' option failed, shutting down system.";
- EnterShutdown(*on_failure_reboot_target_);
+ TriggerShutdown(*on_failure_reboot_target_);
}
if (flags_ & SVC_EXEC) UnSetExec();
@@ -335,7 +335,7 @@
Result<void> Service::ExecStart() {
auto reboot_on_failure = make_scope_guard([this] {
if (on_failure_reboot_target_) {
- EnterShutdown(*on_failure_reboot_target_);
+ TriggerShutdown(*on_failure_reboot_target_);
}
});
@@ -366,7 +366,7 @@
Result<void> Service::Start() {
auto reboot_on_failure = make_scope_guard([this] {
if (on_failure_reboot_target_) {
- EnterShutdown(*on_failure_reboot_target_);
+ TriggerShutdown(*on_failure_reboot_target_);
}
});
diff --git a/init/service.h b/init/service.h
index 788f792..272c9f9 100644
--- a/init/service.h
+++ b/init/service.h
@@ -75,6 +75,7 @@
const std::vector<std::string>& args);
bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; }
+ bool IsEnabled() { return (flags_ & SVC_DISABLED) == 0; }
Result<void> ExecStart();
Result<void> Start();
Result<void> StartIfNotDisabled();
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index 984235d..9b2c7d9 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -28,28 +28,31 @@
#include <android-base/scopeguard.h>
#include <android-base/stringprintf.h>
+#include <thread>
+
#include "init.h"
#include "service.h"
#include "service_list.h"
-using android::base::StringPrintf;
using android::base::boot_clock;
using android::base::make_scope_guard;
+using android::base::StringPrintf;
+using android::base::Timer;
namespace android {
namespace init {
-static bool ReapOneProcess() {
+static pid_t ReapOneProcess() {
siginfo_t siginfo = {};
// This returns a zombie pid or informs us that there are no zombies left to be reaped.
// It does NOT reap the pid; that is done below.
if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) {
PLOG(ERROR) << "waitid failed";
- return false;
+ return 0;
}
auto pid = siginfo.si_pid;
- if (pid == 0) return false;
+ if (pid == 0) return 0;
// At this point we know we have a zombie pid, so we use this scopeguard to reap the pid
// whenever the function returns from this point forward.
@@ -92,7 +95,7 @@
LOG(INFO) << name << " received signal " << siginfo.si_status << wait_string;
}
- if (!service) return true;
+ if (!service) return pid;
service->Reap(siginfo);
@@ -100,13 +103,33 @@
ServiceList::GetInstance().RemoveService(*service);
}
- return true;
+ return pid;
}
void ReapAnyOutstandingChildren() {
- while (ReapOneProcess()) {
+ while (ReapOneProcess() != 0) {
}
}
+void WaitToBeReaped(const std::vector<pid_t>& pids, std::chrono::milliseconds timeout) {
+ Timer t;
+ std::vector<pid_t> alive_pids(pids.begin(), pids.end());
+ while (!alive_pids.empty() && t.duration() < timeout) {
+ pid_t pid;
+ while ((pid = ReapOneProcess()) != 0) {
+ auto it = std::find(alive_pids.begin(), alive_pids.end(), pid);
+ if (it != alive_pids.end()) {
+ alive_pids.erase(it);
+ }
+ }
+ if (alive_pids.empty()) {
+ break;
+ }
+ std::this_thread::sleep_for(50ms);
+ }
+ LOG(INFO) << "Waiting for " << pids.size() << " pids to be reaped took " << t << " with "
+ << alive_pids.size() << " of them still running";
+}
+
} // namespace init
} // namespace android
diff --git a/init/sigchld_handler.h b/init/sigchld_handler.h
index 30063f2..fac1020 100644
--- a/init/sigchld_handler.h
+++ b/init/sigchld_handler.h
@@ -17,11 +17,16 @@
#ifndef _INIT_SIGCHLD_HANDLER_H_
#define _INIT_SIGCHLD_HANDLER_H_
+#include <chrono>
+#include <vector>
+
namespace android {
namespace init {
void ReapAnyOutstandingChildren();
+void WaitToBeReaped(const std::vector<pid_t>& pids, std::chrono::milliseconds timeout);
+
} // namespace init
} // namespace android
diff --git a/init/util.cpp b/init/util.cpp
index 0532375..40db838 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -20,6 +20,7 @@
#include <errno.h>
#include <fcntl.h>
#include <pwd.h>
+#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -481,7 +482,7 @@
return;
}
- InitFatalReboot();
+ InitFatalReboot(SIGABRT);
}
// The kernel opens /dev/console and uses that fd for stdin/stdout/stderr if there is a serial
diff --git a/libcutils/sched_policy_test.cpp b/libcutils/sched_policy_test.cpp
index a321c90..b9e2832 100644
--- a/libcutils/sched_policy_test.cpp
+++ b/libcutils/sched_policy_test.cpp
@@ -107,6 +107,18 @@
TEST(SchedPolicy, get_sched_policy_name) {
EXPECT_STREQ("bg", get_sched_policy_name(SP_BACKGROUND));
- EXPECT_STREQ("error", get_sched_policy_name(SchedPolicy(-2)));
- EXPECT_STREQ("error", get_sched_policy_name(SP_CNT));
+ EXPECT_EQ(nullptr, get_sched_policy_name(SchedPolicy(-2)));
+ EXPECT_EQ(nullptr, get_sched_policy_name(SP_CNT));
+}
+
+TEST(SchedPolicy, get_cpuset_policy_profile_name) {
+ EXPECT_STREQ("CPUSET_SP_BACKGROUND", get_cpuset_policy_profile_name(SP_BACKGROUND));
+ EXPECT_EQ(nullptr, get_cpuset_policy_profile_name(SchedPolicy(-2)));
+ EXPECT_EQ(nullptr, get_cpuset_policy_profile_name(SP_CNT));
+}
+
+TEST(SchedPolicy, get_sched_policy_profile_name) {
+ EXPECT_STREQ("SCHED_SP_BACKGROUND", get_sched_policy_profile_name(SP_BACKGROUND));
+ EXPECT_EQ(nullptr, get_sched_policy_profile_name(SchedPolicy(-2)));
+ EXPECT_EQ(nullptr, get_sched_policy_profile_name(SP_CNT));
}
diff --git a/libcutils/trace-dev.cpp b/libcutils/trace-dev.cpp
index 4da8215..bff16c1 100644
--- a/libcutils/trace-dev.cpp
+++ b/libcutils/trace-dev.cpp
@@ -34,12 +34,9 @@
if (atrace_marker_fd == -1) {
ALOGE("Error opening trace file: %s (%d)", strerror(errno), errno);
atrace_enabled_tags = 0;
- goto done;
+ } else {
+ atrace_enabled_tags = atrace_get_property();
}
-
- atrace_enabled_tags = atrace_get_property();
-
-done:
atomic_store_explicit(&atrace_is_ready, true, memory_order_release);
}
diff --git a/libion/ion.c b/libion/ion.c
index 5141ea8..07b4caf 100644
--- a/libion/ion.c
+++ b/libion/ion.c
@@ -31,7 +31,7 @@
#include <unistd.h>
#include <ion/ion.h>
-#include "ion_4.19.h"
+#include <linux/ion_4.19.h>
#include <log/log.h>
diff --git a/libion/ion_4.12.h b/libion/kernel-headers/linux/ion_4.12.h
similarity index 81%
rename from libion/ion_4.12.h
rename to libion/kernel-headers/linux/ion_4.12.h
index 614510c..1af8284 100644
--- a/libion/ion_4.12.h
+++ b/libion/kernel-headers/linux/ion_4.12.h
@@ -22,27 +22,27 @@
#include <linux/types.h>
#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8)
struct ion_new_allocation_data {
- __u64 len;
- __u32 heap_id_mask;
- __u32 flags;
- __u32 fd;
- __u32 unused;
+ __u64 len;
+ __u32 heap_id_mask;
+ __u32 flags;
+ __u32 fd;
+ __u32 unused;
};
#define MAX_HEAP_NAME 32
struct ion_heap_data {
- char name[MAX_HEAP_NAME];
- __u32 type;
- __u32 heap_id;
- __u32 reserved0;
- __u32 reserved1;
- __u32 reserved2;
+ char name[MAX_HEAP_NAME];
+ __u32 type;
+ __u32 heap_id;
+ __u32 reserved0;
+ __u32 reserved1;
+ __u32 reserved2;
};
struct ion_heap_query {
- __u32 cnt;
- __u32 reserved0;
- __u64 heaps;
- __u32 reserved1;
- __u32 reserved2;
+ __u32 cnt;
+ __u32 reserved0;
+ __u64 heaps;
+ __u32 reserved1;
+ __u32 reserved2;
};
#define ION_IOC_MAGIC 'I'
#define ION_IOC_NEW_ALLOC _IOWR(ION_IOC_MAGIC, 0, struct ion_new_allocation_data)
diff --git a/libion/ion_4.19.h b/libion/kernel-headers/linux/ion_4.19.h
similarity index 100%
rename from libion/ion_4.19.h
rename to libion/kernel-headers/linux/ion_4.19.h
diff --git a/libion/tests/ion_4.12.h b/libion/tests/ion_4.12.h
deleted file mode 100644
index 614510c..0000000
--- a/libion/tests/ion_4.12.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/****************************************************************************
- ****************************************************************************
- ***
- *** This header was automatically generated from a Linux kernel header
- *** of the same name, to make information necessary for userspace to
- *** call into the kernel available to libc. It contains only constants,
- *** structures, and macros generated from the original header, and thus,
- *** contains no copyrightable information.
- ***
- *** To edit the content of this header, modify the corresponding
- *** source file (e.g. under external/kernel-headers/original/) then
- *** run bionic/libc/kernel/tools/update_all.py
- ***
- *** Any manual change here will be lost the next time this script will
- *** be run. You've been warned!
- ***
- ****************************************************************************
- ****************************************************************************/
-#ifndef _UAPI_LINUX_ION_NEW_H
-#define _UAPI_LINUX_ION_NEW_H
-#include <linux/ioctl.h>
-#include <linux/types.h>
-#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8)
-struct ion_new_allocation_data {
- __u64 len;
- __u32 heap_id_mask;
- __u32 flags;
- __u32 fd;
- __u32 unused;
-};
-#define MAX_HEAP_NAME 32
-struct ion_heap_data {
- char name[MAX_HEAP_NAME];
- __u32 type;
- __u32 heap_id;
- __u32 reserved0;
- __u32 reserved1;
- __u32 reserved2;
-};
-struct ion_heap_query {
- __u32 cnt;
- __u32 reserved0;
- __u64 heaps;
- __u32 reserved1;
- __u32 reserved2;
-};
-#define ION_IOC_MAGIC 'I'
-#define ION_IOC_NEW_ALLOC _IOWR(ION_IOC_MAGIC, 0, struct ion_new_allocation_data)
-#define ION_IOC_HEAP_QUERY _IOWR(ION_IOC_MAGIC, 8, struct ion_heap_query)
-#endif
diff --git a/libion/tests/ion_test_fixture.h b/libion/tests/ion_test_fixture.h
index 4f254b8..c78fe41 100644
--- a/libion/tests/ion_test_fixture.h
+++ b/libion/tests/ion_test_fixture.h
@@ -18,8 +18,8 @@
#define ION_TEST_FIXTURE_H_
#include <gtest/gtest.h>
+#include <linux/ion_4.12.h>
#include <vector>
-#include "ion_4.12.h"
using ::testing::Test;
diff --git a/liblog/Android.bp b/liblog/Android.bp
index c40c5ef..91bd52c 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -105,6 +105,11 @@
versions: ["10000"],
},
+ // TODO(tomcherry): Renable this before release branch is cut
+ header_abi_checker: {
+ enabled: false,
+ },
+
cflags: [
"-Wall",
"-Werror",
diff --git a/liblog/README.protocol.md b/liblog/README.protocol.md
new file mode 100644
index 0000000..fef29c9
--- /dev/null
+++ b/liblog/README.protocol.md
@@ -0,0 +1,49 @@
+# liblog -> logd
+
+The data that liblog sends to logd is represented below.
+
+ struct {
+ android_log_header_t header;
+ union {
+ struct {
+ char prio;
+ char tag[...];
+ char message[...];
+ } string;
+ struct {
+ android_event_header_t event_header;
+ android_event_*_t payload[...];
+ } binary;
+ };
+ };
+
+The payload, excluding the header, has a max size of LOGGER_ENTRY_MAX_PAYLOAD.
+
+## header
+
+The header is added immediately before sending the log message to logd.
+
+## `string` payload
+
+The `string` part of the union is for normal buffers (main, system, radio, etc) and consists of a
+single character priority, followed by a variable length null terminated string for the tag, and
+finally a variable length null terminated string for the message.
+
+This payload is used for the `__android_log_buf_write()` family of functions.
+
+## `binary` payload
+
+The `binary` part of the union is for binary buffers (events, security, etc) and consists of an
+android_event_header_t struct followed by a variable number of android_event_*_t
+(android_event_list_t, android_event_int_t, etc) structs.
+
+If multiple android_event_*_t elements are present, then they must be in a list and the first
+element in payload must be an android_event_list_t.
+
+This payload is used for the `__android_log_bwrite()` family of functions. It is additionally used
+for `android_log_write_list()` and the related functions that manipulate event lists.
+
+# logd -> liblog
+
+logd sends a `logger_entry` struct to liblog followed by the payload. The payload is identical to
+the payloads defined above. The max size of the entire message from logd is LOGGER_ENTRY_MAX_LEN.
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index 935590d..7290789 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -96,20 +96,14 @@
* [printf(3)](http://man7.org/linux/man-pages/man3/printf.3.html).
*/
int __android_log_print(int prio, const char* tag, const char* fmt, ...)
-#if defined(__GNUC__)
- __attribute__((__format__(printf, 3, 4)))
-#endif
- ;
+ __attribute__((__format__(printf, 3, 4)));
/**
* Equivalent to `__android_log_print`, but taking a `va_list`.
* (If `__android_log_print` is like `printf`, this is like `vprintf`.)
*/
int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap)
-#if defined(__GNUC__)
- __attribute__((__format__(printf, 3, 0)))
-#endif
- ;
+ __attribute__((__format__(printf, 3, 0)));
/**
* Writes an assertion failure to the log (as `ANDROID_LOG_FATAL`) and to
@@ -127,16 +121,9 @@
* including the source filename and line number more conveniently than this
* function.
*/
-void __android_log_assert(const char* cond, const char* tag, const char* fmt,
- ...)
-#if defined(__GNUC__)
- __attribute__((__noreturn__))
- __attribute__((__format__(printf, 3, 4)))
-#endif
- ;
+void __android_log_assert(const char* cond, const char* tag, const char* fmt, ...)
+ __attribute__((__noreturn__)) __attribute__((__format__(printf, 3, 4)));
-#ifndef log_id_t_defined
-#define log_id_t_defined
/**
* Identifies a specific log buffer for __android_log_buf_write()
* and __android_log_buf_print().
@@ -163,7 +150,6 @@
LOG_ID_MAX
} log_id_t;
-#endif
/**
* Writes the constant string `text` to the log buffer `id`,
@@ -171,8 +157,7 @@
*
* Apps should use __android_log_write() instead.
*/
-int __android_log_buf_write(int bufID, int prio, const char* tag,
- const char* text);
+int __android_log_buf_write(int bufID, int prio, const char* tag, const char* text);
/**
* Writes a formatted string to log buffer `id`,
@@ -182,12 +167,8 @@
*
* Apps should use __android_log_print() instead.
*/
-int __android_log_buf_print(int bufID, int prio, const char* tag,
- const char* fmt, ...)
-#if defined(__GNUC__)
- __attribute__((__format__(printf, 4, 5)))
-#endif
- ;
+int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...)
+ __attribute__((__format__(printf, 4, 5)));
#ifdef __cplusplus
}
diff --git a/liblog/include/log/event_tag_map.h b/liblog/include/log/event_tag_map.h
index 2687b3a..f7ec208 100644
--- a/liblog/include/log/event_tag_map.h
+++ b/liblog/include/log/event_tag_map.h
@@ -16,6 +16,8 @@
#pragma once
+#include <stddef.h>
+
#ifdef __cplusplus
extern "C" {
#endif
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
index 5928649..90d1e76 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -22,7 +22,6 @@
#endif
#include <stdint.h> /* uint16_t, int32_t */
#include <stdio.h>
-#include <sys/types.h>
#include <time.h>
#include <unistd.h>
@@ -65,21 +64,6 @@
#endif
#endif
-/* --------------------------------------------------------------------- */
-
-/*
- * This file uses ", ## __VA_ARGS__" zero-argument token pasting to
- * work around issues with debug-only syntax errors in assertions
- * that are missing format strings. See commit
- * 19299904343daf191267564fe32e6cd5c165cd42
- */
-#if defined(__clang__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
-#endif
-
-/* --------------------------------------------------------------------- */
-
/*
* Event logging.
*/
@@ -164,10 +148,6 @@
*/
void __android_log_close(void);
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
-
#ifdef __cplusplus
}
#endif
diff --git a/liblog/include/log/log_event_list.h b/liblog/include/log/log_event_list.h
index 636d417..deadf20 100644
--- a/liblog/include/log/log_event_list.h
+++ b/liblog/include/log/log_event_list.h
@@ -36,16 +36,11 @@
/*
* The opaque context used to manipulate lists of events.
*/
-#ifndef __android_log_context_defined
-#define __android_log_context_defined
typedef struct android_log_context_internal* android_log_context;
-#endif
/*
* Elements returned when reading a list of events.
*/
-#ifndef __android_log_list_element_defined
-#define __android_log_list_element_defined
typedef struct {
AndroidEventLogType type;
uint16_t complete;
@@ -57,7 +52,6 @@
float float32;
} data;
} android_log_list_element;
-#endif
/*
* Creates a context associated with an event tag to write elements to
@@ -104,8 +98,6 @@
int android_log_destroy(android_log_context* ctx);
#ifdef __cplusplus
-#ifndef __class_android_log_event_list_defined
-#define __class_android_log_event_list_defined
/* android_log_list C++ helpers */
extern "C++" {
class android_log_event_list {
@@ -280,7 +272,6 @@
};
}
#endif
-#endif
#ifdef __cplusplus
}
diff --git a/liblog/include/log/log_id.h b/liblog/include/log/log_id.h
index c052a50..c8fafe7 100644
--- a/liblog/include/log/log_id.h
+++ b/liblog/include/log/log_id.h
@@ -16,41 +16,19 @@
#pragma once
+#include <android/log.h>
+
#ifdef __cplusplus
extern "C" {
#endif
-#ifndef log_id_t_defined
-#define log_id_t_defined
-typedef enum log_id {
- LOG_ID_MIN = 0,
-
- LOG_ID_MAIN = 0,
- LOG_ID_RADIO = 1,
- LOG_ID_EVENTS = 2,
- LOG_ID_SYSTEM = 3,
- LOG_ID_CRASH = 4,
- LOG_ID_STATS = 5,
- LOG_ID_SECURITY = 6,
- LOG_ID_KERNEL = 7, /* place last, third-parties can not use it */
-
- LOG_ID_MAX
-} log_id_t;
-#endif
-#define sizeof_log_id_t sizeof(typeof_log_id_t)
-#define typeof_log_id_t unsigned char
-
/*
* Send a simple string to the log.
*/
int __android_log_buf_write(int bufID, int prio, const char* tag,
const char* text);
-int __android_log_buf_print(int bufID, int prio, const char* tag,
- const char* fmt, ...)
-#if defined(__GNUC__)
- __attribute__((__format__(printf, 4, 5)))
-#endif
- ;
+int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...)
+ __attribute__((__format__(printf, 4, 5)));
/*
* log_id_t helpers
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index fdef306..ee3b250 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -50,64 +50,9 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wzero-length-array"
-/*
- * The userspace structure for version 1 of the logger_entry ABI.
- */
-#ifndef __struct_logger_entry_defined
-#define __struct_logger_entry_defined
struct logger_entry {
- uint16_t len; /* length of the payload */
- uint16_t __pad; /* no matter what, we get 2 bytes of padding */
- int32_t pid; /* generating process's pid */
- int32_t tid; /* generating process's tid */
- int32_t sec; /* seconds since Epoch */
- int32_t nsec; /* nanoseconds */
- char msg[0]; /* the entry's payload */
-};
-#endif
-
-/*
- * The userspace structure for version 2 of the logger_entry ABI.
- */
-#ifndef __struct_logger_entry_v2_defined
-#define __struct_logger_entry_v2_defined
-struct logger_entry_v2 {
uint16_t len; /* length of the payload */
- uint16_t hdr_size; /* sizeof(struct logger_entry_v2) */
- int32_t pid; /* generating process's pid */
- int32_t tid; /* generating process's tid */
- int32_t sec; /* seconds since Epoch */
- int32_t nsec; /* nanoseconds */
- uint32_t euid; /* effective UID of logger */
- char msg[0]; /* the entry's payload */
-} __attribute__((__packed__));
-#endif
-
-/*
- * The userspace structure for version 3 of the logger_entry ABI.
- */
-#ifndef __struct_logger_entry_v3_defined
-#define __struct_logger_entry_v3_defined
-struct logger_entry_v3 {
- uint16_t len; /* length of the payload */
- uint16_t hdr_size; /* sizeof(struct logger_entry_v3) */
- int32_t pid; /* generating process's pid */
- int32_t tid; /* generating process's tid */
- int32_t sec; /* seconds since Epoch */
- int32_t nsec; /* nanoseconds */
- uint32_t lid; /* log id of the payload */
- char msg[0]; /* the entry's payload */
-} __attribute__((__packed__));
-#endif
-
-/*
- * The userspace structure for version 4 of the logger_entry ABI.
- */
-#ifndef __struct_logger_entry_v4_defined
-#define __struct_logger_entry_v4_defined
-struct logger_entry_v4 {
- uint16_t len; /* length of the payload */
- uint16_t hdr_size; /* sizeof(struct logger_entry_v4) */
+ uint16_t hdr_size; /* sizeof(struct logger_entry) */
int32_t pid; /* generating process's pid */
uint32_t tid; /* generating process's tid */
uint32_t sec; /* seconds since Epoch */
@@ -116,7 +61,6 @@
uint32_t uid; /* generating process's uid */
char msg[0]; /* the entry's payload */
};
-#endif
#pragma clang diagnostic pop
/*
@@ -133,16 +77,10 @@
*/
#define LOGGER_ENTRY_MAX_LEN (5 * 1024)
-#ifndef __struct_log_msg_defined
-#define __struct_log_msg_defined
struct log_msg {
union {
unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];
- struct logger_entry_v4 entry;
- struct logger_entry_v4 entry_v4;
- struct logger_entry_v3 entry_v3;
- struct logger_entry_v2 entry_v2;
- struct logger_entry entry_v1;
+ struct logger_entry entry;
} __attribute__((aligned(4)));
#ifdef __cplusplus
/* Matching log_time operators */
@@ -176,22 +114,14 @@
}
char* msg() {
unsigned short hdr_size = entry.hdr_size;
- if (!hdr_size) {
- hdr_size = sizeof(entry_v1);
- }
- if ((hdr_size < sizeof(entry_v1)) || (hdr_size > sizeof(entry))) {
+ if (hdr_size != sizeof(entry)) {
return nullptr;
}
return reinterpret_cast<char*>(buf) + hdr_size;
}
- unsigned int len() {
- return (entry.hdr_size ? entry.hdr_size
- : static_cast<uint16_t>(sizeof(entry_v1))) +
- entry.len;
- }
+ unsigned int len() { return entry.hdr_size + entry.len; }
#endif
};
-#endif
struct logger;
diff --git a/liblog/include/log/log_time.h b/liblog/include/log/log_time.h
index 09c9910..6b4458c 100644
--- a/liblog/include/log/log_time.h
+++ b/liblog/include/log/log_time.h
@@ -24,9 +24,6 @@
#define US_PER_SEC 1000000ULL
#define MS_PER_SEC 1000ULL
-#ifndef __struct_log_time_defined
-#define __struct_log_time_defined
-
#define LOG_TIME_SEC(t) ((t)->tv_sec)
/* next power of two after NS_PER_SEC */
#define LOG_TIME_NSEC(t) ((t)->tv_nsec & (UINT32_MAX >> 2))
@@ -35,11 +32,6 @@
extern "C" {
-/*
- * NB: we did NOT define a copy constructor. This will result in structure
- * no longer being compatible with pass-by-value which is desired
- * efficient behavior. Also, pass-by-reference breaks C/C++ ABI.
- */
struct log_time {
public:
uint32_t tv_sec = 0; /* good to Feb 5 2106 */
@@ -169,5 +161,3 @@
} __attribute__((__packed__)) log_time;
#endif /* __cplusplus */
-
-#endif /* __struct_log_time_defined */
diff --git a/liblog/include/log/logprint.h b/liblog/include/log/logprint.h
index 8f4b187..7dfd914 100644
--- a/liblog/include/log/logprint.h
+++ b/liblog/include/log/logprint.h
@@ -16,7 +16,8 @@
#pragma once
-#include <pthread.h>
+#include <stdint.h>
+#include <sys/types.h>
#include <android/log.h>
#include <log/event_tag_map.h>
diff --git a/liblog/include/private/android_logger.h b/liblog/include/private/android_logger.h
index 5e04148..d3b72bc 100644
--- a/liblog/include/private/android_logger.h
+++ b/liblog/include/private/android_logger.h
@@ -47,7 +47,7 @@
/* Header Structure to logd, and second header for pstore */
typedef struct __attribute__((__packed__)) {
- typeof_log_id_t id;
+ uint8_t id;
uint16_t tid;
log_time realtime;
} android_log_header_t;
@@ -57,6 +57,18 @@
int32_t tag; // Little Endian Order
} android_event_header_t;
+// Event payload EVENT_TYPE_LIST
+typedef struct __attribute__((__packed__)) {
+ int8_t type; // EVENT_TYPE_LIST
+ int8_t element_count;
+} android_event_list_t;
+
+// Event payload EVENT_TYPE_FLOAT
+typedef struct __attribute__((__packed__)) {
+ int8_t type; // EVENT_TYPE_FLOAT
+ float data;
+} android_event_float_t;
+
/* Event payload EVENT_TYPE_INT */
typedef struct __attribute__((__packed__)) {
int8_t type; // EVENT_TYPE_INT
diff --git a/liblog/log_event_list.cpp b/liblog/log_event_list.cpp
index 18ea930..7882c96 100644
--- a/liblog/log_event_list.cpp
+++ b/liblog/log_event_list.cpp
@@ -51,11 +51,9 @@
typedef struct android_log_context_internal android_log_context_internal;
static void init_context(android_log_context_internal* context, uint32_t tag) {
- size_t needed;
-
context->tag = tag;
context->read_write_flag = kAndroidLoggerWrite;
- needed = sizeof(uint8_t) + sizeof(uint8_t);
+ size_t needed = sizeof(android_event_list_t);
if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
context->overflow = true;
}
@@ -143,7 +141,6 @@
}
int android_log_write_list_begin(android_log_context ctx) {
- size_t needed;
android_log_context_internal* context;
context = (android_log_context_internal*)ctx;
@@ -154,7 +151,7 @@
context->overflow = true;
return -EOVERFLOW;
}
- needed = sizeof(uint8_t) + sizeof(uint8_t);
+ size_t needed = sizeof(android_event_list_t);
if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
context->overflow = true;
return -EIO;
@@ -168,8 +165,9 @@
if (context->overflow) {
return -EIO;
}
- context->storage[context->pos + 0] = EVENT_TYPE_LIST;
- context->storage[context->pos + 1] = 0;
+ auto* event_list = reinterpret_cast<android_event_list_t*>(&context->storage[context->pos]);
+ event_list->type = EVENT_TYPE_LIST;
+ event_list->element_count = 0;
context->list[context->list_nest_depth] = context->pos + 1;
context->count[context->list_nest_depth] = 0;
context->pos += needed;
@@ -177,57 +175,49 @@
}
int android_log_write_int32(android_log_context ctx, int32_t value) {
- size_t needed;
- android_log_context_internal* context;
-
- context = (android_log_context_internal*)ctx;
+ android_log_context_internal* context = (android_log_context_internal*)ctx;
if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
return -EBADF;
}
if (context->overflow) {
return -EIO;
}
- needed = sizeof(uint8_t) + sizeof(value);
+ size_t needed = sizeof(android_event_int_t);
if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
context->overflow = true;
return -EIO;
}
context->count[context->list_nest_depth]++;
- context->storage[context->pos + 0] = EVENT_TYPE_INT;
- *reinterpret_cast<int32_t*>(&context->storage[context->pos + 1]) = value;
+ auto* event_int = reinterpret_cast<android_event_int_t*>(&context->storage[context->pos]);
+ event_int->type = EVENT_TYPE_INT;
+ event_int->data = value;
context->pos += needed;
return 0;
}
int android_log_write_int64(android_log_context ctx, int64_t value) {
- size_t needed;
- android_log_context_internal* context;
-
- context = (android_log_context_internal*)ctx;
+ android_log_context_internal* context = (android_log_context_internal*)ctx;
if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
return -EBADF;
}
if (context->overflow) {
return -EIO;
}
- needed = sizeof(uint8_t) + sizeof(value);
+ size_t needed = sizeof(android_event_long_t);
if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
context->overflow = true;
return -EIO;
}
context->count[context->list_nest_depth]++;
- context->storage[context->pos + 0] = EVENT_TYPE_LONG;
- *reinterpret_cast<int64_t*>(&context->storage[context->pos + 1]) = value;
+ auto* event_long = reinterpret_cast<android_event_long_t*>(&context->storage[context->pos]);
+ event_long->type = EVENT_TYPE_LONG;
+ event_long->data = value;
context->pos += needed;
return 0;
}
int android_log_write_string8_len(android_log_context ctx, const char* value, size_t maxlen) {
- size_t needed;
- ssize_t len;
- android_log_context_internal* context;
-
- context = (android_log_context_internal*)ctx;
+ android_log_context_internal* context = (android_log_context_internal*)ctx;
if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
return -EBADF;
}
@@ -237,8 +227,8 @@
if (!value) {
value = "";
}
- len = strnlen(value, maxlen);
- needed = sizeof(uint8_t) + sizeof(int32_t) + len;
+ int32_t len = strnlen(value, maxlen);
+ size_t needed = sizeof(android_event_string_t) + len;
if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
/* Truncate string for delivery */
len = MAX_EVENT_PAYLOAD - context->pos - 1 - sizeof(int32_t);
@@ -248,10 +238,11 @@
}
}
context->count[context->list_nest_depth]++;
- context->storage[context->pos + 0] = EVENT_TYPE_STRING;
- *reinterpret_cast<ssize_t*>(&context->storage[context->pos + 1]) = len;
+ auto* event_string = reinterpret_cast<android_event_string_t*>(&context->storage[context->pos]);
+ event_string->type = EVENT_TYPE_STRING;
+ event_string->length = len;
if (len) {
- memcpy(&context->storage[context->pos + 5], value, len);
+ memcpy(&event_string->data, value, len);
}
context->pos += needed;
return len;
@@ -262,26 +253,22 @@
}
int android_log_write_float32(android_log_context ctx, float value) {
- size_t needed;
- uint32_t ivalue;
- android_log_context_internal* context;
-
- context = (android_log_context_internal*)ctx;
+ android_log_context_internal* context = (android_log_context_internal*)ctx;
if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
return -EBADF;
}
if (context->overflow) {
return -EIO;
}
- needed = sizeof(uint8_t) + sizeof(ivalue);
+ size_t needed = sizeof(android_event_float_t);
if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
context->overflow = true;
return -EIO;
}
- ivalue = *(uint32_t*)&value;
context->count[context->list_nest_depth]++;
- context->storage[context->pos + 0] = EVENT_TYPE_FLOAT;
- *reinterpret_cast<uint32_t*>(&context->storage[context->pos + 1]) = ivalue;
+ auto* event_float = reinterpret_cast<android_event_float_t*>(&context->storage[context->pos]);
+ event_float->type = EVENT_TYPE_FLOAT;
+ event_float->data = value;
context->pos += needed;
return 0;
}
@@ -443,20 +430,22 @@
return elem;
}
- elem.type = static_cast<AndroidEventLogType>(context->storage[pos++]);
+ elem.type = static_cast<AndroidEventLogType>(context->storage[pos]);
switch ((int)elem.type) {
case EVENT_TYPE_FLOAT:
/* Rely on union to translate elem.data.int32 into elem.data.float32 */
/* FALLTHRU */
- case EVENT_TYPE_INT:
+ case EVENT_TYPE_INT: {
elem.len = sizeof(int32_t);
- if ((pos + elem.len) > context->len) {
+ if ((pos + sizeof(android_event_int_t)) > context->len) {
elem.type = EVENT_TYPE_UNKNOWN;
return elem;
}
- elem.data.int32 = *reinterpret_cast<int32_t*>(&context->storage[pos]);
+
+ auto* event_int = reinterpret_cast<android_event_int_t*>(&context->storage[pos]);
+ pos += sizeof(android_event_int_t);
+ elem.data.int32 = event_int->data;
/* common tangeable object suffix */
- pos += elem.len;
elem.complete = !context->list_nest_depth && !context->count[0];
if (!peek) {
if (!context->count[context->list_nest_depth] ||
@@ -466,16 +455,19 @@
context->pos = pos;
}
return elem;
+ }
- case EVENT_TYPE_LONG:
+ case EVENT_TYPE_LONG: {
elem.len = sizeof(int64_t);
- if ((pos + elem.len) > context->len) {
+ if ((pos + sizeof(android_event_long_t)) > context->len) {
elem.type = EVENT_TYPE_UNKNOWN;
return elem;
}
- elem.data.int64 = *reinterpret_cast<int64_t*>(&context->storage[pos]);
+
+ auto* event_long = reinterpret_cast<android_event_long_t*>(&context->storage[pos]);
+ pos += sizeof(android_event_long_t);
+ elem.data.int64 = event_long->data;
/* common tangeable object suffix */
- pos += elem.len;
elem.complete = !context->list_nest_depth && !context->count[0];
if (!peek) {
if (!context->count[context->list_nest_depth] ||
@@ -485,15 +477,22 @@
context->pos = pos;
}
return elem;
+ }
- case EVENT_TYPE_STRING:
- if ((pos + sizeof(int32_t)) > context->len) {
+ case EVENT_TYPE_STRING: {
+ if ((pos + sizeof(android_event_string_t)) > context->len) {
elem.type = EVENT_TYPE_UNKNOWN;
elem.complete = true;
return elem;
}
- elem.len = *reinterpret_cast<int32_t*>(&context->storage[pos]);
- pos += sizeof(int32_t);
+ auto* event_string = reinterpret_cast<android_event_string_t*>(&context->storage[pos]);
+ pos += sizeof(android_event_string_t);
+ // Wire format is int32_t, but elem.len is uint16_t...
+ if (event_string->length >= UINT16_MAX) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ return elem;
+ }
+ elem.len = event_string->length;
if ((pos + elem.len) > context->len) {
elem.len = context->len - pos; /* truncate string */
elem.complete = true;
@@ -502,7 +501,7 @@
return elem;
}
}
- elem.data.string = (char*)&context->storage[pos];
+ elem.data.string = event_string->data;
/* common tangeable object suffix */
pos += elem.len;
elem.complete = !context->list_nest_depth && !context->count[0];
@@ -514,13 +513,16 @@
context->pos = pos;
}
return elem;
+ }
- case EVENT_TYPE_LIST:
- if ((pos + sizeof(uint8_t)) > context->len) {
+ case EVENT_TYPE_LIST: {
+ if ((pos + sizeof(android_event_list_t)) > context->len) {
elem.type = EVENT_TYPE_UNKNOWN;
elem.complete = true;
return elem;
}
+ auto* event_list = reinterpret_cast<android_event_list_t*>(&context->storage[pos]);
+ pos += sizeof(android_event_list_t);
elem.complete = context->list_nest_depth >= ANDROID_MAX_LIST_NEST_DEPTH;
if (peek) {
return elem;
@@ -528,15 +530,17 @@
if (context->count[context->list_nest_depth]) {
context->count[context->list_nest_depth]--;
}
- context->list_stop = !context->storage[pos];
+ context->list_stop = event_list->element_count == 0;
context->list_nest_depth++;
if (context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) {
- context->count[context->list_nest_depth] = context->storage[pos];
+ context->count[context->list_nest_depth] = event_list->element_count;
}
- context->pos = pos + sizeof(uint8_t);
+ context->pos = pos;
return elem;
+ }
case EVENT_TYPE_LIST_STOP: /* Suprise Newline terminates lists. */
+ pos++;
if (!peek) {
context->pos = pos;
}
diff --git a/liblog/logd_reader.cpp b/liblog/logd_reader.cpp
index 916a428..619cf8c 100644
--- a/liblog/logd_reader.cpp
+++ b/liblog/logd_reader.cpp
@@ -67,12 +67,12 @@
.name = "logd",
.available = logdAvailable,
.version = logdVersion,
+ .close = logdClose,
.read = logdRead,
.poll = logdPoll,
- .close = logdClose,
.clear = logdClear,
- .getSize = logdGetSize,
.setSize = logdSetSize,
+ .getSize = logdGetSize,
.getReadableSize = logdGetReadableSize,
.getPrune = logdGetPrune,
.setPrune = logdSetPrune,
@@ -107,7 +107,7 @@
strlcpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path));
int fd = socket(AF_LOCAL, type | SOCK_CLOEXEC, 0);
- if (fd == 0) {
+ if (fd == -1) {
return -1;
}
@@ -316,16 +316,11 @@
return check_log_success(buf, send_log_msg(NULL, NULL, buf, len));
}
-static void caught_signal(int signum __unused) {}
-
static int logdOpen(struct android_log_logger_list* logger_list,
struct android_log_transport_context* transp) {
struct android_log_logger* logger;
- struct sigaction ignore;
- struct sigaction old_sigaction;
- unsigned int old_alarm = 0;
char buffer[256], *cp, c;
- int e, ret, remaining, sock;
+ int ret, remaining, sock;
if (!logger_list) {
return -EINVAL;
@@ -337,12 +332,6 @@
}
sock = socket_local_client("logdr", SOCK_SEQPACKET);
- if (sock == 0) {
- /* Guarantee not file descriptor zero */
- int newsock = socket_local_client("logdr", SOCK_SEQPACKET);
- close(sock);
- sock = newsock;
- }
if (sock <= 0) {
if ((sock == -1) && errno) {
return -errno;
@@ -393,29 +382,13 @@
cp += ret;
}
- if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
- /* Deal with an unresponsive logd */
- memset(&ignore, 0, sizeof(ignore));
- ignore.sa_handler = caught_signal;
- sigemptyset(&ignore.sa_mask);
- /* particularily useful if tombstone is reporting for logd */
- sigaction(SIGALRM, &ignore, &old_sigaction);
- old_alarm = alarm(30);
- }
- ret = write(sock, buffer, cp - buffer);
- e = errno;
- if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
- if (e == EINTR) {
- e = ETIMEDOUT;
- }
- alarm(old_alarm);
- sigaction(SIGALRM, &old_sigaction, NULL);
- }
+ ret = TEMP_FAILURE_RETRY(write(sock, buffer, cp - buffer));
+ int write_errno = errno;
if (ret <= 0) {
close(sock);
- if ((ret == -1) && e) {
- return -e;
+ if (ret == -1) {
+ return -write_errno;
}
if (ret == 0) {
return -EIO;
@@ -433,52 +406,21 @@
/* Read from the selected logs */
static int logdRead(struct android_log_logger_list* logger_list,
struct android_log_transport_context* transp, struct log_msg* log_msg) {
- int ret, e;
- struct sigaction ignore;
- struct sigaction old_sigaction;
- unsigned int old_alarm = 0;
-
- ret = logdOpen(logger_list, transp);
+ int ret = logdOpen(logger_list, transp);
if (ret < 0) {
return ret;
}
memset(log_msg, 0, sizeof(*log_msg));
- unsigned int new_alarm = 0;
- if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
- if ((logger_list->mode & ANDROID_LOG_WRAP) &&
- (logger_list->start.tv_sec || logger_list->start.tv_nsec)) {
- /* b/64143705 */
- new_alarm = (ANDROID_LOG_WRAP_DEFAULT_TIMEOUT * 11) / 10 + 10;
- logger_list->mode &= ~ANDROID_LOG_WRAP;
- } else {
- new_alarm = 30;
- }
-
- memset(&ignore, 0, sizeof(ignore));
- ignore.sa_handler = caught_signal;
- sigemptyset(&ignore.sa_mask);
- /* particularily useful if tombstone is reporting for logd */
- sigaction(SIGALRM, &ignore, &old_sigaction);
- old_alarm = alarm(new_alarm);
- }
-
/* NOTE: SOCK_SEQPACKET guarantees we read exactly one full entry */
- ret = recv(ret, log_msg, LOGGER_ENTRY_MAX_LEN, 0);
- e = errno;
-
- if (new_alarm) {
- if ((ret == 0) || (e == EINTR)) {
- e = EAGAIN;
- ret = -1;
- }
- alarm(old_alarm);
- sigaction(SIGALRM, &old_sigaction, NULL);
+ ret = TEMP_FAILURE_RETRY(recv(ret, log_msg, LOGGER_ENTRY_MAX_LEN, 0));
+ if ((logger_list->mode & ANDROID_LOG_NONBLOCK) && ret == 0) {
+ return -EAGAIN;
}
- if ((ret == -1) && e) {
- return -e;
+ if (ret == -1) {
+ return -errno;
}
return ret;
}
diff --git a/liblog/logd_writer.cpp b/liblog/logd_writer.cpp
index 06a2baf..a22c3be 100644
--- a/liblog/logd_writer.cpp
+++ b/liblog/logd_writer.cpp
@@ -148,24 +148,6 @@
return 0;
}
- /*
- * struct {
- * // what we provide to socket
- * android_log_header_t header;
- * // caller provides
- * union {
- * struct {
- * char prio;
- * char payload[];
- * } string;
- * struct {
- * uint32_t tag
- * char payload[];
- * } binary;
- * };
- * };
- */
-
header.tid = gettid();
header.realtime.tv_sec = ts->tv_sec;
header.realtime.tv_nsec = ts->tv_nsec;
diff --git a/liblog/logger.h b/liblog/logger.h
index 8cae66c..02cad22 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -96,8 +96,6 @@
struct android_log_transport_read* transport;
unsigned logMask; /* mask of requested log buffers */
- int ret; /* return value associated with following data */
- struct log_msg logMsg; /* peek at upcoming data, valid if logMsg.len != 0 */
};
struct android_log_logger_list {
diff --git a/liblog/logger_read.cpp b/liblog/logger_read.cpp
index ff816b7..4b4012a 100644
--- a/liblog/logger_read.cpp
+++ b/liblog/logger_read.cpp
@@ -92,7 +92,6 @@
logger_list->transport_context.transport = transport;
logger_list->transport_context.logMask = logMask;
- logger_list->transport_context.ret = 1;
#endif
return 0;
}
@@ -273,34 +272,24 @@
struct log_msg* log_msg) {
int ret = (*transp->transport->read)(logger_list, transp, log_msg);
+ if (ret < 0) {
+ return ret;
+ }
+
if (ret > (int)sizeof(*log_msg)) {
ret = sizeof(*log_msg);
}
- transp->ret = ret;
-
- /* propagate errors, or make sure len & hdr_size members visible */
- if (ret < (int)(sizeof(log_msg->entry.len) + sizeof(log_msg->entry.hdr_size))) {
- if (ret >= (int)sizeof(log_msg->entry.len)) {
- log_msg->entry.len = 0;
- }
- return ret;
- }
-
- /* hdr_size correction (logger_entry -> logger_entry_v2+ conversion) */
- if (log_msg->entry_v2.hdr_size == 0) {
- log_msg->entry_v2.hdr_size = sizeof(struct logger_entry);
- }
- if ((log_msg->entry_v2.hdr_size < sizeof(log_msg->entry_v1)) ||
- (log_msg->entry_v2.hdr_size > sizeof(log_msg->entry))) {
+ if (ret < static_cast<int>(sizeof(log_msg->entry))) {
return -EINVAL;
}
- /* len validation */
- if (ret <= log_msg->entry_v2.hdr_size) {
- log_msg->entry.len = 0;
- } else {
- log_msg->entry.len = ret - log_msg->entry_v2.hdr_size;
+ if (log_msg->entry.hdr_size != sizeof(log_msg->entry)) {
+ return -EINVAL;
+ }
+
+ if (log_msg->entry.len > ret - log_msg->entry.hdr_size) {
+ return -EINVAL;
}
return ret;
diff --git a/liblog/logprint.cpp b/liblog/logprint.cpp
index dd2c797..4b61828 100644
--- a/liblog/logprint.cpp
+++ b/liblog/logprint.cpp
@@ -35,8 +35,10 @@
#include <wchar.h>
#include <cutils/list.h>
+
#include <log/log.h>
#include <log/logprint.h>
+#include <private/android_logger.h>
#include "log_portability.h"
@@ -530,18 +532,12 @@
int i;
char* msg = buf->msg;
- struct logger_entry_v2* buf2 = (struct logger_entry_v2*)buf;
- if (buf2->hdr_size) {
- if ((buf2->hdr_size < sizeof(((struct log_msg*)NULL)->entry_v1)) ||
- (buf2->hdr_size > sizeof(((struct log_msg*)NULL)->entry))) {
- fprintf(stderr, "+++ LOG: entry illegal hdr_size\n");
- return -1;
- }
- msg = ((char*)buf2) + buf2->hdr_size;
- if (buf2->hdr_size >= sizeof(struct logger_entry_v4)) {
- entry->uid = ((struct logger_entry_v4*)buf)->uid;
- }
+ if (buf->hdr_size != sizeof(struct logger_entry)) {
+ fprintf(stderr, "+++ LOG: entry illegal hdr_size\n");
+ return -1;
}
+ entry->uid = buf->uid;
+
for (i = 1; i < buf->len; i++) {
if (msg[i] == '\0') {
if (msgStart == -1) {
@@ -635,8 +631,7 @@
if (eventDataLen < 1) return -1;
- type = *eventData++;
- eventDataLen--;
+ type = *eventData;
cp = NULL;
len = 0;
@@ -725,22 +720,24 @@
case EVENT_TYPE_INT:
/* 32-bit signed int */
{
- int32_t ival;
-
- if (eventDataLen < 4) return -1;
- ival = *reinterpret_cast<const int32_t*>(eventData);
- eventData += 4;
- eventDataLen -= 4;
-
- lval = ival;
+ if (eventDataLen < sizeof(android_event_int_t)) return -1;
+ auto* event_int = reinterpret_cast<const android_event_int_t*>(eventData);
+ lval = event_int->data;
+ eventData += sizeof(android_event_int_t);
+ eventDataLen -= sizeof(android_event_int_t);
}
goto pr_lval;
case EVENT_TYPE_LONG:
/* 64-bit signed long */
- if (eventDataLen < 8) return -1;
- lval = *reinterpret_cast<const int64_t*>(eventData);
- eventData += 8;
- eventDataLen -= 8;
+ if (eventDataLen < sizeof(android_event_long_t)) {
+ return -1;
+ }
+ {
+ auto* event_long = reinterpret_cast<const android_event_long_t*>(eventData);
+ lval = event_long->data;
+ }
+ eventData += sizeof(android_event_long_t);
+ eventDataLen -= sizeof(android_event_long_t);
pr_lval:
outCount = snprintf(outBuf, outBufLen, "%" PRId64, lval);
if (outCount < outBufLen) {
@@ -754,14 +751,11 @@
case EVENT_TYPE_FLOAT:
/* float */
{
- uint32_t ival;
- float fval;
-
- if (eventDataLen < 4) return -1;
- ival = *reinterpret_cast<const uint32_t*>(eventData);
- fval = *(float*)&ival;
- eventData += 4;
- eventDataLen -= 4;
+ if (eventDataLen < sizeof(android_event_float_t)) return -1;
+ auto* event_float = reinterpret_cast<const android_event_float_t*>(eventData);
+ float fval = event_float->data;
+ eventData += sizeof(android_event_int_t);
+ eventDataLen -= sizeof(android_event_int_t);
outCount = snprintf(outBuf, outBufLen, "%f", fval);
if (outCount < outBufLen) {
@@ -776,12 +770,11 @@
case EVENT_TYPE_STRING:
/* UTF-8 chars, not NULL-terminated */
{
- unsigned int strLen;
-
- if (eventDataLen < 4) return -1;
- strLen = *reinterpret_cast<const uint32_t*>(eventData);
- eventData += 4;
- eventDataLen -= 4;
+ if (eventDataLen < sizeof(android_event_string_t)) return -1;
+ auto* event_string = reinterpret_cast<const android_event_string_t*>(eventData);
+ unsigned int strLen = event_string->length;
+ eventData += sizeof(android_event_string_t);
+ eventDataLen -= sizeof(android_event_string_t);
if (eventDataLen < strLen) {
result = -1; /* mark truncated */
@@ -814,20 +807,19 @@
case EVENT_TYPE_LIST:
/* N items, all different types */
{
- unsigned char count;
- int i;
+ if (eventDataLen < sizeof(android_event_list_t)) return -1;
+ auto* event_list = reinterpret_cast<const android_event_list_t*>(eventData);
- if (eventDataLen < 1) return -1;
-
- count = *eventData++;
- eventDataLen--;
+ int8_t count = event_list->element_count;
+ eventData += sizeof(android_event_list_t);
+ eventDataLen -= sizeof(android_event_list_t);
if (outBufLen <= 0) goto no_room;
*outBuf++ = '[';
outBufLen--;
- for (i = 0; i < count; i++) {
+ for (int i = 0; i < count; i++) {
result = android_log_printBinaryEvent(&eventData, &eventDataLen, &outBuf, &outBufLen,
fmtStr, fmtLen);
if (result != 0) goto bail;
@@ -995,32 +987,21 @@
entry->pid = buf->pid;
entry->tid = buf->tid;
- /*
- * Pull the tag out, fill in some additional details based on incoming
- * buffer version (v3 adds lid, v4 adds uid).
- */
eventData = (const unsigned char*)buf->msg;
- struct logger_entry_v2* buf2 = (struct logger_entry_v2*)buf;
- if (buf2->hdr_size) {
- if ((buf2->hdr_size < sizeof(((struct log_msg*)NULL)->entry_v1)) ||
- (buf2->hdr_size > sizeof(((struct log_msg*)NULL)->entry))) {
- fprintf(stderr, "+++ LOG: entry illegal hdr_size\n");
- return -1;
- }
- eventData = ((unsigned char*)buf2) + buf2->hdr_size;
- if ((buf2->hdr_size >= sizeof(struct logger_entry_v3)) &&
- (((struct logger_entry_v3*)buf)->lid == LOG_ID_SECURITY)) {
- entry->priority = ANDROID_LOG_WARN;
- }
- if (buf2->hdr_size >= sizeof(struct logger_entry_v4)) {
- entry->uid = ((struct logger_entry_v4*)buf)->uid;
- }
+ if (buf->hdr_size != sizeof(struct logger_entry)) {
+ fprintf(stderr, "+++ LOG: entry illegal hdr_size\n");
+ return -1;
}
+ if (buf->lid == LOG_ID_SECURITY) {
+ entry->priority = ANDROID_LOG_WARN;
+ }
+ entry->uid = buf->uid;
inCount = buf->len;
- if (inCount < 4) return -1;
- tagIndex = *reinterpret_cast<const uint32_t*>(eventData);
- eventData += 4;
- inCount -= 4;
+ if (inCount < sizeof(android_event_header_t)) return -1;
+ auto* event_header = reinterpret_cast<const android_event_header_t*>(eventData);
+ tagIndex = event_header->tag;
+ eventData += sizeof(android_event_header_t);
+ inCount -= sizeof(android_event_header_t);
entry->tagLen = 0;
entry->tag = NULL;
@@ -1070,9 +1051,6 @@
if ((result == 1) && fmtStr) {
/* We overflowed :-(, let's repaint the line w/o format dressings */
eventData = (const unsigned char*)buf->msg;
- if (buf2->hdr_size) {
- eventData = ((unsigned char*)buf2) + buf2->hdr_size;
- }
eventData += 4;
outBuf = messageBuf;
outRemaining = messageBufLen - 1;
diff --git a/liblog/pmsg_reader.cpp b/liblog/pmsg_reader.cpp
index 2db45a1..f43ce3a 100644
--- a/liblog/pmsg_reader.cpp
+++ b/liblog/pmsg_reader.cpp
@@ -40,9 +40,9 @@
.name = "pmsg",
.available = pmsgAvailable,
.version = pmsgVersion,
+ .close = pmsgClose,
.read = pmsgRead,
.poll = NULL,
- .close = pmsgClose,
.clear = pmsgClear,
.setSize = NULL,
.getSize = NULL,
@@ -62,58 +62,9 @@
return -EBADF;
}
-/* Determine the credentials of the caller */
-static bool uid_has_log_permission(uid_t uid) {
- return (uid == AID_SYSTEM) || (uid == AID_LOG) || (uid == AID_ROOT) || (uid == AID_LOGD);
-}
-
-static uid_t get_best_effective_uid() {
- uid_t euid;
- uid_t uid;
- gid_t gid;
- ssize_t i;
- static uid_t last_uid = (uid_t)-1;
-
- if (last_uid != (uid_t)-1) {
- return last_uid;
- }
- uid = __android_log_uid();
- if (uid_has_log_permission(uid)) {
- return last_uid = uid;
- }
- euid = geteuid();
- if (uid_has_log_permission(euid)) {
- return last_uid = euid;
- }
- gid = getgid();
- if (uid_has_log_permission(gid)) {
- return last_uid = gid;
- }
- gid = getegid();
- if (uid_has_log_permission(gid)) {
- return last_uid = gid;
- }
- i = getgroups((size_t)0, NULL);
- if (i > 0) {
- gid_t list[i];
-
- getgroups(i, list);
- while (--i >= 0) {
- if (uid_has_log_permission(list[i])) {
- return last_uid = list[i];
- }
- }
- }
- return last_uid = uid;
-}
-
static int pmsgClear(struct android_log_logger* logger __unused,
struct android_log_transport_context* transp __unused) {
- if (uid_has_log_permission(get_best_effective_uid())) {
- return unlink("/sys/fs/pstore/pmsg-ramoops-0");
- }
- errno = EPERM;
- return -1;
+ return unlink("/sys/fs/pstore/pmsg-ramoops-0");
}
/*
@@ -128,14 +79,12 @@
struct android_log_transport_context* transp, struct log_msg* log_msg) {
ssize_t ret;
off_t current, next;
- uid_t uid;
struct __attribute__((__packed__)) {
android_pmsg_log_header_t p;
android_log_header_t l;
uint8_t prio;
} buf;
static uint8_t preread_count;
- bool is_system;
memset(log_msg, 0, sizeof(*log_msg));
@@ -195,37 +144,30 @@
((logger_list->start.tv_sec != buf.l.realtime.tv_sec) ||
(logger_list->start.tv_nsec <= buf.l.realtime.tv_nsec)))) &&
(!logger_list->pid || (logger_list->pid == buf.p.pid))) {
- uid = get_best_effective_uid();
- is_system = uid_has_log_permission(uid);
- if (is_system || (uid == buf.p.uid)) {
- char* msg = is_system ? log_msg->entry_v4.msg : log_msg->entry_v3.msg;
- *msg = buf.prio;
- fd = atomic_load(&transp->context.fd);
- if (fd <= 0) {
- return -EBADF;
- }
- ret = TEMP_FAILURE_RETRY(read(fd, msg + sizeof(buf.prio), buf.p.len - sizeof(buf)));
- if (ret < 0) {
- return -errno;
- }
- if (ret != (ssize_t)(buf.p.len - sizeof(buf))) {
- return -EIO;
- }
-
- log_msg->entry_v4.len = buf.p.len - sizeof(buf) + sizeof(buf.prio);
- log_msg->entry_v4.hdr_size =
- is_system ? sizeof(log_msg->entry_v4) : sizeof(log_msg->entry_v3);
- log_msg->entry_v4.pid = buf.p.pid;
- log_msg->entry_v4.tid = buf.l.tid;
- log_msg->entry_v4.sec = buf.l.realtime.tv_sec;
- log_msg->entry_v4.nsec = buf.l.realtime.tv_nsec;
- log_msg->entry_v4.lid = buf.l.id;
- if (is_system) {
- log_msg->entry_v4.uid = buf.p.uid;
- }
-
- return ret + sizeof(buf.prio) + log_msg->entry_v4.hdr_size;
+ char* msg = log_msg->entry.msg;
+ *msg = buf.prio;
+ fd = atomic_load(&transp->context.fd);
+ if (fd <= 0) {
+ return -EBADF;
}
+ ret = TEMP_FAILURE_RETRY(read(fd, msg + sizeof(buf.prio), buf.p.len - sizeof(buf)));
+ if (ret < 0) {
+ return -errno;
+ }
+ if (ret != (ssize_t)(buf.p.len - sizeof(buf))) {
+ return -EIO;
+ }
+
+ log_msg->entry.len = buf.p.len - sizeof(buf) + sizeof(buf.prio);
+ log_msg->entry.hdr_size = sizeof(log_msg->entry);
+ log_msg->entry.pid = buf.p.pid;
+ log_msg->entry.tid = buf.l.tid;
+ log_msg->entry.sec = buf.l.realtime.tv_sec;
+ log_msg->entry.nsec = buf.l.realtime.tv_nsec;
+ log_msg->entry.lid = buf.l.id;
+ log_msg->entry.uid = buf.p.uid;
+
+ return ret + sizeof(buf.prio) + log_msg->entry.hdr_size;
}
fd = atomic_load(&transp->context.fd);
@@ -273,13 +215,7 @@
struct android_log_transport_context transp;
struct content {
struct listnode node;
- union {
- struct logger_entry_v4 entry;
- struct logger_entry_v4 entry_v4;
- struct logger_entry_v3 entry_v3;
- struct logger_entry_v2 entry_v2;
- struct logger_entry entry_v1;
- };
+ struct logger_entry entry;
} * content;
struct names {
struct listnode node;
@@ -331,25 +267,26 @@
}
/* Read the file content */
- while (pmsgRead(&logger_list, &transp, &transp.logMsg) > 0) {
+ log_msg log_msg;
+ while (pmsgRead(&logger_list, &transp, &log_msg) > 0) {
const char* cp;
- size_t hdr_size = transp.logMsg.entry.hdr_size ? transp.logMsg.entry.hdr_size
- : sizeof(transp.logMsg.entry_v1);
- char* msg = (char*)&transp.logMsg + hdr_size;
+ size_t hdr_size = log_msg.entry.hdr_size;
+
+ char* msg = (char*)&log_msg + hdr_size;
const char* split = NULL;
- if ((hdr_size < sizeof(transp.logMsg.entry_v1)) || (hdr_size > sizeof(transp.logMsg.entry))) {
+ if (hdr_size != sizeof(log_msg.entry)) {
continue;
}
/* Check for invalid sequence number */
- if ((transp.logMsg.entry.nsec % ANDROID_LOG_PMSG_FILE_SEQUENCE) ||
- ((transp.logMsg.entry.nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >=
- ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE)) {
+ if (log_msg.entry.nsec % ANDROID_LOG_PMSG_FILE_SEQUENCE ||
+ (log_msg.entry.nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >=
+ ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE) {
continue;
}
/* Determine if it has <dirbase>:<filebase> format for tag */
- len = transp.logMsg.entry.len - sizeof(prio);
+ len = log_msg.entry.len - sizeof(prio);
for (cp = msg + sizeof(prio); *cp && isprint(*cp) && !isspace(*cp) && --len; ++cp) {
if (*cp == ':') {
if (split) {
@@ -395,8 +332,8 @@
/* check if there is an existing entry */
list_for_each(node, &name_list) {
names = node_to_item(node, struct names, node);
- if (!strcmp(names->name, msg + sizeof(prio)) && (names->id == transp.logMsg.entry.lid) &&
- (names->prio == *msg)) {
+ if (!strcmp(names->name, msg + sizeof(prio)) && names->id == log_msg.entry.lid &&
+ names->prio == *msg) {
break;
}
}
@@ -413,7 +350,7 @@
break;
}
strcpy(names->name, msg + sizeof(prio));
- names->id = static_cast<log_id_t>(transp.logMsg.entry.lid);
+ names->id = static_cast<log_id_t>(log_msg.entry.lid);
names->prio = *msg;
list_init(&names->content);
/*
@@ -466,7 +403,7 @@
/* Remove any file fragments that match our sequence number */
list_for_each_safe(node, n, &names->content) {
content = node_to_item(node, struct content, node);
- if (transp.logMsg.entry.nsec == content->entry.nsec) {
+ if (log_msg.entry.nsec == content->entry.nsec) {
list_remove(&content->node);
free(content);
}
@@ -474,16 +411,16 @@
/* Add content */
content = static_cast<struct content*>(
- calloc(1, sizeof(content->node) + hdr_size + transp.logMsg.entry.len));
+ calloc(1, sizeof(content->node) + hdr_size + log_msg.entry.len));
if (!content) {
ret = -ENOMEM;
break;
}
- memcpy(&content->entry, &transp.logMsg.entry, hdr_size + transp.logMsg.entry.len);
+ memcpy(&content->entry, &log_msg.entry, hdr_size + log_msg.entry.len);
/* Insert in sequence number sorted order, to ease reconstruction */
list_for_each_reverse(node, &names->content) {
- if ((node_to_item(node, struct content, node))->entry.nsec < transp.logMsg.entry.nsec) {
+ if ((node_to_item(node, struct content, node))->entry.nsec < log_msg.entry.nsec) {
break;
}
}
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 4642b9b..56892a2 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -17,6 +17,7 @@
#include <fcntl.h>
#include <inttypes.h>
#include <poll.h>
+#include <sched.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/types.h>
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 9780b28..c402e20 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -27,10 +27,12 @@
#include <sys/types.h>
#include <unistd.h>
+#include <memory>
#include <string>
#include <android-base/file.h>
#include <android-base/macros.h>
+#include <android-base/scopeguard.h>
#include <android-base/stringprintf.h>
#ifdef __ANDROID__ // includes sys/properties.h which does not exist outside
#include <cutils/properties.h>
@@ -42,6 +44,8 @@
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
+using android::base::make_scope_guard;
+
// #define ENABLE_FLAKY_TESTS
// enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and
@@ -58,6 +62,80 @@
_rc; \
})
+// std::unique_ptr doesn't let you provide a pointer to a deleter (android_logger_list_close()) if
+// the type (struct logger_list) is an incomplete type, so we create ListCloser instead.
+struct ListCloser {
+ void operator()(struct logger_list* list) { android_logger_list_close(list); }
+};
+
+// This function is meant to be used for most log tests, it does the following:
+// 1) Open the log_buffer with a blocking reader
+// 2) Write the messages via write_messages
+// 3) Set an alarm for 2 seconds as a timeout
+// 4) Read until check_message returns true, which should be used to indicate the target message
+// is found
+// 5) Open log_buffer with a non_blocking reader and dump all messages
+// 6) Count the number of times check_messages returns true for these messages and assert it's
+// only 1.
+template <typename FWrite, typename FCheck>
+static void RunLogTests(log_id_t log_buffer, FWrite write_messages, FCheck check_message) {
+ pid_t pid = getpid();
+
+ auto logger_list = std::unique_ptr<struct logger_list, ListCloser>{
+ android_logger_list_open(log_buffer, ANDROID_LOG_RDONLY, 1000, pid)};
+ ASSERT_TRUE(logger_list);
+
+ write_messages();
+
+ alarm(2);
+ auto alarm_guard = android::base::make_scope_guard([] { alarm(0); });
+ bool found = false;
+ while (!found) {
+ log_msg log_msg;
+ ASSERT_GT(android_logger_list_read(logger_list.get(), &log_msg), 0);
+
+ ASSERT_EQ(log_buffer, log_msg.id());
+ ASSERT_EQ(pid, log_msg.entry.pid);
+
+ // TODO: Should this be an assert?
+ if (log_msg.msg() == nullptr) {
+ continue;
+ }
+
+ check_message(log_msg, &found);
+ }
+
+ auto logger_list_non_block = std::unique_ptr<struct logger_list, ListCloser>{
+ android_logger_list_open(log_buffer, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)};
+ ASSERT_TRUE(logger_list_non_block);
+
+ size_t count = 0;
+ while (true) {
+ log_msg log_msg;
+ auto ret = android_logger_list_read(logger_list_non_block.get(), &log_msg);
+ if (ret == -EAGAIN) {
+ break;
+ }
+ ASSERT_GT(ret, 0);
+
+ ASSERT_EQ(log_buffer, log_msg.id());
+ ASSERT_EQ(pid, log_msg.entry.pid);
+
+ // TODO: Should this be an assert?
+ if (log_msg.msg() == nullptr) {
+ continue;
+ }
+
+ found = false;
+ check_message(log_msg, &found);
+ if (found) {
+ ++count;
+ }
+ }
+
+ EXPECT_EQ(1U, count);
+}
+
TEST(liblog, __android_log_btwrite) {
int intBuf = 0xDEADBEEF;
EXPECT_LT(0,
@@ -65,14 +143,11 @@
long long longBuf = 0xDEADBEEFA55A5AA5;
EXPECT_LT(
0, __android_log_btwrite(0, EVENT_TYPE_LONG, &longBuf, sizeof(longBuf)));
- usleep(1000);
char Buf[] = "\20\0\0\0DeAdBeEfA55a5aA5";
EXPECT_LT(0,
__android_log_btwrite(0, EVENT_TYPE_STRING, Buf, sizeof(Buf) - 1));
- usleep(1000);
}
-#ifdef ENABLE_FLAKY_TESTS
#if defined(__ANDROID__)
static std::string popenToString(const std::string& command) {
std::string ret;
@@ -141,81 +216,63 @@
static bool tested__android_log_close;
#endif
-#endif // ENABLE_FLAKY_TESTS
TEST(liblog, __android_log_btwrite__android_logger_list_read) {
#ifdef __ANDROID__
- struct logger_list* logger_list;
-
- pid_t pid = getpid();
-
- ASSERT_TRUE(NULL !=
- (logger_list = android_logger_list_open(
- LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
- 1000, pid)));
-
log_time ts(CLOCK_MONOTONIC);
- EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
-#ifdef ENABLE_FLAKY_TESTS
- // Check that we can close and reopen the logger
- bool logdwActiveAfter__android_log_btwrite;
- if (getuid() == AID_ROOT) {
- tested__android_log_close = true;
-#ifndef NO_PSTORE
- bool pmsgActiveAfter__android_log_btwrite = isPmsgActive();
- EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
-#endif /* NO_PSTORE */
- logdwActiveAfter__android_log_btwrite = isLogdwActive();
- EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
- } else if (!tested__android_log_close) {
- fprintf(stderr, "WARNING: can not test __android_log_close()\n");
- }
- __android_log_close();
- if (getuid() == AID_ROOT) {
-#ifndef NO_PSTORE
- bool pmsgActiveAfter__android_log_close = isPmsgActive();
- EXPECT_FALSE(pmsgActiveAfter__android_log_close);
-#endif /* NO_PSTORE */
- bool logdwActiveAfter__android_log_close = isLogdwActive();
- EXPECT_FALSE(logdwActiveAfter__android_log_close);
- }
-#endif // ENABLE_FLAKY_TESTS
+ log_time ts1(ts);
- log_time ts1(CLOCK_MONOTONIC);
- EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts1, sizeof(ts1)));
-#ifdef ENABLE_FLAKY_TESTS
- if (getuid() == AID_ROOT) {
+ auto write_function = [&] {
+ EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+ // Check that we can close and reopen the logger
+ bool logdwActiveAfter__android_log_btwrite;
+ if (getuid() == AID_ROOT) {
+ tested__android_log_close = true;
#ifndef NO_PSTORE
- bool pmsgActiveAfter__android_log_btwrite = isPmsgActive();
- EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
+ bool pmsgActiveAfter__android_log_btwrite = isPmsgActive();
+ EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
#endif /* NO_PSTORE */
- logdwActiveAfter__android_log_btwrite = isLogdwActive();
- EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
- }
-#endif // ENABLE_FLAKY_TESTS
- usleep(1000000);
+ logdwActiveAfter__android_log_btwrite = isLogdwActive();
+ EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
+ } else if (!tested__android_log_close) {
+ fprintf(stderr, "WARNING: can not test __android_log_close()\n");
+ }
+ __android_log_close();
+ if (getuid() == AID_ROOT) {
+#ifndef NO_PSTORE
+ bool pmsgActiveAfter__android_log_close = isPmsgActive();
+ EXPECT_FALSE(pmsgActiveAfter__android_log_close);
+#endif /* NO_PSTORE */
+ bool logdwActiveAfter__android_log_close = isLogdwActive();
+ EXPECT_FALSE(logdwActiveAfter__android_log_close);
+ }
+
+ ts1 = log_time(CLOCK_MONOTONIC);
+ EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts1, sizeof(ts1)));
+ if (getuid() == AID_ROOT) {
+#ifndef NO_PSTORE
+ bool pmsgActiveAfter__android_log_btwrite = isPmsgActive();
+ EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
+#endif /* NO_PSTORE */
+ logdwActiveAfter__android_log_btwrite = isLogdwActive();
+ EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
+ }
+ };
int count = 0;
int second_count = 0;
- for (;;) {
- log_msg log_msg;
- if (android_logger_list_read(logger_list, &log_msg) <= 0) {
- break;
- }
-
- EXPECT_EQ(log_msg.entry.pid, pid);
-
+ auto check_function = [&](log_msg log_msg, bool* found) {
if ((log_msg.entry.len != sizeof(android_log_event_long_t)) ||
(log_msg.id() != LOG_ID_EVENTS)) {
- continue;
+ return;
}
android_log_event_long_t* eventData;
eventData = reinterpret_cast<android_log_event_long_t*>(log_msg.msg());
if (!eventData || (eventData->payload.type != EVENT_TYPE_LONG)) {
- continue;
+ return;
}
log_time tx(reinterpret_cast<char*>(&eventData->payload.data));
@@ -224,12 +281,50 @@
} else if (ts1 == tx) {
++second_count;
}
- }
- EXPECT_EQ(1, count);
- EXPECT_EQ(1, second_count);
+ if (count == 1 && second_count == 1) {
+ count = 0;
+ second_count = 0;
+ *found = true;
+ }
+ };
- android_logger_list_close(logger_list);
+ RunLogTests(LOG_ID_EVENTS, write_function, check_function);
+
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, __android_log_write__android_logger_list_read) {
+#ifdef __ANDROID__
+ pid_t pid = getpid();
+
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ std::string buf = android::base::StringPrintf("pid=%u ts=%ld.%09ld", pid, ts.tv_sec, ts.tv_nsec);
+ static const char tag[] = "liblog.__android_log_write__android_logger_list_read";
+ static const char prio = ANDROID_LOG_DEBUG;
+
+ std::string expected_message =
+ std::string(&prio, sizeof(prio)) + tag + std::string("", 1) + buf + std::string("", 1);
+
+ auto write_function = [&] { ASSERT_LT(0, __android_log_write(prio, tag, buf.c_str())); };
+
+ auto check_function = [&](log_msg log_msg, bool* found) {
+ if (log_msg.entry.len != expected_message.length()) {
+ return;
+ }
+
+ if (expected_message != std::string(log_msg.msg(), log_msg.entry.len)) {
+ return;
+ }
+
+ *found = true;
+ };
+
+ RunLogTests(LOG_ID_MAIN, write_function, check_function);
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
@@ -237,18 +332,10 @@
static void bswrite_test(const char* message) {
#ifdef __ANDROID__
- struct logger_list* logger_list;
-
pid_t pid = getpid();
- ASSERT_TRUE(NULL !=
- (logger_list = android_logger_list_open(
- LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
- 1000, pid)));
-
log_time ts(android_log_clockid());
- EXPECT_LT(0, __android_log_bswrite(0, message));
size_t num_lines = 1, size = 0, length = 0, total = 0;
const char* cp = message;
while (*cp) {
@@ -270,36 +357,25 @@
++cp;
++total;
}
- usleep(1000000);
- int count = 0;
+ auto write_function = [&] { EXPECT_LT(0, __android_log_bswrite(0, message)); };
- for (;;) {
- log_msg log_msg;
- if (android_logger_list_read(logger_list, &log_msg) <= 0) {
- break;
- }
-
- EXPECT_EQ(log_msg.entry.pid, pid);
-
- if ((log_msg.entry.sec < (ts.tv_sec - 1)) ||
- ((ts.tv_sec + 1) < log_msg.entry.sec) ||
- ((size_t)log_msg.entry.len !=
- (sizeof(android_log_event_string_t) + length)) ||
- (log_msg.id() != LOG_ID_EVENTS)) {
- continue;
+ auto check_function = [&](log_msg log_msg, bool* found) {
+ if ((size_t)log_msg.entry.len != (sizeof(android_log_event_string_t) + length) ||
+ log_msg.id() != LOG_ID_EVENTS) {
+ return;
}
android_log_event_string_t* eventData;
eventData = reinterpret_cast<android_log_event_string_t*>(log_msg.msg());
if (!eventData || (eventData->type != EVENT_TYPE_STRING)) {
- continue;
+ return;
}
size_t len = eventData->length;
if (len == total) {
- ++count;
+ *found = true;
AndroidLogFormat* logformat = android_log_format_new();
EXPECT_TRUE(NULL != logformat);
@@ -309,7 +385,7 @@
fprintf(stderr, "Expect \"Binary log entry conversion failed\"\n");
}
int processBinaryLogBuffer = android_log_processBinaryLogBuffer(
- &log_msg.entry_v1, &entry, NULL, msgBuf, sizeof(msgBuf));
+ &log_msg.entry, &entry, nullptr, msgBuf, sizeof(msgBuf));
EXPECT_EQ((length == total) ? 0 : -1, processBinaryLogBuffer);
if ((processBinaryLogBuffer == 0) || entry.message) {
size_t line_overhead = 20;
@@ -326,11 +402,10 @@
}
android_log_format_free(logformat);
}
- }
+ };
- EXPECT_EQ(1, count);
+ RunLogTests(LOG_ID_EVENTS, write_function, check_function);
- android_logger_list_close(logger_list);
#else
message = NULL;
GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -359,20 +434,14 @@
static void buf_write_test(const char* message) {
#ifdef __ANDROID__
- struct logger_list* logger_list;
-
pid_t pid = getpid();
- ASSERT_TRUE(
- NULL !=
- (logger_list = android_logger_list_open(
- LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
-
static const char tag[] = "TEST__android_log_buf_write";
log_time ts(android_log_clockid());
- EXPECT_LT(
- 0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO, tag, message));
+ auto write_function = [&] {
+ EXPECT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO, tag, message));
+ };
size_t num_lines = 1, size = 0, length = 0;
const char* cp = message;
while (*cp) {
@@ -389,32 +458,18 @@
}
++cp;
}
- usleep(1000000);
- int count = 0;
-
- for (;;) {
- log_msg log_msg;
- if (android_logger_list_read(logger_list, &log_msg) <= 0) {
- break;
+ auto check_function = [&](log_msg log_msg, bool* found) {
+ if ((size_t)log_msg.entry.len != (sizeof(tag) + length + 2) || log_msg.id() != LOG_ID_MAIN) {
+ return;
}
- ASSERT_EQ(log_msg.entry.pid, pid);
-
- if ((log_msg.entry.sec < (ts.tv_sec - 1)) ||
- ((ts.tv_sec + 1) < log_msg.entry.sec) ||
- ((size_t)log_msg.entry.len != (sizeof(tag) + length + 2)) ||
- (log_msg.id() != LOG_ID_MAIN)) {
- continue;
- }
-
- ++count;
+ *found = true;
AndroidLogFormat* logformat = android_log_format_new();
EXPECT_TRUE(NULL != logformat);
AndroidLogEntry entry;
- int processLogBuffer =
- android_log_processLogBuffer(&log_msg.entry_v1, &entry);
+ int processLogBuffer = android_log_processLogBuffer(&log_msg.entry, &entry);
EXPECT_EQ(0, processLogBuffer);
if (processLogBuffer == 0) {
size_t line_overhead = 11;
@@ -425,11 +480,10 @@
android_log_printLogLine(logformat, fileno(stderr), &entry));
}
android_log_format_free(logformat);
- }
+ };
- EXPECT_EQ(1, count);
+ RunLogTests(LOG_ID_MAIN, write_function, check_function);
- android_logger_list_close(logger_list);
#else
message = NULL;
GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -892,7 +946,6 @@
when you depart from me, sorrow abides and happiness\n\
takes his leave.";
-#ifdef ENABLE_FLAKY_TESTS
TEST(liblog, max_payload) {
#ifdef __ANDROID__
static const char max_payload_tag[] = "TEST_max_payload_and_longish_tag_XXXX";
@@ -903,32 +956,17 @@
memcpy(tag, max_payload_tag, sizeof(tag));
snprintf(tag + sizeof(tag) - 5, 5, "%04X", pid & 0xFFFF);
- LOG_FAILURE_RETRY(__android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
- tag, max_payload_buf));
- sleep(2);
+ auto write_function = [&] {
+ LOG_FAILURE_RETRY(
+ __android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO, tag, max_payload_buf));
+ };
- struct logger_list* logger_list;
-
- ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
- LOG_ID_SYSTEM, ANDROID_LOG_RDONLY, 100, 0)));
-
- bool matches = false;
ssize_t max_len = 0;
-
- for (;;) {
- log_msg log_msg;
- if (android_logger_list_read(logger_list, &log_msg) <= 0) {
- break;
- }
-
- if ((log_msg.entry.pid != pid) || (log_msg.id() != LOG_ID_SYSTEM)) {
- continue;
- }
-
+ auto check_function = [&](log_msg log_msg, bool* found) {
char* data = log_msg.msg();
if (!data || strcmp(++data, tag)) {
- continue;
+ return;
}
data += strlen(data) + 1;
@@ -945,63 +983,36 @@
}
if (max_len > 512) {
- matches = true;
- break;
+ *found = true;
}
- }
+ };
- android_logger_list_close(logger_list);
-
- EXPECT_EQ(true, matches);
+ RunLogTests(LOG_ID_SYSTEM, write_function, check_function);
EXPECT_LE(SIZEOF_MAX_PAYLOAD_BUF, static_cast<size_t>(max_len));
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
-#endif // ENABLE_FLAKY_TESTS
TEST(liblog, __android_log_buf_print__maxtag) {
#ifdef __ANDROID__
- struct logger_list* logger_list;
+ auto write_function = [&] {
+ EXPECT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO, max_payload_buf,
+ max_payload_buf));
+ };
- pid_t pid = getpid();
-
- ASSERT_TRUE(
- NULL !=
- (logger_list = android_logger_list_open(
- LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
-
- log_time ts(android_log_clockid());
-
- EXPECT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
- max_payload_buf, max_payload_buf));
- usleep(1000000);
-
- int count = 0;
-
- for (;;) {
- log_msg log_msg;
- if (android_logger_list_read(logger_list, &log_msg) <= 0) {
- break;
+ auto check_function = [&](log_msg log_msg, bool* found) {
+ if ((size_t)log_msg.entry.len < LOGGER_ENTRY_MAX_PAYLOAD) {
+ return;
}
- ASSERT_EQ(log_msg.entry.pid, pid);
-
- if ((log_msg.entry.sec < (ts.tv_sec - 1)) ||
- ((ts.tv_sec + 1) < log_msg.entry.sec) ||
- ((size_t)log_msg.entry.len < LOGGER_ENTRY_MAX_PAYLOAD) ||
- (log_msg.id() != LOG_ID_MAIN)) {
- continue;
- }
-
- ++count;
+ *found = true;
AndroidLogFormat* logformat = android_log_format_new();
EXPECT_TRUE(NULL != logformat);
AndroidLogEntry entry;
- int processLogBuffer =
- android_log_processLogBuffer(&log_msg.entry_v1, &entry);
+ int processLogBuffer = android_log_processLogBuffer(&log_msg.entry, &entry);
EXPECT_EQ(0, processLogBuffer);
if (processLogBuffer == 0) {
fflush(stderr);
@@ -1013,16 +1024,18 @@
EXPECT_GT(LOGGER_ENTRY_MAX_PAYLOAD * 13 / 8, printLogLine);
}
android_log_format_free(logformat);
- }
+ };
- EXPECT_EQ(1, count);
+ RunLogTests(LOG_ID_MAIN, write_function, check_function);
- android_logger_list_close(logger_list);
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
+// TODO: This test is tautological. android_logger_list_read() calls recv() with
+// LOGGER_ENTRY_MAX_PAYLOAD as its size argument, so it's not possible for this test to read a
+// payload larger than that size.
TEST(liblog, too_big_payload) {
#ifdef __ANDROID__
pid_t pid = getpid();
@@ -1032,32 +1045,18 @@
snprintf(tag + sizeof(tag) - 5, 5, "%04X", pid & 0xFFFF);
std::string longString(3266519, 'x');
+ ssize_t ret;
- ssize_t ret = LOG_FAILURE_RETRY(__android_log_buf_write(
- LOG_ID_SYSTEM, ANDROID_LOG_INFO, tag, longString.c_str()));
+ auto write_function = [&] {
+ ret = LOG_FAILURE_RETRY(
+ __android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO, tag, longString.c_str()));
+ };
- struct logger_list* logger_list;
-
- ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
- LOG_ID_SYSTEM,
- ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 100, 0)));
-
- ssize_t max_len = 0;
-
- for (;;) {
- log_msg log_msg;
- if (android_logger_list_read(logger_list, &log_msg) <= 0) {
- break;
- }
-
- if ((log_msg.entry.pid != pid) || (log_msg.id() != LOG_ID_SYSTEM)) {
- continue;
- }
-
+ auto check_function = [&](log_msg log_msg, bool* found) {
char* data = log_msg.msg();
if (!data || strcmp(++data, tag)) {
- continue;
+ return;
}
data += strlen(data) + 1;
@@ -1069,33 +1068,38 @@
++right;
}
- if (max_len <= (left - data)) {
- max_len = left - data + 1;
+ ssize_t len = left - data + 1;
+ // Check that we don't see any entries larger than the max payload.
+ EXPECT_LE(static_cast<size_t>(len), LOGGER_ENTRY_MAX_PAYLOAD - sizeof(big_payload_tag));
+
+ // Once we've found our expected entry, break.
+ if (len == LOGGER_ENTRY_MAX_PAYLOAD - sizeof(big_payload_tag)) {
+ EXPECT_EQ(ret, len + static_cast<ssize_t>(sizeof(big_payload_tag)));
+ *found = true;
}
- }
+ };
- android_logger_list_close(logger_list);
+ RunLogTests(LOG_ID_SYSTEM, write_function, check_function);
- EXPECT_LE(LOGGER_ENTRY_MAX_PAYLOAD - sizeof(big_payload_tag),
- static_cast<size_t>(max_len));
-
- // SLOP: Allow the underlying interface to optionally place a
- // terminating nul at the LOGGER_ENTRY_MAX_PAYLOAD's last byte
- // or not.
- if (ret == (max_len + static_cast<ssize_t>(sizeof(big_payload_tag)) - 1)) {
- --max_len;
- }
- EXPECT_EQ(ret, max_len + static_cast<ssize_t>(sizeof(big_payload_tag)));
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
-#ifdef ENABLE_FLAKY_TESTS
TEST(liblog, dual_reader) {
#ifdef __ANDROID__
+ static const int expected_count1 = 25;
+ static const int expected_count2 = 25;
- static const int num = 25;
+ pid_t pid = getpid();
+
+ auto logger_list1 = std::unique_ptr<struct logger_list, ListCloser>{
+ android_logger_list_open(LOG_ID_MAIN, ANDROID_LOG_RDONLY, expected_count1, pid)};
+ ASSERT_TRUE(logger_list1);
+
+ auto logger_list2 = std::unique_ptr<struct logger_list, ListCloser>{
+ android_logger_list_open(LOG_ID_MAIN, ANDROID_LOG_RDONLY, expected_count2, pid)};
+ ASSERT_TRUE(logger_list2);
for (int i = 25; i > 0; --i) {
static const char fmt[] = "dual_reader %02d";
@@ -1104,32 +1108,46 @@
LOG_FAILURE_RETRY(__android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO,
"liblog", buffer));
}
- usleep(1000000);
- struct logger_list* logger_list1;
- ASSERT_TRUE(NULL != (logger_list1 = android_logger_list_open(
- LOG_ID_MAIN,
- ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, num, 0)));
+ alarm(2);
+ auto alarm_guard = android::base::make_scope_guard([] { alarm(0); });
- struct logger_list* logger_list2;
+ // Wait until we see all messages with the blocking reader.
+ int count1 = 0;
+ int count2 = 0;
- if (NULL == (logger_list2 = android_logger_list_open(
- LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
- num - 10, 0))) {
- android_logger_list_close(logger_list1);
- ASSERT_TRUE(NULL != logger_list2);
+ while (count1 != expected_count2 || count2 != expected_count2) {
+ log_msg log_msg;
+ if (count1 < expected_count1) {
+ ASSERT_GT(android_logger_list_read(logger_list1.get(), &log_msg), 0);
+ count1++;
+ }
+ if (count2 < expected_count2) {
+ ASSERT_GT(android_logger_list_read(logger_list2.get(), &log_msg), 0);
+ count2++;
+ }
}
- int count1 = 0;
+ // Test again with the nonblocking reader.
+ auto logger_list_non_block1 =
+ std::unique_ptr<struct logger_list, ListCloser>{android_logger_list_open(
+ LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, expected_count1, pid)};
+ ASSERT_TRUE(logger_list_non_block1);
+
+ auto logger_list_non_block2 =
+ std::unique_ptr<struct logger_list, ListCloser>{android_logger_list_open(
+ LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, expected_count2, pid)};
+ ASSERT_TRUE(logger_list_non_block2);
+ count1 = 0;
+ count2 = 0;
bool done1 = false;
- int count2 = 0;
bool done2 = false;
- do {
+ while (!done1 || !done2) {
log_msg log_msg;
if (!done1) {
- if (android_logger_list_read(logger_list1, &log_msg) <= 0) {
+ if (android_logger_list_read(logger_list_non_block1.get(), &log_msg) <= 0) {
done1 = true;
} else {
++count1;
@@ -1137,26 +1155,21 @@
}
if (!done2) {
- if (android_logger_list_read(logger_list2, &log_msg) <= 0) {
+ if (android_logger_list_read(logger_list_non_block2.get(), &log_msg) <= 0) {
done2 = true;
} else {
++count2;
}
}
- } while ((!done1) || (!done2));
+ }
- android_logger_list_close(logger_list1);
- android_logger_list_close(logger_list2);
-
- EXPECT_EQ(num, count1);
- EXPECT_EQ(num - 10, count2);
+ EXPECT_EQ(expected_count1, count1);
+ EXPECT_EQ(expected_count2, count2);
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
-#endif // ENABLE_FLAKY_TESTS
-#ifdef ENABLE_FLAKY_TESTS
static bool checkPriForTag(AndroidLogFormat* p_format, const char* tag,
android_LogPriority pri) {
return android_log_shouldPrintLine(p_format, tag, pri) &&
@@ -1232,7 +1245,6 @@
android_log_format_free(p_format);
}
-#endif // ENABLE_FLAKY_TESTS
#ifdef ENABLE_FLAKY_TESTS
TEST(liblog, is_loggable) {
@@ -1886,109 +1898,71 @@
#endif // ENABLE_FLAKY_TESTS
#ifdef __ANDROID__
-static void android_errorWriteWithInfoLog_helper(int TAG, const char* SUBTAG,
- int UID, const char* payload,
- int DATA_LEN, int& count) {
- struct logger_list* logger_list;
+static void android_errorWriteWithInfoLog_helper(int tag, const char* subtag, int uid,
+ const char* payload, int data_len) {
+ auto write_function = [&] {
+ int ret = android_errorWriteWithInfoLog(tag, subtag, uid, payload, data_len);
+ ASSERT_LT(0, ret);
+ };
- pid_t pid = getpid();
-
- count = 0;
-
- ASSERT_TRUE(NULL !=
- (logger_list = android_logger_list_open(
- LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
- 1000, pid)));
-
- int retval_android_errorWriteWithinInfoLog =
- android_errorWriteWithInfoLog(TAG, SUBTAG, UID, payload, DATA_LEN);
- if (payload) {
- ASSERT_LT(0, retval_android_errorWriteWithinInfoLog);
- } else {
- ASSERT_GT(0, retval_android_errorWriteWithinInfoLog);
- }
-
- sleep(2);
-
- for (;;) {
- log_msg log_msg;
- if (android_logger_list_read(logger_list, &log_msg) <= 0) {
- break;
- }
-
- char* eventData = log_msg.msg();
- if (!eventData) {
- continue;
- }
-
- char* original = eventData;
+ auto check_function = [&](log_msg log_msg, bool* found) {
+ char* event_data = log_msg.msg();
+ char* original = event_data;
// Tag
- int tag = *reinterpret_cast<int32_t*>(eventData);
- eventData += 4;
-
- if (tag != TAG) {
- continue;
- }
-
- if (!payload) {
- // This tag should not have been written because the data was null
- ++count;
- break;
+ auto* event_header = reinterpret_cast<android_event_header_t*>(event_data);
+ event_data += sizeof(android_event_header_t);
+ if (event_header->tag != tag) {
+ return;
}
// List type
- ASSERT_EQ(EVENT_TYPE_LIST, eventData[0]);
- eventData++;
-
- // Number of elements in list
- ASSERT_EQ(3, eventData[0]);
- eventData++;
+ auto* event_list = reinterpret_cast<android_event_list_t*>(event_data);
+ ASSERT_EQ(EVENT_TYPE_LIST, event_list->type);
+ ASSERT_EQ(3, event_list->element_count);
+ event_data += sizeof(android_event_list_t);
// Element #1: string type for subtag
- ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
- eventData++;
-
- unsigned subtag_len = strlen(SUBTAG);
- if (subtag_len > 32) subtag_len = 32;
- ASSERT_EQ(subtag_len, *reinterpret_cast<uint32_t*>(eventData));
- eventData += 4;
-
- if (memcmp(SUBTAG, eventData, subtag_len)) {
- continue;
+ auto* event_string_subtag = reinterpret_cast<android_event_string_t*>(event_data);
+ ASSERT_EQ(EVENT_TYPE_STRING, event_string_subtag->type);
+ int32_t subtag_len = strlen(subtag);
+ if (subtag_len > 32) {
+ subtag_len = 32;
}
- eventData += subtag_len;
+ ASSERT_EQ(subtag_len, event_string_subtag->length);
+ if (memcmp(subtag, &event_string_subtag->data, subtag_len)) {
+ return;
+ }
+ event_data += sizeof(android_event_string_t) + subtag_len;
// Element #2: int type for uid
- ASSERT_EQ(EVENT_TYPE_INT, eventData[0]);
- eventData++;
-
- ASSERT_EQ(UID, *reinterpret_cast<int32_t*>(eventData));
- eventData += 4;
+ auto* event_int_uid = reinterpret_cast<android_event_int_t*>(event_data);
+ ASSERT_EQ(EVENT_TYPE_INT, event_int_uid->type);
+ ASSERT_EQ(uid, event_int_uid->data);
+ event_data += sizeof(android_event_int_t);
// Element #3: string type for data
- ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
- eventData++;
-
- size_t dataLen = *reinterpret_cast<int32_t*>(eventData);
- eventData += 4;
- if (DATA_LEN < 512) ASSERT_EQ(DATA_LEN, (int)dataLen);
-
- if (memcmp(payload, eventData, dataLen)) {
- continue;
+ auto* event_string_data = reinterpret_cast<android_event_string_t*>(event_data);
+ ASSERT_EQ(EVENT_TYPE_STRING, event_string_data->type);
+ int32_t message_data_len = event_string_data->length;
+ if (data_len < 512) {
+ ASSERT_EQ(data_len, message_data_len);
}
+ if (memcmp(payload, &event_string_data->data, message_data_len) != 0) {
+ return;
+ }
+ event_data += sizeof(android_event_string_t);
- if (DATA_LEN >= 512) {
- eventData += dataLen;
+ if (data_len >= 512) {
+ event_data += message_data_len;
// 4 bytes for the tag, and max_payload_buf should be truncated.
- ASSERT_LE(4 + 512, eventData - original); // worst expectations
- ASSERT_GT(4 + DATA_LEN, eventData - original); // must be truncated
+ ASSERT_LE(4 + 512, event_data - original); // worst expectations
+ ASSERT_GT(4 + data_len, event_data - original); // must be truncated
}
+ *found = true;
+ };
- ++count;
- }
-
- android_logger_list_close(logger_list);
+ RunLogTests(LOG_ID_EVENTS, write_function, check_function);
}
#endif
@@ -2003,10 +1977,7 @@
TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__typical) {
#ifdef __ANDROID__
- int count;
- android_errorWriteWithInfoLog_helper(UNIQUE_TAG(1), "test-subtag", -1,
- max_payload_buf, 200, count);
- EXPECT_EQ(1, count);
+ android_errorWriteWithInfoLog_helper(UNIQUE_TAG(1), "test-subtag", -1, max_payload_buf, 200);
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
@@ -2015,23 +1986,20 @@
TEST(liblog,
android_errorWriteWithInfoLog__android_logger_list_read__data_too_large) {
#ifdef __ANDROID__
- int count;
- android_errorWriteWithInfoLog_helper(UNIQUE_TAG(2), "test-subtag", -1,
- max_payload_buf, sizeof(max_payload_buf),
- count);
- EXPECT_EQ(1, count);
+ android_errorWriteWithInfoLog_helper(UNIQUE_TAG(2), "test-subtag", -1, max_payload_buf,
+ sizeof(max_payload_buf));
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
+// TODO: Do we need to check that we didn't actually write anything if we return a failure here?
TEST(liblog,
android_errorWriteWithInfoLog__android_logger_list_read__null_data) {
#ifdef __ANDROID__
- int count;
- android_errorWriteWithInfoLog_helper(UNIQUE_TAG(3), "test-subtag", -1, NULL,
- 200, count);
- EXPECT_EQ(0, count);
+ int retval_android_errorWriteWithinInfoLog =
+ android_errorWriteWithInfoLog(UNIQUE_TAG(3), "test-subtag", -1, nullptr, 200);
+ ASSERT_GT(0, retval_android_errorWriteWithinInfoLog);
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
@@ -2040,11 +2008,8 @@
TEST(liblog,
android_errorWriteWithInfoLog__android_logger_list_read__subtag_too_long) {
#ifdef __ANDROID__
- int count;
android_errorWriteWithInfoLog_helper(
- UNIQUE_TAG(4), "abcdefghijklmnopqrstuvwxyz now i know my abc", -1,
- max_payload_buf, 200, count);
- EXPECT_EQ(1, count);
+ UNIQUE_TAG(4), "abcdefghijklmnopqrstuvwxyz now i know my abc", -1, max_payload_buf, 200);
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
@@ -2058,126 +2023,44 @@
buf_write_test(max_payload_buf);
}
-#ifdef __ANDROID__
-static void android_errorWriteLog_helper(int TAG, const char* SUBTAG,
- int& count) {
- struct logger_list* logger_list;
-
- pid_t pid = getpid();
-
- count = 0;
-
- // Do a Before and After on the count to measure the effect. Decrement
- // what we find in Before to set the stage.
- ASSERT_TRUE(NULL !=
- (logger_list = android_logger_list_open(
- LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
- 1000, pid)));
-
- for (;;) {
- log_msg log_msg;
- if (android_logger_list_read(logger_list, &log_msg) <= 0) break;
-
- char* eventData = log_msg.msg();
- if (!eventData) continue;
-
- // Tag
- int tag = *reinterpret_cast<int32_t*>(eventData);
- eventData += 4;
-
- if (tag != TAG) continue;
-
- if (!SUBTAG) {
- // This tag should not have been written because the data was null
- --count;
- break;
- }
-
- // List type
- eventData++;
- // Number of elements in list
- eventData++;
- // Element #1: string type for subtag
- eventData++;
-
- eventData += 4;
-
- if (memcmp(SUBTAG, eventData, strlen(SUBTAG))) continue;
- --count;
- }
-
- android_logger_list_close(logger_list);
-
- // Do an After on the count to measure the effect.
- ASSERT_TRUE(NULL !=
- (logger_list = android_logger_list_open(
- LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
- 1000, pid)));
-
- int retval_android_errorWriteLog = android_errorWriteLog(TAG, SUBTAG);
- if (SUBTAG) {
- ASSERT_LT(0, retval_android_errorWriteLog);
- } else {
- ASSERT_GT(0, retval_android_errorWriteLog);
- }
-
- sleep(2);
-
- for (;;) {
- log_msg log_msg;
- if (android_logger_list_read(logger_list, &log_msg) <= 0) {
- break;
- }
-
- char* eventData = log_msg.msg();
- if (!eventData) {
- continue;
- }
-
- // Tag
- int tag = *reinterpret_cast<int32_t*>(eventData);
- eventData += 4;
-
- if (tag != TAG) {
- continue;
- }
-
- if (!SUBTAG) {
- // This tag should not have been written because the data was null
- ++count;
- break;
- }
-
- // List type
- ASSERT_EQ(EVENT_TYPE_LIST, eventData[0]);
- eventData++;
-
- // Number of elements in list
- ASSERT_EQ(3, eventData[0]);
- eventData++;
-
- // Element #1: string type for subtag
- ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
- eventData++;
-
- ASSERT_EQ(strlen(SUBTAG), *reinterpret_cast<uint32_t*>(eventData));
- eventData += 4;
-
- if (memcmp(SUBTAG, eventData, strlen(SUBTAG))) {
- continue;
- }
- ++count;
- }
-
- android_logger_list_close(logger_list);
-}
-#endif
-
TEST(liblog, android_errorWriteLog__android_logger_list_read__success) {
#ifdef __ANDROID__
- int count;
- android_errorWriteLog_helper(UNIQUE_TAG(5), "test-subtag", count);
- EXPECT_EQ(1, count);
+ int kTag = UNIQUE_TAG(5);
+ const char* kSubTag = "test-subtag";
+
+ auto write_function = [&] {
+ int retval_android_errorWriteLog = android_errorWriteLog(kTag, kSubTag);
+ ASSERT_LT(0, retval_android_errorWriteLog);
+ };
+
+ auto check_function = [&](log_msg log_msg, bool* found) {
+ char* event_data = log_msg.msg();
+
+ // Tag
+ auto* event_header = reinterpret_cast<android_event_header_t*>(event_data);
+ event_data += sizeof(android_event_header_t);
+ if (event_header->tag != kTag) {
+ return;
+ }
+
+ // List type
+ auto* event_list = reinterpret_cast<android_event_list_t*>(event_data);
+ ASSERT_EQ(EVENT_TYPE_LIST, event_list->type);
+ ASSERT_EQ(3, event_list->element_count);
+ event_data += sizeof(android_event_list_t);
+
+ // Element #1: string type for subtag
+ auto* event_string_subtag = reinterpret_cast<android_event_string_t*>(event_data);
+ ASSERT_EQ(EVENT_TYPE_STRING, event_string_subtag->type);
+ int32_t subtag_len = strlen(kSubTag);
+ ASSERT_EQ(subtag_len, event_string_subtag->length);
+ if (memcmp(kSubTag, &event_string_subtag->data, subtag_len) == 0) {
+ *found = true;
+ }
+ };
+
+ RunLogTests(LOG_ID_EVENTS, write_function, check_function);
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
@@ -2185,9 +2068,7 @@
TEST(liblog, android_errorWriteLog__android_logger_list_read__null_subtag) {
#ifdef __ANDROID__
- int count;
- android_errorWriteLog_helper(UNIQUE_TAG(6), NULL, count);
- EXPECT_EQ(0, count);
+ EXPECT_LT(android_errorWriteLog(UNIQUE_TAG(6), nullptr), 0);
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
@@ -2605,58 +2486,27 @@
static void create_android_logger(const char* (*fn)(uint32_t tag,
size_t& expected_len)) {
- struct logger_list* logger_list;
+ size_t expected_len;
+ const char* expected_string;
+ auto write_function = [&] {
+ expected_string = (*fn)(1005, expected_len);
+ ASSERT_NE(nullptr, expected_string);
+ };
pid_t pid = getpid();
-
- ASSERT_TRUE(NULL !=
- (logger_list = android_logger_list_open(
- LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
- 1000, pid)));
-
-#ifdef __ANDROID__
- log_time ts(android_log_clockid());
-#else
- log_time ts(CLOCK_REALTIME);
-#endif
-
- size_t expected_len;
- const char* expected_string = (*fn)(1005, expected_len);
-
- if (!expected_string) {
- android_logger_list_close(logger_list);
- return;
- }
-
- usleep(1000000);
-
- int count = 0;
-
- for (;;) {
- log_msg log_msg;
- if (android_logger_list_read(logger_list, &log_msg) <= 0) {
- break;
- }
-
- ASSERT_EQ(log_msg.entry.pid, pid);
-
- if ((log_msg.entry.sec < (ts.tv_sec - 1)) ||
- ((ts.tv_sec + 1) < log_msg.entry.sec) ||
- ((size_t)log_msg.entry.len != expected_len) ||
- (log_msg.id() != LOG_ID_EVENTS)) {
- continue;
+ auto check_function = [&](log_msg log_msg, bool* found) {
+ if (static_cast<size_t>(log_msg.entry.len) != expected_len) {
+ return;
}
char* eventData = log_msg.msg();
- ++count;
-
AndroidLogFormat* logformat = android_log_format_new();
EXPECT_TRUE(NULL != logformat);
AndroidLogEntry entry;
char msgBuf[1024];
- int processBinaryLogBuffer = android_log_processBinaryLogBuffer(
- &log_msg.entry_v1, &entry, NULL, msgBuf, sizeof(msgBuf));
+ int processBinaryLogBuffer =
+ android_log_processBinaryLogBuffer(&log_msg.entry, &entry, nullptr, msgBuf, sizeof(msgBuf));
EXPECT_EQ(0, processBinaryLogBuffer);
if (processBinaryLogBuffer == 0) {
int line_overhead = 20;
@@ -2673,23 +2523,23 @@
// test buffer reading API
int buffer_to_string = -1;
if (eventData) {
- snprintf(msgBuf, sizeof(msgBuf), "I/[%" PRIu32 "]", *reinterpret_cast<uint32_t*>(eventData));
+ auto* event_header = reinterpret_cast<android_event_header_t*>(eventData);
+ eventData += sizeof(android_event_header_t);
+ snprintf(msgBuf, sizeof(msgBuf), "I/[%" PRId32 "]", event_header->tag);
print_barrier();
fprintf(stderr, "%-10s(%5u): ", msgBuf, pid);
memset(msgBuf, 0, sizeof(msgBuf));
- buffer_to_string = android_log_buffer_to_string(
- eventData + sizeof(uint32_t), log_msg.entry.len - sizeof(uint32_t),
- msgBuf, sizeof(msgBuf));
+ buffer_to_string =
+ android_log_buffer_to_string(eventData, log_msg.entry.len, msgBuf, sizeof(msgBuf));
fprintf(stderr, "%s\n", msgBuf);
print_barrier();
}
EXPECT_EQ(0, buffer_to_string);
EXPECT_STREQ(expected_string, msgBuf);
- }
+ *found = true;
+ };
- EXPECT_EQ(1, count);
-
- android_logger_list_close(logger_list);
+ RunLogTests(LOG_ID_EVENTS, write_function, check_function);
}
#endif
@@ -2773,7 +2623,6 @@
#endif
}
-#ifdef ENABLE_FLAKY_TESTS
TEST(liblog, create_android_logger_overflow) {
android_log_context ctx;
@@ -2800,7 +2649,6 @@
EXPECT_LE(0, android_log_destroy(&ctx));
ASSERT_TRUE(NULL == ctx);
}
-#endif // ENABLE_FLAKY_TESTS
#ifdef ENABLE_FLAKY_TESTS
#ifdef __ANDROID__
@@ -2941,7 +2789,6 @@
}
#endif // ENABLE_FLAKY_TESTS
-#ifdef ENABLE_FLAKY_TESTS
TEST(liblog, android_lookupEventTagNum) {
#ifdef __ANDROID__
EventTagMap* map = android_openEventTagMap(NULL);
@@ -2958,4 +2805,3 @@
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
-#endif // ENABLE_FLAKY_TESTS
diff --git a/liblog/tests/log_read_test.cpp b/liblog/tests/log_read_test.cpp
index 443c3ea..1be99aa 100644
--- a/liblog/tests/log_read_test.cpp
+++ b/liblog/tests/log_read_test.cpp
@@ -29,54 +29,6 @@
// Do not use anything in log/log_time.h despite side effects of the above.
#include <private/android_logger.h>
-TEST(liblog, __android_log_write__android_logger_list_read) {
-#ifdef __ANDROID__
- pid_t pid = getpid();
-
- struct logger_list* logger_list;
- ASSERT_TRUE(
- NULL !=
- (logger_list = android_logger_list_open(
- LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
-
- struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
- std::string buf = android::base::StringPrintf("pid=%u ts=%ld.%09ld", pid,
- ts.tv_sec, ts.tv_nsec);
- static const char tag[] =
- "liblog.__android_log_write__android_logger_list_read";
- static const char prio = ANDROID_LOG_DEBUG;
- ASSERT_LT(0, __android_log_write(prio, tag, buf.c_str()));
- usleep(1000000);
-
- buf = std::string(&prio, sizeof(prio)) + tag + std::string("", 1) + buf +
- std::string("", 1);
-
- int count = 0;
-
- for (;;) {
- log_msg log_msg;
- if (android_logger_list_read(logger_list, &log_msg) <= 0) break;
-
- EXPECT_EQ(log_msg.entry.pid, pid);
- // There may be a future where we leak "liblog" tagged LOG_ID_EVENT
- // binary messages through so that logger losses can be correlated?
- EXPECT_EQ(log_msg.id(), LOG_ID_MAIN);
-
- if (log_msg.entry.len != buf.length()) continue;
-
- if (buf != std::string(log_msg.msg(), log_msg.entry.len)) continue;
-
- ++count;
- }
- android_logger_list_close(logger_list);
-
- EXPECT_EQ(1, count);
-#else
- GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
-
TEST(liblog, android_logger_get_) {
#ifdef __ANDROID__
// This test assumes the log buffers are filled with noise from
diff --git a/liblog/tests/log_wrap_test.cpp b/liblog/tests/log_wrap_test.cpp
index c7dd8e8..e06964f 100644
--- a/liblog/tests/log_wrap_test.cpp
+++ b/liblog/tests/log_wrap_test.cpp
@@ -58,60 +58,27 @@
android_logger_list_close(logger_list);
}
-
-static void caught_signal(int /* signum */) {
-}
#endif
// b/64143705 confirm fixed
TEST(liblog, wrap_mode_blocks) {
#ifdef __ANDROID__
+ // The read call is expected to take up to 2 hours in the happy case. There was a previous bug
+ // where it would take only 30 seconds due to an alarm() in logd_reader.cpp. That alarm has been
+ // removed, so we check here that the read call blocks for a reasonable amount of time (5s).
+
+ struct sigaction ignore = {.sa_handler = [](int) { _exit(0); }};
+ struct sigaction old_sigaction;
+ sigaction(SIGALRM, &ignore, &old_sigaction);
+ alarm(5);
android::base::Timer timer;
+ read_with_wrap();
- // The read call is expected to take up to 2 hours in the happy case.
- // We only want to make sure it waits for longer than 30s, but we can't
- // use an alarm as the implementation uses it. So we run the test in
- // a separate process.
- pid_t pid = fork();
-
- if (pid == 0) {
- // child
- read_with_wrap();
- _exit(0);
- }
-
- struct sigaction ignore, old_sigaction;
- memset(&ignore, 0, sizeof(ignore));
- ignore.sa_handler = caught_signal;
- sigemptyset(&ignore.sa_mask);
- sigaction(SIGALRM, &ignore, &old_sigaction);
- alarm(45);
-
- bool killed = false;
- for (;;) {
- siginfo_t info = {};
- // This wait will succeed if the child exits, or fail with EINTR if the
- // alarm goes off first - a loose approximation to a timed wait.
- int ret = waitid(P_PID, pid, &info, WEXITED);
- if (ret >= 0 || errno != EINTR) {
- EXPECT_EQ(ret, 0);
- if (!killed) {
- EXPECT_EQ(info.si_status, 0);
- }
- break;
- }
- unsigned int alarm_left = alarm(0);
- if (alarm_left > 0) {
- alarm(alarm_left);
- } else {
- kill(pid, SIGTERM);
- killed = true;
- }
- }
+ FAIL() << "read_with_wrap() should not return before the alarm is triggered.";
alarm(0);
- EXPECT_GT(timer.duration(), std::chrono::seconds(40));
+ sigaction(SIGALRM, &old_sigaction, nullptr);
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/libmeminfo/include/meminfo/procmeminfo.h b/libmeminfo/include/meminfo/procmeminfo.h
index f782ec5..8c1280f 100644
--- a/libmeminfo/include/meminfo/procmeminfo.h
+++ b/libmeminfo/include/meminfo/procmeminfo.h
@@ -48,6 +48,10 @@
// Same as Maps() except, do not read the usage stats for each map.
const std::vector<Vma>& MapsWithoutUsageStats();
+ // If MapsWithoutUsageStats was called, this function will fill in
+ // usage stats for this single vma.
+ bool FillInVmaStats(Vma& vma);
+
// Collect all 'vma' or 'maps' from /proc/<pid>/smaps and store them in 'maps_'. Returns a
// constant reference to the vma vector after the collection is done.
//
diff --git a/libmeminfo/libmeminfo_test.cpp b/libmeminfo/libmeminfo_test.cpp
index cf5341d..378a4cd 100644
--- a/libmeminfo/libmeminfo_test.cpp
+++ b/libmeminfo/libmeminfo_test.cpp
@@ -101,6 +101,33 @@
}
}
+TEST(ProcMemInfo, MapsUsageFillInLater) {
+ ProcMemInfo proc_mem(pid);
+ const std::vector<Vma>& maps = proc_mem.MapsWithoutUsageStats();
+ EXPECT_FALSE(maps.empty());
+ for (auto& map : maps) {
+ Vma update_map(map);
+ ASSERT_EQ(map.start, update_map.start);
+ ASSERT_EQ(map.end, update_map.end);
+ ASSERT_EQ(map.offset, update_map.offset);
+ ASSERT_EQ(map.flags, update_map.flags);
+ ASSERT_EQ(map.name, update_map.name);
+ ASSERT_EQ(0, update_map.usage.vss);
+ ASSERT_EQ(0, update_map.usage.rss);
+ ASSERT_EQ(0, update_map.usage.pss);
+ ASSERT_EQ(0, update_map.usage.uss);
+ ASSERT_EQ(0, update_map.usage.swap);
+ ASSERT_EQ(0, update_map.usage.swap_pss);
+ ASSERT_EQ(0, update_map.usage.private_clean);
+ ASSERT_EQ(0, update_map.usage.private_dirty);
+ ASSERT_EQ(0, update_map.usage.shared_clean);
+ ASSERT_EQ(0, update_map.usage.shared_dirty);
+ ASSERT_TRUE(proc_mem.FillInVmaStats(update_map));
+ // Check that at least one usage stat was updated.
+ ASSERT_NE(0, update_map.usage.vss);
+ }
+}
+
TEST(ProcMemInfo, PageMapPresent) {
static constexpr size_t kNumPages = 20;
size_t pagesize = getpagesize();
diff --git a/libmeminfo/procmeminfo.cpp b/libmeminfo/procmeminfo.cpp
index 6f68ab4..9e9a705 100644
--- a/libmeminfo/procmeminfo.cpp
+++ b/libmeminfo/procmeminfo.cpp
@@ -244,6 +244,15 @@
return true;
}
+static int GetPagemapFd(pid_t pid) {
+ std::string pagemap_file = ::android::base::StringPrintf("/proc/%d/pagemap", pid);
+ int fd = TEMP_FAILURE_RETRY(open(pagemap_file.c_str(), O_RDONLY | O_CLOEXEC));
+ if (fd == -1) {
+ PLOG(ERROR) << "Failed to open " << pagemap_file;
+ }
+ return fd;
+}
+
bool ProcMemInfo::ReadMaps(bool get_wss, bool use_pageidle, bool get_usage_stats) {
// Each object reads /proc/<pid>/maps only once. This is done to make sure programs that are
// running for the lifetime of the system can recycle the objects and don't have to
@@ -269,11 +278,8 @@
return true;
}
- std::string pagemap_file = ::android::base::StringPrintf("/proc/%d/pagemap", pid_);
- ::android::base::unique_fd pagemap_fd(
- TEMP_FAILURE_RETRY(open(pagemap_file.c_str(), O_RDONLY | O_CLOEXEC)));
- if (pagemap_fd < 0) {
- PLOG(ERROR) << "Failed to open " << pagemap_file;
+ ::android::base::unique_fd pagemap_fd(GetPagemapFd(pid_));
+ if (pagemap_fd == -1) {
return false;
}
@@ -290,6 +296,20 @@
return true;
}
+bool ProcMemInfo::FillInVmaStats(Vma& vma) {
+ ::android::base::unique_fd pagemap_fd(GetPagemapFd(pid_));
+ if (pagemap_fd == -1) {
+ return false;
+ }
+
+ if (!ReadVmaStats(pagemap_fd.get(), vma, get_wss_, false)) {
+ LOG(ERROR) << "Failed to read page map for vma " << vma.name << "[" << vma.start << "-"
+ << vma.end << "]";
+ return false;
+ }
+ return true;
+}
+
bool ProcMemInfo::ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss, bool use_pageidle) {
PageAcct& pinfo = PageAcct::Instance();
if (get_wss && use_pageidle && !pinfo.InitPageAcct(true)) {
diff --git a/libmeminfo/tools/showmap.cpp b/libmeminfo/tools/showmap.cpp
index 8ea2108..72ab0f3 100644
--- a/libmeminfo/tools/showmap.cpp
+++ b/libmeminfo/tools/showmap.cpp
@@ -18,6 +18,7 @@
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
+#include <sys/mman.h>
#include <sys/signal.h>
#include <sys/types.h>
#include <unistd.h>
@@ -139,6 +140,9 @@
if (!g_verbose && !g_show_addr) {
printf(" # ");
}
+ if (g_verbose) {
+ printf(" flags ");
+ }
printf(" object\n");
}
@@ -150,6 +154,9 @@
if (!g_verbose && !g_show_addr) {
printf("---- ");
}
+ if (g_verbose) {
+ printf("------ ");
+ }
printf("------------------------------\n");
}
@@ -169,6 +176,18 @@
if (!g_verbose && !g_show_addr) {
printf("%4" PRIu32 " ", v.count);
}
+ if (g_verbose) {
+ if (total) {
+ printf(" ");
+ } else {
+ std::string flags_str("---");
+ if (v.vma.flags & PROT_READ) flags_str[0] = 'r';
+ if (v.vma.flags & PROT_WRITE) flags_str[1] = 'w';
+ if (v.vma.flags & PROT_EXEC) flags_str[2] = 'x';
+
+ printf("%6s ", flags_str.c_str());
+ }
+ }
}
static int showmap(void) {
diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h
index dcb4ffb..421d826 100644
--- a/libmodprobe/include/modprobe/modprobe.h
+++ b/libmodprobe/include/modprobe/modprobe.h
@@ -19,6 +19,7 @@
#include <set>
#include <string>
#include <unordered_map>
+#include <unordered_set>
#include <vector>
class Modprobe {
@@ -59,5 +60,6 @@
std::vector<std::string> module_load_;
std::unordered_map<std::string, std::string> module_options_;
std::set<std::string> module_blacklist_;
+ std::unordered_set<std::string> module_loaded_;
bool blacklist_enabled = false;
};
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index 73ae15b..3c78ec9 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -330,7 +330,12 @@
bool Modprobe::LoadWithAliases(const std::string& module_name, bool strict,
const std::string& parameters) {
- std::set<std::string> modules_to_load = {MakeCanonical(module_name)};
+ auto canonical_name = MakeCanonical(module_name);
+ if (module_loaded_.count(canonical_name)) {
+ return true;
+ }
+
+ std::set<std::string> modules_to_load = {canonical_name};
bool module_loaded = false;
// use aliases to expand list of modules to load (multiple modules
@@ -338,6 +343,7 @@
for (const auto& [alias, aliased_module] : module_aliases_) {
if (fnmatch(alias.c_str(), module_name.c_str(), 0) != 0) continue;
LOG(VERBOSE) << "Found alias for '" << module_name << "': '" << aliased_module;
+ if (module_loaded_.count(MakeCanonical(aliased_module))) continue;
modules_to_load.emplace(aliased_module);
}
diff --git a/libmodprobe/libmodprobe_ext.cpp b/libmodprobe/libmodprobe_ext.cpp
index 2efcac2..8bebe4c 100644
--- a/libmodprobe/libmodprobe_ext.cpp
+++ b/libmodprobe/libmodprobe_ext.cpp
@@ -30,8 +30,9 @@
return false;
}
+ auto canonical_name = MakeCanonical(path_name);
std::string options = "";
- auto options_iter = module_options_.find(MakeCanonical(path_name));
+ auto options_iter = module_options_.find(canonical_name);
if (options_iter != module_options_.end()) {
options = options_iter->second;
}
@@ -44,6 +45,7 @@
if (ret != 0) {
if (errno == EEXIST) {
// Module already loaded
+ module_loaded_.emplace(canonical_name);
return true;
}
LOG(ERROR) << "Failed to insmod '" << path_name << "' with args '" << options << "'";
@@ -51,15 +53,18 @@
}
LOG(INFO) << "Loaded kernel module " << path_name;
+ module_loaded_.emplace(canonical_name);
return true;
}
bool Modprobe::Rmmod(const std::string& module_name) {
- int ret = syscall(__NR_delete_module, MakeCanonical(module_name).c_str(), O_NONBLOCK);
+ auto canonical_name = MakeCanonical(module_name);
+ int ret = syscall(__NR_delete_module, canonical_name.c_str(), O_NONBLOCK);
if (ret != 0) {
PLOG(ERROR) << "Failed to remove module '" << module_name << "'";
return false;
}
+ module_loaded_.erase(canonical_name);
return true;
}
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
deleted file mode 100644
index 10d42e4..0000000
--- a/libnativebridge/Android.bp
+++ /dev/null
@@ -1,61 +0,0 @@
-cc_defaults {
- name: "libnativebridge-defaults",
- cflags: [
- "-Werror",
- "-Wall",
- ],
- cppflags: [
- "-fvisibility=protected",
- ],
- header_libs: ["libnativebridge-headers"],
- export_header_lib_headers: ["libnativebridge-headers"],
-}
-
-cc_library_headers {
- name: "libnativebridge-headers",
-
- host_supported: true,
- export_include_dirs: ["include"],
-}
-
-cc_library {
- name: "libnativebridge",
- defaults: ["libnativebridge-defaults"],
-
- host_supported: true,
- srcs: ["native_bridge.cc"],
- header_libs: [
- "libbase_headers",
- ],
- shared_libs: [
- "liblog",
- ],
- // TODO(jiyong): remove this line after aosp/885921 lands
- export_include_dirs: ["include"],
-
- target: {
- android: {
- version_script: "libnativebridge.map.txt",
- },
- linux: {
- version_script: "libnativebridge.map.txt",
- },
- },
-
- stubs: {
- symbol_file: "libnativebridge.map.txt",
- versions: ["1"],
- },
-}
-
-// TODO(b/124250621): eliminate the need for this library
-cc_library {
- name: "libnativebridge_lazy",
- defaults: ["libnativebridge-defaults"],
-
- host_supported: false,
- srcs: ["native_bridge_lazy.cc"],
- required: ["libnativebridge"],
-}
-
-subdirs = ["tests"]
diff --git a/libnativebridge/CPPLINT.cfg b/libnativebridge/CPPLINT.cfg
deleted file mode 100644
index 578047b..0000000
--- a/libnativebridge/CPPLINT.cfg
+++ /dev/null
@@ -1,19 +0,0 @@
-#
-# Copyright (C) 2019 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.
-#
-
-filter=-build/header_guard
-filter=-whitespace/comments
-filter=-whitespace/parens
diff --git a/libnativebridge/OWNERS b/libnativebridge/OWNERS
deleted file mode 100644
index daf87f4..0000000
--- a/libnativebridge/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-dimitry@google.com
-eaeltsin@google.com
-ngeoffray@google.com
-oth@google.com
diff --git a/libnativebridge/include/nativebridge/native_bridge.h b/libnativebridge/include/nativebridge/native_bridge.h
deleted file mode 100644
index e9c9500..0000000
--- a/libnativebridge/include/nativebridge/native_bridge.h
+++ /dev/null
@@ -1,418 +0,0 @@
-/*
- * Copyright (C) 2014 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 NATIVE_BRIDGE_H_
-#define NATIVE_BRIDGE_H_
-
-#include <signal.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include "jni.h"
-
-#ifdef __cplusplus
-namespace android {
-extern "C" {
-#endif // __cplusplus
-
-struct NativeBridgeRuntimeCallbacks;
-struct NativeBridgeRuntimeValues;
-
-// Function pointer type for sigaction. This is mostly the signature of a signal handler, except
-// for the return type. The runtime needs to know whether the signal was handled or should be given
-// to the chain.
-typedef bool (*NativeBridgeSignalHandlerFn)(int, siginfo_t*, void*);
-
-// Open the native bridge, if any. Should be called by Runtime::Init(). A null library filename
-// signals that we do not want to load a native bridge.
-bool LoadNativeBridge(const char* native_bridge_library_filename,
- const struct NativeBridgeRuntimeCallbacks* runtime_callbacks);
-
-// Quick check whether a native bridge will be needed. This is based off of the instruction set
-// of the process.
-bool NeedsNativeBridge(const char* instruction_set);
-
-// Do the early initialization part of the native bridge, if necessary. This should be done under
-// high privileges.
-bool PreInitializeNativeBridge(const char* app_data_dir, const char* instruction_set);
-
-// Initialize the native bridge, if any. Should be called by Runtime::DidForkFromZygote. The JNIEnv*
-// will be used to modify the app environment for the bridge.
-bool InitializeNativeBridge(JNIEnv* env, const char* instruction_set);
-
-// Unload the native bridge, if any. Should be called by Runtime::DidForkFromZygote.
-void UnloadNativeBridge();
-
-// Check whether a native bridge is available (opened or initialized). Requires a prior call to
-// LoadNativeBridge.
-bool NativeBridgeAvailable();
-
-// Check whether a native bridge is available (initialized). Requires a prior call to
-// LoadNativeBridge & InitializeNativeBridge.
-bool NativeBridgeInitialized();
-
-// Load a shared library that is supported by the native bridge.
-//
-// Starting with v3, NativeBridge has two scenarios: with/without namespace.
-// Use NativeBridgeLoadLibraryExt() instead in namespace scenario.
-void* NativeBridgeLoadLibrary(const char* libpath, int flag);
-
-// Get a native bridge trampoline for specified native method.
-void* NativeBridgeGetTrampoline(void* handle, const char* name, const char* shorty, uint32_t len);
-
-// True if native library paths are valid and is for an ABI that is supported by native bridge.
-// The *libpath* must point to a library.
-//
-// Starting with v3, NativeBridge has two scenarios: with/without namespace.
-// Use NativeBridgeIsPathSupported() instead in namespace scenario.
-bool NativeBridgeIsSupported(const char* libpath);
-
-// Returns the version number of the native bridge. This information is available after a
-// successful LoadNativeBridge() and before closing it, that is, as long as NativeBridgeAvailable()
-// returns true. Returns 0 otherwise.
-uint32_t NativeBridgeGetVersion();
-
-// Returns a signal handler that the bridge would like to be managed. Only valid for a native
-// bridge supporting the version 2 interface. Will return null if the bridge does not support
-// version 2, or if it doesn't have a signal handler it wants to be known.
-NativeBridgeSignalHandlerFn NativeBridgeGetSignalHandler(int signal);
-
-// Returns whether we have seen a native bridge error. This could happen because the library
-// was not found, rejected, could not be initialized and so on.
-//
-// This functionality is mainly for testing.
-bool NativeBridgeError();
-
-// Returns whether a given string is acceptable as a native bridge library filename.
-//
-// This functionality is exposed mainly for testing.
-bool NativeBridgeNameAcceptable(const char* native_bridge_library_filename);
-
-// Decrements the reference count on the dynamic library handler. If the reference count drops
-// to zero then the dynamic library is unloaded. Returns 0 on success and non-zero on error.
-int NativeBridgeUnloadLibrary(void* handle);
-
-// Get last error message of native bridge when fail to load library or search symbol.
-// This is reflection of dlerror() for native bridge.
-const char* NativeBridgeGetError();
-
-struct native_bridge_namespace_t;
-
-// True if native library paths are valid and is for an ABI that is supported by native bridge.
-// Different from NativeBridgeIsSupported(), the *path* here must be a directory containing
-// libraries of an ABI.
-//
-// Starting with v3, NativeBridge has two scenarios: with/without namespace.
-// Use NativeBridgeIsSupported() instead in non-namespace scenario.
-bool NativeBridgeIsPathSupported(const char* path);
-
-// Initializes anonymous namespace.
-// NativeBridge's peer of android_init_anonymous_namespace() of dynamic linker.
-//
-// The anonymous namespace is used in the case when a NativeBridge implementation
-// cannot identify the caller of dlopen/dlsym which happens for the code not loaded
-// by dynamic linker; for example calls from the mono-compiled code.
-//
-// Starting with v3, NativeBridge has two scenarios: with/without namespace.
-// Should not use in non-namespace scenario.
-bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames,
- const char* anon_ns_library_path);
-
-// Create new namespace in which native libraries will be loaded.
-// NativeBridge's peer of android_create_namespace() of dynamic linker.
-//
-// The libraries in the namespace are searched by folowing order:
-// 1. ld_library_path (Think of this as namespace-local LD_LIBRARY_PATH)
-// 2. In directories specified by DT_RUNPATH of the "needed by" binary.
-// 3. deault_library_path (This of this as namespace-local default library path)
-//
-// Starting with v3, NativeBridge has two scenarios: with/without namespace.
-// Should not use in non-namespace scenario.
-struct native_bridge_namespace_t* NativeBridgeCreateNamespace(
- const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type,
- const char* permitted_when_isolated_path, struct native_bridge_namespace_t* parent_ns);
-
-// Creates a link which shares some libraries from one namespace to another.
-// NativeBridge's peer of android_link_namespaces() of dynamic linker.
-//
-// Starting with v3, NativeBridge has two scenarios: with/without namespace.
-// Should not use in non-namespace scenario.
-bool NativeBridgeLinkNamespaces(struct native_bridge_namespace_t* from,
- struct native_bridge_namespace_t* to,
- const char* shared_libs_sonames);
-
-// Load a shared library with namespace key that is supported by the native bridge.
-// NativeBridge's peer of android_dlopen_ext() of dynamic linker, only supports namespace
-// extension.
-//
-// Starting with v3, NativeBridge has two scenarios: with/without namespace.
-// Use NativeBridgeLoadLibrary() instead in non-namespace scenario.
-void* NativeBridgeLoadLibraryExt(const char* libpath, int flag,
- struct native_bridge_namespace_t* ns);
-
-// Returns exported namespace by the name. This is a reflection of
-// android_get_exported_namespace function. Introduced in v5.
-struct native_bridge_namespace_t* NativeBridgeGetExportedNamespace(const char* name);
-
-// Native bridge interfaces to runtime.
-struct NativeBridgeCallbacks {
- // Version number of the interface.
- uint32_t version;
-
- // Initialize native bridge. Native bridge's internal implementation must ensure MT safety and
- // that the native bridge is initialized only once. Thus it is OK to call this interface for an
- // already initialized native bridge.
- //
- // Parameters:
- // runtime_cbs [IN] the pointer to NativeBridgeRuntimeCallbacks.
- // Returns:
- // true if initialization was successful.
- bool (*initialize)(const struct NativeBridgeRuntimeCallbacks* runtime_cbs,
- const char* private_dir, const char* instruction_set);
-
- // Load a shared library that is supported by the native bridge.
- //
- // Parameters:
- // libpath [IN] path to the shared library
- // flag [IN] the stardard RTLD_XXX defined in bionic dlfcn.h
- // Returns:
- // The opaque handle of the shared library if sucessful, otherwise NULL
- //
- // Starting with v3, NativeBridge has two scenarios: with/without namespace.
- // Use loadLibraryExt instead in namespace scenario.
- void* (*loadLibrary)(const char* libpath, int flag);
-
- // Get a native bridge trampoline for specified native method. The trampoline has same
- // sigature as the native method.
- //
- // Parameters:
- // handle [IN] the handle returned from loadLibrary
- // shorty [IN] short descriptor of native method
- // len [IN] length of shorty
- // Returns:
- // address of trampoline if successful, otherwise NULL
- void* (*getTrampoline)(void* handle, const char* name, const char* shorty, uint32_t len);
-
- // Check whether native library is valid and is for an ABI that is supported by native bridge.
- //
- // Parameters:
- // libpath [IN] path to the shared library
- // Returns:
- // TRUE if library is supported by native bridge, FALSE otherwise
- //
- // Starting with v3, NativeBridge has two scenarios: with/without namespace.
- // Use isPathSupported instead in namespace scenario.
- bool (*isSupported)(const char* libpath);
-
- // Provide environment values required by the app running with native bridge according to the
- // instruction set.
- //
- // Parameters:
- // instruction_set [IN] the instruction set of the app
- // Returns:
- // NULL if not supported by native bridge.
- // Otherwise, return all environment values to be set after fork.
- const struct NativeBridgeRuntimeValues* (*getAppEnv)(const char* instruction_set);
-
- // Added callbacks in version 2.
-
- // Check whether the bridge is compatible with the given version. A bridge may decide not to be
- // forwards- or backwards-compatible, and libnativebridge will then stop using it.
- //
- // Parameters:
- // bridge_version [IN] the version of libnativebridge.
- // Returns:
- // true if the native bridge supports the given version of libnativebridge.
- bool (*isCompatibleWith)(uint32_t bridge_version);
-
- // A callback to retrieve a native bridge's signal handler for the specified signal. The runtime
- // will ensure that the signal handler is being called after the runtime's own handler, but before
- // all chained handlers. The native bridge should not try to install the handler by itself, as
- // that will potentially lead to cycles.
- //
- // Parameters:
- // signal [IN] the signal for which the handler is asked for. Currently, only SIGSEGV is
- // supported by the runtime.
- // Returns:
- // NULL if the native bridge doesn't use a handler or doesn't want it to be managed by the
- // runtime.
- // Otherwise, a pointer to the signal handler.
- NativeBridgeSignalHandlerFn (*getSignalHandler)(int signal);
-
- // Added callbacks in version 3.
-
- // Decrements the reference count on the dynamic library handler. If the reference count drops
- // to zero then the dynamic library is unloaded.
- //
- // Parameters:
- // handle [IN] the handler of a dynamic library.
- //
- // Returns:
- // 0 on success, and nonzero on error.
- int (*unloadLibrary)(void* handle);
-
- // Dump the last failure message of native bridge when fail to load library or search symbol.
- //
- // Parameters:
- //
- // Returns:
- // A string describing the most recent error that occurred when load library
- // or lookup symbol via native bridge.
- const char* (*getError)();
-
- // Check whether library paths are supported by native bridge.
- //
- // Parameters:
- // library_path [IN] search paths for native libraries (directories separated by ':')
- // Returns:
- // TRUE if libraries within search paths are supported by native bridge, FALSE otherwise
- //
- // Starting with v3, NativeBridge has two scenarios: with/without namespace.
- // Use isSupported instead in non-namespace scenario.
- bool (*isPathSupported)(const char* library_path);
-
- // Initializes anonymous namespace at native bridge side.
- // NativeBridge's peer of android_init_anonymous_namespace() of dynamic linker.
- //
- // The anonymous namespace is used in the case when a NativeBridge implementation
- // cannot identify the caller of dlopen/dlsym which happens for the code not loaded
- // by dynamic linker; for example calls from the mono-compiled code.
- //
- // Parameters:
- // public_ns_sonames [IN] the name of "public" libraries.
- // anon_ns_library_path [IN] the library search path of (anonymous) namespace.
- // Returns:
- // true if the pass is ok.
- // Otherwise, false.
- //
- // Starting with v3, NativeBridge has two scenarios: with/without namespace.
- // Should not use in non-namespace scenario.
- bool (*initAnonymousNamespace)(const char* public_ns_sonames, const char* anon_ns_library_path);
-
- // Create new namespace in which native libraries will be loaded.
- // NativeBridge's peer of android_create_namespace() of dynamic linker.
- //
- // Parameters:
- // name [IN] the name of the namespace.
- // ld_library_path [IN] the first set of library search paths of the namespace.
- // default_library_path [IN] the second set of library search path of the namespace.
- // type [IN] the attribute of the namespace.
- // permitted_when_isolated_path [IN] the permitted path for isolated namespace(if it is).
- // parent_ns [IN] the pointer of the parent namespace to be inherited from.
- // Returns:
- // native_bridge_namespace_t* for created namespace or nullptr in the case of error.
- //
- // Starting with v3, NativeBridge has two scenarios: with/without namespace.
- // Should not use in non-namespace scenario.
- struct native_bridge_namespace_t* (*createNamespace)(const char* name,
- const char* ld_library_path,
- const char* default_library_path,
- uint64_t type,
- const char* permitted_when_isolated_path,
- struct native_bridge_namespace_t* parent_ns);
-
- // Creates a link which shares some libraries from one namespace to another.
- // NativeBridge's peer of android_link_namespaces() of dynamic linker.
- //
- // Parameters:
- // from [IN] the namespace where libraries are accessed.
- // to [IN] the namespace where libraries are loaded.
- // shared_libs_sonames [IN] the libraries to be shared.
- //
- // Returns:
- // Whether successed or not.
- //
- // Starting with v3, NativeBridge has two scenarios: with/without namespace.
- // Should not use in non-namespace scenario.
- bool (*linkNamespaces)(struct native_bridge_namespace_t* from,
- struct native_bridge_namespace_t* to, const char* shared_libs_sonames);
-
- // Load a shared library within a namespace.
- // NativeBridge's peer of android_dlopen_ext() of dynamic linker, only supports namespace
- // extension.
- //
- // Parameters:
- // libpath [IN] path to the shared library
- // flag [IN] the stardard RTLD_XXX defined in bionic dlfcn.h
- // ns [IN] the pointer of the namespace in which the library should be loaded.
- // Returns:
- // The opaque handle of the shared library if sucessful, otherwise NULL
- //
- // Starting with v3, NativeBridge has two scenarios: with/without namespace.
- // Use loadLibrary instead in non-namespace scenario.
- void* (*loadLibraryExt)(const char* libpath, int flag, struct native_bridge_namespace_t* ns);
-
- // Get native bridge version of vendor namespace.
- // The vendor namespace is the namespace used to load vendor public libraries.
- // With O release this namespace can be different from the default namespace.
- // For the devices without enable vendor namespaces this function should return null
- //
- // Returns:
- // vendor namespace or null if it was not set up for the device
- //
- // Starting with v5 (Android Q) this function is no longer used.
- // Use getExportedNamespace() below.
- struct native_bridge_namespace_t* (*getVendorNamespace)();
-
- // Get native bridge version of exported namespace. Peer of
- // android_get_exported_namespace(const char*) function.
- //
- // Returns:
- // exported namespace or null if it was not set up for the device
- struct native_bridge_namespace_t* (*getExportedNamespace)(const char* name);
-};
-
-// Runtime interfaces to native bridge.
-struct NativeBridgeRuntimeCallbacks {
- // Get shorty of a Java method. The shorty is supposed to be persistent in memory.
- //
- // Parameters:
- // env [IN] pointer to JNIenv.
- // mid [IN] Java methodID.
- // Returns:
- // short descriptor for method.
- const char* (*getMethodShorty)(JNIEnv* env, jmethodID mid);
-
- // Get number of native methods for specified class.
- //
- // Parameters:
- // env [IN] pointer to JNIenv.
- // clazz [IN] Java class object.
- // Returns:
- // number of native methods.
- uint32_t (*getNativeMethodCount)(JNIEnv* env, jclass clazz);
-
- // Get at most 'method_count' native methods for specified class 'clazz'. Results are outputed
- // via 'methods' [OUT]. The signature pointer in JNINativeMethod is reused as the method shorty.
- //
- // Parameters:
- // env [IN] pointer to JNIenv.
- // clazz [IN] Java class object.
- // methods [OUT] array of method with the name, shorty, and fnPtr.
- // method_count [IN] max number of elements in methods.
- // Returns:
- // number of method it actually wrote to methods.
- uint32_t (*getNativeMethods)(JNIEnv* env, jclass clazz, JNINativeMethod* methods,
- uint32_t method_count);
-};
-
-#ifdef __cplusplus
-} // extern "C"
-} // namespace android
-#endif // __cplusplus
-
-#endif // NATIVE_BRIDGE_H_
diff --git a/libnativebridge/libnativebridge.map.txt b/libnativebridge/libnativebridge.map.txt
deleted file mode 100644
index a6841a3..0000000
--- a/libnativebridge/libnativebridge.map.txt
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-# Copyright (C) 2019 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.
-#
-
-# TODO(b/122710865): Most of these uses come from libnativeloader, which should be bundled
-# together with libnativebridge in the APEX. Once this happens, prune this list.
-LIBNATIVEBRIDGE_1 {
- global:
- NativeBridgeIsSupported;
- NativeBridgeLoadLibrary;
- NativeBridgeUnloadLibrary;
- NativeBridgeGetError;
- NativeBridgeIsPathSupported;
- NativeBridgeCreateNamespace;
- NativeBridgeGetExportedNamespace;
- NativeBridgeLinkNamespaces;
- NativeBridgeLoadLibraryExt;
- NativeBridgeInitAnonymousNamespace;
- NativeBridgeInitialized;
- NativeBridgeGetTrampoline;
- LoadNativeBridge;
- PreInitializeNativeBridge;
- InitializeNativeBridge;
- NativeBridgeGetVersion;
- NativeBridgeGetSignalHandler;
- UnloadNativeBridge;
- NativeBridgeAvailable;
- NeedsNativeBridge;
- NativeBridgeError;
- NativeBridgeNameAcceptable;
- local:
- *;
-};
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
deleted file mode 100644
index 9adba9a..0000000
--- a/libnativebridge/native_bridge.cc
+++ /dev/null
@@ -1,646 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-#define LOG_TAG "nativebridge"
-
-#include "nativebridge/native_bridge.h"
-
-#include <dlfcn.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <cstring>
-
-#include <android-base/macros.h>
-#include <log/log.h>
-
-namespace android {
-
-#ifdef __APPLE__
-template <typename T>
-void UNUSED(const T&) {}
-#endif
-
-extern "C" {
-
-// Environment values required by the apps running with native bridge.
-struct NativeBridgeRuntimeValues {
- const char* os_arch;
- const char* cpu_abi;
- const char* cpu_abi2;
- const char* *supported_abis;
- int32_t abi_count;
-};
-
-// The symbol name exposed by native-bridge with the type of NativeBridgeCallbacks.
-static constexpr const char* kNativeBridgeInterfaceSymbol = "NativeBridgeItf";
-
-enum class NativeBridgeState {
- kNotSetup, // Initial state.
- kOpened, // After successful dlopen.
- kPreInitialized, // After successful pre-initialization.
- kInitialized, // After successful initialization.
- kClosed // Closed or errors.
-};
-
-static constexpr const char* kNotSetupString = "kNotSetup";
-static constexpr const char* kOpenedString = "kOpened";
-static constexpr const char* kPreInitializedString = "kPreInitialized";
-static constexpr const char* kInitializedString = "kInitialized";
-static constexpr const char* kClosedString = "kClosed";
-
-static const char* GetNativeBridgeStateString(NativeBridgeState state) {
- switch (state) {
- case NativeBridgeState::kNotSetup:
- return kNotSetupString;
-
- case NativeBridgeState::kOpened:
- return kOpenedString;
-
- case NativeBridgeState::kPreInitialized:
- return kPreInitializedString;
-
- case NativeBridgeState::kInitialized:
- return kInitializedString;
-
- case NativeBridgeState::kClosed:
- return kClosedString;
- }
-}
-
-// Current state of the native bridge.
-static NativeBridgeState state = NativeBridgeState::kNotSetup;
-
-// The version of NativeBridge implementation.
-// Different Nativebridge interface needs the service of different version of
-// Nativebridge implementation.
-// Used by isCompatibleWith() which is introduced in v2.
-enum NativeBridgeImplementationVersion {
- // first version, not used.
- DEFAULT_VERSION = 1,
- // The version which signal semantic is introduced.
- SIGNAL_VERSION = 2,
- // The version which namespace semantic is introduced.
- NAMESPACE_VERSION = 3,
- // The version with vendor namespaces
- VENDOR_NAMESPACE_VERSION = 4,
- // The version with runtime namespaces
- RUNTIME_NAMESPACE_VERSION = 5,
-};
-
-// Whether we had an error at some point.
-static bool had_error = false;
-
-// Handle of the loaded library.
-static void* native_bridge_handle = nullptr;
-// Pointer to the callbacks. Available as soon as LoadNativeBridge succeeds, but only initialized
-// later.
-static const NativeBridgeCallbacks* callbacks = nullptr;
-// Callbacks provided by the environment to the bridge. Passed to LoadNativeBridge.
-static const NativeBridgeRuntimeCallbacks* runtime_callbacks = nullptr;
-
-// The app's code cache directory.
-static char* app_code_cache_dir = nullptr;
-
-// Code cache directory (relative to the application private directory)
-// Ideally we'd like to call into framework to retrieve this name. However that's considered an
-// implementation detail and will require either hacks or consistent refactorings. We compromise
-// and hard code the directory name again here.
-static constexpr const char* kCodeCacheDir = "code_cache";
-
-// Characters allowed in a native bridge filename. The first character must
-// be in [a-zA-Z] (expected 'l' for "libx"). The rest must be in [a-zA-Z0-9._-].
-static bool CharacterAllowed(char c, bool first) {
- if (first) {
- return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
- } else {
- return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') ||
- (c == '.') || (c == '_') || (c == '-');
- }
-}
-
-static void ReleaseAppCodeCacheDir() {
- if (app_code_cache_dir != nullptr) {
- delete[] app_code_cache_dir;
- app_code_cache_dir = nullptr;
- }
-}
-
-// We only allow simple names for the library. It is supposed to be a file in
-// /system/lib or /vendor/lib. Only allow a small range of characters, that is
-// names consisting of [a-zA-Z0-9._-] and starting with [a-zA-Z].
-bool NativeBridgeNameAcceptable(const char* nb_library_filename) {
- const char* ptr = nb_library_filename;
- if (*ptr == 0) {
- // Emptry string. Allowed, means no native bridge.
- return true;
- } else {
- // First character must be [a-zA-Z].
- if (!CharacterAllowed(*ptr, true)) {
- // Found an invalid fist character, don't accept.
- ALOGE("Native bridge library %s has been rejected for first character %c",
- nb_library_filename,
- *ptr);
- return false;
- } else {
- // For the rest, be more liberal.
- ptr++;
- while (*ptr != 0) {
- if (!CharacterAllowed(*ptr, false)) {
- // Found an invalid character, don't accept.
- ALOGE("Native bridge library %s has been rejected for %c", nb_library_filename, *ptr);
- return false;
- }
- ptr++;
- }
- }
- return true;
- }
-}
-
-// The policy of invoking Nativebridge changed in v3 with/without namespace.
-// Suggest Nativebridge implementation not maintain backward-compatible.
-static bool isCompatibleWith(const uint32_t version) {
- // Libnativebridge is now designed to be forward-compatible. So only "0" is an unsupported
- // version.
- if (callbacks == nullptr || callbacks->version == 0 || version == 0) {
- return false;
- }
-
- // If this is a v2+ bridge, it may not be forwards- or backwards-compatible. Check.
- if (callbacks->version >= SIGNAL_VERSION) {
- return callbacks->isCompatibleWith(version);
- }
-
- return true;
-}
-
-static void CloseNativeBridge(bool with_error) {
- state = NativeBridgeState::kClosed;
- had_error |= with_error;
- ReleaseAppCodeCacheDir();
-}
-
-bool LoadNativeBridge(const char* nb_library_filename,
- const NativeBridgeRuntimeCallbacks* runtime_cbs) {
- // We expect only one place that calls LoadNativeBridge: Runtime::Init. At that point we are not
- // multi-threaded, so we do not need locking here.
-
- if (state != NativeBridgeState::kNotSetup) {
- // Setup has been called before. Ignore this call.
- if (nb_library_filename != nullptr) { // Avoids some log-spam for dalvikvm.
- ALOGW("Called LoadNativeBridge for an already set up native bridge. State is %s.",
- GetNativeBridgeStateString(state));
- }
- // Note: counts as an error, even though the bridge may be functional.
- had_error = true;
- return false;
- }
-
- if (nb_library_filename == nullptr || *nb_library_filename == 0) {
- CloseNativeBridge(false);
- return false;
- } else {
- if (!NativeBridgeNameAcceptable(nb_library_filename)) {
- CloseNativeBridge(true);
- } else {
- // Try to open the library.
- void* handle = dlopen(nb_library_filename, RTLD_LAZY);
- if (handle != nullptr) {
- callbacks = reinterpret_cast<NativeBridgeCallbacks*>(dlsym(handle,
- kNativeBridgeInterfaceSymbol));
- if (callbacks != nullptr) {
- if (isCompatibleWith(NAMESPACE_VERSION)) {
- // Store the handle for later.
- native_bridge_handle = handle;
- } else {
- callbacks = nullptr;
- dlclose(handle);
- ALOGW("Unsupported native bridge interface.");
- }
- } else {
- dlclose(handle);
- }
- }
-
- // Two failure conditions: could not find library (dlopen failed), or could not find native
- // bridge interface (dlsym failed). Both are an error and close the native bridge.
- if (callbacks == nullptr) {
- CloseNativeBridge(true);
- } else {
- runtime_callbacks = runtime_cbs;
- state = NativeBridgeState::kOpened;
- }
- }
- return state == NativeBridgeState::kOpened;
- }
-}
-
-bool NeedsNativeBridge(const char* instruction_set) {
- if (instruction_set == nullptr) {
- ALOGE("Null instruction set in NeedsNativeBridge.");
- return false;
- }
- return strncmp(instruction_set, ABI_STRING, strlen(ABI_STRING) + 1) != 0;
-}
-
-bool PreInitializeNativeBridge(const char* app_data_dir_in, const char* instruction_set) {
- if (state != NativeBridgeState::kOpened) {
- ALOGE("Invalid state: native bridge is expected to be opened.");
- CloseNativeBridge(true);
- return false;
- }
-
- if (app_data_dir_in == nullptr) {
- ALOGE("Application private directory cannot be null.");
- CloseNativeBridge(true);
- return false;
- }
-
- // Create the path to the application code cache directory.
- // The memory will be release after Initialization or when the native bridge is closed.
- const size_t len = strlen(app_data_dir_in) + strlen(kCodeCacheDir) + 2; // '\0' + '/'
- app_code_cache_dir = new char[len];
- snprintf(app_code_cache_dir, len, "%s/%s", app_data_dir_in, kCodeCacheDir);
-
- // Bind-mount /system/lib{,64}/<isa>/cpuinfo to /proc/cpuinfo.
- // Failure is not fatal and will keep the native bridge in kPreInitialized.
- state = NativeBridgeState::kPreInitialized;
-
-#ifndef __APPLE__
- if (instruction_set == nullptr) {
- return true;
- }
- size_t isa_len = strlen(instruction_set);
- if (isa_len > 10) {
- // 10 is a loose upper bound on the currently known instruction sets (a tight bound is 7 for
- // x86_64 [including the trailing \0]). This is so we don't have to change here if there will
- // be another instruction set in the future.
- ALOGW("Instruction set %s is malformed, must be less than or equal to 10 characters.",
- instruction_set);
- return true;
- }
-
- // If the file does not exist, the mount command will fail,
- // so we save the extra file existence check.
- char cpuinfo_path[1024];
-
-#if defined(__ANDROID__)
- snprintf(cpuinfo_path, sizeof(cpuinfo_path), "/system/lib"
-#ifdef __LP64__
- "64"
-#endif // __LP64__
- "/%s/cpuinfo", instruction_set);
-#else // !__ANDROID__
- // To be able to test on the host, we hardwire a relative path.
- snprintf(cpuinfo_path, sizeof(cpuinfo_path), "./cpuinfo");
-#endif
-
- // Bind-mount.
- if (TEMP_FAILURE_RETRY(mount(cpuinfo_path, // Source.
- "/proc/cpuinfo", // Target.
- nullptr, // FS type.
- MS_BIND, // Mount flags: bind mount.
- nullptr)) == -1) { // "Data."
- ALOGW("Failed to bind-mount %s as /proc/cpuinfo: %s", cpuinfo_path, strerror(errno));
- }
-#else // __APPLE__
- UNUSED(instruction_set);
- ALOGW("Mac OS does not support bind-mounting. Host simulation of native bridge impossible.");
-#endif
-
- return true;
-}
-
-static void SetCpuAbi(JNIEnv* env, jclass build_class, const char* field, const char* value) {
- if (value != nullptr) {
- jfieldID field_id = env->GetStaticFieldID(build_class, field, "Ljava/lang/String;");
- if (field_id == nullptr) {
- env->ExceptionClear();
- ALOGW("Could not find %s field.", field);
- return;
- }
-
- jstring str = env->NewStringUTF(value);
- if (str == nullptr) {
- env->ExceptionClear();
- ALOGW("Could not create string %s.", value);
- return;
- }
-
- env->SetStaticObjectField(build_class, field_id, str);
- }
-}
-
-// Set up the environment for the bridged app.
-static void SetupEnvironment(const NativeBridgeCallbacks* callbacks, JNIEnv* env, const char* isa) {
- // Need a JNIEnv* to do anything.
- if (env == nullptr) {
- ALOGW("No JNIEnv* to set up app environment.");
- return;
- }
-
- // Query the bridge for environment values.
- const struct NativeBridgeRuntimeValues* env_values = callbacks->getAppEnv(isa);
- if (env_values == nullptr) {
- return;
- }
-
- // Keep the JNIEnv clean.
- jint success = env->PushLocalFrame(16); // That should be small and large enough.
- if (success < 0) {
- // Out of memory, really borked.
- ALOGW("Out of memory while setting up app environment.");
- env->ExceptionClear();
- return;
- }
-
- // Reset CPU_ABI & CPU_ABI2 to values required by the apps running with native bridge.
- if (env_values->cpu_abi != nullptr || env_values->cpu_abi2 != nullptr ||
- env_values->abi_count >= 0) {
- jclass bclass_id = env->FindClass("android/os/Build");
- if (bclass_id != nullptr) {
- SetCpuAbi(env, bclass_id, "CPU_ABI", env_values->cpu_abi);
- SetCpuAbi(env, bclass_id, "CPU_ABI2", env_values->cpu_abi2);
- } else {
- // For example in a host test environment.
- env->ExceptionClear();
- ALOGW("Could not find Build class.");
- }
- }
-
- if (env_values->os_arch != nullptr) {
- jclass sclass_id = env->FindClass("java/lang/System");
- if (sclass_id != nullptr) {
- jmethodID set_prop_id = env->GetStaticMethodID(sclass_id, "setUnchangeableSystemProperty",
- "(Ljava/lang/String;Ljava/lang/String;)V");
- if (set_prop_id != nullptr) {
- // Init os.arch to the value reqired by the apps running with native bridge.
- env->CallStaticVoidMethod(sclass_id, set_prop_id, env->NewStringUTF("os.arch"),
- env->NewStringUTF(env_values->os_arch));
- } else {
- env->ExceptionClear();
- ALOGW("Could not find System#setUnchangeableSystemProperty.");
- }
- } else {
- env->ExceptionClear();
- ALOGW("Could not find System class.");
- }
- }
-
- // Make it pristine again.
- env->PopLocalFrame(nullptr);
-}
-
-bool InitializeNativeBridge(JNIEnv* env, const char* instruction_set) {
- // We expect only one place that calls InitializeNativeBridge: Runtime::DidForkFromZygote. At that
- // point we are not multi-threaded, so we do not need locking here.
-
- if (state == NativeBridgeState::kPreInitialized) {
- // Check for code cache: if it doesn't exist try to create it.
- struct stat st;
- if (stat(app_code_cache_dir, &st) == -1) {
- if (errno == ENOENT) {
- if (mkdir(app_code_cache_dir, S_IRWXU | S_IRWXG | S_IXOTH) == -1) {
- ALOGW("Cannot create code cache directory %s: %s.", app_code_cache_dir, strerror(errno));
- ReleaseAppCodeCacheDir();
- }
- } else {
- ALOGW("Cannot stat code cache directory %s: %s.", app_code_cache_dir, strerror(errno));
- ReleaseAppCodeCacheDir();
- }
- } else if (!S_ISDIR(st.st_mode)) {
- ALOGW("Code cache is not a directory %s.", app_code_cache_dir);
- ReleaseAppCodeCacheDir();
- }
-
- // If we're still PreInitialized (dind't fail the code cache checks) try to initialize.
- if (state == NativeBridgeState::kPreInitialized) {
- if (callbacks->initialize(runtime_callbacks, app_code_cache_dir, instruction_set)) {
- SetupEnvironment(callbacks, env, instruction_set);
- state = NativeBridgeState::kInitialized;
- // We no longer need the code cache path, release the memory.
- ReleaseAppCodeCacheDir();
- } else {
- // Unload the library.
- dlclose(native_bridge_handle);
- CloseNativeBridge(true);
- }
- }
- } else {
- CloseNativeBridge(true);
- }
-
- return state == NativeBridgeState::kInitialized;
-}
-
-void UnloadNativeBridge() {
- // We expect only one place that calls UnloadNativeBridge: Runtime::DidForkFromZygote. At that
- // point we are not multi-threaded, so we do not need locking here.
-
- switch(state) {
- case NativeBridgeState::kOpened:
- case NativeBridgeState::kPreInitialized:
- case NativeBridgeState::kInitialized:
- // Unload.
- dlclose(native_bridge_handle);
- CloseNativeBridge(false);
- break;
-
- case NativeBridgeState::kNotSetup:
- // Not even set up. Error.
- CloseNativeBridge(true);
- break;
-
- case NativeBridgeState::kClosed:
- // Ignore.
- break;
- }
-}
-
-bool NativeBridgeError() {
- return had_error;
-}
-
-bool NativeBridgeAvailable() {
- return state == NativeBridgeState::kOpened
- || state == NativeBridgeState::kPreInitialized
- || state == NativeBridgeState::kInitialized;
-}
-
-bool NativeBridgeInitialized() {
- // Calls of this are supposed to happen in a state where the native bridge is stable, i.e., after
- // Runtime::DidForkFromZygote. In that case we do not need a lock.
- return state == NativeBridgeState::kInitialized;
-}
-
-void* NativeBridgeLoadLibrary(const char* libpath, int flag) {
- if (NativeBridgeInitialized()) {
- return callbacks->loadLibrary(libpath, flag);
- }
- return nullptr;
-}
-
-void* NativeBridgeGetTrampoline(void* handle, const char* name, const char* shorty,
- uint32_t len) {
- if (NativeBridgeInitialized()) {
- return callbacks->getTrampoline(handle, name, shorty, len);
- }
- return nullptr;
-}
-
-bool NativeBridgeIsSupported(const char* libpath) {
- if (NativeBridgeInitialized()) {
- return callbacks->isSupported(libpath);
- }
- return false;
-}
-
-uint32_t NativeBridgeGetVersion() {
- if (NativeBridgeAvailable()) {
- return callbacks->version;
- }
- return 0;
-}
-
-NativeBridgeSignalHandlerFn NativeBridgeGetSignalHandler(int signal) {
- if (NativeBridgeInitialized()) {
- if (isCompatibleWith(SIGNAL_VERSION)) {
- return callbacks->getSignalHandler(signal);
- } else {
- ALOGE("not compatible with version %d, cannot get signal handler", SIGNAL_VERSION);
- }
- }
- return nullptr;
-}
-
-int NativeBridgeUnloadLibrary(void* handle) {
- if (NativeBridgeInitialized()) {
- if (isCompatibleWith(NAMESPACE_VERSION)) {
- return callbacks->unloadLibrary(handle);
- } else {
- ALOGE("not compatible with version %d, cannot unload library", NAMESPACE_VERSION);
- }
- }
- return -1;
-}
-
-const char* NativeBridgeGetError() {
- if (NativeBridgeInitialized()) {
- if (isCompatibleWith(NAMESPACE_VERSION)) {
- return callbacks->getError();
- } else {
- return "native bridge implementation is not compatible with version 3, cannot get message";
- }
- }
- return "native bridge is not initialized";
-}
-
-bool NativeBridgeIsPathSupported(const char* path) {
- if (NativeBridgeInitialized()) {
- if (isCompatibleWith(NAMESPACE_VERSION)) {
- return callbacks->isPathSupported(path);
- } else {
- ALOGE("not compatible with version %d, cannot check via library path", NAMESPACE_VERSION);
- }
- }
- return false;
-}
-
-bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames,
- const char* anon_ns_library_path) {
- if (NativeBridgeInitialized()) {
- if (isCompatibleWith(NAMESPACE_VERSION)) {
- return callbacks->initAnonymousNamespace(public_ns_sonames, anon_ns_library_path);
- } else {
- ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION);
- }
- }
-
- return false;
-}
-
-native_bridge_namespace_t* NativeBridgeCreateNamespace(const char* name,
- const char* ld_library_path,
- const char* default_library_path,
- uint64_t type,
- const char* permitted_when_isolated_path,
- native_bridge_namespace_t* parent_ns) {
- if (NativeBridgeInitialized()) {
- if (isCompatibleWith(NAMESPACE_VERSION)) {
- return callbacks->createNamespace(name,
- ld_library_path,
- default_library_path,
- type,
- permitted_when_isolated_path,
- parent_ns);
- } else {
- ALOGE("not compatible with version %d, cannot create namespace %s", NAMESPACE_VERSION, name);
- }
- }
-
- return nullptr;
-}
-
-bool NativeBridgeLinkNamespaces(native_bridge_namespace_t* from, native_bridge_namespace_t* to,
- const char* shared_libs_sonames) {
- if (NativeBridgeInitialized()) {
- if (isCompatibleWith(NAMESPACE_VERSION)) {
- return callbacks->linkNamespaces(from, to, shared_libs_sonames);
- } else {
- ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION);
- }
- }
-
- return false;
-}
-
-native_bridge_namespace_t* NativeBridgeGetExportedNamespace(const char* name) {
- if (!NativeBridgeInitialized()) {
- return nullptr;
- }
-
- if (isCompatibleWith(RUNTIME_NAMESPACE_VERSION)) {
- return callbacks->getExportedNamespace(name);
- }
-
- // sphal is vendor namespace name -> use v4 callback in the case NB callbacks
- // are not compatible with v5
- if (isCompatibleWith(VENDOR_NAMESPACE_VERSION) && name != nullptr && strcmp("sphal", name) == 0) {
- return callbacks->getVendorNamespace();
- }
-
- return nullptr;
-}
-
-void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns) {
- if (NativeBridgeInitialized()) {
- if (isCompatibleWith(NAMESPACE_VERSION)) {
- return callbacks->loadLibraryExt(libpath, flag, ns);
- } else {
- ALOGE("not compatible with version %d, cannot load library in namespace", NAMESPACE_VERSION);
- }
- }
- return nullptr;
-}
-
-} // extern "C"
-
-} // namespace android
diff --git a/libnativebridge/native_bridge_lazy.cc b/libnativebridge/native_bridge_lazy.cc
deleted file mode 100644
index 94c8084..0000000
--- a/libnativebridge/native_bridge_lazy.cc
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-#include "nativebridge/native_bridge.h"
-#define LOG_TAG "nativebridge"
-
-#include <dlfcn.h>
-#include <errno.h>
-#include <string.h>
-
-#include <log/log.h>
-
-namespace android {
-
-namespace {
-
-void* GetLibHandle() {
- static void* handle = dlopen("libnativebridge.so", RTLD_NOW);
- LOG_FATAL_IF(handle == nullptr, "Failed to load libnativebridge.so: %s", dlerror());
- return handle;
-}
-
-template <typename FuncPtr>
-FuncPtr GetFuncPtr(const char* function_name) {
- auto f = reinterpret_cast<FuncPtr>(dlsym(GetLibHandle(), function_name));
- LOG_FATAL_IF(f == nullptr, "Failed to get address of %s: %s", function_name, dlerror());
- return f;
-}
-
-#define GET_FUNC_PTR(name) GetFuncPtr<decltype(&name)>(#name)
-
-} // namespace
-
-bool LoadNativeBridge(const char* native_bridge_library_filename,
- const struct NativeBridgeRuntimeCallbacks* runtime_callbacks) {
- static auto f = GET_FUNC_PTR(LoadNativeBridge);
- return f(native_bridge_library_filename, runtime_callbacks);
-}
-
-bool NeedsNativeBridge(const char* instruction_set) {
- static auto f = GET_FUNC_PTR(NeedsNativeBridge);
- return f(instruction_set);
-}
-
-bool PreInitializeNativeBridge(const char* app_data_dir, const char* instruction_set) {
- static auto f = GET_FUNC_PTR(PreInitializeNativeBridge);
- return f(app_data_dir, instruction_set);
-}
-
-bool InitializeNativeBridge(JNIEnv* env, const char* instruction_set) {
- static auto f = GET_FUNC_PTR(InitializeNativeBridge);
- return f(env, instruction_set);
-}
-
-void UnloadNativeBridge() {
- static auto f = GET_FUNC_PTR(UnloadNativeBridge);
- return f();
-}
-
-bool NativeBridgeAvailable() {
- static auto f = GET_FUNC_PTR(NativeBridgeAvailable);
- return f();
-}
-
-bool NativeBridgeInitialized() {
- static auto f = GET_FUNC_PTR(NativeBridgeInitialized);
- return f();
-}
-
-void* NativeBridgeLoadLibrary(const char* libpath, int flag) {
- static auto f = GET_FUNC_PTR(NativeBridgeLoadLibrary);
- return f(libpath, flag);
-}
-
-void* NativeBridgeGetTrampoline(void* handle, const char* name, const char* shorty, uint32_t len) {
- static auto f = GET_FUNC_PTR(NativeBridgeGetTrampoline);
- return f(handle, name, shorty, len);
-}
-
-bool NativeBridgeIsSupported(const char* libpath) {
- static auto f = GET_FUNC_PTR(NativeBridgeIsSupported);
- return f(libpath);
-}
-
-uint32_t NativeBridgeGetVersion() {
- static auto f = GET_FUNC_PTR(NativeBridgeGetVersion);
- return f();
-}
-
-NativeBridgeSignalHandlerFn NativeBridgeGetSignalHandler(int signal) {
- static auto f = GET_FUNC_PTR(NativeBridgeGetSignalHandler);
- return f(signal);
-}
-
-bool NativeBridgeError() {
- static auto f = GET_FUNC_PTR(NativeBridgeError);
- return f();
-}
-
-bool NativeBridgeNameAcceptable(const char* native_bridge_library_filename) {
- static auto f = GET_FUNC_PTR(NativeBridgeNameAcceptable);
- return f(native_bridge_library_filename);
-}
-
-int NativeBridgeUnloadLibrary(void* handle) {
- static auto f = GET_FUNC_PTR(NativeBridgeUnloadLibrary);
- return f(handle);
-}
-
-const char* NativeBridgeGetError() {
- static auto f = GET_FUNC_PTR(NativeBridgeGetError);
- return f();
-}
-
-bool NativeBridgeIsPathSupported(const char* path) {
- static auto f = GET_FUNC_PTR(NativeBridgeIsPathSupported);
- return f(path);
-}
-
-bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames,
- const char* anon_ns_library_path) {
- static auto f = GET_FUNC_PTR(NativeBridgeInitAnonymousNamespace);
- return f(public_ns_sonames, anon_ns_library_path);
-}
-
-struct native_bridge_namespace_t* NativeBridgeCreateNamespace(
- const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type,
- const char* permitted_when_isolated_path, struct native_bridge_namespace_t* parent_ns) {
- static auto f = GET_FUNC_PTR(NativeBridgeCreateNamespace);
- return f(name, ld_library_path, default_library_path, type, permitted_when_isolated_path,
- parent_ns);
-}
-
-bool NativeBridgeLinkNamespaces(struct native_bridge_namespace_t* from,
- struct native_bridge_namespace_t* to,
- const char* shared_libs_sonames) {
- static auto f = GET_FUNC_PTR(NativeBridgeLinkNamespaces);
- return f(from, to, shared_libs_sonames);
-}
-
-void* NativeBridgeLoadLibraryExt(const char* libpath, int flag,
- struct native_bridge_namespace_t* ns) {
- static auto f = GET_FUNC_PTR(NativeBridgeLoadLibraryExt);
- return f(libpath, flag, ns);
-}
-
-struct native_bridge_namespace_t* NativeBridgeGetVendorNamespace() {
- static auto f = GET_FUNC_PTR(NativeBridgeGetVendorNamespace);
- return f();
-}
-
-#undef GET_FUNC_PTR
-
-} // namespace android
diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp
deleted file mode 100644
index 2bb8467..0000000
--- a/libnativebridge/tests/Android.bp
+++ /dev/null
@@ -1,110 +0,0 @@
-//
-// Copyright (C) 2017 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.
-//
-
-cc_defaults {
- name: "libnativebridge-dummy-defaults",
-
- host_supported: true,
- cflags: [
- "-Wall",
- "-Wextra",
- "-Werror",
- ],
- header_libs: ["libnativebridge-headers"],
- cppflags: ["-fvisibility=protected"],
-}
-
-cc_library_shared {
- name: "libnativebridge-dummy",
- srcs: ["DummyNativeBridge.cpp"],
- defaults: ["libnativebridge-dummy-defaults"],
-}
-
-cc_library_shared {
- name: "libnativebridge2-dummy",
- srcs: ["DummyNativeBridge2.cpp"],
- defaults: ["libnativebridge-dummy-defaults"],
-}
-
-cc_library_shared {
- name: "libnativebridge3-dummy",
- srcs: ["DummyNativeBridge3.cpp"],
- defaults: ["libnativebridge-dummy-defaults"],
-}
-
-// Build the unit tests.
-cc_defaults {
- name: "libnativebridge-tests-defaults",
- test_per_src: true,
-
- cflags: [
- "-Wall",
- "-Werror",
- ],
-
- srcs: [
- "CodeCacheCreate_test.cpp",
- "CodeCacheExists_test.cpp",
- "CodeCacheStatFail_test.cpp",
- "CompleteFlow_test.cpp",
- "InvalidCharsNativeBridge_test.cpp",
- "NativeBridge2Signal_test.cpp",
- "NativeBridgeVersion_test.cpp",
- "NeedsNativeBridge_test.cpp",
- "PreInitializeNativeBridge_test.cpp",
- "PreInitializeNativeBridgeFail1_test.cpp",
- "PreInitializeNativeBridgeFail2_test.cpp",
- "ReSetupNativeBridge_test.cpp",
- "UnavailableNativeBridge_test.cpp",
- "ValidNameNativeBridge_test.cpp",
- "NativeBridge3UnloadLibrary_test.cpp",
- "NativeBridge3GetError_test.cpp",
- "NativeBridge3IsPathSupported_test.cpp",
- "NativeBridge3InitAnonymousNamespace_test.cpp",
- "NativeBridge3CreateNamespace_test.cpp",
- "NativeBridge3LoadLibraryExt_test.cpp",
- ],
-
- shared_libs: [
- "liblog",
- "libnativebridge-dummy",
- ],
- header_libs: ["libbase_headers"],
-}
-
-cc_test {
- name: "libnativebridge-tests",
- defaults: ["libnativebridge-tests-defaults"],
- host_supported: true,
- shared_libs: ["libnativebridge"],
-}
-
-cc_test {
- name: "libnativebridge-lazy-tests",
- defaults: ["libnativebridge-tests-defaults"],
- shared_libs: ["libnativebridge_lazy"],
-}
-
-// Build the test for the C API.
-cc_test {
- name: "libnativebridge-api-tests",
- host_supported: true,
- test_per_src: true,
- srcs: [
- "NativeBridgeApi.c",
- ],
- header_libs: ["libnativebridge-headers"],
-}
diff --git a/libnativebridge/tests/CodeCacheCreate_test.cpp b/libnativebridge/tests/CodeCacheCreate_test.cpp
deleted file mode 100644
index 58270c4..0000000
--- a/libnativebridge/tests/CodeCacheCreate_test.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-#include "NativeBridgeTest.h"
-
-#include <errno.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-namespace android {
-
-// Tests that the bridge initialization creates the code_cache if it doesn't
-// exists.
-TEST_F(NativeBridgeTest, CodeCacheCreate) {
- // Make sure that code_cache does not exists
- struct stat st;
- ASSERT_EQ(-1, stat(kCodeCache, &st));
- ASSERT_EQ(ENOENT, errno);
-
- // Init
- ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
- ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
- ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
- ASSERT_TRUE(NativeBridgeAvailable());
- ASSERT_FALSE(NativeBridgeError());
-
- // Check that code_cache was created
- ASSERT_EQ(0, stat(kCodeCache, &st));
- ASSERT_TRUE(S_ISDIR(st.st_mode));
-
- // Clean up
- UnloadNativeBridge();
- ASSERT_EQ(0, rmdir(kCodeCache));
-
- ASSERT_FALSE(NativeBridgeError());
-}
-
-} // namespace android
diff --git a/libnativebridge/tests/CodeCacheExists_test.cpp b/libnativebridge/tests/CodeCacheExists_test.cpp
deleted file mode 100644
index 8ba0158..0000000
--- a/libnativebridge/tests/CodeCacheExists_test.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-#include "NativeBridgeTest.h"
-
-#include <errno.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-namespace android {
-
-// Tests that the bridge is initialized without errors if the code_cache already
-// exists.
-TEST_F(NativeBridgeTest, CodeCacheExists) {
- // Make sure that code_cache does not exists
- struct stat st;
- ASSERT_EQ(-1, stat(kCodeCache, &st));
- ASSERT_EQ(ENOENT, errno);
-
- // Create the code_cache
- ASSERT_EQ(0, mkdir(kCodeCache, S_IRWXU | S_IRWXG | S_IXOTH));
-
- // Init
- ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
- ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
- ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
- ASSERT_TRUE(NativeBridgeAvailable());
- ASSERT_FALSE(NativeBridgeError());
-
- // Check that the code cache is still there
- ASSERT_EQ(0, stat(kCodeCache, &st));
- ASSERT_TRUE(S_ISDIR(st.st_mode));
-
- // Clean up
- UnloadNativeBridge();
- ASSERT_EQ(0, rmdir(kCodeCache));
-
- ASSERT_FALSE(NativeBridgeError());
-}
-
-} // namespace android
diff --git a/libnativebridge/tests/CodeCacheStatFail_test.cpp b/libnativebridge/tests/CodeCacheStatFail_test.cpp
deleted file mode 100644
index 4ea519e..0000000
--- a/libnativebridge/tests/CodeCacheStatFail_test.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-#include "NativeBridgeTest.h"
-
-#include <errno.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-namespace android {
-
-// Tests that the bridge is initialized without errors if the code_cache is
-// existed as a file.
-TEST_F(NativeBridgeTest, CodeCacheStatFail) {
- int fd = creat(kCodeCache, O_RDWR);
- ASSERT_NE(-1, fd);
- close(fd);
-
- struct stat st;
- ASSERT_EQ(-1, stat(kCodeCacheStatFail, &st));
- ASSERT_EQ(ENOTDIR, errno);
-
- // Init
- ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
- ASSERT_TRUE(PreInitializeNativeBridge(kCodeCacheStatFail, "isa"));
- ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
- ASSERT_TRUE(NativeBridgeAvailable());
- ASSERT_FALSE(NativeBridgeError());
-
- // Clean up
- UnloadNativeBridge();
-
- ASSERT_FALSE(NativeBridgeError());
- unlink(kCodeCache);
-}
-
-} // namespace android
diff --git a/libnativebridge/tests/CompleteFlow_test.cpp b/libnativebridge/tests/CompleteFlow_test.cpp
deleted file mode 100644
index b033792..0000000
--- a/libnativebridge/tests/CompleteFlow_test.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-#include "NativeBridgeTest.h"
-
-#include <unistd.h>
-
-namespace android {
-
-TEST_F(NativeBridgeTest, CompleteFlow) {
- // Init
- ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
- ASSERT_TRUE(NativeBridgeAvailable());
- ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
- ASSERT_TRUE(NativeBridgeAvailable());
- ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
- ASSERT_TRUE(NativeBridgeAvailable());
-
- // Basic calls to check that nothing crashes
- ASSERT_FALSE(NativeBridgeIsSupported(nullptr));
- ASSERT_EQ(nullptr, NativeBridgeLoadLibrary(nullptr, 0));
- ASSERT_EQ(nullptr, NativeBridgeGetTrampoline(nullptr, nullptr, nullptr, 0));
-
- // Unload
- UnloadNativeBridge();
-
- ASSERT_FALSE(NativeBridgeAvailable());
- ASSERT_FALSE(NativeBridgeError());
-
- // Clean-up code_cache
- ASSERT_EQ(0, rmdir(kCodeCache));
-}
-
-} // namespace android
diff --git a/libnativebridge/tests/DummyNativeBridge.cpp b/libnativebridge/tests/DummyNativeBridge.cpp
deleted file mode 100644
index b9894f6..0000000
--- a/libnativebridge/tests/DummyNativeBridge.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-// A dummy implementation of the native-bridge interface.
-
-#include "nativebridge/native_bridge.h"
-
-// NativeBridgeCallbacks implementations
-extern "C" bool native_bridge_initialize(const android::NativeBridgeRuntimeCallbacks* /* art_cbs */,
- const char* /* app_code_cache_dir */,
- const char* /* isa */) {
- return true;
-}
-
-extern "C" void* native_bridge_loadLibrary(const char* /* libpath */, int /* flag */) {
- return nullptr;
-}
-
-extern "C" void* native_bridge_getTrampoline(void* /* handle */, const char* /* name */,
- const char* /* shorty */, uint32_t /* len */) {
- return nullptr;
-}
-
-extern "C" bool native_bridge_isSupported(const char* /* libpath */) {
- return false;
-}
-
-extern "C" const struct android::NativeBridgeRuntimeValues* native_bridge_getAppEnv(
- const char* /* abi */) {
- return nullptr;
-}
-
-android::NativeBridgeCallbacks NativeBridgeItf {
- .version = 1,
- .initialize = &native_bridge_initialize,
- .loadLibrary = &native_bridge_loadLibrary,
- .getTrampoline = &native_bridge_getTrampoline,
- .isSupported = &native_bridge_isSupported,
- .getAppEnv = &native_bridge_getAppEnv
-};
diff --git a/libnativebridge/tests/DummyNativeBridge2.cpp b/libnativebridge/tests/DummyNativeBridge2.cpp
deleted file mode 100644
index 6920c74..0000000
--- a/libnativebridge/tests/DummyNativeBridge2.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-// A dummy implementation of the native-bridge interface.
-
-#include "nativebridge/native_bridge.h"
-
-#include <signal.h>
-
-// NativeBridgeCallbacks implementations
-extern "C" bool native_bridge2_initialize(const android::NativeBridgeRuntimeCallbacks* /* art_cbs */,
- const char* /* app_code_cache_dir */,
- const char* /* isa */) {
- return true;
-}
-
-extern "C" void* native_bridge2_loadLibrary(const char* /* libpath */, int /* flag */) {
- return nullptr;
-}
-
-extern "C" void* native_bridge2_getTrampoline(void* /* handle */, const char* /* name */,
- const char* /* shorty */, uint32_t /* len */) {
- return nullptr;
-}
-
-extern "C" bool native_bridge2_isSupported(const char* /* libpath */) {
- return false;
-}
-
-extern "C" const struct android::NativeBridgeRuntimeValues* native_bridge2_getAppEnv(
- const char* /* abi */) {
- return nullptr;
-}
-
-extern "C" bool native_bridge2_is_compatible_compatible_with(uint32_t version) {
- // For testing, allow 1 and 2, but disallow 3+.
- return version <= 2;
-}
-
-static bool native_bridge2_dummy_signal_handler(int, siginfo_t*, void*) {
- // TODO: Implement something here. We'd either have to have a death test with a log here, or
- // we'd have to be able to resume after the faulting instruction...
- return true;
-}
-
-extern "C" android::NativeBridgeSignalHandlerFn native_bridge2_get_signal_handler(int signal) {
- if (signal == SIGSEGV) {
- return &native_bridge2_dummy_signal_handler;
- }
- return nullptr;
-}
-
-android::NativeBridgeCallbacks NativeBridgeItf {
- .version = 2,
- .initialize = &native_bridge2_initialize,
- .loadLibrary = &native_bridge2_loadLibrary,
- .getTrampoline = &native_bridge2_getTrampoline,
- .isSupported = &native_bridge2_isSupported,
- .getAppEnv = &native_bridge2_getAppEnv,
- .isCompatibleWith = &native_bridge2_is_compatible_compatible_with,
- .getSignalHandler = &native_bridge2_get_signal_handler
-};
-
diff --git a/libnativebridge/tests/DummyNativeBridge3.cpp b/libnativebridge/tests/DummyNativeBridge3.cpp
deleted file mode 100644
index 4ef1c82..0000000
--- a/libnativebridge/tests/DummyNativeBridge3.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// A dummy implementation of the native-bridge interface.
-
-#include "nativebridge/native_bridge.h"
-
-#include <signal.h>
-
-// NativeBridgeCallbacks implementations
-extern "C" bool native_bridge3_initialize(
- const android::NativeBridgeRuntimeCallbacks* /* art_cbs */,
- const char* /* app_code_cache_dir */,
- const char* /* isa */) {
- return true;
-}
-
-extern "C" void* native_bridge3_loadLibrary(const char* /* libpath */, int /* flag */) {
- return nullptr;
-}
-
-extern "C" void* native_bridge3_getTrampoline(void* /* handle */, const char* /* name */,
- const char* /* shorty */, uint32_t /* len */) {
- return nullptr;
-}
-
-extern "C" bool native_bridge3_isSupported(const char* /* libpath */) {
- return false;
-}
-
-extern "C" const struct android::NativeBridgeRuntimeValues* native_bridge3_getAppEnv(
- const char* /* abi */) {
- return nullptr;
-}
-
-extern "C" bool native_bridge3_isCompatibleWith(uint32_t version) {
- // For testing, allow 1-3, but disallow 4+.
- return version <= 3;
-}
-
-static bool native_bridge3_dummy_signal_handler(int, siginfo_t*, void*) {
- // TODO: Implement something here. We'd either have to have a death test with a log here, or
- // we'd have to be able to resume after the faulting instruction...
- return true;
-}
-
-extern "C" android::NativeBridgeSignalHandlerFn native_bridge3_getSignalHandler(int signal) {
- if (signal == SIGSEGV) {
- return &native_bridge3_dummy_signal_handler;
- }
- return nullptr;
-}
-
-extern "C" int native_bridge3_unloadLibrary(void* /* handle */) {
- return 0;
-}
-
-extern "C" const char* native_bridge3_getError() {
- return nullptr;
-}
-
-extern "C" bool native_bridge3_isPathSupported(const char* /* path */) {
- return true;
-}
-
-extern "C" bool native_bridge3_initAnonymousNamespace(const char* /* public_ns_sonames */,
- const char* /* anon_ns_library_path */) {
- return true;
-}
-
-extern "C" android::native_bridge_namespace_t*
-native_bridge3_createNamespace(const char* /* name */,
- const char* /* ld_library_path */,
- const char* /* default_library_path */,
- uint64_t /* type */,
- const char* /* permitted_when_isolated_path */,
- android::native_bridge_namespace_t* /* parent_ns */) {
- return nullptr;
-}
-
-extern "C" bool native_bridge3_linkNamespaces(android::native_bridge_namespace_t* /* from */,
- android::native_bridge_namespace_t* /* to */,
- const char* /* shared_libs_soname */) {
- return true;
-}
-
-extern "C" void* native_bridge3_loadLibraryExt(const char* /* libpath */,
- int /* flag */,
- android::native_bridge_namespace_t* /* ns */) {
- return nullptr;
-}
-
-android::NativeBridgeCallbacks NativeBridgeItf{
- // v1
- .version = 3,
- .initialize = &native_bridge3_initialize,
- .loadLibrary = &native_bridge3_loadLibrary,
- .getTrampoline = &native_bridge3_getTrampoline,
- .isSupported = &native_bridge3_isSupported,
- .getAppEnv = &native_bridge3_getAppEnv,
- // v2
- .isCompatibleWith = &native_bridge3_isCompatibleWith,
- .getSignalHandler = &native_bridge3_getSignalHandler,
- // v3
- .unloadLibrary = &native_bridge3_unloadLibrary,
- .getError = &native_bridge3_getError,
- .isPathSupported = &native_bridge3_isPathSupported,
- .initAnonymousNamespace = &native_bridge3_initAnonymousNamespace,
- .createNamespace = &native_bridge3_createNamespace,
- .linkNamespaces = &native_bridge3_linkNamespaces,
- .loadLibraryExt = &native_bridge3_loadLibraryExt};
diff --git a/libnativebridge/tests/InvalidCharsNativeBridge_test.cpp b/libnativebridge/tests/InvalidCharsNativeBridge_test.cpp
deleted file mode 100644
index 8f7973d..0000000
--- a/libnativebridge/tests/InvalidCharsNativeBridge_test.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-#include "NativeBridgeTest.h"
-
-namespace android {
-
-static const char* kTestName = "../librandom$@-bridge_not.existing.so";
-
-TEST_F(NativeBridgeTest, InvalidChars) {
- // Do one test actually calling setup.
- EXPECT_EQ(false, NativeBridgeError());
- LoadNativeBridge(kTestName, nullptr);
- // This should lead to an error for invalid characters.
- EXPECT_EQ(true, NativeBridgeError());
-
- // Further tests need to use NativeBridgeNameAcceptable, as the error
- // state can't be changed back.
- EXPECT_EQ(false, NativeBridgeNameAcceptable("."));
- EXPECT_EQ(false, NativeBridgeNameAcceptable(".."));
- EXPECT_EQ(false, NativeBridgeNameAcceptable("_"));
- EXPECT_EQ(false, NativeBridgeNameAcceptable("-"));
- EXPECT_EQ(false, NativeBridgeNameAcceptable("lib@.so"));
- EXPECT_EQ(false, NativeBridgeNameAcceptable("lib$.so"));
-}
-
-} // namespace android
diff --git a/libnativebridge/tests/NativeBridge2Signal_test.cpp b/libnativebridge/tests/NativeBridge2Signal_test.cpp
deleted file mode 100644
index 44e45e3..0000000
--- a/libnativebridge/tests/NativeBridge2Signal_test.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-#include "NativeBridgeTest.h"
-
-#include <signal.h>
-#include <unistd.h>
-
-namespace android {
-
-constexpr const char* kNativeBridgeLibrary2 = "libnativebridge2-dummy.so";
-
-TEST_F(NativeBridgeTest, V2_Signal) {
- // Init
- ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary2, nullptr));
- ASSERT_TRUE(NativeBridgeAvailable());
- ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
- ASSERT_TRUE(NativeBridgeAvailable());
- ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
- ASSERT_TRUE(NativeBridgeAvailable());
-
- ASSERT_EQ(2U, NativeBridgeGetVersion());
- ASSERT_NE(nullptr, NativeBridgeGetSignalHandler(SIGSEGV));
-
- // Clean-up code_cache
- ASSERT_EQ(0, rmdir(kCodeCache));
-}
-
-} // namespace android
diff --git a/libnativebridge/tests/NativeBridge3CreateNamespace_test.cpp b/libnativebridge/tests/NativeBridge3CreateNamespace_test.cpp
deleted file mode 100644
index 668d942..0000000
--- a/libnativebridge/tests/NativeBridge3CreateNamespace_test.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "NativeBridgeTest.h"
-
-namespace android {
-
-constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
-
-TEST_F(NativeBridgeTest, V3_CreateNamespace) {
- // Init
- ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
- ASSERT_TRUE(NativeBridgeAvailable());
- ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
- ASSERT_TRUE(NativeBridgeAvailable());
- ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
- ASSERT_TRUE(NativeBridgeAvailable());
-
- ASSERT_EQ(3U, NativeBridgeGetVersion());
- ASSERT_EQ(nullptr, NativeBridgeCreateNamespace(nullptr, nullptr, nullptr,
- 0, nullptr, nullptr));
-
- // Clean-up code_cache
- ASSERT_EQ(0, rmdir(kCodeCache));
-}
-
-} // namespace android
diff --git a/libnativebridge/tests/NativeBridge3GetError_test.cpp b/libnativebridge/tests/NativeBridge3GetError_test.cpp
deleted file mode 100644
index 0b9f582..0000000
--- a/libnativebridge/tests/NativeBridge3GetError_test.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "NativeBridgeTest.h"
-
-namespace android {
-
-constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
-
-TEST_F(NativeBridgeTest, V3_GetError) {
- // Init
- ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
- ASSERT_TRUE(NativeBridgeAvailable());
- ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
- ASSERT_TRUE(NativeBridgeAvailable());
- ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
- ASSERT_TRUE(NativeBridgeAvailable());
-
- ASSERT_EQ(3U, NativeBridgeGetVersion());
- ASSERT_EQ(nullptr, NativeBridgeGetError());
-
- // Clean-up code_cache
- ASSERT_EQ(0, rmdir(kCodeCache));
-}
-
-} // namespace android
diff --git a/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp b/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp
deleted file mode 100644
index b0d6b09..0000000
--- a/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "NativeBridgeTest.h"
-
-namespace android {
-
-constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
-
-TEST_F(NativeBridgeTest, V3_InitAnonymousNamespace) {
- // Init
- ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
- ASSERT_TRUE(NativeBridgeAvailable());
- ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
- ASSERT_TRUE(NativeBridgeAvailable());
- ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
- ASSERT_TRUE(NativeBridgeAvailable());
-
- ASSERT_EQ(3U, NativeBridgeGetVersion());
- ASSERT_EQ(true, NativeBridgeInitAnonymousNamespace(nullptr, nullptr));
-
- // Clean-up code_cache
- ASSERT_EQ(0, rmdir(kCodeCache));
-}
-
-} // namespace android
diff --git a/libnativebridge/tests/NativeBridge3IsPathSupported_test.cpp b/libnativebridge/tests/NativeBridge3IsPathSupported_test.cpp
deleted file mode 100644
index 325e40b..0000000
--- a/libnativebridge/tests/NativeBridge3IsPathSupported_test.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "NativeBridgeTest.h"
-
-namespace android {
-
-constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
-
-TEST_F(NativeBridgeTest, V3_IsPathSupported) {
- // Init
- ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
- ASSERT_TRUE(NativeBridgeAvailable());
- ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
- ASSERT_TRUE(NativeBridgeAvailable());
- ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
- ASSERT_TRUE(NativeBridgeAvailable());
-
- ASSERT_EQ(3U, NativeBridgeGetVersion());
- ASSERT_EQ(true, NativeBridgeIsPathSupported(nullptr));
-
- // Clean-up code_cache
- ASSERT_EQ(0, rmdir(kCodeCache));
-}
-
-} // namespace android
diff --git a/libnativebridge/tests/NativeBridge3LoadLibraryExt_test.cpp b/libnativebridge/tests/NativeBridge3LoadLibraryExt_test.cpp
deleted file mode 100644
index 4caeb44..0000000
--- a/libnativebridge/tests/NativeBridge3LoadLibraryExt_test.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "NativeBridgeTest.h"
-
-namespace android {
-
-constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
-
-TEST_F(NativeBridgeTest, V3_LoadLibraryExt) {
- // Init
- ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
- ASSERT_TRUE(NativeBridgeAvailable());
- ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
- ASSERT_TRUE(NativeBridgeAvailable());
- ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
- ASSERT_TRUE(NativeBridgeAvailable());
-
- ASSERT_EQ(3U, NativeBridgeGetVersion());
- ASSERT_EQ(nullptr, NativeBridgeLoadLibraryExt(nullptr, 0, nullptr));
-
- // Clean-up code_cache
- ASSERT_EQ(0, rmdir(kCodeCache));
-}
-
-} // namespace android
diff --git a/libnativebridge/tests/NativeBridge3UnloadLibrary_test.cpp b/libnativebridge/tests/NativeBridge3UnloadLibrary_test.cpp
deleted file mode 100644
index 93a979c..0000000
--- a/libnativebridge/tests/NativeBridge3UnloadLibrary_test.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "NativeBridgeTest.h"
-
-namespace android {
-
-constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
-
-TEST_F(NativeBridgeTest, V3_UnloadLibrary) {
- // Init
- ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
- ASSERT_TRUE(NativeBridgeAvailable());
- ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
- ASSERT_TRUE(NativeBridgeAvailable());
- ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
- ASSERT_TRUE(NativeBridgeAvailable());
-
- ASSERT_EQ(3U, NativeBridgeGetVersion());
- ASSERT_EQ(0, NativeBridgeUnloadLibrary(nullptr));
-
- // Clean-up code_cache
- ASSERT_EQ(0, rmdir(kCodeCache));
-}
-
-} // namespace android
diff --git a/libnativebridge/tests/NativeBridgeApi.c b/libnativebridge/tests/NativeBridgeApi.c
deleted file mode 100644
index 7ab71fe..0000000
--- a/libnativebridge/tests/NativeBridgeApi.c
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* The main purpose of this test is to ensure this C header compiles in C, so
- * that no C++ features inadvertently leak into the C ABI. */
-#include "nativebridge/native_bridge.h"
-
-int main(int argc, char** argv) {
- (void)argc;
- (void)argv;
- return 0;
-}
diff --git a/libnativebridge/tests/NativeBridgeTest.h b/libnativebridge/tests/NativeBridgeTest.h
deleted file mode 100644
index 0f99816..0000000
--- a/libnativebridge/tests/NativeBridgeTest.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2014 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 NATIVE_BRIDGE_TEST_H_
-#define NATIVE_BRIDGE_TEST_H_
-
-#define LOG_TAG "NativeBridge_test"
-
-#include <nativebridge/native_bridge.h>
-#include <gtest/gtest.h>
-
-constexpr const char* kNativeBridgeLibrary = "libnativebridge-dummy.so";
-constexpr const char* kCodeCache = "./code_cache";
-constexpr const char* kCodeCacheStatFail = "./code_cache/temp";
-constexpr const char* kNativeBridgeLibrary2 = "libnativebridge2-dummy.so";
-constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
-
-namespace android {
-
-class NativeBridgeTest : public testing::Test {
-};
-
-}; // namespace android
-
-#endif // NATIVE_BRIDGE_H_
-
diff --git a/libnativebridge/tests/NativeBridgeVersion_test.cpp b/libnativebridge/tests/NativeBridgeVersion_test.cpp
deleted file mode 100644
index d3f9a80..0000000
--- a/libnativebridge/tests/NativeBridgeVersion_test.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.
- */
-
-#include "NativeBridgeTest.h"
-
-#include <unistd.h>
-
-namespace android {
-
-TEST_F(NativeBridgeTest, Version) {
- // When a bridge isn't loaded, we expect 0.
- EXPECT_EQ(NativeBridgeGetVersion(), 0U);
-
- // After our dummy bridge has been loaded, we expect 1.
- ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
- EXPECT_EQ(NativeBridgeGetVersion(), 1U);
-
- // Unload
- UnloadNativeBridge();
-
- // Version information is gone.
- EXPECT_EQ(NativeBridgeGetVersion(), 0U);
-}
-
-} // namespace android
diff --git a/libnativebridge/tests/NeedsNativeBridge_test.cpp b/libnativebridge/tests/NeedsNativeBridge_test.cpp
deleted file mode 100644
index c8ff743..0000000
--- a/libnativebridge/tests/NeedsNativeBridge_test.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-#include "NativeBridgeTest.h"
-
-#include <android-base/macros.h>
-
-namespace android {
-
-static const char* kISAs[] = { "arm", "arm64", "mips", "mips64", "x86", "x86_64", "random", "64arm",
- "64_x86", "64_x86_64", "", "reallylongstringabcd", nullptr };
-
-TEST_F(NativeBridgeTest, NeedsNativeBridge) {
- EXPECT_EQ(false, NeedsNativeBridge(ABI_STRING));
-
- const size_t kISACount = sizeof(kISAs) / sizeof(kISAs[0]);
- for (size_t i = 0; i < kISACount; i++) {
- EXPECT_EQ(kISAs[i] == nullptr ? false : strcmp(kISAs[i], ABI_STRING) != 0,
- NeedsNativeBridge(kISAs[i]));
- }
-}
-
-} // namespace android
diff --git a/libnativebridge/tests/PreInitializeNativeBridgeFail1_test.cpp b/libnativebridge/tests/PreInitializeNativeBridgeFail1_test.cpp
deleted file mode 100644
index 5a2b0a1..0000000
--- a/libnativebridge/tests/PreInitializeNativeBridgeFail1_test.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-#include "NativeBridgeTest.h"
-
-#include <dlfcn.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-
-#include <cstdio>
-#include <cstring>
-
-#include <android/log.h>
-
-namespace android {
-
-TEST_F(NativeBridgeTest, PreInitializeNativeBridgeFail1) {
- // Needs a valid application directory.
- ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
- ASSERT_FALSE(PreInitializeNativeBridge(nullptr, "isa"));
- ASSERT_TRUE(NativeBridgeError());
-}
-
-} // namespace android
diff --git a/libnativebridge/tests/PreInitializeNativeBridgeFail2_test.cpp b/libnativebridge/tests/PreInitializeNativeBridgeFail2_test.cpp
deleted file mode 100644
index af976b1..0000000
--- a/libnativebridge/tests/PreInitializeNativeBridgeFail2_test.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-#include <dlfcn.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-
-#include <cstdio>
-#include <cstring>
-
-#include <android/log.h>
-
-#include "NativeBridgeTest.h"
-
-namespace android {
-
-TEST_F(NativeBridgeTest, PreInitializeNativeBridgeFail2) {
- // Needs LoadNativeBridge() first
- ASSERT_FALSE(PreInitializeNativeBridge(nullptr, "isa"));
- ASSERT_TRUE(NativeBridgeError());
-}
-
-} // namespace android
diff --git a/libnativebridge/tests/PreInitializeNativeBridge_test.cpp b/libnativebridge/tests/PreInitializeNativeBridge_test.cpp
deleted file mode 100644
index cd5a8e2..0000000
--- a/libnativebridge/tests/PreInitializeNativeBridge_test.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-#include <dlfcn.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-
-#include <cstdio>
-#include <cstring>
-
-#include <android/log.h>
-
-#include "NativeBridgeTest.h"
-
-namespace android {
-
-TEST_F(NativeBridgeTest, PreInitializeNativeBridge) {
- ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
-#if !defined(__APPLE__) // Mac OS does not support bind-mount.
-#if !defined(__ANDROID__) // Cannot write into the hard-wired location.
- static constexpr const char* kTestData = "PreInitializeNativeBridge test.";
-
- // Try to create our mount namespace.
- if (unshare(CLONE_NEWNS) != -1) {
- // Create a dummy file.
- FILE* cpuinfo = fopen("./cpuinfo", "w");
- ASSERT_NE(nullptr, cpuinfo) << strerror(errno);
- fprintf(cpuinfo, kTestData);
- fclose(cpuinfo);
-
- ASSERT_TRUE(PreInitializeNativeBridge("does not matter 1", "short 2"));
-
- // Read /proc/cpuinfo
- FILE* proc_cpuinfo = fopen("/proc/cpuinfo", "r");
- ASSERT_NE(nullptr, proc_cpuinfo) << strerror(errno);
- char buf[1024];
- EXPECT_NE(nullptr, fgets(buf, sizeof(buf), proc_cpuinfo)) << "Error reading.";
- fclose(proc_cpuinfo);
-
- EXPECT_EQ(0, strcmp(buf, kTestData));
-
- // Delete the file.
- ASSERT_EQ(0, unlink("./cpuinfo")) << "Error unlinking temporary file.";
- // Ending the test will tear down the mount namespace.
- } else {
- GTEST_LOG_(WARNING) << "Could not create mount namespace. Are you running this as root?";
- }
-#endif
-#endif
-}
-
-} // namespace android
diff --git a/libnativebridge/tests/ReSetupNativeBridge_test.cpp b/libnativebridge/tests/ReSetupNativeBridge_test.cpp
deleted file mode 100644
index 944e5d7..0000000
--- a/libnativebridge/tests/ReSetupNativeBridge_test.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-#include "NativeBridgeTest.h"
-
-namespace android {
-
-TEST_F(NativeBridgeTest, ReSetup) {
- EXPECT_EQ(false, NativeBridgeError());
- LoadNativeBridge("", nullptr);
- EXPECT_EQ(false, NativeBridgeError());
- LoadNativeBridge("", nullptr);
- // This should lead to an error for trying to re-setup a native bridge.
- EXPECT_EQ(true, NativeBridgeError());
-}
-
-} // namespace android
diff --git a/libnativebridge/tests/UnavailableNativeBridge_test.cpp b/libnativebridge/tests/UnavailableNativeBridge_test.cpp
deleted file mode 100644
index ad374a5..0000000
--- a/libnativebridge/tests/UnavailableNativeBridge_test.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "NativeBridgeTest.h"
-
-namespace android {
-
-TEST_F(NativeBridgeTest, NoNativeBridge) {
- EXPECT_EQ(false, NativeBridgeAvailable());
- // Try to initialize. This should fail as we are not set up.
- EXPECT_EQ(false, InitializeNativeBridge(nullptr, nullptr));
- EXPECT_EQ(true, NativeBridgeError());
- EXPECT_EQ(false, NativeBridgeAvailable());
-}
-
-} // namespace android
diff --git a/libnativebridge/tests/ValidNameNativeBridge_test.cpp b/libnativebridge/tests/ValidNameNativeBridge_test.cpp
deleted file mode 100644
index 690be4a..0000000
--- a/libnativebridge/tests/ValidNameNativeBridge_test.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <NativeBridgeTest.h>
-
-namespace android {
-
-static const char* kTestName = "librandom-bridge_not.existing.so";
-
-TEST_F(NativeBridgeTest, ValidName) {
- // Check that the name is acceptable.
- EXPECT_EQ(true, NativeBridgeNameAcceptable(kTestName));
-
- // Now check what happens on LoadNativeBridge.
- EXPECT_EQ(false, NativeBridgeError());
- LoadNativeBridge(kTestName, nullptr);
- // This will lead to an error as the library doesn't exist.
- EXPECT_EQ(true, NativeBridgeError());
- EXPECT_EQ(false, NativeBridgeAvailable());
-}
-
-} // namespace android
diff --git a/libnativeloader/.clang-format b/libnativeloader/.clang-format
deleted file mode 120000
index fd0645f..0000000
--- a/libnativeloader/.clang-format
+++ /dev/null
@@ -1 +0,0 @@
-../.clang-format-2
\ No newline at end of file
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
deleted file mode 100644
index 939bdd7..0000000
--- a/libnativeloader/Android.bp
+++ /dev/null
@@ -1,88 +0,0 @@
-// Shared library for target
-// ========================================================
-cc_defaults {
- name: "libnativeloader-defaults",
- cflags: [
- "-Werror",
- "-Wall",
- ],
- cppflags: [
- "-fvisibility=hidden",
- ],
- header_libs: ["libnativeloader-headers"],
- export_header_lib_headers: ["libnativeloader-headers"],
-}
-
-cc_library {
- name: "libnativeloader",
- defaults: ["libnativeloader-defaults"],
- host_supported: true,
- srcs: [
- "native_loader.cpp",
- ],
- shared_libs: [
- "libnativehelper",
- "liblog",
- "libnativebridge",
- "libbase",
- ],
- target: {
- android: {
- srcs: [
- "library_namespaces.cpp",
- "native_loader_namespace.cpp",
- "public_libraries.cpp",
- ],
- shared_libs: [
- "libdl_android",
- ],
- },
- },
- required: [
- "llndk.libraries.txt",
- "vndksp.libraries.txt",
- ],
- stubs: {
- symbol_file: "libnativeloader.map.txt",
- versions: ["1"],
- },
-}
-
-// TODO(b/124250621) eliminate the need for this library
-cc_library {
- name: "libnativeloader_lazy",
- defaults: ["libnativeloader-defaults"],
- host_supported: false,
- srcs: ["native_loader_lazy.cpp"],
- required: ["libnativeloader"],
-}
-
-cc_library_headers {
- name: "libnativeloader-headers",
- host_supported: true,
- export_include_dirs: ["include"],
-}
-
-cc_test {
- name: "libnativeloader_test",
- srcs: [
- "native_loader_test.cpp",
- "native_loader.cpp",
- "library_namespaces.cpp",
- "native_loader_namespace.cpp",
- "public_libraries.cpp",
- ],
- cflags: ["-DANDROID"],
- static_libs: [
- "libbase",
- "liblog",
- "libnativehelper",
- "libgmock",
- ],
- header_libs: [
- "libnativebridge-headers",
- "libnativeloader-headers",
- ],
- system_shared_libs: ["libc", "libm"],
- test_suites: ["device-tests"],
-}
diff --git a/libnativeloader/CPPLINT.cfg b/libnativeloader/CPPLINT.cfg
deleted file mode 100644
index db98533..0000000
--- a/libnativeloader/CPPLINT.cfg
+++ /dev/null
@@ -1,19 +0,0 @@
-#
-# Copyright (C) 2019 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.
-#
-
-filter=-build/header_guard
-filter=-readability/check
-filter=-build/namespaces
diff --git a/libnativeloader/OWNERS b/libnativeloader/OWNERS
deleted file mode 100644
index f735653..0000000
--- a/libnativeloader/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-dimitry@google.com
-jiyong@google.com
-ngeoffray@google.com
-oth@google.com
-mast@google.com
-rpl@google.com
diff --git a/libnativeloader/README.md b/libnativeloader/README.md
deleted file mode 100644
index 57b9001..0000000
--- a/libnativeloader/README.md
+++ /dev/null
@@ -1,84 +0,0 @@
-libnativeloader
-===============================================================================
-
-Overview
--------------------------------------------------------------------------------
-libnativeloader is responsible for loading native shared libraries (`*.so`
-files) inside the Android Runtime (ART). The native shared libraries could be
-app-provided JNI libraries or public native libraries like `libc.so` provided
-by the platform.
-
-The most typical use case of this library is calling `System.loadLibrary(name)`.
-When the method is called, the ART runtime delegates the call to this library
-along with the reference to the classloader where the call was made. Then this
-library finds the linker namespace (named `classloader-namespace`) that is
-associated with the given classloader, and tries to load the requested library
-from the namespace. The actual searching, loading, and linking of the library
-is performed by the dynamic linker.
-
-The linker namespace is created when an APK is loaded into the process, and is
-associated with the classloader that loaded the APK. The linker namespace is
-configured so that only the JNI libraries embedded in the APK is accessible
-from the namespace, thus preventing an APK from loading JNI libraries of other
-APKs.
-
-The linker namespace is also configured differently depending on other
-characteristics of the APK such as whether or not the APK is bundled with the
-platform. In case of the unbundled, i.e., downloaded or updated APK, only the
-public native libraries that is listed in `/system/etc/public.libraries.txt`
-are available from the platform, whereas in case of the bundled, all libraries
-under `/system/lib` are available (i.e. shared). In case when the unbundled
-app is from `/vendor` or `/product` partition, the app is additionally provided
-with the [VNDK-SP](https://source.android.com/devices/architecture/vndk#sp-hal)
-libraries. As the platform is getting modularized with
-[APEX](https://android.googlesource.com/platform/system/apex/+/refs/heads/master/docs/README.md),
-some libraries are no longer provided from platform, but from the APEXes which
-have their own linker namespaces. For example, ICU libraries `libicuuc.so` and
-`libicui18n.so` are from the runtime APEX.
-
-The list of public native libraries is not static. The default set of libraries
-are defined in AOSP, but partners can extend it to include their own libraries.
-Currently, following extensions are available:
-
-- `/vendor/etc/public.libraries.txt`: libraries in `/vendor/lib` that are
-specific to the underlying SoC, e.g. GPU, DSP, etc.
-- `/{system|product}/etc/public.libraries-<companyname>.txt`: libraries in
-`/{system|product}/lib` that a device manufacturer has newly added. The
-libraries should be named as `lib<name>.<companyname>.so` as in
-`libFoo.acme.so`.
-
-Note that, due to the naming constraint requiring `.<companyname>.so` suffix, it
-is prohibited for a device manufacturer to expose an AOSP-defined private
-library, e.g. libgui.so, libart.so, etc., to APKs.
-
-Lastly, libnativeloader is responsible for abstracting the two types of the
-dynamic linker interface: `libdl.so` and `libnativebridge.so`. The former is
-for non-translated, e.g. ARM-on-ARM, libraries, while the latter is for
-loading libraries in a translated environment such as ARM-on-x86.
-
-Implementation
--------------------------------------------------------------------------------
-Implementation wise, libnativeloader consists of four parts:
-
-- `native_loader.cpp`
-- `library_namespaces.cpp`
-- `native_loader_namespace.cpp`
-- `public_libraries.cpp`
-
-`native_loader.cpp` implements the public interface of this library. It is just
-a thin wrapper around `library_namespaces.cpp` and `native_loader_namespace.cpp`.
-
-`library_namespaces.cpp` implements the singleton class `LibraryNamespaces` which
-is a manager-like entity that is responsible for creating and configuring
-linker namespaces and finding an already created linker namespace for a given
-classloader.
-
-`native_loader_namespace.cpp` implements the class `NativeLoaderNamespace` that
-models a linker namespace. Its main job is to abstract the two types of the
-dynamic linker interface so that other parts of this library do not have to know
-the differences of the interfaces.
-
-`public_libraries.cpp` is responsible for reading `*.txt` files for the public
-native libraries from the various partitions. It can be considered as a part of
-`LibraryNamespaces` but is separated from it to hide the details of the parsing
-routines.
diff --git a/libnativeloader/TEST_MAPPING b/libnativeloader/TEST_MAPPING
deleted file mode 100644
index 7becb77..0000000
--- a/libnativeloader/TEST_MAPPING
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "presubmit": [
- {
- "name": "libnativeloader_test"
- }
- ],
- "imports": [
- {
- "path": "cts/tests/tests/jni"
- }
- ]
-}
diff --git a/libnativeloader/include/nativeloader/dlext_namespaces.h b/libnativeloader/include/nativeloader/dlext_namespaces.h
deleted file mode 100644
index 8937636..0000000
--- a/libnativeloader/include/nativeloader/dlext_namespaces.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __ANDROID_DLEXT_NAMESPACES_H__
-#define __ANDROID_DLEXT_NAMESPACES_H__
-
-#include <android/dlext.h>
-#include <stdbool.h>
-
-__BEGIN_DECLS
-
-enum {
- /* A regular namespace is the namespace with a custom search path that does
- * not impose any restrictions on the location of native libraries.
- */
- ANDROID_NAMESPACE_TYPE_REGULAR = 0,
-
- /* An isolated namespace requires all the libraries to be on the search path
- * or under permitted_when_isolated_path. The search path is the union of
- * ld_library_path and default_library_path.
- */
- ANDROID_NAMESPACE_TYPE_ISOLATED = 1,
-
- /* The shared namespace clones the list of libraries of the caller namespace upon creation
- * which means that they are shared between namespaces - the caller namespace and the new one
- * will use the same copy of a library if it was loaded prior to android_create_namespace call.
- *
- * Note that libraries loaded after the namespace is created will not be shared.
- *
- * Shared namespaces can be isolated or regular. Note that they do not inherit the search path nor
- * permitted_path from the caller's namespace.
- */
- ANDROID_NAMESPACE_TYPE_SHARED = 2,
-
- /* This flag instructs linker to enable grey-list workaround for the namespace.
- * See http://b/26394120 for details.
- */
- ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED = 0x08000000,
-
- /* This flag instructs linker to use this namespace as the anonymous
- * namespace. The anonymous namespace is used in the case when linker cannot
- * identify the caller of dlopen/dlsym. This happens for the code not loaded
- * by dynamic linker; for example calls from the mono-compiled code. There can
- * be only one anonymous namespace in a process. If there already is an
- * anonymous namespace in the process, using this flag when creating a new
- * namespace causes an error.
- */
- ANDROID_NAMESPACE_TYPE_ALSO_USED_AS_ANONYMOUS = 0x10000000,
-
- ANDROID_NAMESPACE_TYPE_SHARED_ISOLATED =
- ANDROID_NAMESPACE_TYPE_SHARED | ANDROID_NAMESPACE_TYPE_ISOLATED,
-};
-
-/*
- * Creates new linker namespace.
- * ld_library_path and default_library_path represent the search path
- * for the libraries in the namespace.
- *
- * The libraries in the namespace are searched by folowing order:
- * 1. ld_library_path (Think of this as namespace-local LD_LIBRARY_PATH)
- * 2. In directories specified by DT_RUNPATH of the "needed by" binary.
- * 3. deault_library_path (This of this as namespace-local default library path)
- *
- * When type is ANDROID_NAMESPACE_TYPE_ISOLATED the resulting namespace requires all of
- * the libraries to be on the search path or under the permitted_when_isolated_path;
- * the search_path is ld_library_path:default_library_path. Note that the
- * permitted_when_isolated_path path is not part of the search_path and
- * does not affect the search order. It is a way to allow loading libraries from specific
- * locations when using absolute path.
- * If a library or any of its dependencies are outside of the permitted_when_isolated_path
- * and search_path, and it is not part of the public namespace dlopen will fail.
- */
-extern struct android_namespace_t* android_create_namespace(
- const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type,
- const char* permitted_when_isolated_path, struct android_namespace_t* parent);
-
-/*
- * Creates a link between namespaces. Every link has list of sonames of
- * shared libraries. These are the libraries which are accessible from
- * namespace 'from' but loaded within namespace 'to' context.
- * When to namespace is nullptr this function establishes a link between
- * 'from' namespace and the default namespace.
- *
- * The lookup order of the libraries in namespaces with links is following:
- * 1. Look inside current namespace using 'this' namespace search path.
- * 2. Look in linked namespaces
- * 2.1. Perform soname check - if library soname is not in the list of shared
- * libraries sonames skip this link, otherwise
- * 2.2. Search library using linked namespace search path. Note that this
- * step will not go deeper into linked namespaces for this library but
- * will do so for DT_NEEDED libraries.
- */
-extern bool android_link_namespaces(struct android_namespace_t* from,
- struct android_namespace_t* to,
- const char* shared_libs_sonames);
-
-extern struct android_namespace_t* android_get_exported_namespace(const char* name);
-
-__END_DECLS
-
-#endif /* __ANDROID_DLEXT_NAMESPACES_H__ */
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
deleted file mode 100644
index 51fb875..0000000
--- a/libnativeloader/include/nativeloader/native_loader.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef NATIVE_LOADER_H_
-#define NATIVE_LOADER_H_
-
-#include <stdbool.h>
-#include <stdint.h>
-#include "jni.h"
-#if defined(__ANDROID__)
-#include <android/dlext.h>
-#endif
-
-#ifdef __cplusplus
-namespace android {
-extern "C" {
-#endif // __cplusplus
-
-// README: the char** error message parameter being passed
-// to the methods below need to be freed through calling NativeLoaderFreeErrorMessage.
-// It's the caller's responsibility to call that method.
-
-__attribute__((visibility("default")))
-void InitializeNativeLoader();
-
-__attribute__((visibility("default"))) jstring CreateClassLoaderNamespace(
- JNIEnv* env, int32_t target_sdk_version, jobject class_loader, bool is_shared, jstring dex_path,
- jstring library_path, jstring permitted_path);
-
-__attribute__((visibility("default"))) void* OpenNativeLibrary(
- JNIEnv* env, int32_t target_sdk_version, const char* path, jobject class_loader,
- const char* caller_location, jstring library_path, bool* needs_native_bridge, char** error_msg);
-
-__attribute__((visibility("default"))) bool CloseNativeLibrary(void* handle,
- const bool needs_native_bridge,
- char** error_msg);
-
-__attribute__((visibility("default"))) void NativeLoaderFreeErrorMessage(char* msg);
-
-#if defined(__ANDROID__)
-// Look up linker namespace by class_loader. Returns nullptr if
-// there is no namespace associated with the class_loader.
-// TODO(b/79940628): move users to FindNativeLoaderNamespaceByClassLoader and remove this function.
-__attribute__((visibility("default"))) struct android_namespace_t* FindNamespaceByClassLoader(
- JNIEnv* env, jobject class_loader);
-// That version works with native bridge namespaces, but requires use of OpenNativeLibrary.
-struct NativeLoaderNamespace;
-__attribute__((visibility("default"))) struct NativeLoaderNamespace*
-FindNativeLoaderNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
-// Load library. Unlinke OpenNativeLibrary above couldn't create namespace on demand, but does
-// not require access to JNIEnv either.
-__attribute__((visibility("default"))) void* OpenNativeLibraryInNamespace(
- struct NativeLoaderNamespace* ns, const char* path, bool* needs_native_bridge,
- char** error_msg);
-#endif
-
-__attribute__((visibility("default")))
-void ResetNativeLoader();
-
-#ifdef __cplusplus
-} // extern "C"
-} // namespace android
-#endif // __cplusplus
-
-#endif // NATIVE_BRIDGE_H_
diff --git a/libnativeloader/libnativeloader.map.txt b/libnativeloader/libnativeloader.map.txt
deleted file mode 100644
index 40c30bd..0000000
--- a/libnativeloader/libnativeloader.map.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-#
-# Copyright (C) 2019 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.
-#
-
-# TODO(b/122710865): Prune these uses once the runtime APEX is complete.
-LIBNATIVELOADER_1 {
- global:
- OpenNativeLibrary;
- InitializeNativeLoader;
- ResetNativeLoader;
- CloseNativeLibrary;
- OpenNativeLibraryInNamespace;
- FindNamespaceByClassLoader;
- FindNativeLoaderNamespaceByClassLoader;
- CreateClassLoaderNamespace;
- NativeLoaderFreeErrorMessage;
- local:
- *;
-};
diff --git a/libnativeloader/library_namespaces.cpp b/libnativeloader/library_namespaces.cpp
deleted file mode 100644
index 9a33b55..0000000
--- a/libnativeloader/library_namespaces.cpp
+++ /dev/null
@@ -1,319 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-#include "library_namespaces.h"
-
-#include <dirent.h>
-#include <dlfcn.h>
-
-#include <regex>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/macros.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <nativehelper/ScopedUtfChars.h>
-
-#include "nativeloader/dlext_namespaces.h"
-#include "public_libraries.h"
-#include "utils.h"
-
-using android::base::Error;
-
-namespace android::nativeloader {
-
-namespace {
-// The device may be configured to have the vendor libraries loaded to a separate namespace.
-// For historical reasons this namespace was named sphal but effectively it is intended
-// to use to load vendor libraries to separate namespace with controlled interface between
-// vendor and system namespaces.
-constexpr const char* kVendorNamespaceName = "sphal";
-constexpr const char* kVndkNamespaceName = "vndk";
-constexpr const char* kRuntimeNamespaceName = "runtime";
-constexpr const char* kNeuralNetworksNamespaceName = "neuralnetworks";
-
-// classloader-namespace is a linker namespace that is created for the loaded
-// app. To be specific, it is created for the app classloader. When
-// System.load() is called from a Java class that is loaded from the
-// classloader, the classloader-namespace namespace associated with that
-// classloader is selected for dlopen. The namespace is configured so that its
-// search path is set to the app-local JNI directory and it is linked to the
-// platform namespace with the names of libs listed in the public.libraries.txt.
-// This way an app can only load its own JNI libraries along with the public libs.
-constexpr const char* kClassloaderNamespaceName = "classloader-namespace";
-// Same thing for vendor APKs.
-constexpr const char* kVendorClassloaderNamespaceName = "vendor-classloader-namespace";
-
-// (http://b/27588281) This is a workaround for apps using custom classloaders and calling
-// System.load() with an absolute path which is outside of the classloader library search path.
-// This list includes all directories app is allowed to access this way.
-constexpr const char* kWhitelistedDirectories = "/data:/mnt/expand";
-
-constexpr const char* kVendorLibPath = "/vendor/" LIB;
-constexpr const char* kProductLibPath = "/product/" LIB ":/system/product/" LIB;
-
-const std::regex kVendorDexPathRegex("(^|:)/vendor/");
-const std::regex kProductDexPathRegex("(^|:)(/system)?/product/");
-
-// Define origin of APK if it is from vendor partition or product partition
-typedef enum {
- APK_ORIGIN_DEFAULT = 0,
- APK_ORIGIN_VENDOR = 1,
- APK_ORIGIN_PRODUCT = 2,
-} ApkOrigin;
-
-jobject GetParentClassLoader(JNIEnv* env, jobject class_loader) {
- jclass class_loader_class = env->FindClass("java/lang/ClassLoader");
- jmethodID get_parent =
- env->GetMethodID(class_loader_class, "getParent", "()Ljava/lang/ClassLoader;");
-
- return env->CallObjectMethod(class_loader, get_parent);
-}
-
-ApkOrigin GetApkOriginFromDexPath(JNIEnv* env, jstring dex_path) {
- ApkOrigin apk_origin = APK_ORIGIN_DEFAULT;
-
- if (dex_path != nullptr) {
- ScopedUtfChars dex_path_utf_chars(env, dex_path);
-
- if (std::regex_search(dex_path_utf_chars.c_str(), kVendorDexPathRegex)) {
- apk_origin = APK_ORIGIN_VENDOR;
- }
-
- if (std::regex_search(dex_path_utf_chars.c_str(), kProductDexPathRegex)) {
- LOG_ALWAYS_FATAL_IF(apk_origin == APK_ORIGIN_VENDOR,
- "Dex path contains both vendor and product partition : %s",
- dex_path_utf_chars.c_str());
-
- apk_origin = APK_ORIGIN_PRODUCT;
- }
- }
- return apk_origin;
-}
-
-} // namespace
-
-void LibraryNamespaces::Initialize() {
- // Once public namespace is initialized there is no
- // point in running this code - it will have no effect
- // on the current list of public libraries.
- if (initialized_) {
- return;
- }
-
- // android_init_namespaces() expects all the public libraries
- // to be loaded so that they can be found by soname alone.
- //
- // TODO(dimitry): this is a bit misleading since we do not know
- // if the vendor public library is going to be opened from /vendor/lib
- // we might as well end up loading them from /system/lib or /product/lib
- // For now we rely on CTS test to catch things like this but
- // it should probably be addressed in the future.
- for (const auto& soname : android::base::Split(preloadable_public_libraries(), ":")) {
- LOG_ALWAYS_FATAL_IF(dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE) == nullptr,
- "Error preloading public library %s: %s", soname.c_str(), dlerror());
- }
-}
-
-Result<NativeLoaderNamespace*> LibraryNamespaces::Create(JNIEnv* env, uint32_t target_sdk_version,
- jobject class_loader, bool is_shared,
- jstring dex_path,
- jstring java_library_path,
- jstring java_permitted_path) {
- std::string library_path; // empty string by default.
-
- if (java_library_path != nullptr) {
- ScopedUtfChars library_path_utf_chars(env, java_library_path);
- library_path = library_path_utf_chars.c_str();
- }
-
- ApkOrigin apk_origin = GetApkOriginFromDexPath(env, dex_path);
-
- // (http://b/27588281) This is a workaround for apps using custom
- // classloaders and calling System.load() with an absolute path which
- // is outside of the classloader library search path.
- //
- // This part effectively allows such a classloader to access anything
- // under /data and /mnt/expand
- std::string permitted_path = kWhitelistedDirectories;
-
- if (java_permitted_path != nullptr) {
- ScopedUtfChars path(env, java_permitted_path);
- if (path.c_str() != nullptr && path.size() > 0) {
- permitted_path = permitted_path + ":" + path.c_str();
- }
- }
-
- LOG_ALWAYS_FATAL_IF(FindNamespaceByClassLoader(env, class_loader) != nullptr,
- "There is already a namespace associated with this classloader");
-
- std::string system_exposed_libraries = default_public_libraries();
- const char* namespace_name = kClassloaderNamespaceName;
- bool unbundled_vendor_or_product_app = false;
- if ((apk_origin == APK_ORIGIN_VENDOR ||
- (apk_origin == APK_ORIGIN_PRODUCT && target_sdk_version > 29)) &&
- !is_shared) {
- unbundled_vendor_or_product_app = true;
- // For vendor / product apks, give access to the vendor / product lib even though
- // they are treated as unbundled; the libs and apks are still bundled
- // together in the vendor / product partition.
- const char* origin_partition;
- const char* origin_lib_path;
-
- switch (apk_origin) {
- case APK_ORIGIN_VENDOR:
- origin_partition = "vendor";
- origin_lib_path = kVendorLibPath;
- break;
- case APK_ORIGIN_PRODUCT:
- origin_partition = "product";
- origin_lib_path = kProductLibPath;
- break;
- default:
- origin_partition = "unknown";
- origin_lib_path = "";
- }
- library_path = library_path + ":" + origin_lib_path;
- permitted_path = permitted_path + ":" + origin_lib_path;
-
- // Also give access to LLNDK libraries since they are available to vendors
- system_exposed_libraries = system_exposed_libraries + ":" + llndk_libraries().c_str();
-
- // Different name is useful for debugging
- namespace_name = kVendorClassloaderNamespaceName;
- ALOGD("classloader namespace configured for unbundled %s apk. library_path=%s",
- origin_partition, library_path.c_str());
- } else {
- // extended public libraries are NOT available to vendor apks, otherwise it
- // would be system->vendor violation.
- if (!extended_public_libraries().empty()) {
- system_exposed_libraries = system_exposed_libraries + ':' + extended_public_libraries();
- }
- }
-
- // Create the app namespace
- NativeLoaderNamespace* parent_ns = FindParentNamespaceByClassLoader(env, class_loader);
- // Heuristic: the first classloader with non-empty library_path is assumed to
- // be the main classloader for app
- // TODO(b/139178525) remove this heuristic by determining this in LoadedApk (or its
- // friends) and then passing it down to here.
- bool is_main_classloader = app_main_namespace_ == nullptr && !library_path.empty();
- // Policy: the namespace for the main classloader is also used as the
- // anonymous namespace.
- bool also_used_as_anonymous = is_main_classloader;
- // Note: this function is executed with g_namespaces_mutex held, thus no
- // racing here.
- auto app_ns = NativeLoaderNamespace::Create(
- namespace_name, library_path, permitted_path, parent_ns, is_shared,
- target_sdk_version < 24 /* is_greylist_enabled */, also_used_as_anonymous);
- if (!app_ns) {
- return app_ns.error();
- }
- // ... and link to other namespaces to allow access to some public libraries
- bool is_bridged = app_ns->IsBridged();
-
- auto platform_ns = NativeLoaderNamespace::GetPlatformNamespace(is_bridged);
- if (!platform_ns) {
- return platform_ns.error();
- }
-
- auto linked = app_ns->Link(*platform_ns, system_exposed_libraries);
- if (!linked) {
- return linked.error();
- }
-
- auto runtime_ns = NativeLoaderNamespace::GetExportedNamespace(kRuntimeNamespaceName, is_bridged);
- // Runtime apex does not exist in host, and under certain build conditions.
- if (runtime_ns) {
- linked = app_ns->Link(*runtime_ns, runtime_public_libraries());
- if (!linked) {
- return linked.error();
- }
- }
-
- // Give access to NNAPI libraries (apex-updated LLNDK library).
- auto nnapi_ns =
- NativeLoaderNamespace::GetExportedNamespace(kNeuralNetworksNamespaceName, is_bridged);
- if (nnapi_ns) {
- linked = app_ns->Link(*nnapi_ns, neuralnetworks_public_libraries());
- if (!linked) {
- return linked.error();
- }
- }
-
- // Give access to VNDK-SP libraries from the 'vndk' namespace.
- if (unbundled_vendor_or_product_app && !vndksp_libraries().empty()) {
- auto vndk_ns = NativeLoaderNamespace::GetExportedNamespace(kVndkNamespaceName, is_bridged);
- if (vndk_ns) {
- linked = app_ns->Link(*vndk_ns, vndksp_libraries());
- if (!linked) {
- return linked.error();
- }
- }
- }
-
- if (!vendor_public_libraries().empty()) {
- auto vendor_ns = NativeLoaderNamespace::GetExportedNamespace(kVendorNamespaceName, is_bridged);
- // when vendor_ns is not configured, link to the platform namespace
- auto target_ns = vendor_ns ? vendor_ns : platform_ns;
- if (target_ns) {
- linked = app_ns->Link(*target_ns, vendor_public_libraries());
- if (!linked) {
- return linked.error();
- }
- }
- }
-
- namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), *app_ns));
- if (is_main_classloader) {
- app_main_namespace_ = &(*app_ns);
- }
-
- return &(namespaces_.back().second);
-}
-
-NativeLoaderNamespace* LibraryNamespaces::FindNamespaceByClassLoader(JNIEnv* env,
- jobject class_loader) {
- auto it = std::find_if(namespaces_.begin(), namespaces_.end(),
- [&](const std::pair<jweak, NativeLoaderNamespace>& value) {
- return env->IsSameObject(value.first, class_loader);
- });
- if (it != namespaces_.end()) {
- return &it->second;
- }
-
- return nullptr;
-}
-
-NativeLoaderNamespace* LibraryNamespaces::FindParentNamespaceByClassLoader(JNIEnv* env,
- jobject class_loader) {
- jobject parent_class_loader = GetParentClassLoader(env, class_loader);
-
- while (parent_class_loader != nullptr) {
- NativeLoaderNamespace* ns;
- if ((ns = FindNamespaceByClassLoader(env, parent_class_loader)) != nullptr) {
- return ns;
- }
-
- parent_class_loader = GetParentClassLoader(env, parent_class_loader);
- }
-
- return nullptr;
-}
-
-} // namespace android::nativeloader
diff --git a/libnativeloader/library_namespaces.h b/libnativeloader/library_namespaces.h
deleted file mode 100644
index 7b3efff..0000000
--- a/libnativeloader/library_namespaces.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-#pragma once
-#if !defined(__ANDROID__)
-#error "Not available for host"
-#endif
-
-#define LOG_TAG "nativeloader"
-
-#include "native_loader_namespace.h"
-
-#include <list>
-#include <string>
-
-#include <android-base/result.h>
-#include <jni.h>
-
-namespace android::nativeloader {
-
-using android::base::Result;
-
-// LibraryNamespaces is a singleton object that manages NativeLoaderNamespace
-// objects for an app process. Its main job is to create (and configure) a new
-// NativeLoaderNamespace object for a Java ClassLoader, and to find an existing
-// object for a given ClassLoader.
-class LibraryNamespaces {
- public:
- LibraryNamespaces() : initialized_(false), app_main_namespace_(nullptr) {}
-
- LibraryNamespaces(LibraryNamespaces&&) = default;
- LibraryNamespaces(const LibraryNamespaces&) = delete;
- LibraryNamespaces& operator=(const LibraryNamespaces&) = delete;
-
- void Initialize();
- void Reset() {
- namespaces_.clear();
- initialized_ = false;
- app_main_namespace_ = nullptr;
- }
- Result<NativeLoaderNamespace*> Create(JNIEnv* env, uint32_t target_sdk_version,
- jobject class_loader, bool is_shared, jstring dex_path,
- jstring java_library_path, jstring java_permitted_path);
- NativeLoaderNamespace* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
-
- private:
- Result<void> InitPublicNamespace(const char* library_path);
- NativeLoaderNamespace* FindParentNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
-
- bool initialized_;
- NativeLoaderNamespace* app_main_namespace_;
- std::list<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
-};
-
-} // namespace android::nativeloader
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
deleted file mode 100644
index 60d462f..0000000
--- a/libnativeloader/native_loader.cpp
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "nativeloader"
-
-#include "nativeloader/native_loader.h"
-
-#include <dlfcn.h>
-#include <sys/types.h>
-
-#include <memory>
-#include <mutex>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/macros.h>
-#include <android-base/strings.h>
-#include <nativebridge/native_bridge.h>
-#include <nativehelper/ScopedUtfChars.h>
-
-#ifdef __ANDROID__
-#include <log/log.h>
-#include "library_namespaces.h"
-#include "nativeloader/dlext_namespaces.h"
-#endif
-
-namespace android {
-
-namespace {
-#if defined(__ANDROID__)
-using android::nativeloader::LibraryNamespaces;
-
-constexpr const char* kApexPath = "/apex/";
-
-std::mutex g_namespaces_mutex;
-LibraryNamespaces* g_namespaces = new LibraryNamespaces;
-
-android_namespace_t* FindExportedNamespace(const char* caller_location) {
- std::string location = caller_location;
- // Lots of implicit assumptions here: we expect `caller_location` to be of the form:
- // /apex/com.android...modulename/...
- //
- // And we extract from it 'modulename', which is the name of the linker namespace.
- if (android::base::StartsWith(location, kApexPath)) {
- size_t slash_index = location.find_first_of('/', strlen(kApexPath));
- LOG_ALWAYS_FATAL_IF((slash_index == std::string::npos),
- "Error finding namespace of apex: no slash in path %s", caller_location);
- size_t dot_index = location.find_last_of('.', slash_index);
- LOG_ALWAYS_FATAL_IF((dot_index == std::string::npos),
- "Error finding namespace of apex: no dot in apex name %s", caller_location);
- std::string name = location.substr(dot_index + 1, slash_index - dot_index - 1);
- // TODO(b/139408016): Rename the runtime namespace to "art".
- if (name == "art") {
- name = "runtime";
- }
- android_namespace_t* boot_namespace = android_get_exported_namespace(name.c_str());
- LOG_ALWAYS_FATAL_IF((boot_namespace == nullptr),
- "Error finding namespace of apex: no namespace called %s", name.c_str());
- return boot_namespace;
- }
- return nullptr;
-}
-#endif // #if defined(__ANDROID__)
-} // namespace
-
-void InitializeNativeLoader() {
-#if defined(__ANDROID__)
- std::lock_guard<std::mutex> guard(g_namespaces_mutex);
- g_namespaces->Initialize();
-#endif
-}
-
-void ResetNativeLoader() {
-#if defined(__ANDROID__)
- std::lock_guard<std::mutex> guard(g_namespaces_mutex);
- g_namespaces->Reset();
-#endif
-}
-
-jstring CreateClassLoaderNamespace(JNIEnv* env, int32_t target_sdk_version, jobject class_loader,
- bool is_shared, jstring dex_path, jstring library_path,
- jstring permitted_path) {
-#if defined(__ANDROID__)
- std::lock_guard<std::mutex> guard(g_namespaces_mutex);
- auto ns = g_namespaces->Create(env, target_sdk_version, class_loader, is_shared, dex_path,
- library_path, permitted_path);
- if (!ns) {
- return env->NewStringUTF(ns.error().message().c_str());
- }
-#else
- UNUSED(env, target_sdk_version, class_loader, is_shared, dex_path, library_path, permitted_path);
-#endif
- return nullptr;
-}
-
-void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
- jobject class_loader, const char* caller_location, jstring library_path,
- bool* needs_native_bridge, char** error_msg) {
-#if defined(__ANDROID__)
- UNUSED(target_sdk_version);
- if (class_loader == nullptr) {
- *needs_native_bridge = false;
- if (caller_location != nullptr) {
- android_namespace_t* boot_namespace = FindExportedNamespace(caller_location);
- if (boot_namespace != nullptr) {
- const android_dlextinfo dlextinfo = {
- .flags = ANDROID_DLEXT_USE_NAMESPACE,
- .library_namespace = boot_namespace,
- };
- void* handle = android_dlopen_ext(path, RTLD_NOW, &dlextinfo);
- if (handle == nullptr) {
- *error_msg = strdup(dlerror());
- }
- return handle;
- }
- }
- void* handle = dlopen(path, RTLD_NOW);
- if (handle == nullptr) {
- *error_msg = strdup(dlerror());
- }
- return handle;
- }
-
- std::lock_guard<std::mutex> guard(g_namespaces_mutex);
- NativeLoaderNamespace* ns;
-
- if ((ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader)) == nullptr) {
- // This is the case where the classloader was not created by ApplicationLoaders
- // In this case we create an isolated not-shared namespace for it.
- Result<NativeLoaderNamespace*> isolated_ns =
- g_namespaces->Create(env, target_sdk_version, class_loader, false /* is_shared */, nullptr,
- library_path, nullptr);
- if (!isolated_ns) {
- *error_msg = strdup(isolated_ns.error().message().c_str());
- return nullptr;
- } else {
- ns = *isolated_ns;
- }
- }
-
- return OpenNativeLibraryInNamespace(ns, path, needs_native_bridge, error_msg);
-#else
- UNUSED(env, target_sdk_version, class_loader, caller_location);
-
- // Do some best effort to emulate library-path support. It will not
- // work for dependencies.
- //
- // Note: null has a special meaning and must be preserved.
- std::string c_library_path; // Empty string by default.
- if (library_path != nullptr && path != nullptr && path[0] != '/') {
- ScopedUtfChars library_path_utf_chars(env, library_path);
- c_library_path = library_path_utf_chars.c_str();
- }
-
- std::vector<std::string> library_paths = base::Split(c_library_path, ":");
-
- for (const std::string& lib_path : library_paths) {
- *needs_native_bridge = false;
- const char* path_arg;
- std::string complete_path;
- if (path == nullptr) {
- // Preserve null.
- path_arg = nullptr;
- } else {
- complete_path = lib_path;
- if (!complete_path.empty()) {
- complete_path.append("/");
- }
- complete_path.append(path);
- path_arg = complete_path.c_str();
- }
- void* handle = dlopen(path_arg, RTLD_NOW);
- if (handle != nullptr) {
- return handle;
- }
- if (NativeBridgeIsSupported(path_arg)) {
- *needs_native_bridge = true;
- handle = NativeBridgeLoadLibrary(path_arg, RTLD_NOW);
- if (handle != nullptr) {
- return handle;
- }
- *error_msg = strdup(NativeBridgeGetError());
- } else {
- *error_msg = strdup(dlerror());
- }
- }
- return nullptr;
-#endif
-}
-
-bool CloseNativeLibrary(void* handle, const bool needs_native_bridge, char** error_msg) {
- bool success;
- if (needs_native_bridge) {
- success = (NativeBridgeUnloadLibrary(handle) == 0);
- if (!success) {
- *error_msg = strdup(NativeBridgeGetError());
- }
- } else {
- success = (dlclose(handle) == 0);
- if (!success) {
- *error_msg = strdup(dlerror());
- }
- }
-
- return success;
-}
-
-void NativeLoaderFreeErrorMessage(char* msg) {
- // The error messages get allocated through strdup, so we must call free on them.
- free(msg);
-}
-
-#if defined(__ANDROID__)
-void* OpenNativeLibraryInNamespace(NativeLoaderNamespace* ns, const char* path,
- bool* needs_native_bridge, char** error_msg) {
- auto handle = ns->Load(path);
- if (!handle && error_msg != nullptr) {
- *error_msg = strdup(handle.error().message().c_str());
- }
- if (needs_native_bridge != nullptr) {
- *needs_native_bridge = ns->IsBridged();
- }
- return handle ? *handle : nullptr;
-}
-
-// native_bridge_namespaces are not supported for callers of this function.
-// This function will return nullptr in the case when application is running
-// on native bridge.
-android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
- std::lock_guard<std::mutex> guard(g_namespaces_mutex);
- NativeLoaderNamespace* ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader);
- if (ns != nullptr && !ns->IsBridged()) {
- return ns->ToRawAndroidNamespace();
- }
- return nullptr;
-}
-
-NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
- std::lock_guard<std::mutex> guard(g_namespaces_mutex);
- return g_namespaces->FindNamespaceByClassLoader(env, class_loader);
-}
-#endif
-
-}; // namespace android
diff --git a/libnativeloader/native_loader_lazy.cpp b/libnativeloader/native_loader_lazy.cpp
deleted file mode 100644
index 2eb1203..0000000
--- a/libnativeloader/native_loader_lazy.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-#include "nativeloader/native_loader.h"
-#define LOG_TAG "nativeloader"
-
-#include <dlfcn.h>
-#include <errno.h>
-#include <string.h>
-
-#include <log/log.h>
-
-namespace android {
-
-namespace {
-
-void* GetLibHandle() {
- static void* handle = dlopen("libnativeloader.so", RTLD_NOW);
- LOG_FATAL_IF(handle == nullptr, "Failed to load libnativeloader.so: %s", dlerror());
- return handle;
-}
-
-template <typename FuncPtr>
-FuncPtr GetFuncPtr(const char* function_name) {
- auto f = reinterpret_cast<FuncPtr>(dlsym(GetLibHandle(), function_name));
- LOG_FATAL_IF(f == nullptr, "Failed to get address of %s: %s", function_name, dlerror());
- return f;
-}
-
-#define GET_FUNC_PTR(name) GetFuncPtr<decltype(&name)>(#name)
-
-} // namespace
-
-void InitializeNativeLoader() {
- static auto f = GET_FUNC_PTR(InitializeNativeLoader);
- return f();
-}
-
-jstring CreateClassLoaderNamespace(JNIEnv* env, int32_t target_sdk_version, jobject class_loader,
- bool is_shared, jstring dex_path, jstring library_path,
- jstring permitted_path) {
- static auto f = GET_FUNC_PTR(CreateClassLoaderNamespace);
- return f(env, target_sdk_version, class_loader, is_shared, dex_path, library_path,
- permitted_path);
-}
-
-void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
- jobject class_loader, const char* caller_location, jstring library_path,
- bool* needs_native_bridge, char** error_msg) {
- static auto f = GET_FUNC_PTR(OpenNativeLibrary);
- return f(env, target_sdk_version, path, class_loader, caller_location, library_path,
- needs_native_bridge, error_msg);
-}
-
-bool CloseNativeLibrary(void* handle, const bool needs_native_bridge, char** error_msg) {
- static auto f = GET_FUNC_PTR(CloseNativeLibrary);
- return f(handle, needs_native_bridge, error_msg);
-}
-
-void NativeLoaderFreeErrorMessage(char* msg) {
- static auto f = GET_FUNC_PTR(NativeLoaderFreeErrorMessage);
- return f(msg);
-}
-
-struct android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
- static auto f = GET_FUNC_PTR(FindNamespaceByClassLoader);
- return f(env, class_loader);
-}
-
-struct NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(JNIEnv* env,
- jobject class_loader) {
- static auto f = GET_FUNC_PTR(FindNativeLoaderNamespaceByClassLoader);
- return f(env, class_loader);
-}
-
-void* OpenNativeLibraryInNamespace(struct NativeLoaderNamespace* ns, const char* path,
- bool* needs_native_bridge, char** error_msg) {
- static auto f = GET_FUNC_PTR(OpenNativeLibraryInNamespace);
- return f(ns, path, needs_native_bridge, error_msg);
-}
-
-void ResetNativeLoader() {
- static auto f = GET_FUNC_PTR(ResetNativeLoader);
- return f();
-}
-
-#undef GET_FUNC_PTR
-
-} // namespace android
diff --git a/libnativeloader/native_loader_namespace.cpp b/libnativeloader/native_loader_namespace.cpp
deleted file mode 100644
index a81fddf..0000000
--- a/libnativeloader/native_loader_namespace.cpp
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-#define LOG_TAG "nativeloader"
-
-#include "native_loader_namespace.h"
-
-#include <dlfcn.h>
-
-#include <functional>
-
-#include <android-base/strings.h>
-#include <log/log.h>
-#include <nativebridge/native_bridge.h>
-
-#include "nativeloader/dlext_namespaces.h"
-
-using android::base::Error;
-using android::base::Errorf;
-
-namespace android {
-
-namespace {
-
-constexpr const char* kDefaultNamespaceName = "default";
-constexpr const char* kPlatformNamespaceName = "platform";
-
-std::string GetLinkerError(bool is_bridged) {
- const char* msg = is_bridged ? NativeBridgeGetError() : dlerror();
- if (msg == nullptr) {
- return "no error";
- }
- return std::string(msg);
-}
-
-} // namespace
-
-Result<NativeLoaderNamespace> NativeLoaderNamespace::GetExportedNamespace(const std::string& name,
- bool is_bridged) {
- if (!is_bridged) {
- auto raw = android_get_exported_namespace(name.c_str());
- if (raw != nullptr) {
- return NativeLoaderNamespace(name, raw);
- }
- } else {
- auto raw = NativeBridgeGetExportedNamespace(name.c_str());
- if (raw != nullptr) {
- return NativeLoaderNamespace(name, raw);
- }
- }
- return Errorf("namespace {} does not exist or exported", name);
-}
-
-// The platform namespace is called "default" for binaries in /system and
-// "platform" for those in the Runtime APEX. Try "platform" first since
-// "default" always exists.
-Result<NativeLoaderNamespace> NativeLoaderNamespace::GetPlatformNamespace(bool is_bridged) {
- auto ns = GetExportedNamespace(kPlatformNamespaceName, is_bridged);
- if (ns) return ns;
- ns = GetExportedNamespace(kDefaultNamespaceName, is_bridged);
- if (ns) return ns;
-
- // If nothing is found, return NativeLoaderNamespace constructed from nullptr.
- // nullptr also means default namespace to the linker.
- if (!is_bridged) {
- return NativeLoaderNamespace(kDefaultNamespaceName, static_cast<android_namespace_t*>(nullptr));
- } else {
- return NativeLoaderNamespace(kDefaultNamespaceName,
- static_cast<native_bridge_namespace_t*>(nullptr));
- }
-}
-
-Result<NativeLoaderNamespace> NativeLoaderNamespace::Create(
- const std::string& name, const std::string& search_paths, const std::string& permitted_paths,
- const NativeLoaderNamespace* parent, bool is_shared, bool is_greylist_enabled,
- bool also_used_as_anonymous) {
- bool is_bridged = false;
- if (parent != nullptr) {
- is_bridged = parent->IsBridged();
- } else if (!search_paths.empty()) {
- is_bridged = NativeBridgeIsPathSupported(search_paths.c_str());
- }
-
- // Fall back to the platform namespace if no parent is set.
- auto platform_ns = GetPlatformNamespace(is_bridged);
- if (!platform_ns) {
- return platform_ns.error();
- }
- const NativeLoaderNamespace& effective_parent = parent != nullptr ? *parent : *platform_ns;
-
- // All namespaces for apps are isolated
- uint64_t type = ANDROID_NAMESPACE_TYPE_ISOLATED;
-
- // The namespace is also used as the anonymous namespace
- // which is used when the linker fails to determine the caller address
- if (also_used_as_anonymous) {
- type |= ANDROID_NAMESPACE_TYPE_ALSO_USED_AS_ANONYMOUS;
- }
-
- // Bundled apps have access to all system libraries that are currently loaded
- // in the default namespace
- if (is_shared) {
- type |= ANDROID_NAMESPACE_TYPE_SHARED;
- }
- if (is_greylist_enabled) {
- type |= ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED;
- }
-
- if (!is_bridged) {
- android_namespace_t* raw =
- android_create_namespace(name.c_str(), nullptr, search_paths.c_str(), type,
- permitted_paths.c_str(), effective_parent.ToRawAndroidNamespace());
- if (raw != nullptr) {
- return NativeLoaderNamespace(name, raw);
- }
- } else {
- native_bridge_namespace_t* raw = NativeBridgeCreateNamespace(
- name.c_str(), nullptr, search_paths.c_str(), type, permitted_paths.c_str(),
- effective_parent.ToRawNativeBridgeNamespace());
- if (raw != nullptr) {
- return NativeLoaderNamespace(name, raw);
- }
- }
- return Errorf("failed to create {} namespace name:{}, search_paths:{}, permitted_paths:{}",
- is_bridged ? "bridged" : "native", name, search_paths, permitted_paths);
-}
-
-Result<void> NativeLoaderNamespace::Link(const NativeLoaderNamespace& target,
- const std::string& shared_libs) const {
- LOG_ALWAYS_FATAL_IF(shared_libs.empty(), "empty share lib when linking %s to %s",
- this->name().c_str(), target.name().c_str());
- if (!IsBridged()) {
- if (android_link_namespaces(this->ToRawAndroidNamespace(), target.ToRawAndroidNamespace(),
- shared_libs.c_str())) {
- return {};
- }
- } else {
- if (NativeBridgeLinkNamespaces(this->ToRawNativeBridgeNamespace(),
- target.ToRawNativeBridgeNamespace(), shared_libs.c_str())) {
- return {};
- }
- }
- return Error() << GetLinkerError(IsBridged());
-}
-
-Result<void*> NativeLoaderNamespace::Load(const char* lib_name) const {
- if (!IsBridged()) {
- android_dlextinfo extinfo;
- extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
- extinfo.library_namespace = this->ToRawAndroidNamespace();
- void* handle = android_dlopen_ext(lib_name, RTLD_NOW, &extinfo);
- if (handle != nullptr) {
- return handle;
- }
- } else {
- void* handle =
- NativeBridgeLoadLibraryExt(lib_name, RTLD_NOW, this->ToRawNativeBridgeNamespace());
- if (handle != nullptr) {
- return handle;
- }
- }
- return Error() << GetLinkerError(IsBridged());
-}
-
-} // namespace android
diff --git a/libnativeloader/native_loader_namespace.h b/libnativeloader/native_loader_namespace.h
deleted file mode 100644
index 7200ee7..0000000
--- a/libnativeloader/native_loader_namespace.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-#pragma once
-#if defined(__ANDROID__)
-
-#include <string>
-#include <variant>
-#include <vector>
-
-#include <android-base/logging.h>
-#include <android-base/result.h>
-#include <android/dlext.h>
-#include <log/log.h>
-#include <nativebridge/native_bridge.h>
-
-namespace android {
-
-using android::base::Result;
-
-// NativeLoaderNamespace abstracts a linker namespace for the native
-// architecture (ex: arm on arm) or the translated architecture (ex: arm on
-// x86). Instances of this class are managed by LibraryNamespaces object.
-struct NativeLoaderNamespace {
- public:
- static Result<NativeLoaderNamespace> Create(const std::string& name,
- const std::string& search_paths,
- const std::string& permitted_paths,
- const NativeLoaderNamespace* parent, bool is_shared,
- bool is_greylist_enabled,
- bool also_used_as_anonymous);
-
- NativeLoaderNamespace(NativeLoaderNamespace&&) = default;
- NativeLoaderNamespace(const NativeLoaderNamespace&) = default;
- NativeLoaderNamespace& operator=(const NativeLoaderNamespace&) = default;
-
- android_namespace_t* ToRawAndroidNamespace() const { return std::get<0>(raw_); }
- native_bridge_namespace_t* ToRawNativeBridgeNamespace() const { return std::get<1>(raw_); }
-
- std::string name() const { return name_; }
- bool IsBridged() const { return raw_.index() == 1; }
-
- Result<void> Link(const NativeLoaderNamespace& target, const std::string& shared_libs) const;
- Result<void*> Load(const char* lib_name) const;
-
- static Result<NativeLoaderNamespace> GetExportedNamespace(const std::string& name,
- bool is_bridged);
- static Result<NativeLoaderNamespace> GetPlatformNamespace(bool is_bridged);
-
- private:
- explicit NativeLoaderNamespace(const std::string& name, android_namespace_t* ns)
- : name_(name), raw_(ns) {}
- explicit NativeLoaderNamespace(const std::string& name, native_bridge_namespace_t* ns)
- : name_(name), raw_(ns) {}
-
- std::string name_;
- std::variant<android_namespace_t*, native_bridge_namespace_t*> raw_;
-};
-
-} // namespace android
-#endif // #if defined(__ANDROID__)
diff --git a/libnativeloader/native_loader_test.cpp b/libnativeloader/native_loader_test.cpp
deleted file mode 100644
index 46e2780..0000000
--- a/libnativeloader/native_loader_test.cpp
+++ /dev/null
@@ -1,663 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-#include <dlfcn.h>
-#include <memory>
-#include <unordered_map>
-
-#include <android-base/strings.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <jni.h>
-
-#include "native_loader_namespace.h"
-#include "nativeloader/dlext_namespaces.h"
-#include "nativeloader/native_loader.h"
-#include "public_libraries.h"
-
-using namespace ::testing;
-using namespace ::android::nativeloader::internal;
-
-namespace android {
-namespace nativeloader {
-
-// gmock interface that represents interested platform APIs on libdl and libnativebridge
-class Platform {
- public:
- virtual ~Platform() {}
-
- // libdl APIs
- virtual void* dlopen(const char* filename, int flags) = 0;
- virtual int dlclose(void* handle) = 0;
- virtual char* dlerror(void) = 0;
-
- // These mock_* are the APIs semantically the same across libdl and libnativebridge.
- // Instead of having two set of mock APIs for the two, define only one set with an additional
- // argument 'bool bridged' to identify the context (i.e., called for libdl or libnativebridge).
- typedef char* mock_namespace_handle;
- virtual bool mock_init_anonymous_namespace(bool bridged, const char* sonames,
- const char* search_paths) = 0;
- virtual mock_namespace_handle mock_create_namespace(
- bool bridged, const char* name, const char* ld_library_path, const char* default_library_path,
- uint64_t type, const char* permitted_when_isolated_path, mock_namespace_handle parent) = 0;
- virtual bool mock_link_namespaces(bool bridged, mock_namespace_handle from,
- mock_namespace_handle to, const char* sonames) = 0;
- virtual mock_namespace_handle mock_get_exported_namespace(bool bridged, const char* name) = 0;
- virtual void* mock_dlopen_ext(bool bridged, const char* filename, int flags,
- mock_namespace_handle ns) = 0;
-
- // libnativebridge APIs for which libdl has no corresponding APIs
- virtual bool NativeBridgeInitialized() = 0;
- virtual const char* NativeBridgeGetError() = 0;
- virtual bool NativeBridgeIsPathSupported(const char*) = 0;
- virtual bool NativeBridgeIsSupported(const char*) = 0;
-
- // To mock "ClassLoader Object.getParent()"
- virtual const char* JniObject_getParent(const char*) = 0;
-};
-
-// The mock does not actually create a namespace object. But simply casts the pointer to the
-// string for the namespace name as the handle to the namespace object.
-#define TO_ANDROID_NAMESPACE(str) \
- reinterpret_cast<struct android_namespace_t*>(const_cast<char*>(str))
-
-#define TO_BRIDGED_NAMESPACE(str) \
- reinterpret_cast<struct native_bridge_namespace_t*>(const_cast<char*>(str))
-
-#define TO_MOCK_NAMESPACE(ns) reinterpret_cast<Platform::mock_namespace_handle>(ns)
-
-// These represents built-in namespaces created by the linker according to ld.config.txt
-static std::unordered_map<std::string, Platform::mock_namespace_handle> namespaces = {
- {"platform", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("platform"))},
- {"default", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("default"))},
- {"runtime", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("runtime"))},
- {"sphal", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("sphal"))},
- {"vndk", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("vndk"))},
- {"neuralnetworks", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("neuralnetworks"))},
-};
-
-// The actual gmock object
-class MockPlatform : public Platform {
- public:
- explicit MockPlatform(bool is_bridged) : is_bridged_(is_bridged) {
- ON_CALL(*this, NativeBridgeIsSupported(_)).WillByDefault(Return(is_bridged_));
- ON_CALL(*this, NativeBridgeIsPathSupported(_)).WillByDefault(Return(is_bridged_));
- ON_CALL(*this, mock_get_exported_namespace(_, _))
- .WillByDefault(Invoke([](bool, const char* name) -> mock_namespace_handle {
- if (namespaces.find(name) != namespaces.end()) {
- return namespaces[name];
- }
- return TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("(namespace not found"));
- }));
- }
-
- // Mocking libdl APIs
- MOCK_METHOD2(dlopen, void*(const char*, int));
- MOCK_METHOD1(dlclose, int(void*));
- MOCK_METHOD0(dlerror, char*());
-
- // Mocking the common APIs
- MOCK_METHOD3(mock_init_anonymous_namespace, bool(bool, const char*, const char*));
- MOCK_METHOD7(mock_create_namespace,
- mock_namespace_handle(bool, const char*, const char*, const char*, uint64_t,
- const char*, mock_namespace_handle));
- MOCK_METHOD4(mock_link_namespaces,
- bool(bool, mock_namespace_handle, mock_namespace_handle, const char*));
- MOCK_METHOD2(mock_get_exported_namespace, mock_namespace_handle(bool, const char*));
- MOCK_METHOD4(mock_dlopen_ext, void*(bool, const char*, int, mock_namespace_handle));
-
- // Mocking libnativebridge APIs
- MOCK_METHOD0(NativeBridgeInitialized, bool());
- MOCK_METHOD0(NativeBridgeGetError, const char*());
- MOCK_METHOD1(NativeBridgeIsPathSupported, bool(const char*));
- MOCK_METHOD1(NativeBridgeIsSupported, bool(const char*));
-
- // Mocking "ClassLoader Object.getParent()"
- MOCK_METHOD1(JniObject_getParent, const char*(const char*));
-
- private:
- bool is_bridged_;
-};
-
-static std::unique_ptr<MockPlatform> mock;
-
-// Provide C wrappers for the mock object.
-extern "C" {
-void* dlopen(const char* file, int flag) {
- return mock->dlopen(file, flag);
-}
-
-int dlclose(void* handle) {
- return mock->dlclose(handle);
-}
-
-char* dlerror(void) {
- return mock->dlerror();
-}
-
-bool android_init_anonymous_namespace(const char* sonames, const char* search_path) {
- return mock->mock_init_anonymous_namespace(false, sonames, search_path);
-}
-
-struct android_namespace_t* android_create_namespace(const char* name, const char* ld_library_path,
- const char* default_library_path,
- uint64_t type,
- const char* permitted_when_isolated_path,
- struct android_namespace_t* parent) {
- return TO_ANDROID_NAMESPACE(
- mock->mock_create_namespace(false, name, ld_library_path, default_library_path, type,
- permitted_when_isolated_path, TO_MOCK_NAMESPACE(parent)));
-}
-
-bool android_link_namespaces(struct android_namespace_t* from, struct android_namespace_t* to,
- const char* sonames) {
- return mock->mock_link_namespaces(false, TO_MOCK_NAMESPACE(from), TO_MOCK_NAMESPACE(to), sonames);
-}
-
-struct android_namespace_t* android_get_exported_namespace(const char* name) {
- return TO_ANDROID_NAMESPACE(mock->mock_get_exported_namespace(false, name));
-}
-
-void* android_dlopen_ext(const char* filename, int flags, const android_dlextinfo* info) {
- return mock->mock_dlopen_ext(false, filename, flags, TO_MOCK_NAMESPACE(info->library_namespace));
-}
-
-// libnativebridge APIs
-bool NativeBridgeIsSupported(const char* libpath) {
- return mock->NativeBridgeIsSupported(libpath);
-}
-
-struct native_bridge_namespace_t* NativeBridgeGetExportedNamespace(const char* name) {
- return TO_BRIDGED_NAMESPACE(mock->mock_get_exported_namespace(true, name));
-}
-
-struct native_bridge_namespace_t* NativeBridgeCreateNamespace(
- const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type,
- const char* permitted_when_isolated_path, struct native_bridge_namespace_t* parent) {
- return TO_BRIDGED_NAMESPACE(
- mock->mock_create_namespace(true, name, ld_library_path, default_library_path, type,
- permitted_when_isolated_path, TO_MOCK_NAMESPACE(parent)));
-}
-
-bool NativeBridgeLinkNamespaces(struct native_bridge_namespace_t* from,
- struct native_bridge_namespace_t* to, const char* sonames) {
- return mock->mock_link_namespaces(true, TO_MOCK_NAMESPACE(from), TO_MOCK_NAMESPACE(to), sonames);
-}
-
-void* NativeBridgeLoadLibraryExt(const char* libpath, int flag,
- struct native_bridge_namespace_t* ns) {
- return mock->mock_dlopen_ext(true, libpath, flag, TO_MOCK_NAMESPACE(ns));
-}
-
-bool NativeBridgeInitialized() {
- return mock->NativeBridgeInitialized();
-}
-
-bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames,
- const char* anon_ns_library_path) {
- return mock->mock_init_anonymous_namespace(true, public_ns_sonames, anon_ns_library_path);
-}
-
-const char* NativeBridgeGetError() {
- return mock->NativeBridgeGetError();
-}
-
-bool NativeBridgeIsPathSupported(const char* path) {
- return mock->NativeBridgeIsPathSupported(path);
-}
-
-} // extern "C"
-
-// A very simple JNI mock.
-// jstring is a pointer to utf8 char array. We don't need utf16 char here.
-// jobject, jclass, and jmethodID are also a pointer to utf8 char array
-// Only a few JNI methods that are actually used in libnativeloader are mocked.
-JNINativeInterface* CreateJNINativeInterface() {
- JNINativeInterface* inf = new JNINativeInterface();
- memset(inf, 0, sizeof(JNINativeInterface));
-
- inf->GetStringUTFChars = [](JNIEnv*, jstring s, jboolean*) -> const char* {
- return reinterpret_cast<const char*>(s);
- };
-
- inf->ReleaseStringUTFChars = [](JNIEnv*, jstring, const char*) -> void { return; };
-
- inf->NewStringUTF = [](JNIEnv*, const char* bytes) -> jstring {
- return reinterpret_cast<jstring>(const_cast<char*>(bytes));
- };
-
- inf->FindClass = [](JNIEnv*, const char* name) -> jclass {
- return reinterpret_cast<jclass>(const_cast<char*>(name));
- };
-
- inf->CallObjectMethodV = [](JNIEnv*, jobject obj, jmethodID mid, va_list) -> jobject {
- if (strcmp("getParent", reinterpret_cast<const char*>(mid)) == 0) {
- // JniObject_getParent can be a valid jobject or nullptr if there is
- // no parent classloader.
- const char* ret = mock->JniObject_getParent(reinterpret_cast<const char*>(obj));
- return reinterpret_cast<jobject>(const_cast<char*>(ret));
- }
- return nullptr;
- };
-
- inf->GetMethodID = [](JNIEnv*, jclass, const char* name, const char*) -> jmethodID {
- return reinterpret_cast<jmethodID>(const_cast<char*>(name));
- };
-
- inf->NewWeakGlobalRef = [](JNIEnv*, jobject obj) -> jobject { return obj; };
-
- inf->IsSameObject = [](JNIEnv*, jobject a, jobject b) -> jboolean {
- return strcmp(reinterpret_cast<const char*>(a), reinterpret_cast<const char*>(b)) == 0;
- };
-
- return inf;
-}
-
-static void* const any_nonnull = reinterpret_cast<void*>(0x12345678);
-
-// Custom matcher for comparing namespace handles
-MATCHER_P(NsEq, other, "") {
- *result_listener << "comparing " << reinterpret_cast<const char*>(arg) << " and " << other;
- return strcmp(reinterpret_cast<const char*>(arg), reinterpret_cast<const char*>(other)) == 0;
-}
-
-/////////////////////////////////////////////////////////////////
-
-// Test fixture
-class NativeLoaderTest : public ::testing::TestWithParam<bool> {
- protected:
- bool IsBridged() { return GetParam(); }
-
- void SetUp() override {
- mock = std::make_unique<NiceMock<MockPlatform>>(IsBridged());
-
- env = std::make_unique<JNIEnv>();
- env->functions = CreateJNINativeInterface();
- }
-
- void SetExpectations() {
- std::vector<std::string> default_public_libs =
- android::base::Split(preloadable_public_libraries(), ":");
- for (auto l : default_public_libs) {
- EXPECT_CALL(*mock, dlopen(StrEq(l.c_str()), RTLD_NOW | RTLD_NODELETE))
- .WillOnce(Return(any_nonnull));
- }
- }
-
- void RunTest() { InitializeNativeLoader(); }
-
- void TearDown() override {
- ResetNativeLoader();
- delete env->functions;
- mock.reset();
- }
-
- std::unique_ptr<JNIEnv> env;
-};
-
-/////////////////////////////////////////////////////////////////
-
-TEST_P(NativeLoaderTest, InitializeLoadsDefaultPublicLibraries) {
- SetExpectations();
- RunTest();
-}
-
-INSTANTIATE_TEST_SUITE_P(NativeLoaderTests, NativeLoaderTest, testing::Bool());
-
-/////////////////////////////////////////////////////////////////
-
-class NativeLoaderTest_Create : public NativeLoaderTest {
- protected:
- // Test inputs (initialized to the default values). Overriding these
- // must be done before calling SetExpectations() and RunTest().
- uint32_t target_sdk_version = 29;
- std::string class_loader = "my_classloader";
- bool is_shared = false;
- std::string dex_path = "/data/app/foo/classes.dex";
- std::string library_path = "/data/app/foo/lib/arm";
- std::string permitted_path = "/data/app/foo/lib";
-
- // expected output (.. for the default test inputs)
- std::string expected_namespace_name = "classloader-namespace";
- uint64_t expected_namespace_flags =
- ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_ALSO_USED_AS_ANONYMOUS;
- std::string expected_library_path = library_path;
- std::string expected_permitted_path = std::string("/data:/mnt/expand:") + permitted_path;
- std::string expected_parent_namespace = "platform";
- bool expected_link_with_platform_ns = true;
- bool expected_link_with_runtime_ns = true;
- bool expected_link_with_sphal_ns = !vendor_public_libraries().empty();
- bool expected_link_with_vndk_ns = false;
- bool expected_link_with_default_ns = false;
- bool expected_link_with_neuralnetworks_ns = true;
- std::string expected_shared_libs_to_platform_ns = default_public_libraries();
- std::string expected_shared_libs_to_runtime_ns = runtime_public_libraries();
- std::string expected_shared_libs_to_sphal_ns = vendor_public_libraries();
- std::string expected_shared_libs_to_vndk_ns = vndksp_libraries();
- std::string expected_shared_libs_to_default_ns = default_public_libraries();
- std::string expected_shared_libs_to_neuralnetworks_ns = neuralnetworks_public_libraries();
-
- void SetExpectations() {
- NativeLoaderTest::SetExpectations();
-
- ON_CALL(*mock, JniObject_getParent(StrEq(class_loader))).WillByDefault(Return(nullptr));
-
- EXPECT_CALL(*mock, NativeBridgeIsPathSupported(_)).Times(AnyNumber());
- EXPECT_CALL(*mock, NativeBridgeInitialized()).Times(AnyNumber());
-
- EXPECT_CALL(*mock, mock_create_namespace(
- Eq(IsBridged()), StrEq(expected_namespace_name), nullptr,
- StrEq(expected_library_path), expected_namespace_flags,
- StrEq(expected_permitted_path), NsEq(expected_parent_namespace.c_str())))
- .WillOnce(Return(TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE(dex_path.c_str()))));
- if (expected_link_with_platform_ns) {
- EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("platform"),
- StrEq(expected_shared_libs_to_platform_ns)))
- .WillOnce(Return(true));
- }
- if (expected_link_with_runtime_ns) {
- EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("runtime"),
- StrEq(expected_shared_libs_to_runtime_ns)))
- .WillOnce(Return(true));
- }
- if (expected_link_with_sphal_ns) {
- EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("sphal"),
- StrEq(expected_shared_libs_to_sphal_ns)))
- .WillOnce(Return(true));
- }
- if (expected_link_with_vndk_ns) {
- EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("vndk"),
- StrEq(expected_shared_libs_to_vndk_ns)))
- .WillOnce(Return(true));
- }
- if (expected_link_with_default_ns) {
- EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("default"),
- StrEq(expected_shared_libs_to_default_ns)))
- .WillOnce(Return(true));
- }
- if (expected_link_with_neuralnetworks_ns) {
- EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("neuralnetworks"),
- StrEq(expected_shared_libs_to_neuralnetworks_ns)))
- .WillOnce(Return(true));
- }
- }
-
- void RunTest() {
- NativeLoaderTest::RunTest();
-
- jstring err = CreateClassLoaderNamespace(
- env(), target_sdk_version, env()->NewStringUTF(class_loader.c_str()), is_shared,
- env()->NewStringUTF(dex_path.c_str()), env()->NewStringUTF(library_path.c_str()),
- env()->NewStringUTF(permitted_path.c_str()));
-
- // no error
- EXPECT_EQ(err, nullptr);
-
- if (!IsBridged()) {
- struct android_namespace_t* ns =
- FindNamespaceByClassLoader(env(), env()->NewStringUTF(class_loader.c_str()));
-
- // The created namespace is for this apk
- EXPECT_EQ(dex_path.c_str(), reinterpret_cast<const char*>(ns));
- } else {
- struct NativeLoaderNamespace* ns =
- FindNativeLoaderNamespaceByClassLoader(env(), env()->NewStringUTF(class_loader.c_str()));
-
- // The created namespace is for the this apk
- EXPECT_STREQ(dex_path.c_str(),
- reinterpret_cast<const char*>(ns->ToRawNativeBridgeNamespace()));
- }
- }
-
- JNIEnv* env() { return NativeLoaderTest::env.get(); }
-};
-
-TEST_P(NativeLoaderTest_Create, DownloadedApp) {
- SetExpectations();
- RunTest();
-}
-
-TEST_P(NativeLoaderTest_Create, BundledSystemApp) {
- dex_path = "/system/app/foo/foo.apk";
- is_shared = true;
-
- expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
- SetExpectations();
- RunTest();
-}
-
-TEST_P(NativeLoaderTest_Create, BundledVendorApp) {
- dex_path = "/vendor/app/foo/foo.apk";
- is_shared = true;
-
- expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
- SetExpectations();
- RunTest();
-}
-
-TEST_P(NativeLoaderTest_Create, UnbundledVendorApp) {
- dex_path = "/vendor/app/foo/foo.apk";
- is_shared = false;
-
- expected_namespace_name = "vendor-classloader-namespace";
- expected_library_path = expected_library_path + ":/vendor/lib";
- expected_permitted_path = expected_permitted_path + ":/vendor/lib";
- expected_shared_libs_to_platform_ns =
- expected_shared_libs_to_platform_ns + ":" + llndk_libraries();
- expected_link_with_vndk_ns = true;
- SetExpectations();
- RunTest();
-}
-
-TEST_P(NativeLoaderTest_Create, BundledProductApp_pre30) {
- dex_path = "/product/app/foo/foo.apk";
- is_shared = true;
-
- expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
- SetExpectations();
- RunTest();
-}
-
-TEST_P(NativeLoaderTest_Create, BundledProductApp_post30) {
- dex_path = "/product/app/foo/foo.apk";
- is_shared = true;
- target_sdk_version = 30;
-
- expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
- SetExpectations();
- RunTest();
-}
-
-TEST_P(NativeLoaderTest_Create, UnbundledProductApp_pre30) {
- dex_path = "/product/app/foo/foo.apk";
- is_shared = false;
- SetExpectations();
- RunTest();
-}
-
-TEST_P(NativeLoaderTest_Create, UnbundledProductApp_post30) {
- dex_path = "/product/app/foo/foo.apk";
- is_shared = false;
- target_sdk_version = 30;
-
- expected_namespace_name = "vendor-classloader-namespace";
- expected_library_path = expected_library_path + ":/product/lib:/system/product/lib";
- expected_permitted_path = expected_permitted_path + ":/product/lib:/system/product/lib";
- expected_shared_libs_to_platform_ns =
- expected_shared_libs_to_platform_ns + ":" + llndk_libraries();
- expected_link_with_vndk_ns = true;
- SetExpectations();
- RunTest();
-}
-
-TEST_P(NativeLoaderTest_Create, NamespaceForSharedLibIsNotUsedAsAnonymousNamespace) {
- if (IsBridged()) {
- // There is no shared lib in translated arch
- // TODO(jiyong): revisit this
- return;
- }
- // compared to apks, for java shared libs, library_path is empty; java shared
- // libs don't have their own native libs. They use platform's.
- library_path = "";
- expected_library_path = library_path;
- // no ALSO_USED_AS_ANONYMOUS
- expected_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED;
- SetExpectations();
- RunTest();
-}
-
-TEST_P(NativeLoaderTest_Create, TwoApks) {
- SetExpectations();
- const uint32_t second_app_target_sdk_version = 29;
- const std::string second_app_class_loader = "second_app_classloader";
- const bool second_app_is_shared = false;
- const std::string second_app_dex_path = "/data/app/bar/classes.dex";
- const std::string second_app_library_path = "/data/app/bar/lib/arm";
- const std::string second_app_permitted_path = "/data/app/bar/lib";
- const std::string expected_second_app_permitted_path =
- std::string("/data:/mnt/expand:") + second_app_permitted_path;
- const std::string expected_second_app_parent_namespace = "classloader-namespace";
- // no ALSO_USED_AS_ANONYMOUS
- const uint64_t expected_second_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED;
-
- // The scenario is that second app is loaded by the first app.
- // So the first app's classloader (`classloader`) is parent of the second
- // app's classloader.
- ON_CALL(*mock, JniObject_getParent(StrEq(second_app_class_loader)))
- .WillByDefault(Return(class_loader.c_str()));
-
- // namespace for the second app is created. Its parent is set to the namespace
- // of the first app.
- EXPECT_CALL(*mock, mock_create_namespace(
- Eq(IsBridged()), StrEq(expected_namespace_name), nullptr,
- StrEq(second_app_library_path), expected_second_namespace_flags,
- StrEq(expected_second_app_permitted_path), NsEq(dex_path.c_str())))
- .WillOnce(Return(TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE(second_app_dex_path.c_str()))));
- EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), NsEq(second_app_dex_path.c_str()), _, _))
- .WillRepeatedly(Return(true));
-
- RunTest();
- jstring err = CreateClassLoaderNamespace(
- env(), second_app_target_sdk_version, env()->NewStringUTF(second_app_class_loader.c_str()),
- second_app_is_shared, env()->NewStringUTF(second_app_dex_path.c_str()),
- env()->NewStringUTF(second_app_library_path.c_str()),
- env()->NewStringUTF(second_app_permitted_path.c_str()));
-
- // success
- EXPECT_EQ(err, nullptr);
-
- if (!IsBridged()) {
- struct android_namespace_t* ns =
- FindNamespaceByClassLoader(env(), env()->NewStringUTF(second_app_class_loader.c_str()));
-
- // The created namespace is for the second apk
- EXPECT_EQ(second_app_dex_path.c_str(), reinterpret_cast<const char*>(ns));
- } else {
- struct NativeLoaderNamespace* ns = FindNativeLoaderNamespaceByClassLoader(
- env(), env()->NewStringUTF(second_app_class_loader.c_str()));
-
- // The created namespace is for the second apk
- EXPECT_STREQ(second_app_dex_path.c_str(),
- reinterpret_cast<const char*>(ns->ToRawNativeBridgeNamespace()));
- }
-}
-
-INSTANTIATE_TEST_SUITE_P(NativeLoaderTests_Create, NativeLoaderTest_Create, testing::Bool());
-
-const std::function<Result<bool>(const struct ConfigEntry&)> always_true =
- [](const struct ConfigEntry&) -> Result<bool> { return true; };
-
-TEST(NativeLoaderConfigParser, NamesAndComments) {
- const char file_content[] = R"(
-######
-
-libA.so
-#libB.so
-
-
- libC.so
-libD.so
- #### libE.so
-)";
- const std::vector<std::string> expected_result = {"libA.so", "libC.so", "libD.so"};
- Result<std::vector<std::string>> result = ParseConfig(file_content, always_true);
- ASSERT_TRUE(result) << result.error().message();
- ASSERT_EQ(expected_result, *result);
-}
-
-TEST(NativeLoaderConfigParser, WithBitness) {
- const char file_content[] = R"(
-libA.so 32
-libB.so 64
-libC.so
-)";
-#if defined(__LP64__)
- const std::vector<std::string> expected_result = {"libB.so", "libC.so"};
-#else
- const std::vector<std::string> expected_result = {"libA.so", "libC.so"};
-#endif
- Result<std::vector<std::string>> result = ParseConfig(file_content, always_true);
- ASSERT_TRUE(result) << result.error().message();
- ASSERT_EQ(expected_result, *result);
-}
-
-TEST(NativeLoaderConfigParser, WithNoPreload) {
- const char file_content[] = R"(
-libA.so nopreload
-libB.so nopreload
-libC.so
-)";
-
- const std::vector<std::string> expected_result = {"libC.so"};
- Result<std::vector<std::string>> result =
- ParseConfig(file_content,
- [](const struct ConfigEntry& entry) -> Result<bool> { return !entry.nopreload; });
- ASSERT_TRUE(result) << result.error().message();
- ASSERT_EQ(expected_result, *result);
-}
-
-TEST(NativeLoaderConfigParser, WithNoPreloadAndBitness) {
- const char file_content[] = R"(
-libA.so nopreload 32
-libB.so 64 nopreload
-libC.so 32
-libD.so 64
-libE.so nopreload
-)";
-
-#if defined(__LP64__)
- const std::vector<std::string> expected_result = {"libD.so"};
-#else
- const std::vector<std::string> expected_result = {"libC.so"};
-#endif
- Result<std::vector<std::string>> result =
- ParseConfig(file_content,
- [](const struct ConfigEntry& entry) -> Result<bool> { return !entry.nopreload; });
- ASSERT_TRUE(result) << result.error().message();
- ASSERT_EQ(expected_result, *result);
-}
-
-TEST(NativeLoaderConfigParser, RejectMalformed) {
- ASSERT_FALSE(ParseConfig("libA.so 32 64", always_true));
- ASSERT_FALSE(ParseConfig("libA.so 32 32", always_true));
- ASSERT_FALSE(ParseConfig("libA.so 32 nopreload 64", always_true));
- ASSERT_FALSE(ParseConfig("32 libA.so nopreload", always_true));
- ASSERT_FALSE(ParseConfig("nopreload libA.so 32", always_true));
- ASSERT_FALSE(ParseConfig("libA.so nopreload # comment", always_true));
-}
-
-} // namespace nativeloader
-} // namespace android
diff --git a/libnativeloader/public_libraries.cpp b/libnativeloader/public_libraries.cpp
deleted file mode 100644
index 010e8cc..0000000
--- a/libnativeloader/public_libraries.cpp
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-#define LOG_TAG "nativeloader"
-
-#include "public_libraries.h"
-
-#include <dirent.h>
-
-#include <algorithm>
-#include <memory>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/result.h>
-#include <android-base/strings.h>
-#include <log/log.h>
-
-#include "utils.h"
-
-namespace android::nativeloader {
-
-using namespace internal;
-using namespace ::std::string_literals;
-using android::base::ErrnoError;
-using android::base::Errorf;
-using android::base::Result;
-
-namespace {
-
-constexpr const char* kDefaultPublicLibrariesFile = "/etc/public.libraries.txt";
-constexpr const char* kExtendedPublicLibrariesFilePrefix = "public.libraries-";
-constexpr const char* kExtendedPublicLibrariesFileSuffix = ".txt";
-constexpr const char* kVendorPublicLibrariesFile = "/vendor/etc/public.libraries.txt";
-constexpr const char* kLlndkLibrariesFile = "/system/etc/llndk.libraries.txt";
-constexpr const char* kVndkLibrariesFile = "/system/etc/vndksp.libraries.txt";
-
-const std::vector<const std::string> kArtApexPublicLibraries = {
- "libicuuc.so",
- "libicui18n.so",
-};
-
-constexpr const char* kArtApexLibPath = "/apex/com.android.art/" LIB;
-
-constexpr const char* kNeuralNetworksApexPublicLibrary = "libneuralnetworks.so";
-
-// TODO(b/130388701): do we need this?
-std::string root_dir() {
- static const char* android_root_env = getenv("ANDROID_ROOT");
- return android_root_env != nullptr ? android_root_env : "/system";
-}
-
-bool debuggable() {
- static bool debuggable = android::base::GetBoolProperty("ro.debuggable", false);
- return debuggable;
-}
-
-std::string vndk_version_str() {
- static std::string version = android::base::GetProperty("ro.vndk.version", "");
- if (version != "" && version != "current") {
- return "." + version;
- }
- return "";
-}
-
-// For debuggable platform builds use ANDROID_ADDITIONAL_PUBLIC_LIBRARIES environment
-// variable to add libraries to the list. This is intended for platform tests only.
-std::string additional_public_libraries() {
- if (debuggable()) {
- const char* val = getenv("ANDROID_ADDITIONAL_PUBLIC_LIBRARIES");
- return val ? val : "";
- }
- return "";
-}
-
-void InsertVndkVersionStr(std::string* file_name) {
- CHECK(file_name != nullptr);
- size_t insert_pos = file_name->find_last_of(".");
- if (insert_pos == std::string::npos) {
- insert_pos = file_name->length();
- }
- file_name->insert(insert_pos, vndk_version_str());
-}
-
-const std::function<Result<bool>(const struct ConfigEntry&)> always_true =
- [](const struct ConfigEntry&) -> Result<bool> { return true; };
-
-Result<std::vector<std::string>> ReadConfig(
- const std::string& configFile,
- const std::function<Result<bool>(const ConfigEntry& /* entry */)>& filter_fn) {
- std::string file_content;
- if (!base::ReadFileToString(configFile, &file_content)) {
- return ErrnoError();
- }
- Result<std::vector<std::string>> result = ParseConfig(file_content, filter_fn);
- if (!result) {
- return Errorf("Cannot parse {}: {}", configFile, result.error().message());
- }
- return result;
-}
-
-void ReadExtensionLibraries(const char* dirname, std::vector<std::string>* sonames) {
- std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname), closedir);
- if (dir != nullptr) {
- // Failing to opening the dir is not an error, which can happen in
- // webview_zygote.
- while (struct dirent* ent = readdir(dir.get())) {
- if (ent->d_type != DT_REG && ent->d_type != DT_LNK) {
- continue;
- }
- const std::string filename(ent->d_name);
- std::string_view fn = filename;
- if (android::base::ConsumePrefix(&fn, kExtendedPublicLibrariesFilePrefix) &&
- android::base::ConsumeSuffix(&fn, kExtendedPublicLibrariesFileSuffix)) {
- const std::string company_name(fn);
- const std::string config_file_path = dirname + "/"s + filename;
- LOG_ALWAYS_FATAL_IF(
- company_name.empty(),
- "Error extracting company name from public native library list file path \"%s\"",
- config_file_path.c_str());
-
- auto ret = ReadConfig(
- config_file_path, [&company_name](const struct ConfigEntry& entry) -> Result<bool> {
- if (android::base::StartsWith(entry.soname, "lib") &&
- android::base::EndsWith(entry.soname, "." + company_name + ".so")) {
- return true;
- } else {
- return Errorf("Library name \"{}\" does not end with the company name {}.",
- entry.soname, company_name);
- }
- });
- if (ret) {
- sonames->insert(sonames->end(), ret->begin(), ret->end());
- } else {
- LOG_ALWAYS_FATAL("Error reading public native library list from \"%s\": %s",
- config_file_path.c_str(), ret.error().message().c_str());
- }
- }
- }
- }
-}
-
-static std::string InitDefaultPublicLibraries(bool for_preload) {
- std::string config_file = root_dir() + kDefaultPublicLibrariesFile;
- auto sonames =
- ReadConfig(config_file, [&for_preload](const struct ConfigEntry& entry) -> Result<bool> {
- if (for_preload) {
- return !entry.nopreload;
- } else {
- return true;
- }
- });
- if (!sonames) {
- LOG_ALWAYS_FATAL("Error reading public native library list from \"%s\": %s",
- config_file.c_str(), sonames.error().message().c_str());
- return "";
- }
-
- std::string additional_libs = additional_public_libraries();
- if (!additional_libs.empty()) {
- auto vec = base::Split(additional_libs, ":");
- std::copy(vec.begin(), vec.end(), std::back_inserter(*sonames));
- }
-
- // If this is for preloading libs, don't remove the libs from APEXes.
- if (for_preload) {
- return android::base::Join(*sonames, ':');
- }
-
- // Remove the public libs in the runtime namespace.
- // These libs are listed in public.android.txt, but we don't want the rest of android
- // in default namespace to dlopen the libs.
- // For example, libicuuc.so is exposed to classloader namespace from runtime namespace.
- // Unfortunately, it does not have stable C symbols, and default namespace should only use
- // stable symbols in libandroidicu.so. http://b/120786417
- for (const std::string& lib_name : kArtApexPublicLibraries) {
- std::string path(kArtApexLibPath);
- path.append("/").append(lib_name);
-
- struct stat s;
- // Do nothing if the path in /apex does not exist.
- // Runtime APEX must be mounted since libnativeloader is in the same APEX
- if (stat(path.c_str(), &s) != 0) {
- continue;
- }
-
- auto it = std::find(sonames->begin(), sonames->end(), lib_name);
- if (it != sonames->end()) {
- sonames->erase(it);
- }
- }
-
- // Remove the public libs in the nnapi namespace.
- auto it = std::find(sonames->begin(), sonames->end(), kNeuralNetworksApexPublicLibrary);
- if (it != sonames->end()) {
- sonames->erase(it);
- }
- return android::base::Join(*sonames, ':');
-}
-
-static std::string InitArtPublicLibraries() {
- CHECK(sizeof(kArtApexPublicLibraries) > 0);
- std::string list = android::base::Join(kArtApexPublicLibraries, ":");
-
- std::string additional_libs = additional_public_libraries();
- if (!additional_libs.empty()) {
- list = list + ':' + additional_libs;
- }
- return list;
-}
-
-static std::string InitVendorPublicLibraries() {
- // This file is optional, quietly ignore if the file does not exist.
- auto sonames = ReadConfig(kVendorPublicLibrariesFile, always_true);
- if (!sonames) {
- return "";
- }
- return android::base::Join(*sonames, ':');
-}
-
-// read /system/etc/public.libraries-<companyname>.txt and
-// /product/etc/public.libraries-<companyname>.txt which contain partner defined
-// system libs that are exposed to apps. The libs in the txt files must be
-// named as lib<name>.<companyname>.so.
-static std::string InitExtendedPublicLibraries() {
- std::vector<std::string> sonames;
- ReadExtensionLibraries("/system/etc", &sonames);
- ReadExtensionLibraries("/product/etc", &sonames);
- return android::base::Join(sonames, ':');
-}
-
-static std::string InitLlndkLibraries() {
- std::string config_file = kLlndkLibrariesFile;
- InsertVndkVersionStr(&config_file);
- auto sonames = ReadConfig(config_file, always_true);
- if (!sonames) {
- LOG_ALWAYS_FATAL("%s", sonames.error().message().c_str());
- return "";
- }
- return android::base::Join(*sonames, ':');
-}
-
-static std::string InitVndkspLibraries() {
- std::string config_file = kVndkLibrariesFile;
- InsertVndkVersionStr(&config_file);
- auto sonames = ReadConfig(config_file, always_true);
- if (!sonames) {
- LOG_ALWAYS_FATAL("%s", sonames.error().message().c_str());
- return "";
- }
- return android::base::Join(*sonames, ':');
-}
-
-static std::string InitNeuralNetworksPublicLibraries() {
- return kNeuralNetworksApexPublicLibrary;
-}
-
-} // namespace
-
-const std::string& preloadable_public_libraries() {
- static std::string list = InitDefaultPublicLibraries(/*for_preload*/ true);
- return list;
-}
-
-const std::string& default_public_libraries() {
- static std::string list = InitDefaultPublicLibraries(/*for_preload*/ false);
- return list;
-}
-
-const std::string& runtime_public_libraries() {
- static std::string list = InitArtPublicLibraries();
- return list;
-}
-
-const std::string& vendor_public_libraries() {
- static std::string list = InitVendorPublicLibraries();
- return list;
-}
-
-const std::string& extended_public_libraries() {
- static std::string list = InitExtendedPublicLibraries();
- return list;
-}
-
-const std::string& neuralnetworks_public_libraries() {
- static std::string list = InitNeuralNetworksPublicLibraries();
- return list;
-}
-
-const std::string& llndk_libraries() {
- static std::string list = InitLlndkLibraries();
- return list;
-}
-
-const std::string& vndksp_libraries() {
- static std::string list = InitVndkspLibraries();
- return list;
-}
-
-namespace internal {
-// Exported for testing
-Result<std::vector<std::string>> ParseConfig(
- const std::string& file_content,
- const std::function<Result<bool>(const ConfigEntry& /* entry */)>& filter_fn) {
- std::vector<std::string> lines = base::Split(file_content, "\n");
-
- std::vector<std::string> sonames;
- for (auto& line : lines) {
- auto trimmed_line = base::Trim(line);
- if (trimmed_line[0] == '#' || trimmed_line.empty()) {
- continue;
- }
-
- std::vector<std::string> tokens = android::base::Split(trimmed_line, " ");
- if (tokens.size() < 1 || tokens.size() > 3) {
- return Errorf("Malformed line \"{}\"", line);
- }
- struct ConfigEntry entry = {.soname = "", .nopreload = false, .bitness = ALL};
- size_t i = tokens.size();
- while (i-- > 0) {
- if (tokens[i] == "nopreload") {
- entry.nopreload = true;
- } else if (tokens[i] == "32" || tokens[i] == "64") {
- if (entry.bitness != ALL) {
- return Errorf("Malformed line \"{}\": bitness can be specified only once", line);
- }
- entry.bitness = tokens[i] == "32" ? ONLY_32 : ONLY_64;
- } else {
- if (i != 0) {
- return Errorf("Malformed line \"{}\"", line);
- }
- entry.soname = tokens[i];
- }
- }
-
- // skip 32-bit lib on 64-bit process and vice versa
-#if defined(__LP64__)
- if (entry.bitness == ONLY_32) continue;
-#else
- if (entry.bitness == ONLY_64) continue;
-#endif
-
- Result<bool> ret = filter_fn(entry);
- if (!ret) {
- return ret.error();
- }
- if (*ret) {
- // filter_fn has returned true.
- sonames.push_back(entry.soname);
- }
- }
- return sonames;
-}
-
-} // namespace internal
-
-} // namespace android::nativeloader
diff --git a/libnativeloader/public_libraries.h b/libnativeloader/public_libraries.h
deleted file mode 100644
index 2de4172..0000000
--- a/libnativeloader/public_libraries.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-#pragma once
-
-#include <algorithm>
-#include <string>
-
-#include <android-base/result.h>
-
-namespace android::nativeloader {
-
-using android::base::Result;
-
-// These provide the list of libraries that are available to the namespace for apps.
-// Not all of the libraries are available to apps. Depending on the context,
-// e.g., if it is a vendor app or not, different set of libraries are made available.
-const std::string& preloadable_public_libraries();
-const std::string& default_public_libraries();
-const std::string& runtime_public_libraries();
-const std::string& vendor_public_libraries();
-const std::string& extended_public_libraries();
-const std::string& neuralnetworks_public_libraries();
-const std::string& llndk_libraries();
-const std::string& vndksp_libraries();
-
-// These are exported for testing
-namespace internal {
-
-enum Bitness { ALL = 0, ONLY_32, ONLY_64 };
-
-struct ConfigEntry {
- std::string soname;
- bool nopreload;
- Bitness bitness;
-};
-
-Result<std::vector<std::string>> ParseConfig(
- const std::string& file_content,
- const std::function<Result<bool>(const ConfigEntry& /* entry */)>& filter_fn);
-
-} // namespace internal
-
-} // namespace android::nativeloader
diff --git a/libnativeloader/test/Android.bp b/libnativeloader/test/Android.bp
deleted file mode 100644
index 4d5c53d..0000000
--- a/libnativeloader/test/Android.bp
+++ /dev/null
@@ -1,82 +0,0 @@
-//
-// Copyright (C) 2017 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.
-//
-
-cc_library {
- name: "libfoo.oem1",
- srcs: ["test.cpp"],
- cflags: ["-DLIBNAME=\"libfoo.oem1.so\""],
- shared_libs: [
- "libbase",
- ],
-}
-
-cc_library {
- name: "libbar.oem1",
- srcs: ["test.cpp"],
- cflags: ["-DLIBNAME=\"libbar.oem1.so\""],
- shared_libs: [
- "libbase",
- ],
-}
-
-cc_library {
- name: "libfoo.oem2",
- srcs: ["test.cpp"],
- cflags: ["-DLIBNAME=\"libfoo.oem2.so\""],
- shared_libs: [
- "libbase",
- ],
-}
-
-cc_library {
- name: "libbar.oem2",
- srcs: ["test.cpp"],
- cflags: ["-DLIBNAME=\"libbar.oem2.so\""],
- shared_libs: [
- "libbase",
- ],
-}
-
-cc_library {
- name: "libfoo.product1",
- srcs: ["test.cpp"],
- cflags: ["-DLIBNAME=\"libfoo.product1.so\""],
- product_specific: true,
- shared_libs: [
- "libbase",
- ],
-}
-
-cc_library {
- name: "libbar.product1",
- srcs: ["test.cpp"],
- cflags: ["-DLIBNAME=\"libbar.product1.so\""],
- product_specific: true,
- shared_libs: [
- "libbase",
- ],
-}
-
-// Build the test for the C API.
-cc_test {
- name: "libnativeloader-api-tests",
- host_supported: true,
- test_per_src: true,
- srcs: [
- "api_test.c",
- ],
- header_libs: ["libnativeloader-headers"],
-}
diff --git a/libnativeloader/test/Android.mk b/libnativeloader/test/Android.mk
deleted file mode 100644
index 65e7b09..0000000
--- a/libnativeloader/test/Android.mk
+++ /dev/null
@@ -1,57 +0,0 @@
-#
-# Copyright (C) 2017 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.
-#
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := public.libraries-oem1.txt
-LOCAL_SRC_FILES:= $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := public.libraries-oem2.txt
-LOCAL_SRC_FILES:= $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := public.libraries-product1.txt
-LOCAL_SRC_FILES:= $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_ETC)
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_PACKAGE_NAME := oemlibrarytest-system
-LOCAL_MODULE_TAGS := tests
-LOCAL_MANIFEST_FILE := system/AndroidManifest.xml
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := current
-LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_MODULE_PATH := $(TARGET_OUT_APPS)
-include $(BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_PACKAGE_NAME := oemlibrarytest-vendor
-LOCAL_MODULE_TAGS := tests
-LOCAL_MANIFEST_FILE := vendor/AndroidManifest.xml
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := current
-LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_APPS)
-include $(BUILD_PACKAGE)
diff --git a/libnativeloader/test/public.libraries-oem1.txt b/libnativeloader/test/public.libraries-oem1.txt
deleted file mode 100644
index f9433e2..0000000
--- a/libnativeloader/test/public.libraries-oem1.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-libfoo.oem1.so
-libbar.oem1.so
diff --git a/libnativeloader/test/public.libraries-oem2.txt b/libnativeloader/test/public.libraries-oem2.txt
deleted file mode 100644
index de6bdb0..0000000
--- a/libnativeloader/test/public.libraries-oem2.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-libfoo.oem2.so
-libbar.oem2.so
diff --git a/libnativeloader/test/public.libraries-product1.txt b/libnativeloader/test/public.libraries-product1.txt
deleted file mode 100644
index 358154c..0000000
--- a/libnativeloader/test/public.libraries-product1.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-libfoo.product1.so
-libbar.product1.so
diff --git a/libnativeloader/test/runtest.sh b/libnativeloader/test/runtest.sh
deleted file mode 100755
index 40beb5b..0000000
--- a/libnativeloader/test/runtest.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-adb root
-adb remount
-adb sync
-adb shell stop
-adb shell start
-sleep 5 # wait until device reboots
-adb logcat -c;
-adb shell am start -n android.test.app.system/android.test.app.TestActivity
-adb shell am start -n android.test.app.vendor/android.test.app.TestActivity
-adb logcat | grep android.test.app
diff --git a/libnativeloader/test/src/android/test/app/TestActivity.java b/libnativeloader/test/src/android/test/app/TestActivity.java
deleted file mode 100644
index a7a455d..0000000
--- a/libnativeloader/test/src/android/test/app/TestActivity.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-package android.test.app;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.util.Log;
-
-public class TestActivity extends Activity {
-
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- tryLoadingLib("foo.oem1");
- tryLoadingLib("bar.oem1");
- tryLoadingLib("foo.oem2");
- tryLoadingLib("bar.oem2");
- tryLoadingLib("foo.product1");
- tryLoadingLib("bar.product1");
- }
-
- private void tryLoadingLib(String name) {
- try {
- System.loadLibrary(name);
- Log.d(getPackageName(), "library " + name + " is successfully loaded");
- } catch (UnsatisfiedLinkError e) {
- Log.d(getPackageName(), "failed to load libarary " + name, e);
- }
- }
-}
diff --git a/libnativeloader/test/system/AndroidManifest.xml b/libnativeloader/test/system/AndroidManifest.xml
deleted file mode 100644
index c304889..0000000
--- a/libnativeloader/test/system/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2018 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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.test.app.system">
-
- <application>
- <activity android:name="android.test.app.TestActivity" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-
-</manifest>
-
diff --git a/libnativeloader/test/test.cpp b/libnativeloader/test/test.cpp
deleted file mode 100644
index b166928..0000000
--- a/libnativeloader/test/test.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-#define LOG_TAG "oemlib"
-#include <android-base/logging.h>
-
-static __attribute__((constructor)) void test_lib_init() {
- LOG(DEBUG) << LIBNAME << " loaded";
-}
diff --git a/libnativeloader/test/vendor/AndroidManifest.xml b/libnativeloader/test/vendor/AndroidManifest.xml
deleted file mode 100644
index c4c1a9c..0000000
--- a/libnativeloader/test/vendor/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2018 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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.test.app.vendor">
-
- <application>
- <activity android:name="android.test.app.TestActivity" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-
-</manifest>
-
diff --git a/libnativeloader/utils.h b/libnativeloader/utils.h
deleted file mode 100644
index a1c2be5..0000000
--- a/libnativeloader/utils.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-#pragma once
-
-namespace android::nativeloader {
-
-#if defined(__LP64__)
-#define LIB "lib64"
-#else
-#define LIB "lib"
-#endif
-
-} // namespace android::nativeloader
diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c
index a098d59..6af49bb 100644
--- a/libnetutils/ifc_utils.c
+++ b/libnetutils/ifc_utils.c
@@ -362,10 +362,12 @@
return err->error;
}
+// Returns zero on success and negative errno on failure.
int ifc_add_address(const char *name, const char *address, int prefixlen) {
return ifc_act_on_address(RTM_NEWADDR, name, address, prefixlen);
}
+// Returns zero on success and negative errno on failure.
int ifc_del_address(const char *name, const char * address, int prefixlen) {
return ifc_act_on_address(RTM_DELADDR, name, address, prefixlen);
}
diff --git a/libprocessgroup/include/processgroup/sched_policy.h b/libprocessgroup/include/processgroup/sched_policy.h
index 3c498da..945d90c 100644
--- a/libprocessgroup/include/processgroup/sched_policy.h
+++ b/libprocessgroup/include/processgroup/sched_policy.h
@@ -70,11 +70,22 @@
extern int get_sched_policy(int tid, SchedPolicy* policy);
/* Return a displayable string corresponding to policy.
- * Return value: non-NULL NUL-terminated name of unspecified length;
+ * Return value: NUL-terminated name of unspecified length, nullptr if invalid;
* the caller is responsible for displaying the useful part of the string.
*/
extern const char* get_sched_policy_name(SchedPolicy policy);
+/* Return the aggregated task profile name corresponding to cpuset policy.
+ * Return value: NUL-terminated name of unspecified length, nullptr if invalid;
+ * the caller could use it to call SetTaskProfiles.
+ */
+extern const char* get_cpuset_policy_profile_name(SchedPolicy policy);
+
+/* Return the aggregated task profile name corresponding to sched policy.
+ * Return value: NUL-terminated name of unspecified length, nullptr if invalid;
+ * the caller could use it to call SetTaskProfiles.
+ */
+extern const char* get_sched_policy_profile_name(SchedPolicy policy);
#ifdef __cplusplus
}
#endif
diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
index 6b0ab87..16339d3 100644
--- a/libprocessgroup/sched_policy.cpp
+++ b/libprocessgroup/sched_policy.cpp
@@ -212,7 +212,45 @@
};
static_assert(arraysize(kSchedPolicyNames) == SP_CNT, "missing name");
if (policy < SP_BACKGROUND || policy >= SP_CNT) {
- return "error";
+ return nullptr;
}
return kSchedPolicyNames[policy];
}
+
+const char* get_cpuset_policy_profile_name(SchedPolicy policy) {
+ /*
+ * cpuset profile array for:
+ * SP_DEFAULT(-1), SP_BACKGROUND(0), SP_FOREGROUND(1),
+ * SP_SYSTEM(2), SP_AUDIO_APP(3), SP_AUDIO_SYS(4),
+ * SP_TOP_APP(5), SP_RT_APP(6), SP_RESTRICTED(7)
+ * index is policy + 1
+ * this need keep in sync with SchedPolicy enum
+ */
+ static constexpr const char* kCpusetProfiles[SP_CNT + 1] = {
+ "CPUSET_SP_DEFAULT", "CPUSET_SP_BACKGROUND", "CPUSET_SP_FOREGROUND",
+ "CPUSET_SP_SYSTEM", "CPUSET_SP_FOREGROUND", "CPUSET_SP_FOREGROUND",
+ "CPUSET_SP_TOP_APP", "CPUSET_SP_DEFAULT", "CPUSET_SP_RESTRICTED"};
+ if (policy < SP_DEFAULT || policy >= SP_CNT) {
+ return nullptr;
+ }
+ return kCpusetProfiles[policy + 1];
+}
+
+const char* get_sched_policy_profile_name(SchedPolicy policy) {
+ /*
+ * sched profile array for:
+ * SP_DEFAULT(-1), SP_BACKGROUND(0), SP_FOREGROUND(1),
+ * SP_SYSTEM(2), SP_AUDIO_APP(3), SP_AUDIO_SYS(4),
+ * SP_TOP_APP(5), SP_RT_APP(6), SP_RESTRICTED(7)
+ * index is policy + 1
+ * this need keep in sync with SchedPolicy enum
+ */
+ static constexpr const char* kSchedProfiles[SP_CNT + 1] = {
+ "SCHED_SP_DEFAULT", "SCHED_SP_BACKGROUND", "SCHED_SP_FOREGROUND",
+ "SCHED_SP_DEFAULT", "SCHED_SP_FOREGROUND", "SCHED_SP_FOREGROUND",
+ "SCHED_SP_TOP_APP", "SCHED_SP_RT_APP", "SCHED_SP_DEFAULT"};
+ if (policy < SP_DEFAULT || policy >= SP_CNT) {
+ return nullptr;
+ }
+ return kSchedProfiles[policy + 1];
+}
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 8d2299f..2573b1c 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -239,12 +239,14 @@
"tests/files/offline/bad_eh_frame_hdr_arm64/*",
"tests/files/offline/debug_frame_first_x86/*",
"tests/files/offline/debug_frame_load_bias_arm/*",
+ "tests/files/offline/eh_frame_bias_x86/*",
"tests/files/offline/eh_frame_hdr_begin_x86_64/*",
"tests/files/offline/invalid_elf_offset_arm/*",
"tests/files/offline/jit_debug_arm/*",
"tests/files/offline/jit_debug_x86/*",
"tests/files/offline/jit_map_arm/*",
"tests/files/offline/gnu_debugdata_arm/*",
+ "tests/files/offline/load_bias_different_section_bias_arm64/*",
"tests/files/offline/load_bias_ro_rx_x86_64/*",
"tests/files/offline/offset_arm/*",
"tests/files/offline/shared_lib_in_apk_arm64/*",
diff --git a/libunwindstack/AndroidVersions.md b/libunwindstack/AndroidVersions.md
new file mode 100644
index 0000000..234f639
--- /dev/null
+++ b/libunwindstack/AndroidVersions.md
@@ -0,0 +1,116 @@
+# Unwinder Support Per Android Release
+This document describes the changes in the way the libunwindstack
+unwinder works on different Android versions. It does not describe
+every change in the code made between different versions, but is
+meant to allow an app developer to know what might be supported
+on different versions. It also describes the different way an unwind
+will display on different versions of Android.
+
+## Android P
+libunwindstack was first introduced in Android P.
+
+* Supports up to and including Dwarf 4 unwinding information.
+ See http://dwarfstd.org/ for Dwarf standards.
+* Supports Arm exidx unwinding.
+* Supports the gdb JIT unwinding interface, which is how ART creates unwinding
+ information for the JIT'd Java frames.
+* Supports special frames added to represent an ART Java interpreter frame.
+ ART has marked the dex pc using cfi information that the unwinder
+ understands and handles by adding a new frame in the stacktrace.
+
+## Note
+By default, lld creates two separate maps of the elf in memory, one read-only
+and one read/executable. The libunwindstack on P and the unwinder on older
+versions of Android will not unwind properly in this case. For apps that
+target Android P or older, make sure that `-Wl,--no-rosegment` is
+included in linker arguments when using lld.
+
+## Android Q
+* Fix bug (b/109824792) that handled load bias data incorrectly when
+ FDEs use pc relative addressing in the eh\_frame\_hdr.
+ Unfortunately, this wasn't fixed correctly in Q since it assumes
+ that the bias is coming from the program header for the executable
+ load. The real fix was to use the bias from the actual section data and
+ is not completely fixed until Android R. For apps targeting Android Q,
+ if it is being compiled with the llvm linker lld, it might be necessary
+ to add the linker option `-Wl,-zseparate-code` to avoid creating an elf
+ created this way.
+* Change the way the exidx section offset is found (b/110704153). Before
+ the p\_vaddr value from the program header minus the load bias was used
+ to find the start of the exidx data. Changed to use the p\_offset since
+ it doesn't require any load bias manipulations.
+* Fix bug handling of dwarf sections without any header (b/110235461).
+ Previously, the code assumed that FDEs are non-overlapping, and the FDEs
+ are always in sorted order from low pc to high pc. Thus the code would
+ read the entire set of CIEs/FDEs and then do a binary search to find
+ the appropriate FDE for a given pc. Now the code does a sequential read
+ and stops when it finds the FDE for a pc. It also understands the
+ overlapping FDEs, so find the first FDE that matches a pc. In practice,
+ elf files with this format only ever occurs if the file was generated
+ without an eh\_frame/eh\_frame\_hdr section and only a debug\_frame. The
+ other way this has been observed is when running simpleperf to unwind since
+ sometimes there is not enough information in the eh\_frame for all points
+ in the executable. On Android P, this would result in some incorrect
+ unwinds coming from simpleperf. Nearly all crashes from Android P should
+ be correct since the eh\_frame information was enough to do the unwind
+ properly.
+* Be permissive of badly formed elf files. Previously, any detected error
+ would result in unwinds stopping even if there is enough valid information
+ to do an unwind.
+ * The code now allows program header/section header offsets to point
+ to unreadable memory. As long as the code can find the unwind tables,
+ that is good enough.
+ * The code allows program headers/section headers to be missing.
+ * Allow a symbol table section header to point to invalid symbol table
+ values.
+* Support for the linker read-only segment option (b/109657296).
+ This is a feature of lld whereby there are two sections that
+ contain elf data. The first is read-only and contains the elf header data,
+ and the second is read-execute or execute only that
+ contains the executable code from the elf. Before this, the unwinder
+ always assumed that there was only a single read-execute section that
+ contained the elf header data and the executable code.
+* Build ID information for elf objects added. This will display the
+ NT\_GNU\_BUILD\_ID note found in elf files. This information can be used
+ to identify the exact version of a shared library to help get symbol
+ information when looking at a crash.
+* Add support for displaying the soname from an apk frame. Previously,
+ a frame map name would be only the apk, but now if the shared library
+ in the apk has set a soname, the map name will be `app.apk!libexample.so`
+ instead of only `app.apk`.
+* Minimal support for Dwarf 5. This merely treats a Dwarf 5 version
+ elf file as Dwarf 4. It does not support the new dwarf ops in Dwarf 5.
+ Since the new ops are not likely to be used very often, this allows
+ continuing to unwind even when encountering Dwarf 5 elf files.
+* Fix bug in pc handling of signal frames (b/130302288). In the previous
+ version, the pc would be wrong in the signal frame. The rest of the
+ unwind was correct, only the frame in the signal handler was incorrect
+ in Android P.
+* Detect when an elf file is not readable so that a message can be
+ displayed indicating that. This can happen when an app puts the shared
+ libraries in non-standard locations that are not readable due to
+ security restrictions (selinux rules).
+
+## Android R
+* Display the offsets for Java interpreter frames. If this frame came
+ from a non-zero offset map, no offset is printed. Previously, the
+ line would look like:
+
+ #17 pc 00500d7a GoogleCamera.apk (com.google.camera.AndroidPriorityThread.run+10)
+
+ to:
+
+ #17 pc 00500d7a GoogleCamera.apk (offset 0x11d0000) (com.google.camera.AndroidPriorityThread.run+10)
+* Fix bug where the load bias was set from the first PT\_LOAD program
+ header that has a zero p\_offset value. Now it is set from the first
+ executable PT\_LOAD program header. This has only ever been a problem
+ for host executables compiled for the x86\_64 architecture.
+* Switched to the libc++ demangler for function names. Previously, the
+ demangler used was not complete, so some less common demangled function
+ names would not be properly demangled or the function name would not be
+ demangled at all.
+* Fix bug in load bias handling. If the unwind information in the eh\_frame
+ or eh\_frame\_hdr does not have the same bias as the executable section,
+ and uses pc relative FDEs, the unwind will be incorrect. This tends
+ to truncate unwinds since the unwinder could not find the correct unwind
+ information for a given pc.
diff --git a/libunwindstack/DexFile.cpp b/libunwindstack/DexFile.cpp
index eaf867f..dff7a8b 100644
--- a/libunwindstack/DexFile.cpp
+++ b/libunwindstack/DexFile.cpp
@@ -22,6 +22,9 @@
#include <memory>
+#define LOG_TAG "unwind"
+#include <log/log.h>
+
#include <android-base/unique_fd.h>
#include <art_api/dex_file_support.h>
@@ -32,6 +35,19 @@
namespace unwindstack {
+static bool CheckDexSupport() {
+ if (std::string err_msg; !art_api::dex::TryLoadLibdexfileExternal(&err_msg)) {
+ ALOGW("Failed to initialize DEX file support: %s", err_msg.c_str());
+ return false;
+ }
+ return true;
+}
+
+static bool HasDexSupport() {
+ static bool has_dex_support = CheckDexSupport();
+ return has_dex_support;
+}
+
std::unique_ptr<DexFile> DexFile::Create(uint64_t dex_file_offset_in_memory, Memory* memory,
MapInfo* info) {
if (!info->name.empty()) {
@@ -57,6 +73,10 @@
std::unique_ptr<DexFileFromFile> DexFileFromFile::Create(uint64_t dex_file_offset_in_file,
const std::string& file) {
+ if (UNLIKELY(!HasDexSupport())) {
+ return nullptr;
+ }
+
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC)));
if (fd == -1) {
return nullptr;
@@ -75,6 +95,10 @@
std::unique_ptr<DexFileFromMemory> DexFileFromMemory::Create(uint64_t dex_file_offset_in_memory,
Memory* memory,
const std::string& name) {
+ if (UNLIKELY(!HasDexSupport())) {
+ return nullptr;
+ }
+
std::vector<uint8_t> backing_memory;
for (size_t size = 0;;) {
diff --git a/libunwindstack/DwarfDebugFrame.h b/libunwindstack/DwarfDebugFrame.h
index 388ab0a..635cefd 100644
--- a/libunwindstack/DwarfDebugFrame.h
+++ b/libunwindstack/DwarfDebugFrame.h
@@ -26,9 +26,9 @@
namespace unwindstack {
template <typename AddressType>
-class DwarfDebugFrame : public DwarfSectionImplNoHdr<AddressType> {
+class DwarfDebugFrame : public DwarfSectionImpl<AddressType> {
public:
- DwarfDebugFrame(Memory* memory) : DwarfSectionImplNoHdr<AddressType>(memory) {
+ DwarfDebugFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {
this->cie32_value_ = static_cast<uint32_t>(-1);
this->cie64_value_ = static_cast<uint64_t>(-1);
}
diff --git a/libunwindstack/DwarfEhFrame.h b/libunwindstack/DwarfEhFrame.h
index df441fb..7a41e45 100644
--- a/libunwindstack/DwarfEhFrame.h
+++ b/libunwindstack/DwarfEhFrame.h
@@ -25,9 +25,9 @@
namespace unwindstack {
template <typename AddressType>
-class DwarfEhFrame : public DwarfSectionImplNoHdr<AddressType> {
+class DwarfEhFrame : public DwarfSectionImpl<AddressType> {
public:
- DwarfEhFrame(Memory* memory) : DwarfSectionImplNoHdr<AddressType>(memory) {}
+ DwarfEhFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
virtual ~DwarfEhFrame() = default;
uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
diff --git a/libunwindstack/DwarfEhFrameWithHdr.cpp b/libunwindstack/DwarfEhFrameWithHdr.cpp
index 802beca..1358e51 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.cpp
+++ b/libunwindstack/DwarfEhFrameWithHdr.cpp
@@ -32,14 +32,19 @@
}
template <typename AddressType>
-bool DwarfEhFrameWithHdr<AddressType>::Init(uint64_t offset, uint64_t size, uint64_t load_bias) {
- load_bias_ = load_bias;
+bool DwarfEhFrameWithHdr<AddressType>::EhFrameInit(uint64_t offset, uint64_t size,
+ int64_t section_bias) {
+ return DwarfSectionImpl<AddressType>::Init(offset, size, section_bias);
+}
+template <typename AddressType>
+bool DwarfEhFrameWithHdr<AddressType>::Init(uint64_t offset, uint64_t, int64_t section_bias) {
memory_.clear_func_offset();
memory_.clear_text_offset();
memory_.set_data_offset(offset);
memory_.set_cur_offset(offset);
- pc_offset_ = offset;
+
+ hdr_section_bias_ = section_bias;
// Read the first four bytes all at once.
uint8_t data[4];
@@ -56,7 +61,7 @@
return false;
}
- ptr_encoding_ = data[1];
+ uint8_t ptr_encoding = data[1];
uint8_t fde_count_encoding = data[2];
table_encoding_ = data[3];
table_entry_size_ = memory_.template GetEncodedSize<AddressType>(table_encoding_);
@@ -70,7 +75,8 @@
}
memory_.set_pc_offset(memory_.cur_offset());
- if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding_, &ptr_offset_)) {
+ uint64_t ptr_offset;
+ if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding, &ptr_offset)) {
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
last_error_.address = memory_.cur_offset();
return false;
@@ -88,10 +94,8 @@
return false;
}
- entries_offset_ = memory_.cur_offset();
- entries_end_ = offset + size;
- entries_data_offset_ = offset;
- cur_entries_offset_ = entries_offset_;
+ hdr_entries_offset_ = memory_.cur_offset();
+ hdr_entries_data_offset_ = offset;
return true;
}
@@ -107,6 +111,16 @@
return nullptr;
}
+ // There is a possibility that this entry points to a zero length FDE
+ // due to a bug. If this happens, try and find the non-zero length FDE
+ // from eh_frame directly. See b/142483624.
+ if (fde->pc_start == fde->pc_end) {
+ fde = DwarfSectionImpl<AddressType>::GetFdeFromPc(pc);
+ if (fde == nullptr) {
+ return nullptr;
+ }
+ }
+
// Guaranteed pc >= pc_start, need to check pc in the fde range.
if (pc < fde->pc_end) {
return fde;
@@ -124,8 +138,8 @@
}
FdeInfo* info = &fde_info_[index];
- memory_.set_data_offset(entries_data_offset_);
- memory_.set_cur_offset(entries_offset_ + 2 * index * table_entry_size_);
+ memory_.set_data_offset(hdr_entries_data_offset_);
+ memory_.set_cur_offset(hdr_entries_offset_ + 2 * index * table_entry_size_);
memory_.set_pc_offset(0);
uint64_t value;
if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
@@ -138,7 +152,7 @@
// Relative encodings require adding in the load bias.
if (IsEncodingRelative(table_encoding_)) {
- value += load_bias_;
+ value += hdr_section_bias_;
}
info->pc = value;
return info;
@@ -190,6 +204,16 @@
if (fde == nullptr) {
break;
}
+
+ // There is a possibility that this entry points to a zero length FDE
+ // due to a bug. If this happens, try and find the non-zero length FDE
+ // from eh_frame directly. See b/142483624.
+ if (fde->pc_start == fde->pc_end) {
+ const DwarfFde* fde_real = DwarfSectionImpl<AddressType>::GetFdeFromPc(fde->pc_start);
+ if (fde_real != nullptr) {
+ fde = fde_real;
+ }
+ }
fdes->push_back(fde);
}
}
diff --git a/libunwindstack/DwarfEhFrameWithHdr.h b/libunwindstack/DwarfEhFrameWithHdr.h
index 0e5eef7..f7c010c 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.h
+++ b/libunwindstack/DwarfEhFrameWithHdr.h
@@ -34,11 +34,7 @@
// Add these so that the protected members of DwarfSectionImpl
// can be accessed without needing a this->.
using DwarfSectionImpl<AddressType>::memory_;
- using DwarfSectionImpl<AddressType>::pc_offset_;
- using DwarfSectionImpl<AddressType>::entries_offset_;
- using DwarfSectionImpl<AddressType>::entries_end_;
using DwarfSectionImpl<AddressType>::last_error_;
- using DwarfSectionImpl<AddressType>::load_bias_;
struct FdeInfo {
AddressType pc;
@@ -49,19 +45,20 @@
virtual ~DwarfEhFrameWithHdr() = default;
uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
- return this->memory_.cur_offset() - pointer - 4;
+ return memory_.cur_offset() - pointer - 4;
}
uint64_t GetCieOffsetFromFde64(uint64_t pointer) override {
- return this->memory_.cur_offset() - pointer - 8;
+ return memory_.cur_offset() - pointer - 8;
}
uint64_t AdjustPcFromFde(uint64_t pc) override {
// The eh_frame uses relative pcs.
- return pc + this->memory_.cur_offset() - 4;
+ return pc + memory_.cur_offset() - 4;
}
- bool Init(uint64_t offset, uint64_t size, uint64_t load_bias) override;
+ bool EhFrameInit(uint64_t offset, uint64_t size, int64_t section_bias);
+ bool Init(uint64_t offset, uint64_t size, int64_t section_bias) override;
const DwarfFde* GetFdeFromPc(uint64_t pc) override;
@@ -72,17 +69,15 @@
void GetFdes(std::vector<const DwarfFde*>* fdes) override;
protected:
- uint8_t version_;
- uint8_t ptr_encoding_;
- uint8_t table_encoding_;
- size_t table_entry_size_;
+ uint8_t version_ = 0;
+ uint8_t table_encoding_ = 0;
+ size_t table_entry_size_ = 0;
- uint64_t ptr_offset_;
+ uint64_t hdr_entries_offset_ = 0;
+ uint64_t hdr_entries_data_offset_ = 0;
+ uint64_t hdr_section_bias_ = 0;
- uint64_t entries_data_offset_;
- uint64_t cur_entries_offset_ = 0;
-
- uint64_t fde_count_;
+ uint64_t fde_count_ = 0;
std::unordered_map<uint64_t, FdeInfo> fde_info_;
};
diff --git a/libunwindstack/DwarfMemory.cpp b/libunwindstack/DwarfMemory.cpp
index b505900..2e388c6 100644
--- a/libunwindstack/DwarfMemory.cpp
+++ b/libunwindstack/DwarfMemory.cpp
@@ -111,7 +111,7 @@
// Nothing to do.
break;
case DW_EH_PE_pcrel:
- if (pc_offset_ == static_cast<uint64_t>(-1)) {
+ if (pc_offset_ == INT64_MAX) {
// Unsupported encoding.
return false;
}
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 849a31a..e6263f8 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -69,6 +69,7 @@
return &cie_entry->second;
}
DwarfCie* cie = &cie_entries_[offset];
+ memory_.set_data_offset(entries_offset_);
memory_.set_cur_offset(offset);
if (!FillInCieHeader(cie) || !FillInCie(cie)) {
// Erase the cached entry.
@@ -251,6 +252,7 @@
return &fde_entry->second;
}
DwarfFde* fde = &fde_entries_[offset];
+ memory_.set_data_offset(entries_offset_);
memory_.set_cur_offset(offset);
if (!FillInFdeHeader(fde) || !FillInFde(fde)) {
fde_entries_.erase(offset);
@@ -333,7 +335,7 @@
memory_.set_cur_offset(cur_offset);
// The load bias only applies to the start.
- memory_.set_pc_offset(load_bias_);
+ memory_.set_pc_offset(section_bias_);
bool valid = memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding, &fde->pc_start);
fde->pc_start = AdjustPcFromFde(fde->pc_start);
@@ -591,8 +593,8 @@
}
template <typename AddressType>
-bool DwarfSectionImplNoHdr<AddressType>::Init(uint64_t offset, uint64_t size, uint64_t load_bias) {
- load_bias_ = load_bias;
+bool DwarfSectionImpl<AddressType>::Init(uint64_t offset, uint64_t size, int64_t section_bias) {
+ section_bias_ = section_bias;
entries_offset_ = offset;
next_entries_offset_ = offset;
entries_end_ = offset + size;
@@ -600,7 +602,6 @@
memory_.clear_func_offset();
memory_.clear_text_offset();
memory_.set_cur_offset(offset);
- memory_.set_data_offset(offset);
pc_offset_ = offset;
return true;
@@ -616,7 +617,7 @@
// and an fde has a start pc of 0x100 and end pc of 0x500, two new entries
// will be added: 0x200, 0x100 and 0x500, 0x400.
template <typename AddressType>
-void DwarfSectionImplNoHdr<AddressType>::InsertFde(const DwarfFde* fde) {
+void DwarfSectionImpl<AddressType>::InsertFde(const DwarfFde* fde) {
uint64_t start = fde->pc_start;
uint64_t end = fde->pc_end;
auto it = fdes_.upper_bound(start);
@@ -653,9 +654,10 @@
}
template <typename AddressType>
-bool DwarfSectionImplNoHdr<AddressType>::GetNextCieOrFde(DwarfFde** fde_entry) {
+bool DwarfSectionImpl<AddressType>::GetNextCieOrFde(const DwarfFde** fde_entry) {
uint64_t start_offset = next_entries_offset_;
+ memory_.set_data_offset(entries_offset_);
memory_.set_cur_offset(next_entries_offset_);
uint32_t value32;
if (!memory_.ReadBytes(&value32, sizeof(value32))) {
@@ -688,7 +690,7 @@
entry_is_cie = true;
cie_fde_encoding = DW_EH_PE_sdata8;
} else {
- cie_offset = this->GetCieOffsetFromFde64(value64);
+ cie_offset = GetCieOffsetFromFde64(value64);
}
} else {
next_entries_offset_ = memory_.cur_offset() + value32;
@@ -704,37 +706,45 @@
entry_is_cie = true;
cie_fde_encoding = DW_EH_PE_sdata4;
} else {
- cie_offset = this->GetCieOffsetFromFde32(value32);
+ cie_offset = GetCieOffsetFromFde32(value32);
}
}
if (entry_is_cie) {
- DwarfCie* cie = &cie_entries_[start_offset];
- cie->lsda_encoding = DW_EH_PE_omit;
- cie->cfa_instructions_end = next_entries_offset_;
- cie->fde_address_encoding = cie_fde_encoding;
+ auto entry = cie_entries_.find(start_offset);
+ if (entry == cie_entries_.end()) {
+ DwarfCie* cie = &cie_entries_[start_offset];
+ cie->lsda_encoding = DW_EH_PE_omit;
+ cie->cfa_instructions_end = next_entries_offset_;
+ cie->fde_address_encoding = cie_fde_encoding;
- if (!this->FillInCie(cie)) {
- cie_entries_.erase(start_offset);
- return false;
+ if (!FillInCie(cie)) {
+ cie_entries_.erase(start_offset);
+ return false;
+ }
}
*fde_entry = nullptr;
} else {
- DwarfFde* fde = &fde_entries_[start_offset];
- fde->cfa_instructions_end = next_entries_offset_;
- fde->cie_offset = cie_offset;
+ auto entry = fde_entries_.find(start_offset);
+ if (entry != fde_entries_.end()) {
+ *fde_entry = &entry->second;
+ } else {
+ DwarfFde* fde = &fde_entries_[start_offset];
+ fde->cfa_instructions_end = next_entries_offset_;
+ fde->cie_offset = cie_offset;
- if (!this->FillInFde(fde)) {
- fde_entries_.erase(start_offset);
- return false;
+ if (!FillInFde(fde)) {
+ fde_entries_.erase(start_offset);
+ return false;
+ }
+ *fde_entry = fde;
}
- *fde_entry = fde;
}
return true;
}
template <typename AddressType>
-void DwarfSectionImplNoHdr<AddressType>::GetFdes(std::vector<const DwarfFde*>* fdes) {
+void DwarfSectionImpl<AddressType>::GetFdes(std::vector<const DwarfFde*>* fdes) {
// Loop through the already cached entries.
uint64_t entry_offset = entries_offset_;
while (entry_offset < next_entries_offset_) {
@@ -753,7 +763,7 @@
}
while (next_entries_offset_ < entries_end_) {
- DwarfFde* fde;
+ const DwarfFde* fde;
if (!GetNextCieOrFde(&fde)) {
break;
}
@@ -770,7 +780,7 @@
}
template <typename AddressType>
-const DwarfFde* DwarfSectionImplNoHdr<AddressType>::GetFdeFromPc(uint64_t pc) {
+const DwarfFde* DwarfSectionImpl<AddressType>::GetFdeFromPc(uint64_t pc) {
// Search in the list of fdes we already have.
auto it = fdes_.upper_bound(pc);
if (it != fdes_.end()) {
@@ -783,7 +793,7 @@
// to do a linear search of the fdes by pc. As fdes are read, a cached
// search map is created.
while (next_entries_offset_ < entries_end_) {
- DwarfFde* fde;
+ const DwarfFde* fde;
if (!GetNextCieOrFde(&fde)) {
return nullptr;
}
@@ -806,10 +816,6 @@
template class DwarfSectionImpl<uint32_t>;
template class DwarfSectionImpl<uint64_t>;
-// Explicitly instantiate DwarfSectionImplNoHdr
-template class DwarfSectionImplNoHdr<uint32_t>;
-template class DwarfSectionImplNoHdr<uint64_t>;
-
// Explicitly instantiate DwarfDebugFrame
template class DwarfDebugFrame<uint32_t>;
template class DwarfDebugFrame<uint64_t>;
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 3454913..c141b2e 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -53,7 +53,7 @@
valid_ = interface_->Init(&load_bias_);
if (valid_) {
- interface_->InitHeaders(load_bias_);
+ interface_->InitHeaders();
InitGnuDebugdata();
} else {
interface_.reset(nullptr);
@@ -77,9 +77,9 @@
// Ignore the load_bias from the compressed section, the correct load bias
// is in the uncompressed data.
- uint64_t load_bias;
+ int64_t load_bias;
if (gnu->Init(&load_bias)) {
- gnu->InitHeaders(load_bias);
+ gnu->InitHeaders();
interface_->SetGnuDebugdataInterface(gnu);
} else {
// Free all of the memory associated with the gnu_debugdata section.
@@ -124,7 +124,7 @@
}
// Adjust by the load bias.
- if (*memory_address < load_bias_) {
+ if (load_bias_ > 0 && *memory_address < static_cast<uint64_t>(load_bias_)) {
return false;
}
@@ -229,7 +229,7 @@
}
bool Elf::IsValidPc(uint64_t pc) {
- if (!valid_ || pc < load_bias_) {
+ if (!valid_ || (load_bias_ > 0 && pc < static_cast<uint64_t>(load_bias_))) {
return false;
}
@@ -299,7 +299,7 @@
return interface.release();
}
-uint64_t Elf::GetLoadBias(Memory* memory) {
+int64_t Elf::GetLoadBias(Memory* memory) {
if (!IsValidElf(memory)) {
return 0;
}
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index be1f092..5f95fa8 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -124,10 +124,12 @@
}
template <typename AddressType>
-void ElfInterface::InitHeadersWithTemplate(uint64_t load_bias) {
+void ElfInterface::InitHeadersWithTemplate() {
if (eh_frame_hdr_offset_ != 0) {
- eh_frame_.reset(new DwarfEhFrameWithHdr<AddressType>(memory_));
- if (!eh_frame_->Init(eh_frame_hdr_offset_, eh_frame_hdr_size_, load_bias)) {
+ DwarfEhFrameWithHdr<AddressType>* eh_frame_hdr = new DwarfEhFrameWithHdr<AddressType>(memory_);
+ eh_frame_.reset(eh_frame_hdr);
+ if (!eh_frame_hdr->EhFrameInit(eh_frame_offset_, eh_frame_size_, eh_frame_section_bias_) ||
+ !eh_frame_->Init(eh_frame_hdr_offset_, eh_frame_hdr_size_, eh_frame_hdr_section_bias_)) {
eh_frame_.reset(nullptr);
}
}
@@ -136,21 +138,23 @@
// If there is an eh_frame section without an eh_frame_hdr section,
// or using the frame hdr object failed to init.
eh_frame_.reset(new DwarfEhFrame<AddressType>(memory_));
- if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_, load_bias)) {
+ if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_, eh_frame_section_bias_)) {
eh_frame_.reset(nullptr);
}
}
if (eh_frame_.get() == nullptr) {
eh_frame_hdr_offset_ = 0;
+ eh_frame_hdr_section_bias_ = 0;
eh_frame_hdr_size_ = static_cast<uint64_t>(-1);
eh_frame_offset_ = 0;
+ eh_frame_section_bias_ = 0;
eh_frame_size_ = static_cast<uint64_t>(-1);
}
if (debug_frame_offset_ != 0) {
debug_frame_.reset(new DwarfDebugFrame<AddressType>(memory_));
- if (!debug_frame_->Init(debug_frame_offset_, debug_frame_size_, load_bias)) {
+ if (!debug_frame_->Init(debug_frame_offset_, debug_frame_size_, debug_frame_section_bias_)) {
debug_frame_.reset(nullptr);
debug_frame_offset_ = 0;
debug_frame_size_ = static_cast<uint64_t>(-1);
@@ -159,7 +163,7 @@
}
template <typename EhdrType, typename PhdrType, typename ShdrType>
-bool ElfInterface::ReadAllHeaders(uint64_t* load_bias) {
+bool ElfInterface::ReadAllHeaders(int64_t* load_bias) {
EhdrType ehdr;
if (!memory_->ReadFully(0, &ehdr, sizeof(ehdr))) {
last_error_.code = ERROR_MEMORY_INVALID;
@@ -175,7 +179,7 @@
}
template <typename EhdrType, typename PhdrType>
-uint64_t ElfInterface::GetLoadBias(Memory* memory) {
+int64_t ElfInterface::GetLoadBias(Memory* memory) {
EhdrType ehdr;
if (!memory->ReadFully(0, &ehdr, sizeof(ehdr))) {
return false;
@@ -190,17 +194,14 @@
// Find the first executable load when looking for the load bias.
if (phdr.p_type == PT_LOAD && (phdr.p_flags & PF_X)) {
- if (phdr.p_vaddr > phdr.p_offset) {
- return phdr.p_vaddr - phdr.p_offset;
- }
- break;
+ return static_cast<uint64_t>(phdr.p_vaddr) - phdr.p_offset;
}
}
return 0;
}
template <typename EhdrType, typename PhdrType>
-void ElfInterface::ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias) {
+void ElfInterface::ReadProgramHeaders(const EhdrType& ehdr, int64_t* load_bias) {
uint64_t offset = ehdr.e_phoff;
bool first_exec_load_header = true;
for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
@@ -219,8 +220,8 @@
pt_loads_[phdr.p_offset] = LoadInfo{phdr.p_offset, phdr.p_vaddr,
static_cast<size_t>(phdr.p_memsz)};
// Only set the load bias from the first executable load header.
- if (first_exec_load_header && phdr.p_vaddr > phdr.p_offset) {
- *load_bias = phdr.p_vaddr - phdr.p_offset;
+ if (first_exec_load_header) {
+ *load_bias = static_cast<uint64_t>(phdr.p_vaddr) - phdr.p_offset;
}
first_exec_load_header = false;
break;
@@ -229,6 +230,7 @@
case PT_GNU_EH_FRAME:
// This is really the pointer to the .eh_frame_hdr section.
eh_frame_hdr_offset_ = phdr.p_offset;
+ eh_frame_hdr_section_bias_ = static_cast<uint64_t>(phdr.p_vaddr) - phdr.p_offset;
eh_frame_hdr_size_ = phdr.p_memsz;
break;
@@ -343,24 +345,21 @@
if (shdr.sh_name < sec_size) {
std::string name;
if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) {
- uint64_t* offset_ptr = nullptr;
- uint64_t* size_ptr = nullptr;
if (name == ".debug_frame") {
- offset_ptr = &debug_frame_offset_;
- size_ptr = &debug_frame_size_;
+ debug_frame_offset_ = shdr.sh_offset;
+ debug_frame_size_ = shdr.sh_size;
+ debug_frame_section_bias_ = static_cast<uint64_t>(shdr.sh_addr) - shdr.sh_offset;
} else if (name == ".gnu_debugdata") {
- offset_ptr = &gnu_debugdata_offset_;
- size_ptr = &gnu_debugdata_size_;
+ gnu_debugdata_offset_ = shdr.sh_offset;
+ gnu_debugdata_size_ = shdr.sh_size;
} else if (name == ".eh_frame") {
- offset_ptr = &eh_frame_offset_;
- size_ptr = &eh_frame_size_;
+ eh_frame_offset_ = shdr.sh_offset;
+ eh_frame_section_bias_ = static_cast<uint64_t>(shdr.sh_addr) - shdr.sh_offset;
+ eh_frame_size_ = shdr.sh_size;
} else if (eh_frame_hdr_offset_ == 0 && name == ".eh_frame_hdr") {
- offset_ptr = &eh_frame_hdr_offset_;
- size_ptr = &eh_frame_hdr_size_;
- }
- if (offset_ptr != nullptr) {
- *offset_ptr = shdr.sh_offset;
- *size_ptr = shdr.sh_size;
+ eh_frame_hdr_offset_ = shdr.sh_offset;
+ eh_frame_hdr_section_bias_ = static_cast<uint64_t>(shdr.sh_addr) - shdr.sh_offset;
+ eh_frame_hdr_size_ = shdr.sh_size;
}
}
}
@@ -642,16 +641,14 @@
}
// Instantiate all of the needed template functions.
-template void ElfInterface::InitHeadersWithTemplate<uint32_t>(uint64_t);
-template void ElfInterface::InitHeadersWithTemplate<uint64_t>(uint64_t);
+template void ElfInterface::InitHeadersWithTemplate<uint32_t>();
+template void ElfInterface::InitHeadersWithTemplate<uint64_t>();
-template bool ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(uint64_t*);
-template bool ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(uint64_t*);
+template bool ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(int64_t*);
+template bool ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(int64_t*);
-template void ElfInterface::ReadProgramHeaders<Elf32_Ehdr, Elf32_Phdr>(const Elf32_Ehdr&,
- uint64_t*);
-template void ElfInterface::ReadProgramHeaders<Elf64_Ehdr, Elf64_Phdr>(const Elf64_Ehdr&,
- uint64_t*);
+template void ElfInterface::ReadProgramHeaders<Elf32_Ehdr, Elf32_Phdr>(const Elf32_Ehdr&, int64_t*);
+template void ElfInterface::ReadProgramHeaders<Elf64_Ehdr, Elf64_Phdr>(const Elf64_Ehdr&, int64_t*);
template void ElfInterface::ReadSectionHeaders<Elf32_Ehdr, Elf32_Shdr>(const Elf32_Ehdr&);
template void ElfInterface::ReadSectionHeaders<Elf64_Ehdr, Elf64_Shdr>(const Elf64_Ehdr&);
@@ -673,8 +670,8 @@
template void ElfInterface::GetMaxSizeWithTemplate<Elf32_Ehdr>(Memory*, uint64_t*);
template void ElfInterface::GetMaxSizeWithTemplate<Elf64_Ehdr>(Memory*, uint64_t*);
-template uint64_t ElfInterface::GetLoadBias<Elf32_Ehdr, Elf32_Phdr>(Memory*);
-template uint64_t ElfInterface::GetLoadBias<Elf64_Ehdr, Elf64_Phdr>(Memory*);
+template int64_t ElfInterface::GetLoadBias<Elf32_Ehdr, Elf32_Phdr>(Memory*);
+template int64_t ElfInterface::GetLoadBias<Elf64_Ehdr, Elf64_Phdr>(Memory*);
template std::string ElfInterface::ReadBuildIDFromMemory<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr>(
Memory*);
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index 3dd5d54..76f2dc8 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -26,7 +26,7 @@
namespace unwindstack {
-bool ElfInterfaceArm::Init(uint64_t* load_bias) {
+bool ElfInterfaceArm::Init(int64_t* load_bias) {
if (!ElfInterface32::Init(load_bias)) {
return false;
}
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index 4c3a0c3..1d71cac 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -64,7 +64,7 @@
iterator begin() { return iterator(this, 0); }
iterator end() { return iterator(this, total_entries_); }
- bool Init(uint64_t* load_bias) override;
+ bool Init(int64_t* section_bias) override;
bool GetPrel31Addr(uint32_t offset, uint32_t* addr);
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 5b30a4d..f2dad84 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <stdint.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
@@ -263,8 +264,8 @@
}
uint64_t MapInfo::GetLoadBias(const std::shared_ptr<Memory>& process_memory) {
- uint64_t cur_load_bias = load_bias.load();
- if (cur_load_bias != static_cast<uint64_t>(-1)) {
+ int64_t cur_load_bias = load_bias.load();
+ if (cur_load_bias != INT64_MAX) {
return cur_load_bias;
}
diff --git a/libunwindstack/include/unwindstack/DwarfMemory.h b/libunwindstack/include/unwindstack/DwarfMemory.h
index 8dd8d2b..c45699a 100644
--- a/libunwindstack/include/unwindstack/DwarfMemory.h
+++ b/libunwindstack/include/unwindstack/DwarfMemory.h
@@ -49,8 +49,8 @@
uint64_t cur_offset() { return cur_offset_; }
void set_cur_offset(uint64_t cur_offset) { cur_offset_ = cur_offset; }
- void set_pc_offset(uint64_t offset) { pc_offset_ = offset; }
- void clear_pc_offset() { pc_offset_ = static_cast<uint64_t>(-1); }
+ void set_pc_offset(int64_t offset) { pc_offset_ = offset; }
+ void clear_pc_offset() { pc_offset_ = INT64_MAX; }
void set_data_offset(uint64_t offset) { data_offset_ = offset; }
void clear_data_offset() { data_offset_ = static_cast<uint64_t>(-1); }
@@ -65,7 +65,7 @@
Memory* memory_;
uint64_t cur_offset_ = 0;
- uint64_t pc_offset_ = static_cast<uint64_t>(-1);
+ int64_t pc_offset_ = INT64_MAX;
uint64_t data_offset_ = static_cast<uint64_t>(-1);
uint64_t func_offset_ = static_cast<uint64_t>(-1);
uint64_t text_offset_ = static_cast<uint64_t>(-1);
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
index e9942de..c244749 100644
--- a/libunwindstack/include/unwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -86,7 +86,7 @@
DwarfErrorCode LastErrorCode() { return last_error_.code; }
uint64_t LastErrorAddress() { return last_error_.address; }
- virtual bool Init(uint64_t offset, uint64_t size, uint64_t load_bias) = 0;
+ virtual bool Init(uint64_t offset, uint64_t size, int64_t section_bias) = 0;
virtual bool Eval(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*) = 0;
@@ -125,10 +125,16 @@
DwarfSectionImpl(Memory* memory) : DwarfSection(memory) {}
virtual ~DwarfSectionImpl() = default;
+ bool Init(uint64_t offset, uint64_t size, int64_t section_bias) override;
+
const DwarfCie* GetCieFromOffset(uint64_t offset);
const DwarfFde* GetFdeFromOffset(uint64_t offset);
+ const DwarfFde* GetFdeFromPc(uint64_t pc) override;
+
+ void GetFdes(std::vector<const DwarfFde*>* fdes) override;
+
bool EvalRegister(const DwarfLocation* loc, uint32_t reg, AddressType* reg_ptr, void* info);
bool Eval(const DwarfCie* cie, Memory* regular_memory, const dwarf_loc_regs_t& loc_regs,
@@ -139,6 +145,8 @@
bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) override;
protected:
+ bool GetNextCieOrFde(const DwarfFde** fde_entry);
+
bool FillInCieHeader(DwarfCie* cie);
bool FillInCie(DwarfCie* cie);
@@ -150,43 +158,13 @@
bool EvalExpression(const DwarfLocation& loc, Memory* regular_memory, AddressType* value,
RegsInfo<AddressType>* regs_info, bool* is_dex_pc);
- uint64_t load_bias_ = 0;
- uint64_t entries_offset_ = 0;
- uint64_t entries_end_ = 0;
- uint64_t pc_offset_ = 0;
-};
-
-template <typename AddressType>
-class DwarfSectionImplNoHdr : public DwarfSectionImpl<AddressType> {
- public:
- // Add these so that the protected members of DwarfSectionImpl
- // can be accessed without needing a this->.
- using DwarfSectionImpl<AddressType>::memory_;
- using DwarfSectionImpl<AddressType>::pc_offset_;
- using DwarfSectionImpl<AddressType>::entries_offset_;
- using DwarfSectionImpl<AddressType>::entries_end_;
- using DwarfSectionImpl<AddressType>::last_error_;
- using DwarfSectionImpl<AddressType>::load_bias_;
- using DwarfSectionImpl<AddressType>::cie_entries_;
- using DwarfSectionImpl<AddressType>::fde_entries_;
- using DwarfSectionImpl<AddressType>::cie32_value_;
- using DwarfSectionImpl<AddressType>::cie64_value_;
-
- DwarfSectionImplNoHdr(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
- virtual ~DwarfSectionImplNoHdr() = default;
-
- bool Init(uint64_t offset, uint64_t size, uint64_t load_bias) override;
-
- const DwarfFde* GetFdeFromPc(uint64_t pc) override;
-
- void GetFdes(std::vector<const DwarfFde*>* fdes) override;
-
- protected:
- bool GetNextCieOrFde(DwarfFde** fde_entry);
-
void InsertFde(const DwarfFde* fde);
+ int64_t section_bias_ = 0;
+ uint64_t entries_offset_ = 0;
+ uint64_t entries_end_ = 0;
uint64_t next_entries_offset_ = 0;
+ uint64_t pc_offset_ = 0;
std::map<uint64_t, std::pair<uint64_t, const DwarfFde*>> fdes_;
};
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index 56bf318..fc3f2a6 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -75,7 +75,7 @@
std::string GetBuildID();
- uint64_t GetLoadBias() { return load_bias_; }
+ int64_t GetLoadBias() { return load_bias_; }
bool IsValidPc(uint64_t pc);
@@ -101,7 +101,7 @@
static bool GetInfo(Memory* memory, uint64_t* size);
- static uint64_t GetLoadBias(Memory* memory);
+ static int64_t GetLoadBias(Memory* memory);
static std::string GetBuildID(Memory* memory);
@@ -116,7 +116,7 @@
protected:
bool valid_ = false;
- uint64_t load_bias_ = 0;
+ int64_t load_bias_ = 0;
std::unique_ptr<ElfInterface> interface_;
std::unique_ptr<Memory> memory_;
uint32_t machine_type_;
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index dbd917d..ae9bd9a 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -52,9 +52,9 @@
ElfInterface(Memory* memory) : memory_(memory) {}
virtual ~ElfInterface();
- virtual bool Init(uint64_t* load_bias) = 0;
+ virtual bool Init(int64_t* load_bias) = 0;
- virtual void InitHeaders(uint64_t load_bias) = 0;
+ virtual void InitHeaders() = 0;
virtual std::string GetSoname() = 0;
@@ -80,10 +80,13 @@
uint64_t dynamic_vaddr() { return dynamic_vaddr_; }
uint64_t dynamic_size() { return dynamic_size_; }
uint64_t eh_frame_hdr_offset() { return eh_frame_hdr_offset_; }
+ int64_t eh_frame_hdr_section_bias() { return eh_frame_hdr_section_bias_; }
uint64_t eh_frame_hdr_size() { return eh_frame_hdr_size_; }
uint64_t eh_frame_offset() { return eh_frame_offset_; }
+ int64_t eh_frame_section_bias() { return eh_frame_section_bias_; }
uint64_t eh_frame_size() { return eh_frame_size_; }
uint64_t debug_frame_offset() { return debug_frame_offset_; }
+ int64_t debug_frame_section_bias() { return debug_frame_section_bias_; }
uint64_t debug_frame_size() { return debug_frame_size_; }
uint64_t gnu_debugdata_offset() { return gnu_debugdata_offset_; }
uint64_t gnu_debugdata_size() { return gnu_debugdata_size_; }
@@ -98,20 +101,20 @@
uint64_t LastErrorAddress() { return last_error_.address; }
template <typename EhdrType, typename PhdrType>
- static uint64_t GetLoadBias(Memory* memory);
+ static int64_t GetLoadBias(Memory* memory);
template <typename EhdrType, typename ShdrType, typename NhdrType>
static std::string ReadBuildIDFromMemory(Memory* memory);
protected:
template <typename AddressType>
- void InitHeadersWithTemplate(uint64_t load_bias);
+ void InitHeadersWithTemplate();
template <typename EhdrType, typename PhdrType, typename ShdrType>
- bool ReadAllHeaders(uint64_t* load_bias);
+ bool ReadAllHeaders(int64_t* load_bias);
template <typename EhdrType, typename PhdrType>
- void ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias);
+ void ReadProgramHeaders(const EhdrType& ehdr, int64_t* load_bias);
template <typename EhdrType, typename ShdrType>
void ReadSectionHeaders(const EhdrType& ehdr);
@@ -142,12 +145,15 @@
uint64_t dynamic_size_ = 0;
uint64_t eh_frame_hdr_offset_ = 0;
+ int64_t eh_frame_hdr_section_bias_ = 0;
uint64_t eh_frame_hdr_size_ = 0;
uint64_t eh_frame_offset_ = 0;
+ int64_t eh_frame_section_bias_ = 0;
uint64_t eh_frame_size_ = 0;
uint64_t debug_frame_offset_ = 0;
+ int64_t debug_frame_section_bias_ = 0;
uint64_t debug_frame_size_ = 0;
uint64_t gnu_debugdata_offset_ = 0;
@@ -175,13 +181,11 @@
ElfInterface32(Memory* memory) : ElfInterface(memory) {}
virtual ~ElfInterface32() = default;
- bool Init(uint64_t* load_bias) override {
+ bool Init(int64_t* load_bias) override {
return ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(load_bias);
}
- void InitHeaders(uint64_t load_bias) override {
- ElfInterface::InitHeadersWithTemplate<uint32_t>(load_bias);
- }
+ void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint32_t>(); }
std::string GetSoname() override { return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(); }
@@ -205,13 +209,11 @@
ElfInterface64(Memory* memory) : ElfInterface(memory) {}
virtual ~ElfInterface64() = default;
- bool Init(uint64_t* load_bias) override {
+ bool Init(int64_t* load_bias) override {
return ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(load_bias);
}
- void InitHeaders(uint64_t load_bias) override {
- ElfInterface::InitHeadersWithTemplate<uint64_t>(load_bias);
- }
+ void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint64_t>(); }
std::string GetSoname() override { return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(); }
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index 6c5cfc4..8f0c516 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -39,7 +39,7 @@
flags(flags),
name(name),
prev_map(map_info),
- load_bias(static_cast<uint64_t>(-1)),
+ load_bias(INT64_MAX),
build_id(0) {}
MapInfo(MapInfo* map_info, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
const std::string& name)
@@ -49,7 +49,7 @@
flags(flags),
name(name),
prev_map(map_info),
- load_bias(static_cast<uint64_t>(-1)),
+ load_bias(INT64_MAX),
build_id(0) {}
~MapInfo();
@@ -72,7 +72,7 @@
MapInfo* prev_map = nullptr;
- std::atomic_uint64_t load_bias;
+ std::atomic_int64_t load_bias;
// This is a pointer to a new'd std::string.
// Using an atomic value means that we don't need to lock and will
diff --git a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
index 78608e3..768a808 100644
--- a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
@@ -36,10 +36,8 @@
~TestDwarfEhFrameWithHdr() = default;
void TestSetTableEncoding(uint8_t encoding) { this->table_encoding_ = encoding; }
- void TestSetEntriesOffset(uint64_t offset) { this->entries_offset_ = offset; }
- void TestSetEntriesEnd(uint64_t end) { this->entries_end_ = end; }
- void TestSetEntriesDataOffset(uint64_t offset) { this->entries_data_offset_ = offset; }
- void TestSetCurEntriesOffset(uint64_t offset) { this->cur_entries_offset_ = offset; }
+ void TestSetHdrEntriesOffset(uint64_t offset) { this->hdr_entries_offset_ = offset; }
+ void TestSetHdrEntriesDataOffset(uint64_t offset) { this->hdr_entries_data_offset_ = offset; }
void TestSetTableEntrySize(size_t size) { this->table_entry_size_ = size; }
void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; }
@@ -48,15 +46,11 @@
}
uint8_t TestGetVersion() { return this->version_; }
- uint8_t TestGetPtrEncoding() { return this->ptr_encoding_; }
- uint64_t TestGetPtrOffset() { return this->ptr_offset_; }
uint8_t TestGetTableEncoding() { return this->table_encoding_; }
uint64_t TestGetTableEntrySize() { return this->table_entry_size_; }
uint64_t TestGetFdeCount() { return this->fde_count_; }
- uint64_t TestGetEntriesOffset() { return this->entries_offset_; }
- uint64_t TestGetEntriesEnd() { return this->entries_end_; }
- uint64_t TestGetEntriesDataOffset() { return this->entries_data_offset_; }
- uint64_t TestGetCurEntriesOffset() { return this->cur_entries_offset_; }
+ uint64_t TestGetHdrEntriesOffset() { return this->hdr_entries_offset_; }
+ uint64_t TestGetHdrEntriesDataOffset() { return this->hdr_entries_data_offset_; }
};
template <typename TypeParam>
@@ -85,15 +79,11 @@
ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0));
EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
- EXPECT_EQ(DW_EH_PE_udata2, this->eh_frame_->TestGetPtrEncoding());
EXPECT_EQ(DW_EH_PE_sdata4, this->eh_frame_->TestGetTableEncoding());
EXPECT_EQ(4U, this->eh_frame_->TestGetTableEntrySize());
EXPECT_EQ(126U, this->eh_frame_->TestGetFdeCount());
- EXPECT_EQ(0x500U, this->eh_frame_->TestGetPtrOffset());
- EXPECT_EQ(0x100aU, this->eh_frame_->TestGetEntriesOffset());
- EXPECT_EQ(0x1100U, this->eh_frame_->TestGetEntriesEnd());
- EXPECT_EQ(0x1000U, this->eh_frame_->TestGetEntriesDataOffset());
- EXPECT_EQ(0x100aU, this->eh_frame_->TestGetCurEntriesOffset());
+ EXPECT_EQ(0x100aU, this->eh_frame_->TestGetHdrEntriesOffset());
+ EXPECT_EQ(0x1000U, this->eh_frame_->TestGetHdrEntriesDataOffset());
// Verify a zero table entry size fails to init.
this->memory_.SetData8(0x1003, 0x1);
@@ -137,17 +127,14 @@
this->memory_.SetData32(0x140c, 0x200);
this->memory_.SetData16(0x1410, 0);
+ ASSERT_TRUE(this->eh_frame_->EhFrameInit(0x1300, 0x200, 0x2000));
ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0x2000));
EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
- EXPECT_EQ(DW_EH_PE_udata2, this->eh_frame_->TestGetPtrEncoding());
EXPECT_EQ(0x1b, this->eh_frame_->TestGetTableEncoding());
EXPECT_EQ(4U, this->eh_frame_->TestGetTableEntrySize());
EXPECT_EQ(1U, this->eh_frame_->TestGetFdeCount());
- EXPECT_EQ(0x500U, this->eh_frame_->TestGetPtrOffset());
- EXPECT_EQ(0x100aU, this->eh_frame_->TestGetEntriesOffset());
- EXPECT_EQ(0x1100U, this->eh_frame_->TestGetEntriesEnd());
- EXPECT_EQ(0x1000U, this->eh_frame_->TestGetEntriesDataOffset());
- EXPECT_EQ(0x100aU, this->eh_frame_->TestGetCurEntriesOffset());
+ EXPECT_EQ(0x100aU, this->eh_frame_->TestGetHdrEntriesOffset());
+ EXPECT_EQ(0x1000U, this->eh_frame_->TestGetHdrEntriesDataOffset());
const DwarfFde* fde = this->eh_frame_->GetFdeFromPc(0x4600);
ASSERT_TRUE(fde != nullptr);
@@ -155,6 +142,115 @@
EXPECT_EQ(0x4700U, fde->pc_end);
}
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, Init_non_zero_load_bias_different_from_eh_frame_bias) {
+ this->memory_.SetMemory(0x1000, std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata4,
+ DW_EH_PE_pcrel | DW_EH_PE_sdata4});
+ this->memory_.SetData16(0x1004, 0x500);
+ this->memory_.SetData32(0x1006, 1);
+ this->memory_.SetData32(0x100a, 0x2500);
+ this->memory_.SetData32(0x100e, 0x1400);
+
+ // CIE 32 information.
+ this->memory_.SetData32(0x1300, 0xfc);
+ this->memory_.SetData32(0x1304, 0);
+ this->memory_.SetMemory(0x1308, std::vector<uint8_t>{1, 'z', 'R', '\0', 0, 0, 0, 0, 0x1b});
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x1400, 0xfc);
+ this->memory_.SetData32(0x1404, 0x104);
+ this->memory_.SetData32(0x1408, 0x20f8);
+ this->memory_.SetData32(0x140c, 0x200);
+ this->memory_.SetData16(0x1410, 0);
+
+ ASSERT_TRUE(this->eh_frame_->EhFrameInit(0x1300, 0x200, 0x1000));
+ ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0x2000));
+ EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
+ EXPECT_EQ(0x1b, this->eh_frame_->TestGetTableEncoding());
+ EXPECT_EQ(4U, this->eh_frame_->TestGetTableEntrySize());
+ EXPECT_EQ(1U, this->eh_frame_->TestGetFdeCount());
+ EXPECT_EQ(0x100aU, this->eh_frame_->TestGetHdrEntriesOffset());
+ EXPECT_EQ(0x1000U, this->eh_frame_->TestGetHdrEntriesDataOffset());
+
+ const DwarfFde* fde = this->eh_frame_->GetFdeFromPc(0x4600);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x4500U, fde->pc_start);
+ EXPECT_EQ(0x4700U, fde->pc_end);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeFromPc_wtih_empty_fde) {
+ this->memory_.SetMemory(0x1000, std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata4,
+ DW_EH_PE_pcrel | DW_EH_PE_sdata4});
+ this->memory_.SetData16(0x1004, 0x500);
+ this->memory_.SetData32(0x1006, 1);
+ this->memory_.SetData32(0x100a, 0x2500);
+ this->memory_.SetData32(0x100e, 0x1400);
+
+ // CIE 32 information.
+ this->memory_.SetData32(0x1300, 0xfc);
+ this->memory_.SetData32(0x1304, 0);
+ this->memory_.SetMemory(0x1308, std::vector<uint8_t>{1, 'z', 'R', '\0', 0, 0, 0, 0, 0x1b});
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x1400, 0xfc);
+ this->memory_.SetData32(0x1404, 0x104);
+ this->memory_.SetData32(0x1408, 0x30f8);
+ this->memory_.SetData32(0x140c, 0);
+ this->memory_.SetData16(0x1410, 0);
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x1500, 0xfc);
+ this->memory_.SetData32(0x1504, 0x204);
+ this->memory_.SetData32(0x1508, 0x2ff8);
+ this->memory_.SetData32(0x150c, 0x200);
+ this->memory_.SetData16(0x1510, 0);
+
+ ASSERT_TRUE(this->eh_frame_->EhFrameInit(0x1300, 0x300, 0));
+ ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0));
+
+ const DwarfFde* fde = this->eh_frame_->GetFdeFromPc(0x4600);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x4500U, fde->pc_start);
+ EXPECT_EQ(0x4700U, fde->pc_end);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdes_with_empty_fde) {
+ this->memory_.SetMemory(0x1000, std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata4,
+ DW_EH_PE_pcrel | DW_EH_PE_sdata4});
+ this->memory_.SetData16(0x1004, 0x500);
+ this->memory_.SetData32(0x1006, 1);
+ this->memory_.SetData32(0x100a, 0x2500);
+ this->memory_.SetData32(0x100e, 0x1400);
+
+ // CIE 32 information.
+ this->memory_.SetData32(0x1300, 0xfc);
+ this->memory_.SetData32(0x1304, 0);
+ this->memory_.SetMemory(0x1308, std::vector<uint8_t>{1, 'z', 'R', '\0', 0, 0, 0, 0, 0x1b});
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x1400, 0xfc);
+ this->memory_.SetData32(0x1404, 0x104);
+ this->memory_.SetData32(0x1408, 0x30f8);
+ this->memory_.SetData32(0x140c, 0);
+ this->memory_.SetData16(0x1410, 0);
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x1500, 0xfc);
+ this->memory_.SetData32(0x1504, 0x204);
+ this->memory_.SetData32(0x1508, 0x2ff8);
+ this->memory_.SetData32(0x150c, 0x200);
+ this->memory_.SetData16(0x1510, 0);
+
+ ASSERT_TRUE(this->eh_frame_->EhFrameInit(0x1300, 0x300, 0));
+ ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0));
+
+ std::vector<const DwarfFde*> fdes;
+ this->eh_frame_->GetFdes(&fdes);
+ ASSERT_FALSE(fdes.empty());
+ ASSERT_EQ(1U, fdes.size());
+ EXPECT_EQ(0x4500U, fdes[0]->pc_start);
+ EXPECT_EQ(0x4700U, fdes[0]->pc_end);
+}
+
TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdes) {
this->memory_.SetMemory(
0x1000, std::vector<uint8_t>{1, DW_EH_PE_udata2, DW_EH_PE_udata4, DW_EH_PE_sdata4});
@@ -220,7 +316,7 @@
TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_expect_cache_fail) {
this->eh_frame_->TestSetTableEntrySize(0x10);
this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
- this->eh_frame_->TestSetEntriesOffset(0x1000);
+ this->eh_frame_->TestSetHdrEntriesOffset(0x1000);
ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr);
ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->LastErrorCode());
@@ -233,8 +329,8 @@
// We are assuming that pc rel, is really relative to the load_bias.
TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_read_pcrel) {
this->eh_frame_->TestSetTableEncoding(DW_EH_PE_pcrel | DW_EH_PE_udata4);
- this->eh_frame_->TestSetEntriesOffset(0x1000);
- this->eh_frame_->TestSetEntriesDataOffset(0x3000);
+ this->eh_frame_->TestSetHdrEntriesOffset(0x1000);
+ this->eh_frame_->TestSetHdrEntriesDataOffset(0x3000);
this->eh_frame_->TestSetTableEntrySize(0x10);
this->memory_.SetData32(0x1040, 0x340);
@@ -248,8 +344,8 @@
TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_read_datarel) {
this->eh_frame_->TestSetTableEncoding(DW_EH_PE_datarel | DW_EH_PE_udata4);
- this->eh_frame_->TestSetEntriesOffset(0x1000);
- this->eh_frame_->TestSetEntriesDataOffset(0x3000);
+ this->eh_frame_->TestSetHdrEntriesOffset(0x1000);
+ this->eh_frame_->TestSetHdrEntriesDataOffset(0x3000);
this->eh_frame_->TestSetTableEntrySize(0x10);
this->memory_.SetData32(0x1040, 0x340);
@@ -263,7 +359,7 @@
TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_cached) {
this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
- this->eh_frame_->TestSetEntriesOffset(0x1000);
+ this->eh_frame_->TestSetHdrEntriesOffset(0x1000);
this->eh_frame_->TestSetTableEntrySize(0x10);
this->memory_.SetData32(0x1040, 0x340);
@@ -446,7 +542,9 @@
ASSERT_EQ(nullptr, this->eh_frame_->GetFdeFromPc(0x800));
}
-REGISTER_TYPED_TEST_SUITE_P(DwarfEhFrameWithHdrTest, Init, Init_non_zero_load_bias, GetFdes,
+REGISTER_TYPED_TEST_SUITE_P(DwarfEhFrameWithHdrTest, Init, Init_non_zero_load_bias,
+ Init_non_zero_load_bias_different_from_eh_frame_bias,
+ GetFdeFromPc_wtih_empty_fde, GetFdes_with_empty_fde, GetFdes,
GetFdeInfoFromIndex_expect_cache_fail, GetFdeInfoFromIndex_read_pcrel,
GetFdeInfoFromIndex_read_datarel, GetFdeInfoFromIndex_cached,
GetFdeOffsetFromPc_verify, GetFdeOffsetFromPc_index_fail,
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index b386ef4..a9d6dad 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -35,7 +35,7 @@
TestDwarfSectionImpl(Memory* memory) : DwarfSectionImpl<TypeParam>(memory) {}
virtual ~TestDwarfSectionImpl() = default;
- bool Init(uint64_t, uint64_t, uint64_t) override { return false; }
+ bool Init(uint64_t, uint64_t, int64_t) override { return false; }
void GetFdes(std::vector<const DwarfFde*>*) override {}
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
index d754fcc..953dc75 100644
--- a/libunwindstack/tests/DwarfSectionTest.cpp
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -30,23 +30,24 @@
MockDwarfSection(Memory* memory) : DwarfSection(memory) {}
virtual ~MockDwarfSection() = default;
- MOCK_METHOD3(Init, bool(uint64_t, uint64_t, uint64_t));
+ MOCK_METHOD(bool, Init, (uint64_t, uint64_t, int64_t), (override));
- MOCK_METHOD5(Eval, bool(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*));
+ MOCK_METHOD(bool, Eval, (const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*),
+ (override));
- MOCK_METHOD3(Log, bool(uint8_t, uint64_t, const DwarfFde*));
+ MOCK_METHOD(bool, Log, (uint8_t, uint64_t, const DwarfFde*), (override));
- MOCK_METHOD1(GetFdes, void(std::vector<const DwarfFde*>*));
+ MOCK_METHOD(void, GetFdes, (std::vector<const DwarfFde*>*), (override));
- MOCK_METHOD1(GetFdeFromPc, const DwarfFde*(uint64_t));
+ MOCK_METHOD(const DwarfFde*, GetFdeFromPc, (uint64_t), (override));
- MOCK_METHOD3(GetCfaLocationInfo, bool(uint64_t, const DwarfFde*, dwarf_loc_regs_t*));
+ MOCK_METHOD(bool, GetCfaLocationInfo, (uint64_t, const DwarfFde*, dwarf_loc_regs_t*), (override));
- MOCK_METHOD1(GetCieOffsetFromFde32, uint64_t(uint32_t));
+ MOCK_METHOD(uint64_t, GetCieOffsetFromFde32, (uint32_t), (override));
- MOCK_METHOD1(GetCieOffsetFromFde64, uint64_t(uint64_t));
+ MOCK_METHOD(uint64_t, GetCieOffsetFromFde64, (uint64_t), (override));
- MOCK_METHOD1(AdjustPcFromFde, uint64_t(uint64_t));
+ MOCK_METHOD(uint64_t, AdjustPcFromFde, (uint64_t), (override));
};
class DwarfSectionTest : public ::testing::Test {
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index bd3083c..832e64a 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -66,8 +66,8 @@
ElfInterfaceFake(Memory* memory) : ElfInterface(memory) {}
virtual ~ElfInterfaceFake() = default;
- bool Init(uint64_t*) override { return false; }
- void InitHeaders(uint64_t) override {}
+ bool Init(int64_t*) override { return false; }
+ void InitHeaders() override {}
std::string GetSoname() override { return fake_soname_; }
bool GetFunctionName(uint64_t, std::string*, uint64_t*) override;
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index 5b2036b..ea27e3e 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -112,6 +112,21 @@
template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
void InitSectionHeadersOffsets();
+ template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+ void InitSectionHeadersOffsetsEhFrameSectionBias(uint64_t addr, uint64_t offset,
+ int64_t expected_bias);
+
+ template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+ void InitSectionHeadersOffsetsEhFrameHdrSectionBias(uint64_t addr, uint64_t offset,
+ int64_t expected_bias);
+
+ template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+ void InitSectionHeadersOffsetsDebugFrameSectionBias(uint64_t addr, uint64_t offset,
+ int64_t expected_bias);
+
+ template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
+ void CheckGnuEhFrame(uint64_t addr, uint64_t offset, int64_t expected_bias);
+
template <typename Sym>
void InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset,
uint64_t sym_offset, const char* name);
@@ -132,10 +147,10 @@
void BuildIDSectionTooSmallForHeader();
template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
- void CheckLoadBiasInFirstPhdr(uint64_t load_bias);
+ void CheckLoadBiasInFirstPhdr(int64_t load_bias);
template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
- void CheckLoadBiasInFirstExecPhdr(uint64_t offset, uint64_t vaddr, uint64_t load_bias);
+ void CheckLoadBiasInFirstExecPhdr(uint64_t offset, uint64_t vaddr, int64_t load_bias);
MemoryFake memory_;
};
@@ -172,9 +187,9 @@
phdr.p_align = 0x1000;
memory_.SetMemory(0x100, &phdr, sizeof(phdr));
- uint64_t load_bias = 0;
+ int64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
- EXPECT_EQ(0x2000U, load_bias);
+ EXPECT_EQ(0x2000, load_bias);
const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
ASSERT_EQ(1U, pt_loads.size());
@@ -184,11 +199,11 @@
ASSERT_EQ(0x10000U, load_data.table_size);
}
-TEST_F(ElfInterfaceTest, elf32_single_pt_load) {
+TEST_F(ElfInterfaceTest, single_pt_load_32) {
SinglePtLoad<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
}
-TEST_F(ElfInterfaceTest, elf64_single_pt_load) {
+TEST_F(ElfInterfaceTest, single_pt_load_64) {
SinglePtLoad<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
}
@@ -228,9 +243,9 @@
phdr.p_align = 0x1002;
memory_.SetMemory(0x100 + 2 * sizeof(phdr), &phdr, sizeof(phdr));
- uint64_t load_bias = 0;
+ int64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
- EXPECT_EQ(0x2000U, load_bias);
+ EXPECT_EQ(0x2000, load_bias);
const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
ASSERT_EQ(3U, pt_loads.size());
@@ -251,11 +266,11 @@
ASSERT_EQ(0x10002U, load_data.table_size);
}
-TEST_F(ElfInterfaceTest, elf32_multiple_executable_pt_loads) {
+TEST_F(ElfInterfaceTest, multiple_executable_pt_loads_32) {
MultipleExecutablePtLoads<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
}
-TEST_F(ElfInterfaceTest, elf64_multiple_executable_pt_loads) {
+TEST_F(ElfInterfaceTest, multiple_executable_pt_loads_64) {
MultipleExecutablePtLoads<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
}
@@ -295,9 +310,9 @@
phdr.p_align = 0x1002;
memory_.SetMemory(0x100 + 2 * (sizeof(phdr) + 100), &phdr, sizeof(phdr));
- uint64_t load_bias = 0;
+ int64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
- EXPECT_EQ(0x2000U, load_bias);
+ EXPECT_EQ(0x2000, load_bias);
const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
ASSERT_EQ(3U, pt_loads.size());
@@ -318,12 +333,12 @@
ASSERT_EQ(0x10002U, load_data.table_size);
}
-TEST_F(ElfInterfaceTest, elf32_multiple_executable_pt_loads_increments_not_size_of_phdr) {
+TEST_F(ElfInterfaceTest, multiple_executable_pt_loads_increments_not_size_of_phdr_32) {
MultipleExecutablePtLoadsIncrementsNotSizeOfPhdr<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn,
ElfInterface32>();
}
-TEST_F(ElfInterfaceTest, elf64_multiple_executable_pt_loads_increments_not_size_of_phdr) {
+TEST_F(ElfInterfaceTest, multiple_executable_pt_loads_increments_not_size_of_phdr_64) {
MultipleExecutablePtLoadsIncrementsNotSizeOfPhdr<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn,
ElfInterface64>();
}
@@ -364,9 +379,9 @@
phdr.p_align = 0x1002;
memory_.SetMemory(0x100 + 2 * sizeof(phdr), &phdr, sizeof(phdr));
- uint64_t load_bias = 0;
+ int64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
- EXPECT_EQ(0x1001U, load_bias);
+ EXPECT_EQ(0x1001, load_bias);
const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
ASSERT_EQ(1U, pt_loads.size());
@@ -377,11 +392,11 @@
ASSERT_EQ(0x10001U, load_data.table_size);
}
-TEST_F(ElfInterfaceTest, elf32_non_executable_pt_loads) {
+TEST_F(ElfInterfaceTest, non_executable_pt_loads_32) {
NonExecutablePtLoads<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
}
-TEST_F(ElfInterfaceTest, elf64_non_executable_pt_loads) {
+TEST_F(ElfInterfaceTest, non_executable_pt_loads_64) {
NonExecutablePtLoads<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
}
@@ -434,11 +449,10 @@
memset(&phdr, 0, sizeof(phdr));
phdr.p_type = PT_GNU_EH_FRAME;
memory_.SetMemory(phdr_offset, &phdr, sizeof(phdr));
- phdr_offset += sizeof(phdr);
- uint64_t load_bias = 0;
+ int64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
- EXPECT_EQ(0x2000U, load_bias);
+ EXPECT_EQ(0x2000, load_bias);
const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
ASSERT_EQ(1U, pt_loads.size());
@@ -449,15 +463,15 @@
ASSERT_EQ(0x10000U, load_data.table_size);
}
-TEST_F(ElfInterfaceTest, elf32_many_phdrs) {
+TEST_F(ElfInterfaceTest, many_phdrs_32) {
ElfInterfaceTest::ManyPhdrs<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
}
-TEST_F(ElfInterfaceTest, elf64_many_phdrs) {
+TEST_F(ElfInterfaceTest, many_phdrs_64) {
ElfInterfaceTest::ManyPhdrs<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
}
-TEST_F(ElfInterfaceTest, elf32_arm) {
+TEST_F(ElfInterfaceTest, arm32) {
ElfInterfaceArm elf_arm(&memory_);
Elf32_Ehdr ehdr = {};
@@ -476,9 +490,9 @@
memory_.SetData32(0x2000, 0x1000);
memory_.SetData32(0x2008, 0x1000);
- uint64_t load_bias = 0;
+ int64_t load_bias = 0;
ASSERT_TRUE(elf_arm.Init(&load_bias));
- EXPECT_EQ(0U, load_bias);
+ EXPECT_EQ(0, load_bias);
std::vector<uint32_t> entries;
for (auto addr : elf_arm) {
@@ -557,19 +571,19 @@
void ElfInterfaceTest::Soname() {
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
- uint64_t load_bias = 0;
+ int64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
- EXPECT_EQ(0U, load_bias);
+ EXPECT_EQ(0, load_bias);
ASSERT_EQ("fake_soname.so", elf->GetSoname());
}
-TEST_F(ElfInterfaceTest, elf32_soname) {
+TEST_F(ElfInterfaceTest, soname_32) {
SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>();
Soname<ElfInterface32>();
}
-TEST_F(ElfInterfaceTest, elf64_soname) {
+TEST_F(ElfInterfaceTest, soname_64) {
SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>();
Soname<ElfInterface64>();
}
@@ -578,19 +592,19 @@
void ElfInterfaceTest::SonameAfterDtNull() {
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
- uint64_t load_bias = 0;
+ int64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
- EXPECT_EQ(0U, load_bias);
+ EXPECT_EQ(0, load_bias);
ASSERT_EQ("", elf->GetSoname());
}
-TEST_F(ElfInterfaceTest, elf32_soname_after_dt_null) {
+TEST_F(ElfInterfaceTest, soname_after_dt_null_32) {
SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>(SONAME_DTNULL_AFTER);
SonameAfterDtNull<ElfInterface32>();
}
-TEST_F(ElfInterfaceTest, elf64_soname_after_dt_null) {
+TEST_F(ElfInterfaceTest, soname_after_dt_null_64) {
SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>(SONAME_DTNULL_AFTER);
SonameAfterDtNull<ElfInterface64>();
}
@@ -599,19 +613,19 @@
void ElfInterfaceTest::SonameSize() {
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
- uint64_t load_bias = 0;
+ int64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
- EXPECT_EQ(0U, load_bias);
+ EXPECT_EQ(0, load_bias);
ASSERT_EQ("", elf->GetSoname());
}
-TEST_F(ElfInterfaceTest, elf32_soname_size) {
+TEST_F(ElfInterfaceTest, soname_size_32) {
SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>(SONAME_DTSIZE_SMALL);
SonameSize<ElfInterface32>();
}
-TEST_F(ElfInterfaceTest, elf64_soname_size) {
+TEST_F(ElfInterfaceTest, soname_size_64) {
SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>(SONAME_DTSIZE_SMALL);
SonameSize<ElfInterface64>();
}
@@ -622,19 +636,19 @@
void ElfInterfaceTest::SonameMissingMap() {
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
- uint64_t load_bias = 0;
+ int64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
- EXPECT_EQ(0U, load_bias);
+ EXPECT_EQ(0, load_bias);
ASSERT_EQ("", elf->GetSoname());
}
-TEST_F(ElfInterfaceTest, elf32_soname_missing_map) {
+TEST_F(ElfInterfaceTest, soname_missing_map_32) {
SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>(SONAME_MISSING_MAP);
SonameMissingMap<ElfInterface32>();
}
-TEST_F(ElfInterfaceTest, elf64_soname_missing_map) {
+TEST_F(ElfInterfaceTest, soname_missing_map_64) {
SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>(SONAME_MISSING_MAP);
SonameMissingMap<ElfInterface64>();
}
@@ -653,17 +667,17 @@
memory_.SetData32(0x10004, 0x500);
memory_.SetData32(0x10008, 250);
- elf.InitHeaders(0);
+ elf.InitHeaders();
EXPECT_FALSE(elf.eh_frame() == nullptr);
EXPECT_TRUE(elf.debug_frame() == nullptr);
}
-TEST_F(ElfInterfaceTest, init_headers_eh_frame32) {
+TEST_F(ElfInterfaceTest, init_headers_eh_frame_32) {
InitHeadersEhFrameTest<ElfInterface32Fake>();
}
-TEST_F(ElfInterfaceTest, init_headers_eh_frame64) {
+TEST_F(ElfInterfaceTest, init_headers_eh_frame_64) {
InitHeadersEhFrameTest<ElfInterface64Fake>();
}
@@ -685,17 +699,17 @@
memory_.SetData32(0x5108, 0x1500);
memory_.SetData32(0x510c, 0x200);
- elf.InitHeaders(0);
+ elf.InitHeaders();
EXPECT_TRUE(elf.eh_frame() == nullptr);
EXPECT_FALSE(elf.debug_frame() == nullptr);
}
-TEST_F(ElfInterfaceTest, init_headers_debug_frame32) {
+TEST_F(ElfInterfaceTest, init_headers_debug_frame_32) {
InitHeadersDebugFrame<ElfInterface32Fake>();
}
-TEST_F(ElfInterfaceTest, init_headers_debug_frame64) {
+TEST_F(ElfInterfaceTest, init_headers_debug_frame_64) {
InitHeadersDebugFrame<ElfInterface64Fake>();
}
@@ -709,16 +723,16 @@
ehdr.e_phentsize = sizeof(Phdr);
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
- uint64_t load_bias = 0;
+ int64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
- EXPECT_EQ(0U, load_bias);
+ EXPECT_EQ(0, load_bias);
}
-TEST_F(ElfInterfaceTest, init_program_headers_malformed32) {
+TEST_F(ElfInterfaceTest, init_program_headers_malformed_32) {
InitProgramHeadersMalformed<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>();
}
-TEST_F(ElfInterfaceTest, init_program_headers_malformed64) {
+TEST_F(ElfInterfaceTest, init_program_headers_malformed_64) {
InitProgramHeadersMalformed<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>();
}
@@ -732,16 +746,16 @@
ehdr.e_shentsize = sizeof(Shdr);
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
- uint64_t load_bias = 0;
+ int64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
- EXPECT_EQ(0U, load_bias);
+ EXPECT_EQ(0, load_bias);
}
-TEST_F(ElfInterfaceTest, init_section_headers_malformed32) {
+TEST_F(ElfInterfaceTest, init_section_headers_malformed_32) {
InitSectionHeadersMalformed<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>();
}
-TEST_F(ElfInterfaceTest, init_section_headers_malformed64) {
+TEST_F(ElfInterfaceTest, init_section_headers_malformed_64) {
InitSectionHeadersMalformed<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
}
@@ -796,11 +810,10 @@
shdr.sh_offset = 0xf000;
shdr.sh_size = 0x1000;
memory_.SetMemory(offset, &shdr, sizeof(shdr));
- offset += ehdr.e_shentsize;
- uint64_t load_bias = 0;
+ int64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
- EXPECT_EQ(0U, load_bias);
+ EXPECT_EQ(0, load_bias);
EXPECT_EQ(0U, elf->debug_frame_offset());
EXPECT_EQ(0U, elf->debug_frame_size());
EXPECT_EQ(0U, elf->gnu_debugdata_offset());
@@ -811,11 +824,11 @@
ASSERT_FALSE(elf->GetFunctionName(0x90010, &name, &name_offset));
}
-TEST_F(ElfInterfaceTest, init_section_headers_malformed_symdata32) {
+TEST_F(ElfInterfaceTest, init_section_headers_malformed_symdata_32) {
InitSectionHeadersMalformedSymData<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>();
}
-TEST_F(ElfInterfaceTest, init_section_headers_malformed_symdata64) {
+TEST_F(ElfInterfaceTest, init_section_headers_malformed_symdata_64) {
InitSectionHeadersMalformedSymData<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
}
@@ -866,14 +879,13 @@
shdr.sh_offset = 0xf000;
shdr.sh_size = 0x1000;
memory_.SetMemory(offset, &shdr, sizeof(shdr));
- offset += ehdr.e_shentsize;
InitSym<Sym>(0x5000, 0x90000, 0x1000, 0x100, 0xf000, "function_one");
InitSym<Sym>(0x6000, 0xd0000, 0x1000, 0x300, 0xf000, "function_two");
- uint64_t load_bias = 0;
+ int64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
- EXPECT_EQ(0U, load_bias);
+ EXPECT_EQ(0, load_bias);
EXPECT_EQ(0U, elf->debug_frame_offset());
EXPECT_EQ(0U, elf->debug_frame_size());
EXPECT_EQ(0U, elf->gnu_debugdata_offset());
@@ -890,19 +902,19 @@
EXPECT_EQ(32U, name_offset);
}
-TEST_F(ElfInterfaceTest, init_section_headers32) {
+TEST_F(ElfInterfaceTest, init_section_headers_32) {
InitSectionHeaders<Elf32_Ehdr, Elf32_Shdr, Elf32_Sym, ElfInterface32>(sizeof(Elf32_Shdr));
}
-TEST_F(ElfInterfaceTest, init_section_headers64) {
+TEST_F(ElfInterfaceTest, init_section_headers_64) {
InitSectionHeaders<Elf64_Ehdr, Elf64_Shdr, Elf64_Sym, ElfInterface64>(sizeof(Elf64_Shdr));
}
-TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size32) {
+TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size_32) {
InitSectionHeaders<Elf32_Ehdr, Elf32_Shdr, Elf32_Sym, ElfInterface32>(0x100);
}
-TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size64) {
+TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size_64) {
InitSectionHeaders<Elf64_Ehdr, Elf64_Shdr, Elf64_Sym, ElfInterface64>(0x100);
}
@@ -967,7 +979,7 @@
shdr.sh_type = SHT_PROGBITS;
shdr.sh_link = 2;
shdr.sh_name = 0x400;
- shdr.sh_addr = 0x6000;
+ shdr.sh_addr = 0xa000;
shdr.sh_offset = 0xa000;
shdr.sh_entsize = 0x100;
shdr.sh_size = 0xf00;
@@ -977,10 +989,10 @@
memset(&shdr, 0, sizeof(shdr));
shdr.sh_type = SHT_NOTE;
shdr.sh_name = 0x500;
+ shdr.sh_addr = 0xb000;
shdr.sh_offset = 0xb000;
shdr.sh_size = 0xf00;
memory_.SetMemory(offset, &shdr, sizeof(shdr));
- offset += ehdr.e_shentsize;
memory_.SetMemory(0xf100, ".debug_frame", sizeof(".debug_frame"));
memory_.SetMemory(0xf200, ".gnu_debugdata", sizeof(".gnu_debugdata"));
@@ -988,29 +1000,352 @@
memory_.SetMemory(0xf400, ".eh_frame_hdr", sizeof(".eh_frame_hdr"));
memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
- uint64_t load_bias = 0;
+ int64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
- EXPECT_EQ(0U, load_bias);
+ EXPECT_EQ(0, load_bias);
EXPECT_EQ(0x6000U, elf->debug_frame_offset());
+ EXPECT_EQ(0, elf->debug_frame_section_bias());
EXPECT_EQ(0x500U, elf->debug_frame_size());
+
EXPECT_EQ(0x5000U, elf->gnu_debugdata_offset());
EXPECT_EQ(0x800U, elf->gnu_debugdata_size());
+
EXPECT_EQ(0x7000U, elf->eh_frame_offset());
+ EXPECT_EQ(0, elf->eh_frame_section_bias());
EXPECT_EQ(0x800U, elf->eh_frame_size());
+
EXPECT_EQ(0xa000U, elf->eh_frame_hdr_offset());
+ EXPECT_EQ(0, elf->eh_frame_hdr_section_bias());
EXPECT_EQ(0xf00U, elf->eh_frame_hdr_size());
+
EXPECT_EQ(0xb000U, elf->gnu_build_id_offset());
EXPECT_EQ(0xf00U, elf->gnu_build_id_size());
}
-TEST_F(ElfInterfaceTest, init_section_headers_offsets32) {
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_32) {
InitSectionHeadersOffsets<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>();
}
-TEST_F(ElfInterfaceTest, init_section_headers_offsets64) {
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_64) {
InitSectionHeadersOffsets<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
}
+template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeadersOffsetsEhFrameSectionBias(uint64_t addr, uint64_t offset,
+ int64_t expected_bias) {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ uint64_t elf_offset = 0x2000;
+
+ Ehdr ehdr = {};
+ ehdr.e_shoff = elf_offset;
+ ehdr.e_shnum = 4;
+ ehdr.e_shentsize = sizeof(Shdr);
+ ehdr.e_shstrndx = 2;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ elf_offset += ehdr.e_shentsize;
+
+ Shdr shdr = {};
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_link = 2;
+ shdr.sh_name = 0x200;
+ shdr.sh_addr = 0x8000;
+ shdr.sh_offset = 0x8000;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = 0x800;
+ memory_.SetMemory(elf_offset, &shdr, sizeof(shdr));
+ elf_offset += ehdr.e_shentsize;
+
+ // The string data for section header names.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_.SetMemory(elf_offset, &shdr, sizeof(shdr));
+ elf_offset += ehdr.e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_link = 2;
+ shdr.sh_name = 0x100;
+ shdr.sh_addr = addr;
+ shdr.sh_offset = offset;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = 0x500;
+ memory_.SetMemory(elf_offset, &shdr, sizeof(shdr));
+
+ memory_.SetMemory(0xf100, ".eh_frame", sizeof(".eh_frame"));
+ memory_.SetMemory(0xf200, ".eh_frame_hdr", sizeof(".eh_frame_hdr"));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0, load_bias);
+ EXPECT_EQ(offset, elf->eh_frame_offset());
+ EXPECT_EQ(expected_bias, elf->eh_frame_section_bias());
+ EXPECT_EQ(0x500U, elf->eh_frame_size());
+
+ EXPECT_EQ(0x8000U, elf->eh_frame_hdr_offset());
+ EXPECT_EQ(0, elf->eh_frame_hdr_section_bias());
+ EXPECT_EQ(0x800U, elf->eh_frame_hdr_size());
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_section_bias_zero_32) {
+ InitSectionHeadersOffsetsEhFrameSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(0x4000,
+ 0x4000, 0);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_section_bias_zero_64) {
+ InitSectionHeadersOffsetsEhFrameSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(0x6000,
+ 0x6000, 0);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_section_bias_positive_32) {
+ InitSectionHeadersOffsetsEhFrameSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(
+ 0x5000, 0x4000, 0x1000);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_section_bias_positive_64) {
+ InitSectionHeadersOffsetsEhFrameSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(
+ 0x6000, 0x4000, 0x2000);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_section_bias_negative_32) {
+ InitSectionHeadersOffsetsEhFrameSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(
+ 0x3000, 0x4000, -0x1000);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_section_bias_negative_64) {
+ InitSectionHeadersOffsetsEhFrameSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(
+ 0x6000, 0x9000, -0x3000);
+}
+
+template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeadersOffsetsEhFrameHdrSectionBias(uint64_t addr,
+ uint64_t offset,
+ int64_t expected_bias) {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ uint64_t elf_offset = 0x2000;
+
+ Ehdr ehdr = {};
+ ehdr.e_shoff = elf_offset;
+ ehdr.e_shnum = 4;
+ ehdr.e_shentsize = sizeof(Shdr);
+ ehdr.e_shstrndx = 2;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ elf_offset += ehdr.e_shentsize;
+
+ Shdr shdr = {};
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_link = 2;
+ shdr.sh_name = 0x200;
+ shdr.sh_addr = addr;
+ shdr.sh_offset = offset;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = 0x800;
+ memory_.SetMemory(elf_offset, &shdr, sizeof(shdr));
+ elf_offset += ehdr.e_shentsize;
+
+ // The string data for section header names.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_.SetMemory(elf_offset, &shdr, sizeof(shdr));
+ elf_offset += ehdr.e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_link = 2;
+ shdr.sh_name = 0x100;
+ shdr.sh_addr = 0x5000;
+ shdr.sh_offset = 0x5000;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = 0x500;
+ memory_.SetMemory(elf_offset, &shdr, sizeof(shdr));
+
+ memory_.SetMemory(0xf100, ".eh_frame", sizeof(".eh_frame"));
+ memory_.SetMemory(0xf200, ".eh_frame_hdr", sizeof(".eh_frame_hdr"));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0, load_bias);
+ EXPECT_EQ(0x5000U, elf->eh_frame_offset());
+ EXPECT_EQ(0, elf->eh_frame_section_bias());
+ EXPECT_EQ(0x500U, elf->eh_frame_size());
+ EXPECT_EQ(offset, elf->eh_frame_hdr_offset());
+ EXPECT_EQ(expected_bias, elf->eh_frame_hdr_section_bias());
+ EXPECT_EQ(0x800U, elf->eh_frame_hdr_size());
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_hdr_section_bias_zero_32) {
+ InitSectionHeadersOffsetsEhFrameHdrSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(0x9000,
+ 0x9000, 0);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_hdr_section_bias_zero_64) {
+ InitSectionHeadersOffsetsEhFrameHdrSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(0xa000,
+ 0xa000, 0);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_hdr_section_bias_positive_32) {
+ InitSectionHeadersOffsetsEhFrameHdrSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(
+ 0x9000, 0x4000, 0x5000);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_hdr_section_bias_positive_64) {
+ InitSectionHeadersOffsetsEhFrameHdrSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(
+ 0x6000, 0x1000, 0x5000);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_hdr_section_bias_negative_32) {
+ InitSectionHeadersOffsetsEhFrameHdrSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(
+ 0x3000, 0x5000, -0x2000);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_hdr_section_bias_negative_64) {
+ InitSectionHeadersOffsetsEhFrameHdrSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(
+ 0x5000, 0x9000, -0x4000);
+}
+
+template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeadersOffsetsDebugFrameSectionBias(uint64_t addr,
+ uint64_t offset,
+ int64_t expected_bias) {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ uint64_t elf_offset = 0x2000;
+
+ Ehdr ehdr = {};
+ ehdr.e_shoff = elf_offset;
+ ehdr.e_shnum = 3;
+ ehdr.e_shentsize = sizeof(Shdr);
+ ehdr.e_shstrndx = 2;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ elf_offset += ehdr.e_shentsize;
+
+ Shdr shdr = {};
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_link = 2;
+ shdr.sh_name = 0x100;
+ shdr.sh_addr = addr;
+ shdr.sh_offset = offset;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = 0x800;
+ memory_.SetMemory(elf_offset, &shdr, sizeof(shdr));
+ elf_offset += ehdr.e_shentsize;
+
+ // The string data for section header names.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_.SetMemory(elf_offset, &shdr, sizeof(shdr));
+
+ memory_.SetMemory(0xf100, ".debug_frame", sizeof(".debug_frame"));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0, load_bias);
+ EXPECT_EQ(offset, elf->debug_frame_offset());
+ EXPECT_EQ(expected_bias, elf->debug_frame_section_bias());
+ EXPECT_EQ(0x800U, elf->debug_frame_size());
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_debug_frame_section_bias_zero_32) {
+ InitSectionHeadersOffsetsDebugFrameSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(0x5000,
+ 0x5000, 0);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_debug_frame_section_bias_zero_64) {
+ InitSectionHeadersOffsetsDebugFrameSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(0xa000,
+ 0xa000, 0);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_debug_frame_section_bias_positive_32) {
+ InitSectionHeadersOffsetsDebugFrameSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(
+ 0x5000, 0x2000, 0x3000);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_debug_frame_section_bias_positive_64) {
+ InitSectionHeadersOffsetsDebugFrameSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(
+ 0x7000, 0x1000, 0x6000);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_debug_frame_section_bias_negative_32) {
+ InitSectionHeadersOffsetsDebugFrameSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(
+ 0x6000, 0x7000, -0x1000);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_debug_frame_section_bias_negative_64) {
+ InitSectionHeadersOffsetsDebugFrameSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(
+ 0x3000, 0x5000, -0x2000);
+}
+
+template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
+void ElfInterfaceTest::CheckGnuEhFrame(uint64_t addr, uint64_t offset, int64_t expected_bias) {
+ std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
+
+ Ehdr ehdr = {};
+ ehdr.e_phoff = 0x100;
+ ehdr.e_phnum = 2;
+ ehdr.e_phentsize = sizeof(Phdr);
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ uint64_t phdr_offset = 0x100;
+
+ Phdr phdr = {};
+ phdr.p_type = PT_LOAD;
+ phdr.p_memsz = 0x10000;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1000;
+ memory_.SetMemory(phdr_offset, &phdr, sizeof(phdr));
+ phdr_offset += sizeof(phdr);
+
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_GNU_EH_FRAME;
+ phdr.p_vaddr = addr;
+ phdr.p_offset = offset;
+ memory_.SetMemory(phdr_offset, &phdr, sizeof(phdr));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0, load_bias);
+ EXPECT_EQ(expected_bias, elf->eh_frame_hdr_section_bias());
+}
+
+TEST_F(ElfInterfaceTest, eh_frame_zero_section_bias_32) {
+ ElfInterfaceTest::CheckGnuEhFrame<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x4000, 0x4000, 0);
+}
+
+TEST_F(ElfInterfaceTest, eh_frame_zero_section_bias_64) {
+ ElfInterfaceTest::CheckGnuEhFrame<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x4000, 0x4000, 0);
+}
+
+TEST_F(ElfInterfaceTest, eh_frame_positive_section_bias_32) {
+ ElfInterfaceTest::CheckGnuEhFrame<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x4000, 0x1000, 0x3000);
+}
+
+TEST_F(ElfInterfaceTest, eh_frame_positive_section_bias_64) {
+ ElfInterfaceTest::CheckGnuEhFrame<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x4000, 0x1000, 0x3000);
+}
+
+TEST_F(ElfInterfaceTest, eh_frame_negative_section_bias_32) {
+ ElfInterfaceTest::CheckGnuEhFrame<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x4000, 0x5000,
+ -0x1000);
+}
+
+TEST_F(ElfInterfaceTest, eh_frame_negative_section_bias_64) {
+ ElfInterfaceTest::CheckGnuEhFrame<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x4000, 0x5000,
+ -0x1000);
+}
+
TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load) {
std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
@@ -1028,9 +1363,9 @@
phdr.p_align = 0x1000;
memory_.SetMemory(0x100, &phdr, sizeof(phdr));
- uint64_t load_bias = 0;
+ int64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
- EXPECT_EQ(0U, load_bias);
+ EXPECT_EQ(0, load_bias);
EXPECT_TRUE(elf->IsValidPc(0));
EXPECT_TRUE(elf->IsValidPc(0x5000));
EXPECT_TRUE(elf->IsValidPc(0xffff));
@@ -1054,9 +1389,9 @@
phdr.p_align = 0x1000;
memory_.SetMemory(0x100, &phdr, sizeof(phdr));
- uint64_t load_bias = 0;
+ int64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
- EXPECT_EQ(0x2000U, load_bias);
+ EXPECT_EQ(0x2000, load_bias);
EXPECT_FALSE(elf->IsValidPc(0));
EXPECT_FALSE(elf->IsValidPc(0x1000));
EXPECT_FALSE(elf->IsValidPc(0x1fff));
@@ -1111,10 +1446,10 @@
memory_.SetData32(0x708, 0x2100);
memory_.SetData32(0x70c, 0x200);
- uint64_t load_bias = 0;
+ int64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
- elf->InitHeaders(0);
- EXPECT_EQ(0U, load_bias);
+ elf->InitHeaders();
+ EXPECT_EQ(0, load_bias);
EXPECT_FALSE(elf->IsValidPc(0));
EXPECT_FALSE(elf->IsValidPc(0x20ff));
EXPECT_TRUE(elf->IsValidPc(0x2100));
@@ -1168,10 +1503,10 @@
memory_.SetData32(0x708, 0x20f8);
memory_.SetData32(0x70c, 0x200);
- uint64_t load_bias = 0;
+ int64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
- elf->InitHeaders(0);
- EXPECT_EQ(0U, load_bias);
+ elf->InitHeaders();
+ EXPECT_EQ(0, load_bias);
EXPECT_FALSE(elf->IsValidPc(0));
EXPECT_FALSE(elf->IsValidPc(0x27ff));
EXPECT_TRUE(elf->IsValidPc(0x2800));
@@ -1207,7 +1542,6 @@
note_offset += sizeof("GNU");
// This part of the note does not contain any trailing '\0'.
memcpy(¬e_section[note_offset], "BUILDID", 7);
- note_offset += 8;
Shdr shdr = {};
shdr.sh_type = SHT_NOTE;
@@ -1224,16 +1558,23 @@
shdr.sh_offset = 0xf000;
shdr.sh_size = 0x1000;
memory_.SetMemory(offset, &shdr, sizeof(shdr));
- offset += ehdr.e_shentsize;
memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
memory_.SetMemory(0xb000, note_section, sizeof(note_section));
- uint64_t load_bias = 0;
+ int64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
ASSERT_EQ("BUILDID", elf->GetBuildID());
}
+TEST_F(ElfInterfaceTest, build_id_32) {
+ BuildID<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_64) {
+ BuildID<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
+}
+
template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
void ElfInterfaceTest::BuildIDTwoNotes() {
std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
@@ -1272,7 +1613,6 @@
note_offset += sizeof("GNU");
// This part of the note does not contain any trailing '\0'.
memcpy(¬e_section[note_offset], "BUILDID", 7);
- note_offset += 8;
Shdr shdr = {};
shdr.sh_type = SHT_NOTE;
@@ -1289,16 +1629,23 @@
shdr.sh_offset = 0xf000;
shdr.sh_size = 0x1000;
memory_.SetMemory(offset, &shdr, sizeof(shdr));
- offset += ehdr.e_shentsize;
memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
memory_.SetMemory(0xb000, note_section, sizeof(note_section));
- uint64_t load_bias = 0;
+ int64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
ASSERT_EQ("BUILDID", elf->GetBuildID());
}
+TEST_F(ElfInterfaceTest, build_id_two_notes_32) {
+ BuildIDTwoNotes<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_two_notes_64) {
+ BuildIDTwoNotes<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
+}
+
template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
void ElfInterfaceTest::BuildIDSectionTooSmallForName () {
std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
@@ -1326,7 +1673,6 @@
note_offset += sizeof("GNU");
// This part of the note does not contain any trailing '\0'.
memcpy(¬e_section[note_offset], "BUILDID", 7);
- note_offset += 8;
Shdr shdr = {};
shdr.sh_type = SHT_NOTE;
@@ -1343,16 +1689,23 @@
shdr.sh_offset = 0xf000;
shdr.sh_size = 0x1000;
memory_.SetMemory(offset, &shdr, sizeof(shdr));
- offset += ehdr.e_shentsize;
memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
memory_.SetMemory(0xb000, note_section, sizeof(note_section));
- uint64_t load_bias = 0;
+ int64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
ASSERT_EQ("", elf->GetBuildID());
}
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_name_32) {
+ BuildIDSectionTooSmallForName<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_name_64) {
+ BuildIDSectionTooSmallForName<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
+}
+
template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
void ElfInterfaceTest::BuildIDSectionTooSmallForDesc () {
std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
@@ -1380,7 +1733,6 @@
note_offset += sizeof("GNU");
// This part of the note does not contain any trailing '\0'.
memcpy(¬e_section[note_offset], "BUILDID", 7);
- note_offset += 8;
Shdr shdr = {};
shdr.sh_type = SHT_NOTE;
@@ -1397,16 +1749,23 @@
shdr.sh_offset = 0xf000;
shdr.sh_size = 0x1000;
memory_.SetMemory(offset, &shdr, sizeof(shdr));
- offset += ehdr.e_shentsize;
memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
memory_.SetMemory(0xb000, note_section, sizeof(note_section));
- uint64_t load_bias = 0;
+ int64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
ASSERT_EQ("", elf->GetBuildID());
}
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_desc_32) {
+ BuildIDSectionTooSmallForDesc<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_desc_64) {
+ BuildIDSectionTooSmallForDesc<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
+}
+
template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
void ElfInterfaceTest::BuildIDSectionTooSmallForHeader () {
std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
@@ -1434,7 +1793,6 @@
note_offset += sizeof("GNU");
// This part of the note does not contain any trailing '\0'.
memcpy(¬e_section[note_offset], "BUILDID", 7);
- note_offset += 8;
Shdr shdr = {};
shdr.sh_type = SHT_NOTE;
@@ -1451,58 +1809,25 @@
shdr.sh_offset = 0xf000;
shdr.sh_size = 0x1000;
memory_.SetMemory(offset, &shdr, sizeof(shdr));
- offset += ehdr.e_shentsize;
memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
memory_.SetMemory(0xb000, note_section, sizeof(note_section));
- uint64_t load_bias = 0;
+ int64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
ASSERT_EQ("", elf->GetBuildID());
}
-TEST_F(ElfInterfaceTest, build_id32) {
- BuildID<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
-}
-
-TEST_F(ElfInterfaceTest, build_id64) {
- BuildID<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
-}
-
-TEST_F(ElfInterfaceTest, build_id_two_notes32) {
- BuildIDTwoNotes<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
-}
-
-TEST_F(ElfInterfaceTest, build_id_two_notes64) {
- BuildIDTwoNotes<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
-}
-
-TEST_F(ElfInterfaceTest, build_id_section_too_small_for_name32) {
- BuildIDSectionTooSmallForName<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
-}
-
-TEST_F(ElfInterfaceTest, build_id_section_too_small_for_name64) {
- BuildIDSectionTooSmallForName<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
-}
-
-TEST_F(ElfInterfaceTest, build_id_section_too_small_for_desc32) {
- BuildIDSectionTooSmallForDesc<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
-}
-
-TEST_F(ElfInterfaceTest, build_id_section_too_small_for_desc64) {
- BuildIDSectionTooSmallForDesc<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
-}
-
-TEST_F(ElfInterfaceTest, build_id_section_too_small_for_header32) {
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_header_32) {
BuildIDSectionTooSmallForHeader<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
}
-TEST_F(ElfInterfaceTest, build_id_section_too_small_for_header64) {
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_header_64) {
BuildIDSectionTooSmallForHeader<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
}
template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
-void ElfInterfaceTest::CheckLoadBiasInFirstPhdr(uint64_t load_bias) {
+void ElfInterfaceTest::CheckLoadBiasInFirstPhdr(int64_t load_bias) {
Ehdr ehdr = {};
ehdr.e_phoff = 0x100;
ehdr.e_phnum = 2;
@@ -1526,11 +1851,11 @@
phdr.p_align = 0x1000;
memory_.SetMemory(0x100 + sizeof(phdr), &phdr, sizeof(phdr));
- uint64_t static_load_bias = ElfInterface::GetLoadBias<Ehdr, Phdr>(&memory_);
+ int64_t static_load_bias = ElfInterface::GetLoadBias<Ehdr, Phdr>(&memory_);
ASSERT_EQ(load_bias, static_load_bias);
std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
- uint64_t init_load_bias = 0;
+ int64_t init_load_bias = 0;
ASSERT_TRUE(elf->Init(&init_load_bias));
ASSERT_EQ(init_load_bias, static_load_bias);
}
@@ -1553,7 +1878,7 @@
template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
void ElfInterfaceTest::CheckLoadBiasInFirstExecPhdr(uint64_t offset, uint64_t vaddr,
- uint64_t load_bias) {
+ int64_t load_bias) {
Ehdr ehdr = {};
ehdr.e_phoff = 0x100;
ehdr.e_phnum = 3;
@@ -1586,11 +1911,11 @@
phdr.p_align = 0x1000;
memory_.SetMemory(0x200 + sizeof(phdr), &phdr, sizeof(phdr));
- uint64_t static_load_bias = ElfInterface::GetLoadBias<Ehdr, Phdr>(&memory_);
+ int64_t static_load_bias = ElfInterface::GetLoadBias<Ehdr, Phdr>(&memory_);
ASSERT_EQ(load_bias, static_load_bias);
std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
- uint64_t init_load_bias = 0;
+ int64_t init_load_bias = 0;
ASSERT_TRUE(elf->Init(&init_load_bias));
ASSERT_EQ(init_load_bias, static_load_bias);
}
@@ -1603,20 +1928,20 @@
CheckLoadBiasInFirstExecPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x1000, 0x1000, 0);
}
-TEST_F(ElfInterfaceTest, get_load_bias_exec_non_zero_32) {
+TEST_F(ElfInterfaceTest, get_load_bias_exec_positive_32) {
CheckLoadBiasInFirstExecPhdr<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x1000, 0x4000, 0x3000);
}
-TEST_F(ElfInterfaceTest, get_load_bias_exec_non_zero_64) {
+TEST_F(ElfInterfaceTest, get_load_bias_exec_positive_64) {
CheckLoadBiasInFirstExecPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x1000, 0x4000, 0x3000);
}
-TEST_F(ElfInterfaceTest, get_load_bias_exec_zero_from_error_32) {
- CheckLoadBiasInFirstExecPhdr<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x5000, 0x1000, 0);
+TEST_F(ElfInterfaceTest, get_load_bias_exec_negative_32) {
+ CheckLoadBiasInFirstExecPhdr<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x5000, 0x1000, -0x4000);
}
-TEST_F(ElfInterfaceTest, get_load_bias_exec_zero_from_error_64) {
- CheckLoadBiasInFirstExecPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x5000, 0x1000, 0);
+TEST_F(ElfInterfaceTest, get_load_bias_exec_negative_64) {
+ CheckLoadBiasInFirstExecPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x5000, 0x1000, -0x4000);
}
} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index c432d6d..e6728a0 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -310,15 +310,15 @@
ElfInterfaceMock(Memory* memory) : ElfInterface(memory) {}
virtual ~ElfInterfaceMock() = default;
- bool Init(uint64_t*) override { return false; }
- void InitHeaders(uint64_t) override {}
+ bool Init(int64_t*) override { return false; }
+ void InitHeaders() override {}
std::string GetSoname() override { return ""; }
bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; }
std::string GetBuildID() override { return ""; }
- MOCK_METHOD4(Step, bool(uint64_t, Regs*, Memory*, bool*));
- MOCK_METHOD2(GetGlobalVariable, bool(const std::string&, uint64_t*));
- MOCK_METHOD1(IsValidPc, bool(uint64_t));
+ MOCK_METHOD(bool, Step, (uint64_t, Regs*, Memory*, bool*), (override));
+ MOCK_METHOD(bool, GetGlobalVariable, (const std::string&, uint64_t*), (override));
+ MOCK_METHOD(bool, IsValidPc, (uint64_t), (override));
void MockSetDynamicOffset(uint64_t offset) { dynamic_offset_ = offset; }
void MockSetDynamicVaddr(uint64_t vaddr) { dynamic_vaddr_ = vaddr; }
diff --git a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
index 2c98928..da3dbf2 100644
--- a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
+++ b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
@@ -84,7 +84,7 @@
elf_->FakeSetLoadBias(0);
EXPECT_EQ(0U, map_info_->GetLoadBias(process_memory_));
- map_info_->load_bias = static_cast<uint64_t>(-1);
+ map_info_->load_bias = INT64_MAX;
elf_->FakeSetLoadBias(0x1000);
EXPECT_EQ(0x1000U, map_info_->GetLoadBias(process_memory_));
}
diff --git a/libunwindstack/tests/MapInfoTest.cpp b/libunwindstack/tests/MapInfoTest.cpp
index e2cbb98..ef76b1b 100644
--- a/libunwindstack/tests/MapInfoTest.cpp
+++ b/libunwindstack/tests/MapInfoTest.cpp
@@ -35,7 +35,7 @@
EXPECT_EQ(3UL, map_info.offset);
EXPECT_EQ(4UL, map_info.flags);
EXPECT_EQ("map", map_info.name);
- EXPECT_EQ(static_cast<uint64_t>(-1), map_info.load_bias);
+ EXPECT_EQ(INT64_MAX, map_info.load_bias);
EXPECT_EQ(0UL, map_info.elf_offset);
EXPECT_TRUE(map_info.elf.get() == nullptr);
}
@@ -51,7 +51,7 @@
EXPECT_EQ(3UL, map_info.offset);
EXPECT_EQ(4UL, map_info.flags);
EXPECT_EQ("string_map", map_info.name);
- EXPECT_EQ(static_cast<uint64_t>(-1), map_info.load_bias);
+ EXPECT_EQ(INT64_MAX, map_info.load_bias);
EXPECT_EQ(0UL, map_info.elf_offset);
EXPECT_TRUE(map_info.elf.get() == nullptr);
}
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index bded57a..0d58c09 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -1534,4 +1534,99 @@
EXPECT_EQ(0x7ffd22415e90ULL, unwinder.frames()[16].sp);
}
+TEST_F(UnwindOfflineTest, load_bias_different_section_bias_arm64) {
+ ASSERT_NO_FATAL_FAILURE(Init("load_bias_different_section_bias_arm64/", ARCH_ARM64));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(12U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 00000000000d59bc linker64 (__dl_syscall+28)\n"
+ " #01 pc 00000000000554e8 linker64 (__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1148)\n"
+ " #02 pc 00000000000008c0 vdso (__kernel_rt_sigreturn)\n"
+ " #03 pc 000000000007f3e8 libc.so (abort+168)\n"
+ " #04 pc 00000000000459fc test (std::__ndk1::__throw_bad_cast()+4)\n"
+ " #05 pc 0000000000056d80 test (testing::Test::Run()+88)\n"
+ " #06 pc 000000000005724c test (testing::TestInfo::Run()+112)\n"
+ " #07 pc 0000000000057558 test (testing::TestSuite::Run()+116)\n"
+ " #08 pc 000000000005bffc test (testing::internal::UnitTestImpl::RunAllTests()+464)\n"
+ " #09 pc 000000000005bd9c test (testing::UnitTest::Run()+116)\n"
+ " #10 pc 00000000000464e4 test (main+144)\n"
+ " #11 pc 000000000007aa34 libc.so (__libc_init+108)\n",
+ frame_info);
+
+ EXPECT_EQ(0x7112cb99bcULL, unwinder.frames()[0].pc);
+ EXPECT_EQ(0x7112bdbbf0ULL, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x7112c394e8ULL, unwinder.frames()[1].pc);
+ EXPECT_EQ(0x7112bdbbf0ULL, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x7112be28c0ULL, unwinder.frames()[2].pc);
+ EXPECT_EQ(0x7112bdbda0ULL, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x71115ab3e8ULL, unwinder.frames()[3].pc);
+ EXPECT_EQ(0x7fdd4a3f00ULL, unwinder.frames()[3].sp);
+ EXPECT_EQ(0x5f739dc9fcULL, unwinder.frames()[4].pc);
+ EXPECT_EQ(0x7fdd4a3fe0ULL, unwinder.frames()[4].sp);
+ EXPECT_EQ(0x5f739edd80ULL, unwinder.frames()[5].pc);
+ EXPECT_EQ(0x7fdd4a3ff0ULL, unwinder.frames()[5].sp);
+ EXPECT_EQ(0x5f739ee24cULL, unwinder.frames()[6].pc);
+ EXPECT_EQ(0x7fdd4a4010ULL, unwinder.frames()[6].sp);
+ EXPECT_EQ(0x5f739ee558ULL, unwinder.frames()[7].pc);
+ EXPECT_EQ(0x7fdd4a4040ULL, unwinder.frames()[7].sp);
+ EXPECT_EQ(0x5f739f2ffcULL, unwinder.frames()[8].pc);
+ EXPECT_EQ(0x7fdd4a4070ULL, unwinder.frames()[8].sp);
+ EXPECT_EQ(0x5f739f2d9cULL, unwinder.frames()[9].pc);
+ EXPECT_EQ(0x7fdd4a4100ULL, unwinder.frames()[9].sp);
+ EXPECT_EQ(0x5f739dd4e4ULL, unwinder.frames()[10].pc);
+ EXPECT_EQ(0x7fdd4a4130ULL, unwinder.frames()[10].sp);
+ EXPECT_EQ(0x71115a6a34ULL, unwinder.frames()[11].pc);
+ EXPECT_EQ(0x7fdd4a4170ULL, unwinder.frames()[11].sp);
+}
+
+TEST_F(UnwindOfflineTest, eh_frame_bias_x86) {
+ ASSERT_NO_FATAL_FAILURE(Init("eh_frame_bias_x86/", ARCH_X86));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(11U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc ffffe430 vdso.so (__kernel_vsyscall+16)\n"
+ " #01 pc 00082a4b libc.so (__epoll_pwait+43)\n"
+ " #02 pc 000303a3 libc.so (epoll_pwait+115)\n"
+ " #03 pc 000303ed libc.so (epoll_wait+45)\n"
+ " #04 pc 00010ea2 tombstoned (epoll_dispatch+226)\n"
+ " #05 pc 0000c5e7 tombstoned (event_base_loop+1095)\n"
+ " #06 pc 0000c193 tombstoned (event_base_dispatch+35)\n"
+ " #07 pc 00005c77 tombstoned (main+884)\n"
+ " #08 pc 00015f66 libc.so (__libc_init+102)\n"
+ " #09 pc 0000360e tombstoned (_start+98)\n"
+ " #10 pc 00000001 <unknown>\n",
+ frame_info);
+
+ EXPECT_EQ(0xffffe430ULL, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xfffe1a30ULL, unwinder.frames()[0].sp);
+ EXPECT_EQ(0xeb585a4bULL, unwinder.frames()[1].pc);
+ EXPECT_EQ(0xfffe1a40ULL, unwinder.frames()[1].sp);
+ EXPECT_EQ(0xeb5333a3ULL, unwinder.frames()[2].pc);
+ EXPECT_EQ(0xfffe1a60ULL, unwinder.frames()[2].sp);
+ EXPECT_EQ(0xeb5333edULL, unwinder.frames()[3].pc);
+ EXPECT_EQ(0xfffe1ab0ULL, unwinder.frames()[3].sp);
+ EXPECT_EQ(0xeb841ea2ULL, unwinder.frames()[4].pc);
+ EXPECT_EQ(0xfffe1ae0ULL, unwinder.frames()[4].sp);
+ EXPECT_EQ(0xeb83d5e7ULL, unwinder.frames()[5].pc);
+ EXPECT_EQ(0xfffe1b30ULL, unwinder.frames()[5].sp);
+ EXPECT_EQ(0xeb83d193ULL, unwinder.frames()[6].pc);
+ EXPECT_EQ(0xfffe1bd0ULL, unwinder.frames()[6].sp);
+ EXPECT_EQ(0xeb836c77ULL, unwinder.frames()[7].pc);
+ EXPECT_EQ(0xfffe1c00ULL, unwinder.frames()[7].sp);
+ EXPECT_EQ(0xeb518f66ULL, unwinder.frames()[8].pc);
+ EXPECT_EQ(0xfffe1d00ULL, unwinder.frames()[8].sp);
+ EXPECT_EQ(0xeb83460eULL, unwinder.frames()[9].pc);
+ EXPECT_EQ(0xfffe1d40ULL, unwinder.frames()[9].sp);
+ EXPECT_EQ(0x00000001ULL, unwinder.frames()[10].pc);
+ EXPECT_EQ(0xfffe1d74ULL, unwinder.frames()[10].sp);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/libc.so b/libunwindstack/tests/files/offline/eh_frame_bias_x86/libc.so
new file mode 100644
index 0000000..f3eb615
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_bias_x86/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/maps.txt b/libunwindstack/tests/files/offline/eh_frame_bias_x86/maps.txt
new file mode 100644
index 0000000..7d52483
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_bias_x86/maps.txt
@@ -0,0 +1,3 @@
+eb503000-eb5e8000 r-xp 0 00:00 0 libc.so
+eb831000-eb852000 r-xp 0 00:00 0 tombstoned
+ffffe000-fffff000 r-xp 0 00:00 0 vdso.so
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/regs.txt b/libunwindstack/tests/files/offline/eh_frame_bias_x86/regs.txt
new file mode 100644
index 0000000..821928e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_bias_x86/regs.txt
@@ -0,0 +1,9 @@
+eax: fffffffc
+ebx: 4
+ecx: eb290180
+edx: 20
+ebp: 8
+edi: 0
+esi: ffffffff
+esp: fffe1a30
+eip: ffffe430
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/stack.data b/libunwindstack/tests/files/offline/eh_frame_bias_x86/stack.data
new file mode 100644
index 0000000..b95bfac
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_bias_x86/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/tombstoned b/libunwindstack/tests/files/offline/eh_frame_bias_x86/tombstoned
new file mode 100644
index 0000000..aefdb6b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_bias_x86/tombstoned
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/vdso.so b/libunwindstack/tests/files/offline/eh_frame_bias_x86/vdso.so
new file mode 100644
index 0000000..c71dcfb
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_bias_x86/vdso.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/libc.so b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/libc.so
new file mode 100644
index 0000000..7bb7156
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/linker64 b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/linker64
new file mode 100644
index 0000000..00a3896
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/linker64
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/maps.txt b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/maps.txt
new file mode 100644
index 0000000..a2babee
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/maps.txt
@@ -0,0 +1,7 @@
+5f73997000-5f739dc000 r--p 0 00:00 0 test
+5f739dc000-5f73a43000 r-xp 44000 00:00 0 test
+711152c000-711156e000 r--p 0 00:00 0 libc.so
+711156e000-7111611000 --xp 42000 00:00 0 libc.so
+7112be2000-7112be4000 r-xp 0 00:00 0 vdso
+7112be4000-7112c1c000 r--p 0 00:00 0 linker64
+7112c1c000-7112ce1000 r-xp 38000 00:00 0 linker64
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/regs.txt b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/regs.txt
new file mode 100644
index 0000000..3c601e1
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/regs.txt
@@ -0,0 +1,33 @@
+x0: 7112bdbc24
+x1: 0
+x2: ffffffff
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 7f7f7f7f7f7f7f7f
+x8: 62
+x9: a78826643b37f4a1
+x10: 7112bdbc20
+x11: 4100
+x12: 7112bdbb70
+x13: 18
+x14: 1d6518077
+x15: 2a43148faf732a
+x16: 16fc0
+x17: 71115f61a0
+x18: 7111d6a000
+x19: 7112cef1b0
+x20: 7112bdbda0
+x21: 59616d61
+x22: 1
+x23: 7112bdbc24
+x24: 4b0e
+x25: 62
+x26: 2
+x27: 0
+x28: 7111934020
+x29: 7112bdbd90
+sp: 7112bdbbf0
+lr: 7112c394ec
+pc: 7112cb99bc
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack0.data b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack0.data
new file mode 100644
index 0000000..1674733
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack1.data b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack1.data
new file mode 100644
index 0000000..6d7b48a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/test b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/test
new file mode 100644
index 0000000..3a75b8f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/test
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/vdso b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/vdso
new file mode 100644
index 0000000..4940916
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/vdso
Binary files differ
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
index 4f67d67..64b58a8 100644
--- a/libunwindstack/tools/unwind_for_offline.cpp
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -275,6 +275,9 @@
if (maps_by_start.count(frame.map_start) == 0) {
map_info = maps->Find(frame.map_start);
+ if (map_info == nullptr) {
+ continue;
+ }
auto info = FillInAndGetMapInfo(maps_by_start, map_info);
bool file_copied = false;
diff --git a/libutils/include/utils/Flattenable.h b/libutils/include/utils/Flattenable.h
index 9d00602..953b859 100644
--- a/libutils/include/utils/Flattenable.h
+++ b/libutils/include/utils/Flattenable.h
@@ -17,6 +17,9 @@
#ifndef ANDROID_UTILS_FLATTENABLE_H
#define ANDROID_UTILS_FLATTENABLE_H
+// DO NOT USE: please use parcelable instead
+// This code is deprecated and will not be supported via AIDL code gen. For data
+// to be sent over binder, please use parcelables.
#include <stdint.h>
#include <string.h>
@@ -28,7 +31,9 @@
namespace android {
-
+// DO NOT USE: please use parcelable instead
+// This code is deprecated and will not be supported via AIDL code gen. For data
+// to be sent over binder, please use parcelables.
class FlattenableUtils {
public:
template<size_t N>
@@ -79,7 +84,9 @@
}
};
-
+// DO NOT USE: please use parcelable instead
+// This code is deprecated and will not be supported via AIDL code gen. For data
+// to be sent over binder, please use parcelables.
/*
* The Flattenable protocol allows an object to serialize itself out
* to a byte-buffer and an array of file descriptors.
@@ -131,6 +138,9 @@
return static_cast<T*>(this)->T::unflatten(buffer, size, fds, count);
}
+// DO NOT USE: please use parcelable instead
+// This code is deprecated and will not be supported via AIDL code gen. For data
+// to be sent over binder, please use parcelables.
/*
* LightFlattenable is a protocol allowing object to serialize themselves out
* to a byte-buffer. Because it doesn't handle file-descriptors,
@@ -171,6 +181,9 @@
return static_cast<T*>(this)->T::unflatten(buffer, size);
}
+// DO NOT USE: please use parcelable instead
+// This code is deprecated and will not be supported via AIDL code gen. For data
+// to be sent over binder, please use parcelables.
/*
* LightFlattenablePod is an implementation of the LightFlattenable protocol
* for POD (plain-old-data) objects.
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 0253f2f..667bddc 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -175,7 +175,7 @@
}
cc_binary {
- name: "unzip",
+ name: "ziptool",
defaults: ["libziparchive_flags"],
srcs: ["unzip.cpp"],
shared_libs: [
@@ -183,4 +183,17 @@
"libziparchive",
],
recovery_available: true,
+ host_supported: true,
+ target: {
+ android: {
+ symlinks: ["unzip", "zipinfo"],
+ },
+ },
+}
+
+cc_fuzz {
+ name: "libziparchive_fuzzer",
+ srcs: ["libziparchive_fuzzer.cpp"],
+ static_libs: ["libziparchive", "libbase", "libz", "liblog"],
+ host_supported: true,
}
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
index e3ac114..047af90 100644
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -40,8 +40,8 @@
* Represents information about a zip entry in a zip file.
*/
struct ZipEntry {
- // Compression method: One of kCompressStored or
- // kCompressDeflated.
+ // Compression method. One of kCompressStored or kCompressDeflated.
+ // See also `gpbf` for deflate subtypes.
uint16_t method;
// Modification time. The zipfile format specifies
@@ -55,7 +55,7 @@
struct tm GetModificationTime() const;
// Suggested Unix mode for this entry, from the zip archive if created on
- // Unix, or a default otherwise.
+ // Unix, or a default otherwise. See also `external_file_attributes`.
mode_t unix_mode;
// 1 if this entry contains a data descriptor segment, 0
@@ -79,6 +79,18 @@
// The offset to the start of data for this ZipEntry.
off64_t offset;
+
+ // The version of zip and the host file system this came from (for zipinfo).
+ uint16_t version_made_by;
+
+ // The raw attributes, whose interpretation depends on the host
+ // file system in `version_made_by` (for zipinfo). See also `unix_mode`.
+ uint32_t external_file_attributes;
+
+ // Specifics about the deflation (for zipinfo).
+ uint16_t gpbf;
+ // Whether this entry is believed to be text or binary (for zipinfo).
+ bool is_text;
};
struct ZipArchive;
@@ -114,7 +126,7 @@
int32_t OpenArchiveFd(const int fd, const char* debugFileName, ZipArchiveHandle* handle,
bool assume_ownership = true);
-int32_t OpenArchiveFromMemory(void* address, size_t length, const char* debugFileName,
+int32_t OpenArchiveFromMemory(const void* address, size_t length, const char* debugFileName,
ZipArchiveHandle* handle);
/*
* Close archive, releasing resources associated with it. This will
@@ -125,6 +137,19 @@
*/
void CloseArchive(ZipArchiveHandle archive);
+/** See GetArchiveInfo(). */
+struct ZipArchiveInfo {
+ /** The size in bytes of the archive itself. Used by zipinfo. */
+ off64_t archive_size;
+ /** The number of entries in the archive. */
+ size_t entry_count;
+};
+
+/**
+ * Returns information about the given archive.
+ */
+ZipArchiveInfo GetArchiveInfo(ZipArchiveHandle archive);
+
/*
* Find an entry in the Zip archive, by name. |data| must be non-null.
*
diff --git a/libziparchive/libziparchive_fuzzer.cpp b/libziparchive/libziparchive_fuzzer.cpp
new file mode 100644
index 0000000..75e7939
--- /dev/null
+++ b/libziparchive/libziparchive_fuzzer.cpp
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <ziparchive/zip_archive.h>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ ZipArchiveHandle handle = nullptr;
+ OpenArchiveFromMemory(data, size, "fuzz", &handle);
+ CloseArchive(handle);
+ return 0;
+}
diff --git a/libziparchive/unzip.cpp b/libziparchive/unzip.cpp
index 426325e..af70f1d 100644
--- a/libziparchive/unzip.cpp
+++ b/libziparchive/unzip.cpp
@@ -34,18 +34,29 @@
#include <android-base/strings.h>
#include <ziparchive/zip_archive.h>
+using android::base::EndsWith;
+using android::base::StartsWith;
+
enum OverwriteMode {
kAlways,
kNever,
kPrompt,
};
+enum Role {
+ kUnzip,
+ kZipinfo,
+};
+
+static Role role;
static OverwriteMode overwrite_mode = kPrompt;
+static bool flag_1 = false;
static const char* flag_d = nullptr;
static bool flag_l = false;
static bool flag_p = false;
static bool flag_q = false;
static bool flag_v = false;
+static bool flag_x = false;
static const char* archive_name = nullptr;
static std::set<std::string> includes;
static std::set<std::string> excludes;
@@ -88,32 +99,51 @@
return static_cast<int>((100LL * (uncompressed - compressed)) / uncompressed);
}
-static void MaybeShowHeader() {
- if (!flag_q) printf("Archive: %s\n", archive_name);
- if (flag_v) {
- printf(
- " Length Method Size Cmpr Date Time CRC-32 Name\n"
- "-------- ------ ------- ---- ---------- ----- -------- ----\n");
- } else if (flag_l) {
- printf(
- " Length Date Time Name\n"
- "--------- ---------- ----- ----\n");
+static void MaybeShowHeader(ZipArchiveHandle zah) {
+ if (role == kUnzip) {
+ // unzip has three formats.
+ if (!flag_q) printf("Archive: %s\n", archive_name);
+ if (flag_v) {
+ printf(
+ " Length Method Size Cmpr Date Time CRC-32 Name\n"
+ "-------- ------ ------- ---- ---------- ----- -------- ----\n");
+ } else if (flag_l) {
+ printf(
+ " Length Date Time Name\n"
+ "--------- ---------- ----- ----\n");
+ }
+ } else {
+ // zipinfo.
+ if (!flag_1 && includes.empty() && excludes.empty()) {
+ ZipArchiveInfo info{GetArchiveInfo(zah)};
+ printf("Archive: %s\n", archive_name);
+ printf("Zip file size: %" PRId64 " bytes, number of entries: %zu\n", info.archive_size,
+ info.entry_count);
+ }
}
}
static void MaybeShowFooter() {
- if (flag_v) {
- printf(
- "-------- ------- --- -------\n"
- "%8" PRId64 " %8" PRId64 " %3d%% %zu file%s\n",
- total_uncompressed_length, total_compressed_length,
- CompressionRatio(total_uncompressed_length, total_compressed_length), file_count,
- (file_count == 1) ? "" : "s");
- } else if (flag_l) {
- printf(
- "--------- -------\n"
- "%9" PRId64 " %zu file%s\n",
- total_uncompressed_length, file_count, (file_count == 1) ? "" : "s");
+ if (role == kUnzip) {
+ if (flag_v) {
+ printf(
+ "-------- ------- --- -------\n"
+ "%8" PRId64 " %8" PRId64 " %3d%% %zu file%s\n",
+ total_uncompressed_length, total_compressed_length,
+ CompressionRatio(total_uncompressed_length, total_compressed_length), file_count,
+ (file_count == 1) ? "" : "s");
+ } else if (flag_l) {
+ printf(
+ "--------- -------\n"
+ "%9" PRId64 " %zu file%s\n",
+ total_uncompressed_length, file_count, (file_count == 1) ? "" : "s");
+ }
+ } else {
+ if (!flag_1 && includes.empty() && excludes.empty()) {
+ printf("%zu files, %" PRId64 " bytes uncompressed, %" PRId64 " bytes compressed: %3d%%\n",
+ file_count, total_uncompressed_length, total_compressed_length,
+ CompressionRatio(total_uncompressed_length, total_compressed_length));
+ }
}
}
@@ -163,8 +193,7 @@
static void ExtractOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
// Bad filename?
- if (android::base::StartsWith(name, "/") || android::base::StartsWith(name, "../") ||
- name.find("/../") != std::string::npos) {
+ if (StartsWith(name, "/") || StartsWith(name, "../") || name.find("/../") != std::string::npos) {
error(1, 0, "bad filename %s", name.c_str());
}
@@ -172,7 +201,7 @@
std::string dst;
if (flag_d) {
dst = flag_d;
- if (!android::base::EndsWith(dst, "/")) dst += '/';
+ if (!EndsWith(dst, "/")) dst += '/';
}
dst += name;
@@ -182,7 +211,7 @@
}
// An entry in a zip file can just be a directory itself.
- if (android::base::EndsWith(name, "/")) {
+ if (EndsWith(name, "/")) {
if (mkdir(name.c_str(), entry.unix_mode) == -1) {
// If the directory already exists, that's fine.
if (errno == EEXIST) {
@@ -226,17 +255,82 @@
}
}
+static void InfoOne(const ZipEntry& entry, const std::string& name) {
+ if (flag_1) {
+ // "android-ndk-r19b/sources/android/NOTICE"
+ printf("%s\n", name.c_str());
+ return;
+ }
+
+ int version = entry.version_made_by & 0xff;
+ int os = (entry.version_made_by >> 8) & 0xff;
+
+ // TODO: Support suid/sgid? Non-Unix/non-FAT host file system attributes?
+ const char* src_fs = "???";
+ char mode[] = "??? ";
+ if (os == 0) {
+ src_fs = "fat";
+ // https://docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
+ int attrs = entry.external_file_attributes & 0xff;
+ mode[0] = (attrs & 0x10) ? 'd' : '-';
+ mode[1] = 'r';
+ mode[2] = (attrs & 0x01) ? '-' : 'w';
+ // The man page also mentions ".btm", but that seems to be obsolete?
+ mode[3] = EndsWith(name, ".exe") || EndsWith(name, ".com") || EndsWith(name, ".bat") ||
+ EndsWith(name, ".cmd")
+ ? 'x'
+ : '-';
+ mode[4] = (attrs & 0x20) ? 'a' : '-';
+ mode[5] = (attrs & 0x02) ? 'h' : '-';
+ mode[6] = (attrs & 0x04) ? 's' : '-';
+ } else if (os == 3) {
+ src_fs = "unx";
+ mode[0] = S_ISDIR(entry.unix_mode) ? 'd' : (S_ISREG(entry.unix_mode) ? '-' : '?');
+ mode[1] = entry.unix_mode & S_IRUSR ? 'r' : '-';
+ mode[2] = entry.unix_mode & S_IWUSR ? 'w' : '-';
+ mode[3] = entry.unix_mode & S_IXUSR ? 'x' : '-';
+ mode[4] = entry.unix_mode & S_IRGRP ? 'r' : '-';
+ mode[5] = entry.unix_mode & S_IWGRP ? 'w' : '-';
+ mode[6] = entry.unix_mode & S_IXGRP ? 'x' : '-';
+ mode[7] = entry.unix_mode & S_IROTH ? 'r' : '-';
+ mode[8] = entry.unix_mode & S_IWOTH ? 'w' : '-';
+ mode[9] = entry.unix_mode & S_IXOTH ? 'x' : '-';
+ }
+
+ char method[5] = "stor";
+ if (entry.method == kCompressDeflated) {
+ snprintf(method, sizeof(method), "def%c", "NXFS"[(entry.gpbf >> 1) & 0x3]);
+ }
+
+ // TODO: zipinfo (unlike unzip) sometimes uses time zone?
+ // TODO: this uses 4-digit years because we're not barbarians unless interoperability forces it.
+ tm t = entry.GetModificationTime();
+ char time[32];
+ snprintf(time, sizeof(time), "%04d-%02d-%02d %02d:%02d", t.tm_year + 1900, t.tm_mon + 1,
+ t.tm_mday, t.tm_hour, t.tm_min);
+
+ // "-rw-r--r-- 3.0 unx 577 t- defX 19-Feb-12 16:09 android-ndk-r19b/sources/android/NOTICE"
+ printf("%s %2d.%d %s %8d %c%c %s %s %s\n", mode, version / 10, version % 10, src_fs,
+ entry.uncompressed_length, entry.is_text ? 't' : 'b',
+ entry.has_data_descriptor ? 'X' : 'x', method, time, name.c_str());
+}
+
static void ProcessOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
- if (flag_l || flag_v) {
- // -l or -lv or -lq or -v.
- ListOne(entry, name);
- } else {
- // Actually extract.
- if (flag_p) {
- ExtractToPipe(zah, entry, name);
+ if (role == kUnzip) {
+ if (flag_l || flag_v) {
+ // -l or -lv or -lq or -v.
+ ListOne(entry, name);
} else {
- ExtractOne(zah, entry, name);
+ // Actually extract.
+ if (flag_p) {
+ ExtractToPipe(zah, entry, name);
+ } else {
+ ExtractOne(zah, entry, name);
+ }
}
+ } else {
+ // zipinfo or zipinfo -1.
+ InfoOne(entry, name);
}
total_uncompressed_length += entry.uncompressed_length;
total_compressed_length += entry.compressed_length;
@@ -244,7 +338,7 @@
}
static void ProcessAll(ZipArchiveHandle zah) {
- MaybeShowHeader();
+ MaybeShowHeader(zah);
// libziparchive iteration order doesn't match the central directory.
// We could sort, but that would cost extra and wouldn't match either.
@@ -267,73 +361,126 @@
}
static void ShowHelp(bool full) {
- fprintf(full ? stdout : stderr, "usage: unzip [-d DIR] [-lnopqv] ZIP [FILE...] [-x FILE...]\n");
- if (!full) exit(EXIT_FAILURE);
+ if (role == kUnzip) {
+ fprintf(full ? stdout : stderr, "usage: unzip [-d DIR] [-lnopqv] ZIP [FILE...] [-x FILE...]\n");
+ if (!full) exit(EXIT_FAILURE);
- printf(
- "\n"
- "Extract FILEs from ZIP archive. Default is all files. Both the include and\n"
- "exclude (-x) lists use shell glob patterns.\n"
- "\n"
- "-d DIR Extract into DIR\n"
- "-l List contents (-lq excludes archive name, -lv is verbose)\n"
- "-n Never overwrite files (default: prompt)\n"
- "-o Always overwrite files\n"
- "-p Pipe to stdout\n"
- "-q Quiet\n"
- "-v List contents verbosely\n"
- "-x FILE Exclude files\n");
+ printf(
+ "\n"
+ "Extract FILEs from ZIP archive. Default is all files. Both the include and\n"
+ "exclude (-x) lists use shell glob patterns.\n"
+ "\n"
+ "-d DIR Extract into DIR\n"
+ "-l List contents (-lq excludes archive name, -lv is verbose)\n"
+ "-n Never overwrite files (default: prompt)\n"
+ "-o Always overwrite files\n"
+ "-p Pipe to stdout\n"
+ "-q Quiet\n"
+ "-v List contents verbosely\n"
+ "-x FILE Exclude files\n");
+ } else {
+ fprintf(full ? stdout : stderr, "usage: zipinfo [-1] ZIP [FILE...] [-x FILE...]\n");
+ if (!full) exit(EXIT_FAILURE);
+
+ printf(
+ "\n"
+ "Show information about FILEs from ZIP archive. Default is all files.\n"
+ "Both the include and exclude (-x) lists use shell glob patterns.\n"
+ "\n"
+ "-1 Show filenames only, one per line\n"
+ "-x FILE Exclude files\n");
+ }
exit(EXIT_SUCCESS);
}
+static void HandleCommonOption(int opt) {
+ switch (opt) {
+ case 'h':
+ ShowHelp(true);
+ break;
+ case 'x':
+ flag_x = true;
+ break;
+ case 1:
+ // -x swallows all following arguments, so we use '-' in the getopt
+ // string and collect files here.
+ if (!archive_name) {
+ archive_name = optarg;
+ } else if (flag_x) {
+ excludes.insert(optarg);
+ } else {
+ includes.insert(optarg);
+ }
+ break;
+ default:
+ ShowHelp(false);
+ break;
+ }
+}
+
int main(int argc, char* argv[]) {
- static struct option opts[] = {
+ // Who am I, and what am I doing?
+ const char* base = basename(argv[0]);
+ if (!strcmp(base, "ziptool") && argc > 1) return main(argc - 1, argv + 1);
+ if (!strcmp(base, "unzip")) {
+ role = kUnzip;
+ } else if (!strcmp(base, "zipinfo")) {
+ role = kZipinfo;
+ } else {
+ error(1, 0, "run as ziptool with unzip or zipinfo as the first argument, or symlink");
+ }
+
+ static const struct option opts[] = {
{"help", no_argument, 0, 'h'},
};
- bool saw_x = false;
- int opt;
- while ((opt = getopt_long(argc, argv, "-d:hlnopqvx", opts, nullptr)) != -1) {
- switch (opt) {
- case 'd':
- flag_d = optarg;
- break;
- case 'h':
- ShowHelp(true);
- break;
- case 'l':
- flag_l = true;
- break;
- case 'n':
- overwrite_mode = kNever;
- break;
- case 'o':
- overwrite_mode = kAlways;
- break;
- case 'p':
- flag_p = flag_q = true;
- break;
- case 'q':
- flag_q = true;
- break;
- case 'v':
- flag_v = true;
- break;
- case 'x':
- saw_x = true;
- break;
- case 1:
- // -x swallows all following arguments, so we use '-' in the getopt
- // string and collect files here.
- if (!archive_name) {
- archive_name = optarg;
- } else if (saw_x) {
- excludes.insert(optarg);
- } else {
- includes.insert(optarg);
- }
- break;
- default:
- ShowHelp(false);
+
+ if (role == kUnzip) {
+ // `unzip -Z` is "zipinfo mode", so in that case just restart...
+ if (argc > 1 && !strcmp(argv[1], "-Z")) {
+ argv[1] = const_cast<char*>("zipinfo");
+ return main(argc - 1, argv + 1);
+ }
+
+ int opt;
+ while ((opt = getopt_long(argc, argv, "-d:hlnopqvx", opts, nullptr)) != -1) {
+ switch (opt) {
+ case 'd':
+ flag_d = optarg;
+ break;
+ case 'l':
+ flag_l = true;
+ break;
+ case 'n':
+ overwrite_mode = kNever;
+ break;
+ case 'o':
+ overwrite_mode = kAlways;
+ break;
+ case 'p':
+ flag_p = flag_q = true;
+ break;
+ case 'q':
+ flag_q = true;
+ break;
+ case 'v':
+ flag_v = true;
+ break;
+ default:
+ HandleCommonOption(opt);
+ break;
+ }
+ }
+ } else {
+ int opt;
+ while ((opt = getopt_long(argc, argv, "-1hx", opts, nullptr)) != -1) {
+ switch (opt) {
+ case '1':
+ flag_1 = true;
+ break;
+ default:
+ HandleCommonOption(opt);
+ break;
+ }
}
}
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index c95b035..ef29188 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -178,7 +178,7 @@
#endif
}
-ZipArchive::ZipArchive(void* address, size_t length)
+ZipArchive::ZipArchive(const void* address, size_t length)
: mapped_zip(address, length),
close_file(false),
directory_offset(0),
@@ -471,13 +471,20 @@
return OpenArchiveInternal(archive, fileName);
}
-int32_t OpenArchiveFromMemory(void* address, size_t length, const char* debug_file_name,
+int32_t OpenArchiveFromMemory(const void* address, size_t length, const char* debug_file_name,
ZipArchiveHandle* handle) {
ZipArchive* archive = new ZipArchive(address, length);
*handle = archive;
return OpenArchiveInternal(archive, debug_file_name);
}
+ZipArchiveInfo GetArchiveInfo(ZipArchiveHandle archive) {
+ ZipArchiveInfo result;
+ result.archive_size = archive->mapped_zip.GetFileLength();
+ result.entry_count = archive->num_entries;
+ return result;
+}
+
/*
* Close a ZipArchive, closing the file and freeing the contents.
*/
@@ -614,12 +621,21 @@
}
// 4.4.2.1: the upper byte of `version_made_by` gives the source OS. Unix is 3.
- if ((cdr->version_made_by >> 8) == 3) {
+ data->version_made_by = cdr->version_made_by;
+ data->external_file_attributes = cdr->external_file_attributes;
+ if ((data->version_made_by >> 8) == 3) {
data->unix_mode = (cdr->external_file_attributes >> 16) & 0xffff;
} else {
data->unix_mode = 0777;
}
+ // 4.4.4: general purpose bit flags.
+ data->gpbf = lfh->gpb_flags;
+
+ // 4.4.14: the lowest bit of the internal file attributes field indicates text.
+ // Currently only needed to implement zipinfo.
+ data->is_text = (cdr->internal_file_attributes & 1);
+
// Check that the local file header name matches the declared
// name in the central directory.
if (lfh->file_name_length != nameLen) {
@@ -1152,7 +1168,7 @@
return fd_;
}
-void* MappedZipFile::GetBasePtr() const {
+const void* MappedZipFile::GetBasePtr() const {
if (has_fd_) {
ALOGW("Zip: MappedZipFile doesn't have a base pointer.");
return nullptr;
@@ -1188,13 +1204,14 @@
ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64 "\n", off, data_length_);
return false;
}
- memcpy(buf, static_cast<uint8_t*>(base_ptr_) + off, len);
+ memcpy(buf, static_cast<const uint8_t*>(base_ptr_) + off, len);
}
return true;
}
-void CentralDirectory::Initialize(void* map_base_ptr, off64_t cd_start_offset, size_t cd_size) {
- base_ptr_ = static_cast<uint8_t*>(map_base_ptr) + cd_start_offset;
+void CentralDirectory::Initialize(const void* map_base_ptr, off64_t cd_start_offset,
+ size_t cd_size) {
+ base_ptr_ = static_cast<const uint8_t*>(map_base_ptr) + cd_start_offset;
length_ = cd_size;
}
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 30a1d72..60fdec0 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -95,14 +95,14 @@
explicit MappedZipFile(const int fd)
: has_fd_(true), fd_(fd), base_ptr_(nullptr), data_length_(0) {}
- explicit MappedZipFile(void* address, size_t length)
+ explicit MappedZipFile(const void* address, size_t length)
: has_fd_(false), fd_(-1), base_ptr_(address), data_length_(static_cast<off64_t>(length)) {}
bool HasFd() const { return has_fd_; }
int GetFileDescriptor() const;
- void* GetBasePtr() const;
+ const void* GetBasePtr() const;
off64_t GetFileLength() const;
@@ -117,7 +117,7 @@
const int fd_;
- void* const base_ptr_;
+ const void* const base_ptr_;
const off64_t data_length_;
};
@@ -129,7 +129,7 @@
size_t GetMapLength() const { return length_; }
- void Initialize(void* map_base_ptr, off64_t cd_start_offset, size_t cd_size);
+ void Initialize(const void* map_base_ptr, off64_t cd_start_offset, size_t cd_size);
private:
const uint8_t* base_ptr_;
@@ -177,7 +177,7 @@
ZipStringOffset* hash_table;
ZipArchive(const int fd, bool assume_ownership);
- ZipArchive(void* address, size_t length);
+ ZipArchive(const void* address, size_t length);
~ZipArchive();
bool InitializeCentralDirectory(off64_t cd_start_offset, size_t cd_size);
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 372e10f..9de7ff7 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -31,6 +31,7 @@
#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/socket.h>
+#include <sys/syscall.h>
#include <sys/sysinfo.h>
#include <sys/time.h>
#include <sys/types.h>
@@ -139,6 +140,15 @@
/* ro.lmk.psi_complete_stall_ms property defaults */
#define DEF_COMPLETE_STALL 700
+static inline int sys_pidfd_open(pid_t pid, unsigned int flags) {
+ return syscall(__NR_pidfd_open, pid, flags);
+}
+
+static inline int sys_pidfd_send_signal(int pidfd, int sig, siginfo_t *info,
+ unsigned int flags) {
+ return syscall(__NR_pidfd_send_signal, pidfd, sig, info, flags);
+}
+
/* default to old in-kernel interface if no memory pressure events */
static bool use_inkernel_interface = true;
static bool has_inkernel_module;
@@ -169,6 +179,11 @@
static int level_oomadj[VMPRESS_LEVEL_COUNT];
static int mpevfd[VMPRESS_LEVEL_COUNT] = { -1, -1, -1 };
+static bool pidfd_supported;
+static int last_kill_pid_or_fd = -1;
+static struct timespec last_kill_tm;
+
+/* lmkd configurable parameters */
static bool debug_process_killing;
static bool enable_pressure_upgrade;
static int64_t upgrade_pressure;
@@ -197,6 +212,8 @@
POLLING_DO_NOT_CHANGE,
POLLING_START,
POLLING_STOP,
+ POLLING_PAUSE,
+ POLLING_RESUME,
};
/*
@@ -207,6 +224,7 @@
*/
struct polling_params {
struct event_handler_info* poll_handler;
+ struct event_handler_info* paused_handler;
struct timespec poll_start_tm;
struct timespec last_poll_tm;
int polling_interval_ms;
@@ -235,8 +253,11 @@
/* vmpressure event handler data */
static struct event_handler_info vmpressure_hinfo[VMPRESS_LEVEL_COUNT];
-/* 3 memory pressure levels, 1 ctrl listen socket, 2 ctrl data socket, 1 lmk events */
-#define MAX_EPOLL_EVENTS (2 + MAX_DATA_CONN + VMPRESS_LEVEL_COUNT)
+/*
+ * 1 ctrl listen socket, 2 ctrl data socket, 3 memory pressure levels,
+ * 1 lmk events + 1 fd to wait for process death
+ */
+#define MAX_EPOLL_EVENTS (1 + MAX_DATA_CONN + VMPRESS_LEVEL_COUNT + 1 + 1)
static int epollfd;
static int maxevents;
@@ -466,6 +487,7 @@
struct proc {
struct adjslot_list asl;
int pid;
+ int pidfd;
uid_t uid;
int oomadj;
struct proc *pidhash_next;
@@ -682,6 +704,13 @@
prevp->pidhash_next = procp->pidhash_next;
proc_unslot(procp);
+ /*
+ * Close pidfd here if we are not waiting for corresponding process to die,
+ * in which case stop_wait_for_proc_kill() will close the pidfd later
+ */
+ if (procp->pidfd >= 0 && procp->pidfd != last_kill_pid_or_fd) {
+ close(procp->pidfd);
+ }
free(procp);
return 0;
}
@@ -782,15 +811,15 @@
close(fd);
return -1;
}
+ line[ret] = '\0';
sscanf(line, "%d %d ", &total, &rss);
close(fd);
return rss;
}
-static char *proc_get_name(int pid) {
+static char *proc_get_name(int pid, char *buf, size_t buf_size) {
char path[PATH_MAX];
- static char line[LINE_MAX];
int fd;
char *cp;
ssize_t ret;
@@ -801,25 +830,24 @@
if (fd == -1) {
return NULL;
}
- ret = read_all(fd, line, sizeof(line) - 1);
+ ret = read_all(fd, buf, buf_size - 1);
close(fd);
if (ret < 0) {
return NULL;
}
+ buf[ret] = '\0';
- cp = strchr(line, ' ');
+ cp = strchr(buf, ' ');
if (cp) {
*cp = '\0';
- } else {
- line[ret] = '\0';
}
- return line;
+ return buf;
}
static void cmd_procprio(LMKD_CTRL_PACKET packet) {
struct proc *procp;
- char path[80];
+ char path[LINE_MAX];
char val[20];
int soft_limit_mult;
struct lmk_procprio params;
@@ -856,7 +884,8 @@
}
if (use_inkernel_interface) {
- stats_store_taskname(params.pid, proc_get_name(params.pid), kpoll_info.poll_fd);
+ stats_store_taskname(params.pid, proc_get_name(params.pid, path, sizeof(path)),
+ kpoll_info.poll_fd);
return;
}
@@ -906,16 +935,27 @@
procp = pid_lookup(params.pid);
if (!procp) {
- procp = malloc(sizeof(struct proc));
- if (!procp) {
- // Oh, the irony. May need to rebuild our state.
+ int pidfd = -1;
+
+ if (pidfd_supported) {
+ pidfd = TEMP_FAILURE_RETRY(sys_pidfd_open(params.pid, 0));
+ if (pidfd < 0) {
+ ALOGE("pidfd_open for pid %d failed; errno=%d", params.pid, errno);
return;
}
+ }
- procp->pid = params.pid;
- procp->uid = params.uid;
- procp->oomadj = params.oomadj;
- proc_insert(procp);
+ procp = calloc(1, sizeof(struct proc));
+ if (!procp) {
+ // Oh, the irony. May need to rebuild our state.
+ return;
+ }
+
+ procp->pid = params.pid;
+ procp->pidfd = pidfd;
+ procp->uid = params.uid;
+ procp->oomadj = params.oomadj;
+ proc_insert(procp);
} else {
proc_unslot(procp);
procp->oomadj = params.oomadj;
@@ -1647,12 +1687,109 @@
closedir(d);
}
-static int last_killed_pid = -1;
+static bool is_kill_pending(void) {
+ char buf[24];
+
+ if (last_kill_pid_or_fd < 0) {
+ return false;
+ }
+
+ if (pidfd_supported) {
+ return true;
+ }
+
+ /* when pidfd is not supported base the decision on /proc/<pid> existence */
+ snprintf(buf, sizeof(buf), "/proc/%d/", last_kill_pid_or_fd);
+ if (access(buf, F_OK) == 0) {
+ return true;
+ }
+
+ return false;
+}
+
+static bool is_waiting_for_kill(void) {
+ return pidfd_supported && last_kill_pid_or_fd >= 0;
+}
+
+static void stop_wait_for_proc_kill(bool finished) {
+ struct epoll_event epev;
+
+ if (last_kill_pid_or_fd < 0) {
+ return;
+ }
+
+ if (debug_process_killing) {
+ struct timespec curr_tm;
+
+ if (clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm) != 0) {
+ /*
+ * curr_tm is used here merely to report kill duration, so this failure is not fatal.
+ * Log an error and continue.
+ */
+ ALOGE("Failed to get current time");
+ }
+
+ if (finished) {
+ ALOGI("Process got killed in %ldms",
+ get_time_diff_ms(&last_kill_tm, &curr_tm));
+ } else {
+ ALOGI("Stop waiting for process kill after %ldms",
+ get_time_diff_ms(&last_kill_tm, &curr_tm));
+ }
+ }
+
+ if (pidfd_supported) {
+ /* unregister fd */
+ if (epoll_ctl(epollfd, EPOLL_CTL_DEL, last_kill_pid_or_fd, &epev) != 0) {
+ ALOGE("epoll_ctl for last killed process failed; errno=%d", errno);
+ return;
+ }
+ maxevents--;
+ close(last_kill_pid_or_fd);
+ }
+
+ last_kill_pid_or_fd = -1;
+}
+
+static void kill_done_handler(int data __unused, uint32_t events __unused,
+ struct polling_params *poll_params) {
+ stop_wait_for_proc_kill(true);
+ poll_params->update = POLLING_RESUME;
+}
+
+static void start_wait_for_proc_kill(int pid_or_fd) {
+ static struct event_handler_info kill_done_hinfo = { 0, kill_done_handler };
+ struct epoll_event epev;
+
+ if (last_kill_pid_or_fd >= 0) {
+ /* Should not happen but if it does we should stop previous wait */
+ ALOGE("Attempt to wait for a kill while another wait is in progress");
+ stop_wait_for_proc_kill(false);
+ }
+
+ last_kill_pid_or_fd = pid_or_fd;
+
+ if (!pidfd_supported) {
+ /* If pidfd is not supported just store PID and exit */
+ return;
+ }
+
+ epev.events = EPOLLIN;
+ epev.data.ptr = (void *)&kill_done_hinfo;
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, last_kill_pid_or_fd, &epev) != 0) {
+ ALOGE("epoll_ctl for last kill failed; errno=%d", errno);
+ close(last_kill_pid_or_fd);
+ last_kill_pid_or_fd = -1;
+ return;
+ }
+ maxevents++;
+}
/* Kill one process specified by procp. Returns the size of the process killed */
static int kill_one_process(struct proc* procp, int min_oom_score, int kill_reason,
- const char *kill_desc, union meminfo *mi) {
+ const char *kill_desc, union meminfo *mi, struct timespec *tm) {
int pid = procp->pid;
+ int pidfd = procp->pidfd;
uid_t uid = procp->uid;
int tgid;
char *taskname;
@@ -1660,6 +1797,7 @@
int r;
int result = -1;
struct memory_stat *mem_st;
+ char buf[LINE_MAX];
tgid = proc_get_tgid(pid);
if (tgid >= 0 && tgid != pid) {
@@ -1667,7 +1805,7 @@
goto out;
}
- taskname = proc_get_name(pid);
+ taskname = proc_get_name(pid, buf, sizeof(buf));
if (!taskname) {
goto out;
}
@@ -1682,11 +1820,18 @@
TRACE_KILL_START(pid);
/* CAP_KILL required */
- r = kill(pid, SIGKILL);
+ if (pidfd < 0) {
+ start_wait_for_proc_kill(pid);
+ r = kill(pid, SIGKILL);
+ } else {
+ start_wait_for_proc_kill(pidfd);
+ r = sys_pidfd_send_signal(pidfd, SIGKILL, NULL, 0);
+ }
TRACE_KILL_END();
if (r) {
+ stop_wait_for_proc_kill(false);
ALOGE("kill(%d): errno=%d", pid, errno);
/* Delete process record even when we fail to kill so that we don't get stuck on it */
goto out;
@@ -1694,6 +1839,8 @@
set_process_group_and_prio(pid, SP_FOREGROUND, ANDROID_PRIORITY_HIGHEST);
+ last_kill_tm = *tm;
+
inc_killcnt(procp->oomadj);
killinfo_log(procp, min_oom_score, tasksize, kill_reason, mi);
@@ -1706,8 +1853,6 @@
uid, procp->oomadj, tasksize * page_k);
}
- last_killed_pid = pid;
-
stats_write_lmk_kill_occurred(LMK_KILL_OCCURRED, uid, taskname,
procp->oomadj, min_oom_score, tasksize, mem_st);
@@ -1727,7 +1872,7 @@
* Returns size of the killed process.
*/
static int find_and_kill_process(int min_score_adj, int kill_reason, const char *kill_desc,
- union meminfo *mi) {
+ union meminfo *mi, struct timespec *tm) {
int i;
int killed_size = 0;
bool lmk_state_change_start = false;
@@ -1742,7 +1887,7 @@
if (!procp)
break;
- killed_size = kill_one_process(procp, min_score_adj, kill_reason, kill_desc, mi);
+ killed_size = kill_one_process(procp, min_score_adj, kill_reason, kill_desc, mi, tm);
if (killed_size >= 0) {
if (!lmk_state_change_start) {
lmk_state_change_start = true;
@@ -1821,23 +1966,6 @@
level - 1 : level);
}
-static bool is_kill_pending(void) {
- char buf[24];
-
- if (last_killed_pid < 0) {
- return false;
- }
-
- snprintf(buf, sizeof(buf), "/proc/%d/", last_killed_pid);
- if (access(buf, F_OK) == 0) {
- return true;
- }
-
- // reset last killed PID because there's nothing pending
- last_killed_pid = -1;
- return false;
-}
-
enum zone_watermark {
WMARK_MIN = 0,
WMARK_LOW,
@@ -1933,9 +2061,13 @@
/* Skip while still killing a process */
if (is_kill_pending()) {
- /* TODO: replace this quick polling with pidfd polling if kernel supports */
goto no_kill;
}
+ /*
+ * Process is dead, stop waiting. This has no effect if pidfds are supported and
+ * death notification already caused waiting to stop.
+ */
+ stop_wait_for_proc_kill(true);
if (clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm) != 0) {
ALOGE("Failed to get current time");
@@ -2066,7 +2198,8 @@
/* Kill a process if necessary */
if (kill_reason != NONE) {
- int pages_freed = find_and_kill_process(min_score_adj, kill_reason, kill_desc, &mi);
+ int pages_freed = find_and_kill_process(min_score_adj, kill_reason, kill_desc, &mi,
+ &curr_tm);
if (pages_freed > 0) {
killing = true;
if (cut_thrashing_limit) {
@@ -2080,6 +2213,13 @@
}
no_kill:
+ /* Do not poll if kernel supports pidfd waiting */
+ if (is_waiting_for_kill()) {
+ /* Pause polling if we are waiting for process death notification */
+ poll_params->update = POLLING_PAUSE;
+ return;
+ }
+
/*
* Start polling after initial PSI event;
* extend polling while device is in direct reclaim or process is being killed;
@@ -2109,7 +2249,6 @@
union meminfo mi;
struct zoneinfo zi;
struct timespec curr_tm;
- static struct timespec last_kill_tm;
static unsigned long kill_skip_count = 0;
enum vmpressure_level level = (enum vmpressure_level)data;
long other_free = 0, other_file = 0;
@@ -2158,15 +2297,26 @@
return;
}
- if (kill_timeout_ms) {
- // If we're within the timeout, see if there's pending reclaim work
- // from the last killed process. If there is (as evidenced by
- // /proc/<pid> continuing to exist), skip killing for now.
- if ((get_time_diff_ms(&last_kill_tm, &curr_tm) < kill_timeout_ms) &&
- (low_ram_device || is_kill_pending())) {
+ if (kill_timeout_ms && get_time_diff_ms(&last_kill_tm, &curr_tm) < kill_timeout_ms) {
+ /*
+ * If we're within the no-kill timeout, see if there's pending reclaim work
+ * from the last killed process. If so, skip killing for now.
+ */
+ if (is_kill_pending()) {
kill_skip_count++;
return;
}
+ /*
+ * Process is dead, stop waiting. This has no effect if pidfds are supported and
+ * death notification already caused waiting to stop.
+ */
+ stop_wait_for_proc_kill(true);
+ } else {
+ /*
+ * Killing took longer than no-kill timeout. Stop waiting for the last process
+ * to die because we are ready to kill again.
+ */
+ stop_wait_for_proc_kill(false);
}
if (kill_skip_count > 0) {
@@ -2265,7 +2415,7 @@
do_kill:
if (low_ram_device) {
/* For Go devices kill only one task */
- if (find_and_kill_process(level_oomadj[level], -1, NULL, &mi) == 0) {
+ if (find_and_kill_process(level_oomadj[level], -1, NULL, &mi, &curr_tm) == 0) {
if (debug_process_killing) {
ALOGI("Nothing to kill");
}
@@ -2288,7 +2438,7 @@
min_score_adj = level_oomadj[level];
}
- pages_freed = find_and_kill_process(min_score_adj, -1, NULL, &mi);
+ pages_freed = find_and_kill_process(min_score_adj, -1, NULL, &mi, &curr_tm);
if (pages_freed == 0) {
/* Rate limit kill reports when nothing was reclaimed */
@@ -2296,9 +2446,6 @@
report_skip_count++;
return;
}
- } else {
- /* If we killed anything, update the last killed timestamp. */
- last_kill_tm = curr_tm;
}
/* Log whenever we kill or when report rate limit allows */
@@ -2321,6 +2468,10 @@
last_report_tm = curr_tm;
}
+ if (is_waiting_for_kill()) {
+ /* pause polling if we are waiting for process death notification */
+ poll_params->update = POLLING_PAUSE;
+ }
}
static bool init_mp_psi(enum vmpressure_level level, bool use_new_strategy) {
@@ -2472,6 +2623,7 @@
.fd = -1,
};
struct epoll_event epev;
+ int pidfd;
int i;
int ret;
@@ -2562,9 +2714,59 @@
ALOGE("Failed to read %s: %s", file_data.filename, strerror(errno));
}
+ /* check if kernel supports pidfd_open syscall */
+ pidfd = TEMP_FAILURE_RETRY(sys_pidfd_open(getpid(), 0));
+ if (pidfd < 0) {
+ pidfd_supported = (errno != ENOSYS);
+ } else {
+ pidfd_supported = true;
+ close(pidfd);
+ }
+ ALOGI("Process polling is %s", pidfd_supported ? "supported" : "not supported" );
+
return 0;
}
+static void call_handler(struct event_handler_info* handler_info,
+ struct polling_params *poll_params, uint32_t events) {
+ struct timespec curr_tm;
+
+ handler_info->handler(handler_info->data, events, poll_params);
+ clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm);
+ poll_params->last_poll_tm = curr_tm;
+
+ switch (poll_params->update) {
+ case POLLING_START:
+ /*
+ * Poll for the duration of PSI_WINDOW_SIZE_MS after the
+ * initial PSI event because psi events are rate-limited
+ * at one per sec.
+ */
+ poll_params->poll_start_tm = curr_tm;
+ poll_params->poll_handler = handler_info;
+ break;
+ case POLLING_STOP:
+ poll_params->poll_handler = NULL;
+ break;
+ case POLLING_PAUSE:
+ poll_params->paused_handler = handler_info;
+ poll_params->poll_handler = NULL;
+ break;
+ case POLLING_RESUME:
+ poll_params->poll_start_tm = curr_tm;
+ poll_params->poll_handler = poll_params->paused_handler;
+ break;
+ case POLLING_DO_NOT_CHANGE:
+ if (get_time_diff_ms(&poll_params->poll_start_tm, &curr_tm) > PSI_WINDOW_SIZE_MS) {
+ /* Polled for the duration of PSI window, time to stop */
+ poll_params->poll_handler = NULL;
+ }
+ /* WARNING: skipping the rest of the function */
+ return;
+ }
+ poll_params->update = POLLING_DO_NOT_CHANGE;
+}
+
static void mainloop(void) {
struct event_handler_info* handler_info;
struct polling_params poll_params;
@@ -2581,41 +2783,33 @@
int i;
if (poll_params.poll_handler) {
- /* Calculate next timeout */
- clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm);
- delay = get_time_diff_ms(&poll_params.last_poll_tm, &curr_tm);
- delay = (delay < poll_params.polling_interval_ms) ?
- poll_params.polling_interval_ms - delay : poll_params.polling_interval_ms;
-
- /* Wait for events until the next polling timeout */
- nevents = epoll_wait(epollfd, events, maxevents, delay);
+ bool poll_now;
clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm);
- if (get_time_diff_ms(&poll_params.last_poll_tm, &curr_tm) >=
- poll_params.polling_interval_ms) {
- /* Set input params for the call */
- poll_params.poll_handler->handler(poll_params.poll_handler->data, 0, &poll_params);
- poll_params.last_poll_tm = curr_tm;
+ if (poll_params.poll_handler == poll_params.paused_handler) {
+ /*
+ * Just transitioned into POLLING_RESUME. Reset paused_handler
+ * and poll immediately
+ */
+ poll_params.paused_handler = NULL;
+ poll_now = true;
+ nevents = 0;
+ } else {
+ /* Calculate next timeout */
+ delay = get_time_diff_ms(&poll_params.last_poll_tm, &curr_tm);
+ delay = (delay < poll_params.polling_interval_ms) ?
+ poll_params.polling_interval_ms - delay : poll_params.polling_interval_ms;
- if (poll_params.update != POLLING_DO_NOT_CHANGE) {
- switch (poll_params.update) {
- case POLLING_START:
- poll_params.poll_start_tm = curr_tm;
- break;
- case POLLING_STOP:
- poll_params.poll_handler = NULL;
- break;
- default:
- break;
- }
- poll_params.update = POLLING_DO_NOT_CHANGE;
- } else {
- if (get_time_diff_ms(&poll_params.poll_start_tm, &curr_tm) >
- PSI_WINDOW_SIZE_MS) {
- /* Polled for the duration of PSI window, time to stop */
- poll_params.poll_handler = NULL;
- }
- }
+ /* Wait for events until the next polling timeout */
+ nevents = epoll_wait(epollfd, events, maxevents, delay);
+
+ /* Update current time after wait */
+ clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm);
+ poll_now = (get_time_diff_ms(&poll_params.last_poll_tm, &curr_tm) >=
+ poll_params.polling_interval_ms);
+ }
+ if (poll_now) {
+ call_handler(poll_params.poll_handler, &poll_params, 0);
}
} else {
/* Wait for events with no timeout */
@@ -2655,29 +2849,7 @@
}
if (evt->data.ptr) {
handler_info = (struct event_handler_info*)evt->data.ptr;
- /* Set input params for the call */
- handler_info->handler(handler_info->data, evt->events, &poll_params);
-
- if (poll_params.update != POLLING_DO_NOT_CHANGE) {
- switch (poll_params.update) {
- case POLLING_START:
- /*
- * Poll for the duration of PSI_WINDOW_SIZE_MS after the
- * initial PSI event because psi events are rate-limited
- * at one per sec.
- */
- clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm);
- poll_params.poll_start_tm = poll_params.last_poll_tm = curr_tm;
- poll_params.poll_handler = handler_info;
- break;
- case POLLING_STOP:
- poll_params.poll_handler = NULL;
- break;
- default:
- break;
- }
- poll_params.update = POLLING_DO_NOT_CHANGE;
- }
+ call_handler(handler_info, &poll_params, evt->events);
}
}
}
diff --git a/logcat/Android.bp b/logcat/Android.bp
index e6b0c7d..61fba59 100644
--- a/logcat/Android.bp
+++ b/logcat/Android.bp
@@ -21,6 +21,7 @@
"-Wall",
"-Wextra",
"-Werror",
+ "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION=1",
],
shared_libs: [
"libbase",
@@ -35,19 +36,13 @@
defaults: ["logcat_defaults"],
srcs: [
- "logcat_main.cpp",
"logcat.cpp",
],
}
-cc_binary {
+sh_binary {
name: "logcatd",
-
- defaults: ["logcat_defaults"],
- srcs: [
- "logcatd_main.cpp",
- "logcat.cpp",
- ],
+ src: "logcatd",
}
sh_binary {
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 4dcb338..70ccb80 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -14,18 +14,12 @@
* limitations under the License.
*/
-#include "logcat.h"
-
-#include <android-base/macros.h>
-#include <arpa/inet.h>
-#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <math.h>
-#include <pthread.h>
#include <sched.h>
#include <stdarg.h>
#include <stdio.h>
@@ -34,13 +28,11 @@
#include <sys/cdefs.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
-#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
-#include <atomic>
#include <memory>
#include <regex>
#include <string>
@@ -48,11 +40,15 @@
#include <vector>
#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <cutils/sockets.h>
+#include <android-base/unique_fd.h>
+#include <android/log.h>
#include <log/event_tag_map.h>
+#include <log/log_id.h>
#include <log/logprint.h>
#include <private/android_logger.h>
#include <processgroup/sched_policy.h>
@@ -60,104 +56,61 @@
#define DEFAULT_MAX_ROTATED_LOGS 4
-struct log_device_t {
- const char* device;
- bool binary;
- struct logger* logger;
- struct logger_list* logger_list;
- bool printed;
+using android::base::Join;
+using android::base::ParseByteCount;
+using android::base::ParseUint;
+using android::base::Split;
+using android::base::StringPrintf;
- log_device_t* next;
+class Logcat {
+ public:
+ int Run(int argc, char** argv);
- log_device_t(const char* d, bool b) {
- device = d;
- binary = b;
- next = nullptr;
- printed = false;
- logger = nullptr;
- logger_list = nullptr;
- }
+ private:
+ void RotateLogs();
+ void ProcessBuffer(struct log_msg* buf);
+ void PrintDividers(log_id_t log_id, bool print_dividers);
+ void SetupOutputAndSchedulingPolicy(bool blocking);
+ int SetLogFormat(const char* format_string);
+
+ // Used for all options
+ android::base::unique_fd output_fd_{dup(STDOUT_FILENO)};
+ std::unique_ptr<AndroidLogFormat, decltype(&android_log_format_free)> logformat_{
+ android_log_format_new(), &android_log_format_free};
+
+ // For logging to a file and log rotation
+ const char* output_file_name_ = nullptr;
+ size_t log_rotate_size_kb_ = 0; // 0 means "no log rotation"
+ size_t max_rotated_logs_ = DEFAULT_MAX_ROTATED_LOGS; // 0 means "unbounded"
+ size_t out_byte_count_ = 0;
+
+ // For binary log buffers
+ int print_binary_ = 0;
+ std::unique_ptr<EventTagMap, decltype(&android_closeEventTagMap)> event_tag_map_{
+ nullptr, &android_closeEventTagMap};
+ bool has_opened_event_tag_map_ = false;
+
+ // For the related --regex, --max-count, --print
+ std::unique_ptr<std::regex> regex_;
+ size_t max_count_ = 0; // 0 means "infinite"
+ size_t print_count_ = 0;
+ bool print_it_anyways_ = false;
+
+ // For PrintDividers()
+ log_id_t last_printed_id_ = LOG_ID_MAX;
+ bool printed_start_[LOG_ID_MAX] = {};
+
+ bool debug_ = false;
};
-struct android_logcat_context_internal {
- // status
- volatile std::atomic_int retval; // valid if thread_stopped set
- // Arguments passed in, or copies and storage thereof if a thread.
- int argc;
- char* const* argv;
- char* const* envp;
- std::vector<std::string> args;
- std::vector<const char*> argv_hold;
- std::vector<std::string> envs;
- std::vector<const char*> envp_hold;
- int output_fd; // duplication of fileno(output) (below)
- int error_fd; // duplication of fileno(error) (below)
-
- // library
- int fds[2]; // From popen call
- FILE* output; // everything writes to fileno(output), buffer unused
- FILE* error; // unless error == output.
- pthread_t thr;
- volatile std::atomic_bool stop; // quick exit flag
- volatile std::atomic_bool thread_stopped;
- bool stderr_null; // shell "2>/dev/null"
- bool stderr_stdout; // shell "2>&1"
-
- // global variables
- AndroidLogFormat* logformat;
- const char* outputFileName;
- // 0 means "no log rotation"
- size_t logRotateSizeKBytes;
- // 0 means "unbounded"
- size_t maxRotatedLogs;
- size_t outByteCount;
- int printBinary;
- int devCount; // >1 means multiple
- std::unique_ptr<std::regex> regex;
- log_device_t* devices;
- EventTagMap* eventTagMap;
- // 0 means "infinite"
- size_t maxCount;
- size_t printCount;
-
- bool printItAnyways;
- bool debug;
- bool hasOpenedEventTagMap;
-};
-
-// Creates a context associated with this logcat instance
-android_logcat_context create_android_logcat() {
- android_logcat_context_internal* context;
-
- context = (android_logcat_context_internal*)calloc(
- 1, sizeof(android_logcat_context_internal));
- if (!context) return nullptr;
-
- context->fds[0] = -1;
- context->fds[1] = -1;
- context->output_fd = -1;
- context->error_fd = -1;
- context->maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS;
-
- context->argv_hold.clear();
- context->args.clear();
- context->envp_hold.clear();
- context->envs.clear();
-
- return (android_logcat_context)context;
-}
-
// logd prefixes records with a length field
#define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t)
-namespace android {
-
enum helpType { HELP_FALSE, HELP_TRUE, HELP_FORMAT };
-// if showHelp is set, newline required in fmt statement to transition to usage
-static void logcat_panic(android_logcat_context_internal* context,
- enum helpType showHelp, const char* fmt, ...)
- __printflike(3, 4);
+// if show_help is set, newline required in fmt statement to transition to usage
+static void LogcatPanic(enum helpType showHelp, const char* fmt, ...) __printflike(2, 3)
+ __attribute__((__noreturn__));
#ifndef F2FS_IOC_SET_PIN_FILE
#define F2FS_IOCTL_MAGIC 0xf5
@@ -165,7 +118,7 @@
#endif
static int openLogFile(const char* pathname, size_t sizeKB) {
- int fd = open(pathname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
+ int fd = open(pathname, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
if (fd < 0) {
return fd;
}
@@ -177,107 +130,29 @@
return fd;
}
-static void close_output(android_logcat_context_internal* context) {
- // split output_from_error
- if (context->error == context->output) {
- context->output = nullptr;
- context->output_fd = -1;
- }
- if (context->error && (context->output_fd == fileno(context->error))) {
- context->output_fd = -1;
- }
- if (context->output_fd == context->error_fd) {
- context->output_fd = -1;
- }
- // close output channel
- if (context->output) {
- if (context->output != stdout) {
- if (context->output_fd == fileno(context->output)) {
- context->output_fd = -1;
- }
- if (context->fds[1] == fileno(context->output)) {
- context->fds[1] = -1;
- }
- fclose(context->output);
- }
- context->output = nullptr;
- }
- if (context->output_fd >= 0) {
- if (context->output_fd != fileno(stdout)) {
- if (context->fds[1] == context->output_fd) {
- context->fds[1] = -1;
- }
- posix_fadvise(context->output_fd, 0, 0, POSIX_FADV_DONTNEED);
- close(context->output_fd);
- }
- context->output_fd = -1;
- }
-}
-
-static void close_error(android_logcat_context_internal* context) {
- // split error_from_output
- if (context->output == context->error) {
- context->error = nullptr;
- context->error_fd = -1;
- }
- if (context->output && (context->error_fd == fileno(context->output))) {
- context->error_fd = -1;
- }
- if (context->error_fd == context->output_fd) {
- context->error_fd = -1;
- }
- // close error channel
- if (context->error) {
- if ((context->error != stderr) && (context->error != stdout)) {
- if (context->error_fd == fileno(context->error)) {
- context->error_fd = -1;
- }
- if (context->fds[1] == fileno(context->error)) {
- context->fds[1] = -1;
- }
- fclose(context->error);
- }
- context->error = nullptr;
- }
- if (context->error_fd >= 0) {
- if ((context->error_fd != fileno(stdout)) &&
- (context->error_fd != fileno(stderr))) {
- if (context->fds[1] == context->error_fd) context->fds[1] = -1;
- close(context->error_fd);
- }
- context->error_fd = -1;
- }
-}
-
-static void rotateLogs(android_logcat_context_internal* context) {
- int err;
-
+void Logcat::RotateLogs() {
// Can't rotate logs if we're not outputting to a file
- if (!context->outputFileName) return;
+ if (!output_file_name_) return;
- close_output(context);
+ output_fd_.reset();
// Compute the maximum number of digits needed to count up to
// maxRotatedLogs in decimal. eg:
// maxRotatedLogs == 30
// -> log10(30) == 1.477
// -> maxRotationCountDigits == 2
- int maxRotationCountDigits =
- (context->maxRotatedLogs > 0)
- ? (int)(floor(log10(context->maxRotatedLogs) + 1))
- : 0;
+ int max_rotation_count_digits =
+ max_rotated_logs_ > 0 ? (int)(floor(log10(max_rotated_logs_) + 1)) : 0;
- for (int i = context->maxRotatedLogs; i > 0; i--) {
- std::string file1 = android::base::StringPrintf(
- "%s.%.*d", context->outputFileName, maxRotationCountDigits, i);
+ for (int i = max_rotated_logs_; i > 0; i--) {
+ std::string file1 =
+ StringPrintf("%s.%.*d", output_file_name_, max_rotation_count_digits, i);
std::string file0;
if (!(i - 1)) {
- file0 = android::base::StringPrintf("%s", context->outputFileName);
+ file0 = output_file_name_;
} else {
- file0 =
- android::base::StringPrintf("%s.%.*d", context->outputFileName,
- maxRotationCountDigits, i - 1);
+ file0 = StringPrintf("%s.%.*d", output_file_name_, max_rotation_count_digits, i - 1);
}
if (!file0.length() || !file1.length()) {
@@ -285,170 +160,129 @@
break;
}
- err = rename(file0.c_str(), file1.c_str());
+ int err = rename(file0.c_str(), file1.c_str());
if (err < 0 && errno != ENOENT) {
perror("while rotating log files");
}
}
- context->output_fd = openLogFile(context->outputFileName, context->logRotateSizeKBytes);
+ output_fd_.reset(openLogFile(output_file_name_, log_rotate_size_kb_));
- if (context->output_fd < 0) {
- logcat_panic(context, HELP_FALSE, "couldn't open output file");
- return;
- }
- context->output = fdopen(context->output_fd, "web");
- if (!context->output) {
- logcat_panic(context, HELP_FALSE, "couldn't fdopen output file");
- return;
- }
- if (context->stderr_stdout) {
- close_error(context);
- context->error = context->output;
- context->error_fd = context->output_fd;
+ if (!output_fd_.ok()) {
+ LogcatPanic(HELP_FALSE, "couldn't open output file");
}
- context->outByteCount = 0;
+ out_byte_count_ = 0;
}
-void printBinary(android_logcat_context_internal* context, struct log_msg* buf) {
- size_t size = buf->len();
-
- TEMP_FAILURE_RETRY(write(context->output_fd, buf, size));
-}
-
-static bool regexOk(android_logcat_context_internal* context,
- const AndroidLogEntry& entry) {
- if (!context->regex) return true;
-
- return std::regex_search(entry.message, entry.message + entry.messageLen, *context->regex);
-}
-
-static void processBuffer(android_logcat_context_internal* context,
- log_device_t* dev, struct log_msg* buf) {
+void Logcat::ProcessBuffer(struct log_msg* buf) {
int bytesWritten = 0;
int err;
AndroidLogEntry entry;
char binaryMsgBuf[1024];
- if (dev->binary) {
- if (!context->eventTagMap && !context->hasOpenedEventTagMap) {
- context->eventTagMap = android_openEventTagMap(nullptr);
- context->hasOpenedEventTagMap = true;
+ bool is_binary =
+ buf->id() == LOG_ID_EVENTS || buf->id() == LOG_ID_STATS || buf->id() == LOG_ID_SECURITY;
+
+ if (is_binary) {
+ if (!event_tag_map_ && !has_opened_event_tag_map_) {
+ event_tag_map_.reset(android_openEventTagMap(nullptr));
+ has_opened_event_tag_map_ = true;
}
- err = android_log_processBinaryLogBuffer(
- &buf->entry_v1, &entry, context->eventTagMap, binaryMsgBuf,
- sizeof(binaryMsgBuf));
+ err = android_log_processBinaryLogBuffer(&buf->entry, &entry, event_tag_map_.get(),
+ binaryMsgBuf, sizeof(binaryMsgBuf));
// printf(">>> pri=%d len=%d msg='%s'\n",
// entry.priority, entry.messageLen, entry.message);
} else {
- err = android_log_processLogBuffer(&buf->entry_v1, &entry);
+ err = android_log_processLogBuffer(&buf->entry, &entry);
}
- if ((err < 0) && !context->debug) return;
+ if (err < 0 && !debug_) return;
- if (android_log_shouldPrintLine(
- context->logformat, std::string(entry.tag, entry.tagLen).c_str(),
- entry.priority)) {
- bool match = regexOk(context, entry);
+ if (android_log_shouldPrintLine(logformat_.get(), std::string(entry.tag, entry.tagLen).c_str(),
+ entry.priority)) {
+ bool match = !regex_ ||
+ std::regex_search(entry.message, entry.message + entry.messageLen, *regex_);
- context->printCount += match;
- if (match || context->printItAnyways) {
- bytesWritten = android_log_printLogLine(context->logformat,
- context->output_fd, &entry);
+ print_count_ += match;
+ if (match || print_it_anyways_) {
+ bytesWritten = android_log_printLogLine(logformat_.get(), output_fd_.get(), &entry);
if (bytesWritten < 0) {
- logcat_panic(context, HELP_FALSE, "output error");
- return;
+ LogcatPanic(HELP_FALSE, "output error");
}
}
}
- context->outByteCount += bytesWritten;
+ out_byte_count_ += bytesWritten;
- if (context->logRotateSizeKBytes > 0 &&
- (context->outByteCount / 1024) >= context->logRotateSizeKBytes) {
- rotateLogs(context);
+ if (log_rotate_size_kb_ > 0 && (out_byte_count_ / 1024) >= log_rotate_size_kb_) {
+ RotateLogs();
}
}
-static void maybePrintStart(android_logcat_context_internal* context,
- log_device_t* dev, bool printDividers) {
- if (!dev->printed || printDividers) {
- if (context->devCount > 1 && !context->printBinary) {
- char buf[1024];
- snprintf(buf, sizeof(buf), "--------- %s %s\n",
- dev->printed ? "switch to" : "beginning of", dev->device);
- if (write(context->output_fd, buf, strlen(buf)) < 0) {
- logcat_panic(context, HELP_FALSE, "output error");
- return;
- }
+void Logcat::PrintDividers(log_id_t log_id, bool print_dividers) {
+ if (log_id == last_printed_id_ || print_binary_) {
+ return;
+ }
+ if (!printed_start_[log_id] || print_dividers) {
+ if (dprintf(output_fd_.get(), "--------- %s %s\n",
+ printed_start_[log_id] ? "switch to" : "beginning of",
+ android_log_id_to_name(log_id)) < 0) {
+ LogcatPanic(HELP_FALSE, "output error");
}
- dev->printed = true;
}
+ last_printed_id_ = log_id;
+ printed_start_[log_id] = true;
}
-static void setupOutputAndSchedulingPolicy(
- android_logcat_context_internal* context, bool blocking) {
- if (!context->outputFileName) return;
+void Logcat::SetupOutputAndSchedulingPolicy(bool blocking) {
+ if (!output_file_name_) return;
if (blocking) {
// Lower priority and set to batch scheduling if we are saving
// the logs into files and taking continuous content.
- if ((set_sched_policy(0, SP_BACKGROUND) < 0) && context->error) {
- fprintf(context->error,
- "failed to set background scheduling policy\n");
+ if (set_sched_policy(0, SP_BACKGROUND) < 0) {
+ fprintf(stderr, "failed to set background scheduling policy\n");
}
- struct sched_param param;
- memset(¶m, 0, sizeof(param));
+ struct sched_param param = {};
if (sched_setscheduler((pid_t)0, SCHED_BATCH, ¶m) < 0) {
fprintf(stderr, "failed to set to batch scheduler\n");
}
- if ((setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) &&
- context->error) {
- fprintf(context->error, "failed set to priority\n");
+ if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
+ fprintf(stderr, "failed set to priority\n");
}
}
- close_output(context);
+ output_fd_.reset(openLogFile(output_file_name_, log_rotate_size_kb_));
- context->output_fd = openLogFile(context->outputFileName, context->logRotateSizeKBytes);
-
- if (context->output_fd < 0) {
- logcat_panic(context, HELP_FALSE, "couldn't open output file");
- return;
+ if (!output_fd_.ok()) {
+ LogcatPanic(HELP_FALSE, "couldn't open output file");
}
struct stat statbuf;
- if (fstat(context->output_fd, &statbuf) == -1) {
- close_output(context);
- logcat_panic(context, HELP_FALSE, "couldn't get output file stat\n");
- return;
+ if (fstat(output_fd_.get(), &statbuf) == -1) {
+ output_fd_.reset();
+ LogcatPanic(HELP_FALSE, "couldn't get output file stat\n");
}
if ((size_t)statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) {
- close_output(context);
- logcat_panic(context, HELP_FALSE, "invalid output file stat\n");
- return;
+ output_fd_.reset();
+ LogcatPanic(HELP_FALSE, "invalid output file stat\n");
}
- context->output = fdopen(context->output_fd, "web");
-
- context->outByteCount = statbuf.st_size;
+ out_byte_count_ = statbuf.st_size;
}
// clang-format off
-static void show_help(android_logcat_context_internal* context) {
- if (!context->error) return;
+static void show_help() {
+ const char* cmd = getprogname();
- const char* cmd = strrchr(context->argv[0], '/');
- cmd = cmd ? cmd + 1 : context->argv[0];
+ fprintf(stderr, "Usage: %s [options] [filterspecs]\n", cmd);
- fprintf(context->error, "Usage: %s [options] [filterspecs]\n", cmd);
-
- fprintf(context->error, "options include:\n"
+ fprintf(stderr, "options include:\n"
" -s Set default filter to silent. Equivalent to filterspec '*:S'\n"
" -f <file>, --file=<file> Log to file. Default is stdout\n"
" -r <kbytes>, --rotate-kbytes=<kbytes>\n"
@@ -515,7 +349,7 @@
" comes first. Improves efficiency of polling by providing\n"
" an about-to-wrap wakeup.\n");
- fprintf(context->error, "\nfilterspecs are a series of \n"
+ fprintf(stderr, "\nfilterspecs are a series of \n"
" <tag>[:priority]\n\n"
"where <tag> is a log component tag (or * for all) and priority is:\n"
" V Verbose (default for <tag>)\n"
@@ -533,9 +367,8 @@
"or defaults to \"threadtime\"\n\n");
}
-static void show_format_help(android_logcat_context_internal* context) {
- if (!context->error) return;
- fprintf(context->error,
+static void show_format_help() {
+ fprintf(stderr,
"-v <format>, --format=<format> options:\n"
" Sets log print format verb and adverbs, where <format> is:\n"
" brief long process raw tag thread threadtime time\n"
@@ -569,16 +402,13 @@
}
// clang-format on
-static int setLogFormat(android_logcat_context_internal* context,
- const char* formatString) {
- AndroidLogPrintFormat format;
-
- format = android_log_formatFromString(formatString);
+int Logcat::SetLogFormat(const char* format_string) {
+ AndroidLogPrintFormat format = android_log_formatFromString(format_string);
// invalid string?
if (format == FORMAT_OFF) return -1;
- return android_log_setPrintFormat(context->logformat, format);
+ return android_log_setPrintFormat(logformat_.get(), format);
}
static std::pair<unsigned long, const char*> format_of_size(unsigned long value) {
@@ -591,49 +421,25 @@
return std::make_pair(value, multipliers[i]);
}
-// String to unsigned int, returns -1 if it fails
-static bool getSizeTArg(const char* ptr, size_t* val, size_t min = 0,
- size_t max = SIZE_MAX) {
- if (!ptr) return false;
-
- char* endp;
- errno = 0;
- size_t ret = (size_t)strtoll(ptr, &endp, 0);
-
- if (endp[0] || errno) return false;
-
- if ((ret > max) || (ret < min)) return false;
-
- *val = ret;
- return true;
-}
-
-static void logcat_panic(android_logcat_context_internal* context,
- enum helpType showHelp, const char* fmt, ...) {
- context->retval = EXIT_FAILURE;
- if (!context->error) {
- context->stop = true;
- return;
- }
-
+static void LogcatPanic(enum helpType showHelp, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
- vfprintf(context->error, fmt, args);
+ vfprintf(stderr, fmt, args);
va_end(args);
switch (showHelp) {
case HELP_TRUE:
- show_help(context);
+ show_help();
break;
case HELP_FORMAT:
- show_format_help(context);
+ show_format_help();
break;
case HELP_FALSE:
default:
break;
}
- context->stop = true;
+ exit(EXIT_FAILURE);
}
static char* parseTime(log_time& t, const char* cp) {
@@ -713,32 +519,18 @@
return retval;
}
-const char* getenv(android_logcat_context_internal* context, const char* name) {
- if (!context->envp || !name || !*name) return nullptr;
-
- for (size_t len = strlen(name), i = 0; context->envp[i]; ++i) {
- if (strncmp(context->envp[i], name, len)) continue;
- if (context->envp[i][len] == '=') return &context->envp[i][len + 1];
- }
- return nullptr;
-}
-
-} // namespace android
-
-void reportErrorName(const char** current, const char* name,
- bool blockSecurity) {
- if (*current) return;
- if (!blockSecurity || (android_name_to_log_id(name) != LOG_ID_SECURITY)) {
- *current = name;
+void ReportErrorName(const std::string& name, bool allow_security,
+ std::vector<std::string>* errors) {
+ if (allow_security || name != "security") {
+ errors->emplace_back(name);
}
}
-static int __logcat(android_logcat_context_internal* context) {
- using namespace android;
- int err;
+int Logcat::Run(int argc, char** argv) {
bool hasSetLogFormat = false;
bool clearLog = false;
- bool allSelected = false;
+ bool security_buffer_selected =
+ false; // Do not report errors on the security buffer unless it is explicitly named.
bool getLogSize = false;
bool getPruneList = false;
bool printStatistics = false;
@@ -748,114 +540,15 @@
const char* setId = nullptr;
int mode = ANDROID_LOG_RDONLY;
std::string forceFilters;
- log_device_t* dev;
- struct logger_list* logger_list;
size_t tail_lines = 0;
log_time tail_time(log_time::EPOCH);
size_t pid = 0;
bool got_t = false;
-
- // object instantiations before goto's can happen
- log_device_t unexpected("unexpected", false);
- const char* openDeviceFail = nullptr;
- const char* clearFail = nullptr;
- const char* setSizeFail = nullptr;
- const char* getSizeFail = nullptr;
- int argc = context->argc;
- char* const* argv = context->argv;
-
- context->output = stdout;
- context->error = stderr;
-
- for (int i = 0; i < argc; ++i) {
- // Simulate shell stderr redirect parsing
- if ((argv[i][0] != '2') || (argv[i][1] != '>')) continue;
-
- // Append to file not implemented, just open file
- size_t skip = (argv[i][2] == '>') + 2;
- if (!strcmp(&argv[i][skip], "/dev/null")) {
- context->stderr_null = true;
- } else if (!strcmp(&argv[i][skip], "&1")) {
- context->stderr_stdout = true;
- } else {
- // stderr file redirections are not supported
- fprintf(context->stderr_stdout ? stdout : stderr,
- "stderr redirection to file %s unsupported, skipping\n",
- &argv[i][skip]);
- }
- // Only the first one
- break;
- }
-
- const char* filename = nullptr;
- for (int i = 0; i < argc; ++i) {
- // Simulate shell stdout redirect parsing
- if (argv[i][0] != '>') continue;
-
- // Append to file not implemented, just open file
- filename = &argv[i][(argv[i][1] == '>') + 1];
- // Only the first one
- break;
- }
-
- // Deal with setting up file descriptors and FILE pointers
- if (context->error_fd >= 0) { // Is an error file descriptor supplied?
- if (context->error_fd == context->output_fd) {
- context->stderr_stdout = true;
- } else if (context->stderr_null) { // redirection told us to close it
- close(context->error_fd);
- context->error_fd = -1;
- } else { // All Ok, convert error to a FILE pointer
- context->error = fdopen(context->error_fd, "web");
- if (!context->error) {
- context->retval = -errno;
- fprintf(context->stderr_stdout ? stdout : stderr,
- "Failed to fdopen(error_fd=%d) %s\n", context->error_fd,
- strerror(errno));
- goto exit;
- }
- }
- }
- if (context->output_fd >= 0) { // Is an output file descriptor supplied?
- if (filename) { // redirect to file, close supplied file descriptor.
- close(context->output_fd);
- context->output_fd = -1;
- } else { // All Ok, convert output to a FILE pointer
- context->output = fdopen(context->output_fd, "web");
- if (!context->output) {
- context->retval = -errno;
- fprintf(context->stderr_stdout ? stdout : context->error,
- "Failed to fdopen(output_fd=%d) %s\n",
- context->output_fd, strerror(errno));
- goto exit;
- }
- }
- }
- if (filename) { // We supplied an output file redirected in command line
- context->output = fopen(filename, "web");
- }
- // Deal with 2>&1
- if (context->stderr_stdout) context->error = context->output;
- // Deal with 2>/dev/null
- if (context->stderr_null) {
- context->error_fd = -1;
- context->error = nullptr;
- }
- // Only happens if output=stdout or output=filename
- if ((context->output_fd < 0) && context->output) {
- context->output_fd = fileno(context->output);
- }
- // Only happens if error=stdout || error=stderr
- if ((context->error_fd < 0) && context->error) {
- context->error_fd = fileno(context->error);
- }
-
- context->logformat = android_log_format_new();
+ unsigned id_mask = 0;
if (argc == 2 && !strcmp(argv[1], "--help")) {
- show_help(context);
- context->retval = EXIT_SUCCESS;
- goto exit;
+ show_help();
+ return EXIT_SUCCESS;
}
// meant to catch comma-delimited values, but cast a wider
@@ -913,15 +606,13 @@
// only long options
if (long_options[option_index].name == pid_str) {
if (pid != 0) {
- logcat_panic(context, HELP_TRUE, "Only supports one PID argument.\n");
- goto exit;
+ LogcatPanic(HELP_TRUE, "Only supports one PID argument.\n");
}
// ToDo: determine runtime PID_MAX?
- if (!getSizeTArg(optarg, &pid, 1)) {
- logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
- long_options[option_index].name, optarg);
- goto exit;
+ if (!ParseUint(optarg, &pid) || pid < 1) {
+ LogcatPanic(HELP_TRUE, "%s %s out of range\n",
+ long_options[option_index].name, optarg);
}
break;
}
@@ -930,26 +621,23 @@
ANDROID_LOG_NONBLOCK;
// ToDo: implement API that supports setting a wrap timeout
size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
- if (optarg && !getSizeTArg(optarg, &dummy, 1)) {
- logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
- long_options[option_index].name, optarg);
- goto exit;
+ if (optarg && (!ParseUint(optarg, &dummy) || dummy < 1)) {
+ LogcatPanic(HELP_TRUE, "%s %s out of range\n",
+ long_options[option_index].name, optarg);
}
- if ((dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) &&
- context->error) {
- fprintf(context->error,
- "WARNING: %s %u seconds, ignoring %zu\n",
- long_options[option_index].name,
- ANDROID_LOG_WRAP_DEFAULT_TIMEOUT, dummy);
+ if (dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) {
+ fprintf(stderr, "WARNING: %s %u seconds, ignoring %zu\n",
+ long_options[option_index].name, ANDROID_LOG_WRAP_DEFAULT_TIMEOUT,
+ dummy);
}
break;
}
if (long_options[option_index].name == print_str) {
- context->printItAnyways = true;
+ print_it_anyways_ = true;
break;
}
if (long_options[option_index].name == debug_str) {
- context->debug = true;
+ debug_ = true;
break;
}
if (long_options[option_index].name == id_str) {
@@ -959,7 +647,7 @@
case 's':
// default to all silent
- android_log_addFilterRule(context->logformat, "*:s");
+ android_log_addFilterRule(logformat_.get(), "*:s");
break;
case 'c':
@@ -984,25 +672,18 @@
if (strspn(optarg, "0123456789") != strlen(optarg)) {
char* cp = parseTime(tail_time, optarg);
if (!cp) {
- logcat_panic(context, HELP_FALSE, "-%c \"%s\" not in time format\n", c,
- optarg);
- goto exit;
+ LogcatPanic(HELP_FALSE, "-%c \"%s\" not in time format\n", c, optarg);
}
if (*cp) {
char ch = *cp;
*cp = '\0';
- if (context->error) {
- fprintf(context->error, "WARNING: -%c \"%s\"\"%c%s\" time truncated\n",
- c, optarg, ch, cp + 1);
- }
+ fprintf(stderr, "WARNING: -%c \"%s\"\"%c%s\" time truncated\n", c, optarg,
+ ch, cp + 1);
*cp = ch;
}
} else {
- if (!getSizeTArg(optarg, &tail_lines, 1)) {
- if (context->error) {
- fprintf(context->error, "WARNING: -%c %s invalid, setting to 1\n", c,
- optarg);
- }
+ if (!ParseUint(optarg, &tail_lines) || tail_lines < 1) {
+ fprintf(stderr, "WARNING: -%c %s invalid, setting to 1\n", c, optarg);
tail_lines = 1;
}
}
@@ -1013,14 +694,13 @@
break;
case 'e':
- context->regex.reset(new std::regex(optarg));
+ regex_.reset(new std::regex(optarg));
break;
case 'm': {
- if (!getSizeTArg(optarg, &context->maxCount)) {
- logcat_panic(context, HELP_FALSE,
- "-%c \"%s\" isn't an integer greater than zero\n", c, optarg);
- goto exit;
+ if (!ParseUint(optarg, &max_count_) || max_count_ < 1) {
+ LogcatPanic(HELP_FALSE, "-%c \"%s\" isn't an integer greater than zero\n", c,
+ optarg);
}
} break;
@@ -1032,37 +712,8 @@
FALLTHROUGH_INTENDED;
case 'G': {
- char* cp;
- if (strtoll(optarg, &cp, 0) > 0) {
- setLogSize = strtoll(optarg, &cp, 0);
- } else {
- setLogSize = 0;
- }
-
- switch (*cp) {
- case 'g':
- case 'G':
- setLogSize *= 1024;
- FALLTHROUGH_INTENDED;
- case 'm':
- case 'M':
- setLogSize *= 1024;
- FALLTHROUGH_INTENDED;
- case 'k':
- case 'K':
- setLogSize *= 1024;
- FALLTHROUGH_INTENDED;
- case '\0':
- break;
-
- default:
- setLogSize = 0;
- }
-
- if (!setLogSize) {
- logcat_panic(context, HELP_FALSE,
- "ERROR: -G <num><multiplier>\n");
- goto exit;
+ if (!ParseByteCount(optarg, &setLogSize) || setLogSize < 1) {
+ LogcatPanic(HELP_FALSE, "ERROR: -G <num><multiplier>\n");
}
} break;
@@ -1077,67 +728,27 @@
setPruneList = optarg;
break;
- case 'b': {
- std::unique_ptr<char, void (*)(void*)> buffers(strdup(optarg), free);
- char* arg = buffers.get();
- unsigned idMask = 0;
- char* sv = nullptr; // protect against -ENOMEM above
- while (!!(arg = strtok_r(arg, delimiters, &sv))) {
- if (!strcmp(arg, "default")) {
- idMask |= (1 << LOG_ID_MAIN) | (1 << LOG_ID_SYSTEM) |
- (1 << LOG_ID_CRASH);
- } else if (!strcmp(arg, "all")) {
- allSelected = true;
- idMask = (unsigned)-1;
+ case 'b':
+ for (const auto& buffer : Split(optarg, delimiters)) {
+ if (buffer == "default") {
+ id_mask |= (1 << LOG_ID_MAIN) | (1 << LOG_ID_SYSTEM) | (1 << LOG_ID_CRASH);
+ } else if (buffer == "all") {
+ id_mask = -1;
} else {
- log_id_t log_id = android_name_to_log_id(arg);
- const char* name = android_log_id_to_name(log_id);
-
- if (!!strcmp(name, arg)) {
- logcat_panic(context, HELP_TRUE,
- "unknown buffer %s\n", arg);
- goto exit;
+ log_id_t log_id = android_name_to_log_id(buffer.c_str());
+ if (log_id >= LOG_ID_MAX) {
+ LogcatPanic(HELP_TRUE, "unknown buffer %s\n", buffer.c_str());
}
- if (log_id == LOG_ID_SECURITY) allSelected = false;
- idMask |= (1 << log_id);
- }
- arg = nullptr;
- }
-
- for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
- const char* name = android_log_id_to_name((log_id_t)i);
- log_id_t log_id = android_name_to_log_id(name);
-
- if (log_id != (log_id_t)i) continue;
- if (!(idMask & (1 << i))) continue;
-
- bool found = false;
- for (dev = context->devices; dev; dev = dev->next) {
- if (!strcmp(name, dev->device)) {
- found = true;
- break;
+ if (log_id == LOG_ID_SECURITY) {
+ security_buffer_selected = true;
}
- if (!dev->next) break;
+ id_mask |= (1 << log_id);
}
- if (found) continue;
-
- bool binary = !strcmp(name, "events") ||
- !strcmp(name, "security") ||
- !strcmp(name, "stats");
- log_device_t* d = new log_device_t(name, binary);
-
- if (dev) {
- dev->next = d;
- dev = d;
- } else {
- context->devices = dev = d;
- }
- context->devCount++;
}
- } break;
+ break;
case 'B':
- context->printBinary = 1;
+ print_binary_ = 1;
break;
case 'f':
@@ -1145,43 +756,34 @@
tail_time = lastLogTime(optarg);
}
// redirect output to a file
- context->outputFileName = optarg;
+ output_file_name_ = optarg;
break;
case 'r':
- if (!getSizeTArg(optarg, &context->logRotateSizeKBytes, 1)) {
- logcat_panic(context, HELP_TRUE, "Invalid parameter \"%s\" to -r\n", optarg);
- goto exit;
+ if (!ParseUint(optarg, &log_rotate_size_kb_) || log_rotate_size_kb_ < 1) {
+ LogcatPanic(HELP_TRUE, "Invalid parameter \"%s\" to -r\n", optarg);
}
break;
case 'n':
- if (!getSizeTArg(optarg, &context->maxRotatedLogs, 1)) {
- logcat_panic(context, HELP_TRUE, "Invalid parameter \"%s\" to -n\n", optarg);
- goto exit;
+ if (!ParseUint(optarg, &max_rotated_logs_) || max_rotated_logs_ < 1) {
+ LogcatPanic(HELP_TRUE, "Invalid parameter \"%s\" to -n\n", optarg);
}
break;
- case 'v': {
+ case 'v':
if (!strcmp(optarg, "help") || !strcmp(optarg, "--help")) {
- show_format_help(context);
- context->retval = EXIT_SUCCESS;
- goto exit;
+ show_format_help();
+ return EXIT_SUCCESS;
}
- std::unique_ptr<char, void (*)(void*)> formats(strdup(optarg), free);
- char* arg = formats.get();
- char* sv = nullptr; // protect against -ENOMEM above
- while (!!(arg = strtok_r(arg, delimiters, &sv))) {
- err = setLogFormat(context, arg);
+ for (const auto& arg : Split(optarg, delimiters)) {
+ int err = SetLogFormat(arg.c_str());
if (err < 0) {
- logcat_panic(context, HELP_FORMAT,
- "Invalid parameter \"%s\" to -v\n", arg);
- goto exit;
+ LogcatPanic(HELP_FORMAT, "Invalid parameter \"%s\" to -v\n", arg.c_str());
}
- arg = nullptr;
if (err) hasSetLogFormat = true;
}
- } break;
+ break;
case 'Q':
#define LOGCAT_FILTER "androidboot.logcat="
@@ -1199,8 +801,7 @@
{
// if not in emulator, exit quietly
if (false == android::base::GetBoolProperty(QEMU_PROPERTY, false)) {
- context->retval = EXIT_SUCCESS;
- goto exit;
+ return EXIT_SUCCESS;
}
std::string cmdline = android::base::GetProperty(QEMU_CMDLINE, "");
@@ -1211,8 +812,7 @@
const char* logcatFilter = strstr(cmdline.c_str(), LOGCAT_FILTER);
// if nothing found or invalid filters, exit quietly
if (!logcatFilter) {
- context->retval = EXIT_SUCCESS;
- goto exit;
+ return EXIT_SUCCESS;
}
const char* p = logcatFilter + strlen(LOGCAT_FILTER);
@@ -1231,8 +831,7 @@
} else if (console) {
p = console + strlen(CONSOLE_OPTION);
} else {
- context->retval = EXIT_FAILURE;
- goto exit;
+ return EXIT_FAILURE;
}
q = strpbrk(p, " \t\n\r");
@@ -1249,37 +848,26 @@
devname = devname.substr(0, pos);
}
}
- cmdline.erase();
- if (context->error) {
- fprintf(context->error, "logcat using %s\n",
- devname.c_str());
+ fprintf(stderr, "logcat using %s\n", devname.c_str());
+
+ int fd = open(devname.c_str(), O_WRONLY | O_CLOEXEC);
+ if (fd < 0) {
+ break;
}
- FILE* fp = fopen(devname.c_str(), "web");
- devname.erase();
- if (!fp) break;
-
if (consolePipe) {
// need the trailing '\0'
- if(!android::base::WriteFully(fileno(fp), pipePurpose.c_str(),
- pipePurpose.size() + 1)) {
- fclose(fp);
- context->retval = EXIT_FAILURE;
- goto exit;
+ if (!android::base::WriteFully(fd, pipePurpose.c_str(),
+ pipePurpose.size() + 1)) {
+ close(fd);
+ return EXIT_FAILURE;
}
}
-
// close output and error channels, replace with console
- android::close_output(context);
- android::close_error(context);
- context->stderr_stdout = true;
- context->output = fp;
- context->output_fd = fileno(fp);
- if (context->stderr_null) break;
- context->stderr_stdout = true;
- context->error = fp;
- context->error_fd = fileno(fp);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ close(fd);
}
break;
@@ -1288,69 +876,48 @@
break;
case ':':
- logcat_panic(context, HELP_TRUE, "Option -%c needs an argument\n", optopt);
- goto exit;
+ LogcatPanic(HELP_TRUE, "Option -%c needs an argument\n", optopt);
case 'h':
- show_help(context);
- show_format_help(context);
- goto exit;
+ show_help();
+ show_format_help();
+ return EXIT_SUCCESS;
default:
- logcat_panic(context, HELP_TRUE, "Unrecognized Option %c\n", optopt);
- goto exit;
+ LogcatPanic(HELP_TRUE, "Unrecognized Option %c\n", optopt);
}
}
- if (context->maxCount && got_t) {
- logcat_panic(context, HELP_TRUE,
- "Cannot use -m (--max-count) and -t together\n");
- goto exit;
+ if (max_count_ && got_t) {
+ LogcatPanic(HELP_TRUE, "Cannot use -m (--max-count) and -t together\n");
}
- if (context->printItAnyways && (!context->regex || !context->maxCount)) {
+ if (print_it_anyways_ && (!regex_ || !max_count_)) {
// One day it would be nice if --print -v color and --regex <expr>
// could play with each other and show regex highlighted content.
- // clang-format off
- if (context->error) {
- fprintf(context->error, "WARNING: "
- "--print ignored, to be used in combination with\n"
- " "
- "--regex <expr> and --max-count <N>\n");
- }
- context->printItAnyways = false;
+ fprintf(stderr,
+ "WARNING: "
+ "--print ignored, to be used in combination with\n"
+ " "
+ "--regex <expr> and --max-count <N>\n");
+ print_it_anyways_ = false;
}
- if (!context->devices) {
- dev = context->devices = new log_device_t("main", false);
- context->devCount = 1;
- if (android_name_to_log_id("system") == LOG_ID_SYSTEM) {
- dev = dev->next = new log_device_t("system", false);
- context->devCount++;
- }
- if (android_name_to_log_id("crash") == LOG_ID_CRASH) {
- dev = dev->next = new log_device_t("crash", false);
- context->devCount++;
- }
- if (android_name_to_log_id("kernel") == LOG_ID_KERNEL) {
- dev = dev->next = new log_device_t("kernel", false);
- context->devCount++;
- }
+ // If no buffers are specified, default to using these buffers.
+ if (id_mask == 0) {
+ id_mask = (1 << LOG_ID_MAIN) | (1 << LOG_ID_SYSTEM) | (1 << LOG_ID_CRASH) |
+ (1 << LOG_ID_KERNEL);
}
- if (!!context->logRotateSizeKBytes && !context->outputFileName) {
- logcat_panic(context, HELP_TRUE, "-r requires -f as well\n");
- goto exit;
+ if (log_rotate_size_kb_ != 0 && !output_file_name_) {
+ LogcatPanic(HELP_TRUE, "-r requires -f as well\n");
}
- if (!!setId) {
- if (!context->outputFileName) {
- logcat_panic(context, HELP_TRUE,
- "--id='%s' requires -f as well\n", setId);
- goto exit;
+ if (setId != 0) {
+ if (!output_file_name_) {
+ LogcatPanic(HELP_TRUE, "--id='%s' requires -f as well\n", setId);
}
- std::string file_name = android::base::StringPrintf(
- "%s.id", context->outputFileName);
+ std::string file_name = StringPrintf("%s.id", output_file_name_);
std::string file;
bool file_ok = android::base::ReadFileToString(file_name, &file);
android::base::WriteStringToFile(setId, file_name, S_IRUSR | S_IWUSR,
@@ -1359,175 +926,149 @@
}
if (!hasSetLogFormat) {
- const char* logFormat = android::getenv(context, "ANDROID_PRINTF_LOG");
+ const char* logFormat = getenv("ANDROID_PRINTF_LOG");
if (!!logFormat) {
- std::unique_ptr<char, void (*)(void*)> formats(strdup(logFormat),
- free);
- char* sv = nullptr; // protect against -ENOMEM above
- char* arg = formats.get();
- while (!!(arg = strtok_r(arg, delimiters, &sv))) {
- err = setLogFormat(context, arg);
+ for (const auto& arg : Split(logFormat, delimiters)) {
+ int err = SetLogFormat(arg.c_str());
// environment should not cause crash of logcat
- if ((err < 0) && context->error) {
- fprintf(context->error,
- "invalid format in ANDROID_PRINTF_LOG '%s'\n", arg);
+ if (err < 0) {
+ fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n", arg.c_str());
}
- arg = nullptr;
if (err > 0) hasSetLogFormat = true;
}
}
if (!hasSetLogFormat) {
- setLogFormat(context, "threadtime");
+ SetLogFormat("threadtime");
}
}
if (forceFilters.size()) {
- err = android_log_addFilterString(context->logformat,
- forceFilters.c_str());
+ int err = android_log_addFilterString(logformat_.get(), forceFilters.c_str());
if (err < 0) {
- logcat_panic(context, HELP_FALSE,
- "Invalid filter expression in logcat args\n");
- goto exit;
+ LogcatPanic(HELP_FALSE, "Invalid filter expression in logcat args\n");
}
} else if (argc == optind) {
// Add from environment variable
- const char* env_tags_orig = android::getenv(context, "ANDROID_LOG_TAGS");
+ const char* env_tags_orig = getenv("ANDROID_LOG_TAGS");
if (!!env_tags_orig) {
- err = android_log_addFilterString(context->logformat,
- env_tags_orig);
+ int err = android_log_addFilterString(logformat_.get(), env_tags_orig);
if (err < 0) {
- logcat_panic(context, HELP_TRUE,
- "Invalid filter expression in ANDROID_LOG_TAGS\n");
- goto exit;
+ LogcatPanic(HELP_TRUE, "Invalid filter expression in ANDROID_LOG_TAGS\n");
}
}
} else {
// Add from commandline
for (int i = optind ; i < argc ; i++) {
- // skip stderr redirections of _all_ kinds
- if ((argv[i][0] == '2') && (argv[i][1] == '>')) continue;
- // skip stdout redirections of _all_ kinds
- if (argv[i][0] == '>') continue;
-
- err = android_log_addFilterString(context->logformat, argv[i]);
+ int err = android_log_addFilterString(logformat_.get(), argv[i]);
if (err < 0) {
- logcat_panic(context, HELP_TRUE,
- "Invalid filter expression '%s'\n", argv[i]);
- goto exit;
+ LogcatPanic(HELP_TRUE, "Invalid filter expression '%s'\n", argv[i]);
}
}
}
- dev = context->devices;
+ std::unique_ptr<logger_list, decltype(&android_logger_list_free)> logger_list{
+ nullptr, &android_logger_list_free};
if (tail_time != log_time::EPOCH) {
- logger_list = android_logger_list_alloc_time(mode, tail_time, pid);
+ logger_list.reset(android_logger_list_alloc_time(mode, tail_time, pid));
} else {
- logger_list = android_logger_list_alloc(mode, tail_lines, pid);
+ logger_list.reset(android_logger_list_alloc(mode, tail_lines, pid));
}
// We have three orthogonal actions below to clear, set log size and
// get log size. All sharing the same iteration loop.
- while (dev) {
- dev->logger_list = logger_list;
- dev->logger = android_logger_open(logger_list,
- android_name_to_log_id(dev->device));
- if (!dev->logger) {
- reportErrorName(&openDeviceFail, dev->device, allSelected);
- dev = dev->next;
+ std::vector<std::string> open_device_failures;
+ std::vector<std::string> clear_failures;
+ std::vector<std::string> set_size_failures;
+ std::vector<std::string> get_size_failures;
+
+ for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
+ if (!(id_mask & (1 << i))) continue;
+ const char* buffer_name = android_log_id_to_name(static_cast<log_id_t>(i));
+
+ auto logger = android_logger_open(logger_list.get(), static_cast<log_id_t>(i));
+ if (logger == nullptr) {
+ ReportErrorName(buffer_name, security_buffer_selected, &open_device_failures);
continue;
}
if (clearLog || setId) {
- if (context->outputFileName) {
- int maxRotationCountDigits =
- (context->maxRotatedLogs > 0) ?
- (int)(floor(log10(context->maxRotatedLogs) + 1)) :
- 0;
+ if (output_file_name_) {
+ int max_rotation_count_digits =
+ max_rotated_logs_ > 0 ? (int)(floor(log10(max_rotated_logs_) + 1)) : 0;
- for (int i = context->maxRotatedLogs ; i >= 0 ; --i) {
+ for (int i = max_rotated_logs_; i >= 0; --i) {
std::string file;
if (!i) {
- file = android::base::StringPrintf(
- "%s", context->outputFileName);
+ file = output_file_name_;
} else {
- file = android::base::StringPrintf("%s.%.*d",
- context->outputFileName, maxRotationCountDigits, i);
+ file = StringPrintf("%s.%.*d", output_file_name_, max_rotation_count_digits,
+ i);
}
if (!file.length()) {
perror("while clearing log files");
- reportErrorName(&clearFail, dev->device, allSelected);
+ ReportErrorName(buffer_name, security_buffer_selected, &clear_failures);
break;
}
- err = unlink(file.c_str());
+ int err = unlink(file.c_str());
- if (err < 0 && errno != ENOENT && !clearFail) {
+ if (err < 0 && errno != ENOENT) {
perror("while clearing log files");
- reportErrorName(&clearFail, dev->device, allSelected);
+ ReportErrorName(buffer_name, security_buffer_selected, &clear_failures);
}
}
- } else if (android_logger_clear(dev->logger)) {
- reportErrorName(&clearFail, dev->device, allSelected);
+ } else if (android_logger_clear(logger)) {
+ ReportErrorName(buffer_name, security_buffer_selected, &clear_failures);
}
}
if (setLogSize) {
- if (android_logger_set_log_size(dev->logger, setLogSize)) {
- reportErrorName(&setSizeFail, dev->device, allSelected);
+ if (android_logger_set_log_size(logger, setLogSize)) {
+ ReportErrorName(buffer_name, security_buffer_selected, &set_size_failures);
}
}
if (getLogSize) {
- long size = android_logger_get_log_size(dev->logger);
- long readable = android_logger_get_log_readable_size(dev->logger);
+ long size = android_logger_get_log_size(logger);
+ long readable = android_logger_get_log_readable_size(logger);
- if ((size < 0) || (readable < 0)) {
- reportErrorName(&getSizeFail, dev->device, allSelected);
+ if (size < 0 || readable < 0) {
+ ReportErrorName(buffer_name, security_buffer_selected, &get_size_failures);
} else {
auto size_format = format_of_size(size);
auto readable_format = format_of_size(readable);
std::string str = android::base::StringPrintf(
- "%s: ring buffer is %lu %sB (%lu %sB consumed),"
- " max entry is %d B, max payload is %d B\n",
- dev->device,
- size_format.first, size_format.second,
- readable_format.first, readable_format.second,
- (int)LOGGER_ENTRY_MAX_LEN,
- (int)LOGGER_ENTRY_MAX_PAYLOAD);
- TEMP_FAILURE_RETRY(write(context->output_fd,
- str.data(), str.length()));
+ "%s: ring buffer is %lu %sB (%lu %sB consumed),"
+ " max entry is %d B, max payload is %d B\n",
+ buffer_name, size_format.first, size_format.second, readable_format.first,
+ readable_format.second, (int)LOGGER_ENTRY_MAX_LEN,
+ (int)LOGGER_ENTRY_MAX_PAYLOAD);
+ TEMP_FAILURE_RETRY(write(output_fd_.get(), str.data(), str.length()));
}
}
-
- dev = dev->next;
}
- context->retval = EXIT_SUCCESS;
-
// report any errors in the above loop and exit
- if (openDeviceFail) {
- logcat_panic(context, HELP_FALSE,
- "Unable to open log device '%s'\n", openDeviceFail);
- goto close;
+ if (!open_device_failures.empty()) {
+ LogcatPanic(HELP_FALSE, "Unable to open log device%s '%s'\n",
+ open_device_failures.size() > 1 ? "s" : "",
+ Join(open_device_failures, ",").c_str());
}
- if (clearFail) {
- logcat_panic(context, HELP_FALSE,
- "failed to clear the '%s' log\n", clearFail);
- goto close;
+ if (!clear_failures.empty()) {
+ LogcatPanic(HELP_FALSE, "failed to clear the '%s' log%s\n",
+ Join(clear_failures, ",").c_str(), clear_failures.size() > 1 ? "s" : "");
}
- if (setSizeFail) {
- logcat_panic(context, HELP_FALSE,
- "failed to set the '%s' log size\n", setSizeFail);
- goto close;
+ if (!set_size_failures.empty()) {
+ LogcatPanic(HELP_FALSE, "failed to set the '%s' log size%s\n",
+ Join(set_size_failures, ",").c_str(), set_size_failures.size() > 1 ? "s" : "");
}
- if (getSizeFail) {
- logcat_panic(context, HELP_FALSE,
- "failed to get the readable '%s' log size", getSizeFail);
- goto close;
+ if (!get_size_failures.empty()) {
+ LogcatPanic(HELP_FALSE, "failed to get the readable '%s' log size%s\n",
+ Join(get_size_failures, ",").c_str(), get_size_failures.size() > 1 ? "s" : "");
}
if (setPruneList) {
@@ -1537,16 +1078,14 @@
char* buf = nullptr;
if (asprintf(&buf, "%-*s", (int)(bLen - 1), setPruneList) > 0) {
buf[len] = '\0';
- if (android_logger_set_prune_list(logger_list, buf, bLen)) {
- logcat_panic(context, HELP_FALSE,
- "failed to set the prune list");
+ if (android_logger_set_prune_list(logger_list.get(), buf, bLen)) {
+ LogcatPanic(HELP_FALSE, "failed to set the prune list");
}
free(buf);
} else {
- logcat_panic(context, HELP_FALSE,
- "failed to set the prune list (alloc)");
+ LogcatPanic(HELP_FALSE, "failed to set the prune list (alloc)");
}
- goto close;
+ return EXIT_SUCCESS;
}
if (printStatistics || getPruneList) {
@@ -1556,9 +1095,9 @@
for (int retry = 32; (retry >= 0) && ((buf = new char[len]));
delete[] buf, buf = nullptr, --retry) {
if (getPruneList) {
- android_logger_get_prune_list(logger_list, buf, len);
+ android_logger_get_prune_list(logger_list.get(), buf, len);
} else {
- android_logger_get_statistics(logger_list, buf, len);
+ android_logger_get_statistics(logger_list.get(), buf, len);
}
buf[len - 1] = '\0';
if (atol(buf) < 3) {
@@ -1575,8 +1114,7 @@
}
if (!buf) {
- logcat_panic(context, HELP_FALSE, "failed to read data");
- goto close;
+ LogcatPanic(HELP_FALSE, "failed to read data");
}
// remove trailing FF
@@ -1593,163 +1131,51 @@
}
len = strlen(cp);
- TEMP_FAILURE_RETRY(write(context->output_fd, cp, len));
+ TEMP_FAILURE_RETRY(write(output_fd_.get(), cp, len));
delete[] buf;
- goto close;
+ return EXIT_SUCCESS;
}
- if (getLogSize || setLogSize || clearLog) goto close;
+ if (getLogSize || setLogSize || clearLog) return EXIT_SUCCESS;
- setupOutputAndSchedulingPolicy(context, !(mode & ANDROID_LOG_NONBLOCK));
- if (context->stop) goto close;
+ SetupOutputAndSchedulingPolicy(!(mode & ANDROID_LOG_NONBLOCK));
- // LOG_EVENT_INT(10, 12345);
- // LOG_EVENT_LONG(11, 0x1122334455667788LL);
- // LOG_EVENT_STRING(0, "whassup, doc?");
-
- dev = nullptr;
-
- while (!context->stop &&
- (!context->maxCount || (context->printCount < context->maxCount))) {
+ while (!max_count_ || print_count_ < max_count_) {
struct log_msg log_msg;
- int ret = android_logger_list_read(logger_list, &log_msg);
+ int ret = android_logger_list_read(logger_list.get(), &log_msg);
if (!ret) {
- logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n");
- break;
+ LogcatPanic(HELP_FALSE, "read: unexpected EOF!\n");
}
if (ret < 0) {
if (ret == -EAGAIN) break;
if (ret == -EIO) {
- logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n");
- break;
+ LogcatPanic(HELP_FALSE, "read: unexpected EOF!\n");
}
if (ret == -EINVAL) {
- logcat_panic(context, HELP_FALSE, "read: unexpected length.\n");
- break;
+ LogcatPanic(HELP_FALSE, "read: unexpected length.\n");
}
- logcat_panic(context, HELP_FALSE, "logcat read failure\n");
- break;
+ LogcatPanic(HELP_FALSE, "logcat read failure\n");
}
- log_device_t* d;
- for (d = context->devices; d; d = d->next) {
- if (android_name_to_log_id(d->device) == log_msg.id()) break;
- }
- if (!d) {
- context->devCount = 2; // set to Multiple
- d = &unexpected;
- d->binary = log_msg.id() == LOG_ID_EVENTS;
+ if (log_msg.id() > LOG_ID_MAX) {
+ LogcatPanic(HELP_FALSE, "read: unexpected log id (%d) over LOG_ID_MAX (%d)",
+ log_msg.id(), LOG_ID_MAX);
}
- if (dev != d) {
- dev = d;
- maybePrintStart(context, dev, printDividers);
- if (context->stop) break;
- }
- if (context->printBinary) {
- printBinary(context, &log_msg);
+ PrintDividers(log_msg.id(), printDividers);
+
+ if (print_binary_) {
+ TEMP_FAILURE_RETRY(write(output_fd_.get(), &log_msg, log_msg.len()));
} else {
- processBuffer(context, dev, &log_msg);
+ ProcessBuffer(&log_msg);
}
}
-
-close:
- // Short and sweet. Implemented generic version in android_logcat_destroy.
- while (!!(dev = context->devices)) {
- context->devices = dev->next;
- delete dev;
- }
- android_logger_list_free(logger_list);
-
-exit:
- // close write end of pipe to help things along
- if (context->output_fd == context->fds[1]) {
- android::close_output(context);
- }
- if (context->error_fd == context->fds[1]) {
- android::close_error(context);
- }
- if (context->fds[1] >= 0) {
- // NB: should be closed by the above
- int save_errno = errno;
- close(context->fds[1]);
- errno = save_errno;
- context->fds[1] = -1;
- }
- context->thread_stopped = true;
- return context->retval;
+ return EXIT_SUCCESS;
}
-// Can block
-int android_logcat_run_command(android_logcat_context ctx,
- int output, int error,
- int argc, char* const* argv,
- char* const* envp) {
- android_logcat_context_internal* context = ctx;
-
- context->output_fd = output;
- context->error_fd = error;
- context->argc = argc;
- context->argv = argv;
- context->envp = envp;
- context->stop = false;
- context->thread_stopped = false;
- return __logcat(context);
-}
-
-// Finished with context
-int android_logcat_destroy(android_logcat_context* ctx) {
- android_logcat_context_internal* context = *ctx;
-
- if (!context) return -EBADF;
-
- *ctx = nullptr;
-
- context->stop = true;
-
- while (context->thread_stopped == false) {
- // Makes me sad, replace thread_stopped with semaphore. Short lived.
- sched_yield();
- }
-
- context->argv_hold.clear();
- context->args.clear();
- context->envp_hold.clear();
- context->envs.clear();
- if (context->fds[0] >= 0) {
- close(context->fds[0]);
- context->fds[0] = -1;
- }
- android::close_output(context);
- android::close_error(context);
-
- if (context->fds[1] >= 0) {
- // NB: this should be closed by close_output, but just in case...
- close(context->fds[1]);
- context->fds[1] = -1;
- }
-
- android_closeEventTagMap(context->eventTagMap);
-
- // generic cleanup of devices list to handle all possible dirty cases
- log_device_t* dev;
- while (!!(dev = context->devices)) {
- struct logger_list* logger_list = dev->logger_list;
- if (logger_list) {
- for (log_device_t* d = dev; d; d = d->next) {
- if (d->logger_list == logger_list) d->logger_list = nullptr;
- }
- android_logger_list_free(logger_list);
- }
- context->devices = dev->next;
- delete dev;
- }
-
- int retval = context->retval;
-
- free(context);
-
- return retval;
+int main(int argc, char** argv) {
+ Logcat logcat;
+ return logcat.Run(argc, argv);
}
diff --git a/logcat/logcat.h b/logcat/logcat.h
deleted file mode 100644
index 85ed7da..0000000
--- a/logcat/logcat.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2005-2017 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.
- */
-
-#pragma once
-
-#include <stdio.h>
-
-/*
- * The opaque context
- */
-typedef struct android_logcat_context_internal* android_logcat_context;
-
-/* Creates a context associated with this logcat instance
- *
- * Returns a pointer to the context, or a NULL on error.
- */
-android_logcat_context create_android_logcat();
-
-/* Collects and outputs the logcat data to output and error file descriptors
- *
- * Will block, performed in-thread and in-process
- *
- * The output file descriptor variable, if greater than or equal to 0, is
- * where the output (ie: stdout) will be sent. The file descriptor is closed
- * on android_logcat_destroy which terminates the instance, or when an -f flag
- * (output redirect to a file) is present in the command. The error file
- * descriptor variable, if greater than or equal to 0, is where the error
- * stream (ie: stderr) will be sent, also closed on android_logcat_destroy.
- * The error file descriptor can be set to equal to the output file descriptor,
- * which will mix output and error stream content, and will defer closure of
- * the file descriptor on -f flag redirection. Negative values for the file
- * descriptors will use stdout and stderr FILE references respectively
- * internally, and will not close the references as noted above.
- *
- * Return value is 0 for success, non-zero for errors.
- */
-int android_logcat_run_command(android_logcat_context ctx, int output, int error, int argc,
- char* const* argv, char* const* envp);
-
-/* Finished with context
- *
- * Kill the command thread ASAP (if any), and free up all associated resources.
- *
- * Return value is the result of the android_logcat_run_command, or
- * non-zero for any errors.
- */
-int android_logcat_destroy(android_logcat_context* ctx);
diff --git a/logcat/logcat_main.cpp b/logcat/logcat_main.cpp
deleted file mode 100644
index ecfa2ba..0000000
--- a/logcat/logcat_main.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#include <signal.h>
-#include <stdlib.h>
-
-#include "logcat.h"
-
-int main(int argc, char** argv, char** envp) {
- android_logcat_context ctx = create_android_logcat();
- if (!ctx) return -1;
- signal(SIGPIPE, exit);
- int retval = android_logcat_run_command(ctx, -1, -1, argc, argv, envp);
- int ret = android_logcat_destroy(&ctx);
- if (!ret) ret = retval;
- return ret;
-}
diff --git a/logcat/logcatd b/logcat/logcatd
new file mode 100755
index 0000000..622e567
--- /dev/null
+++ b/logcat/logcatd
@@ -0,0 +1,25 @@
+#! /system/bin/sh
+
+# This is primarily meant to be used by logpersist. This script is run as an init service, which
+# first reads the 'last' logcat to persistent storage with `-L` then run logcat again without
+# `-L` to read the current logcat buffers to persistent storage.
+
+has_last="false"
+for arg in "$@"; do
+ if [ "$arg" == "-L" -o "$arg" == "--last" ]; then
+ has_last="true"
+ fi
+done
+
+if [ "$has_last" == "true" ]; then
+ logcat "$@"
+fi
+
+args_without_last=()
+for arg in "$@"; do
+ if [ "$arg" != "-L" -a "$arg" != "--last" ]; then
+ ARGS+=("$arg")
+ fi
+done
+
+exec logcat "${ARGS[@]}"
diff --git a/logcat/logcatd_main.cpp b/logcat/logcatd_main.cpp
deleted file mode 100644
index c131846..0000000
--- a/logcat/logcatd_main.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <string>
-#include <vector>
-
-#include "logcat.h"
-
-int main(int argc, char** argv, char** envp) {
- android_logcat_context ctx = create_android_logcat();
- if (!ctx) return -1;
-
- signal(SIGPIPE, exit);
-
- // Save and detect presence of -L or --last flag
- std::vector<std::string> args;
- bool last = false;
- for (int i = 0; i < argc; ++i) {
- if (!argv[i]) continue;
- args.push_back(std::string(argv[i]));
- if (!strcmp(argv[i], "-L") || !strcmp(argv[i], "--last")) last = true;
- }
-
- // Generate argv from saved content
- std::vector<const char*> argv_hold;
- for (auto& str : args) argv_hold.push_back(str.c_str());
- argv_hold.push_back(nullptr);
-
- int ret = 0;
- if (last) {
- // Run logcat command with -L flag
- ret = android_logcat_run_command(ctx, -1, -1, argv_hold.size() - 1,
- (char* const*)&argv_hold[0], envp);
- // Remove -L and --last flags from argument list
- for (std::vector<const char*>::iterator it = argv_hold.begin();
- it != argv_hold.end();) {
- if (!*it || (strcmp(*it, "-L") && strcmp(*it, "--last"))) {
- ++it;
- } else {
- it = argv_hold.erase(it);
- }
- }
- // fall through to re-run the command regardless of the arguments
- // passed in. For instance, we expect -h to report help stutter.
- }
-
- // Run logcat command without -L flag
- int retval = android_logcat_run_command(ctx, -1, -1, argv_hold.size() - 1,
- (char* const*)&argv_hold[0], envp);
- if (!ret) ret = retval;
- retval = android_logcat_destroy(&ctx);
- if (!ret) ret = retval;
- return ret;
-}
diff --git a/logcat/logpersist b/logcat/logpersist
index c09b6b2..05b46f0 100755
--- a/logcat/logpersist
+++ b/logcat/logpersist
@@ -148,9 +148,9 @@
echo "WARNING: Can not use --size or --buffer with ${progname%.*}.stop" >&2
fi
if [ "true" = "${clear}" ]; then
- setprop ${property} "clear"
+ setprop ${property#persist.} "clear"
else
- setprop ${property} "stop"
+ setprop ${property#persist.} "stop"
fi
if [ -n "`getprop ${property#persist.}.buffer`" ]; then
setprop ${property}.buffer ""
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 9cbc7c4..ba05a06 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -1185,7 +1185,7 @@
unlock();
// range locking in LastLogTimes looks after us
- curr = element->flushTo(reader, this, privileged, sameTid);
+ curr = element->flushTo(reader, this, sameTid);
if (curr == element->FLUSH_ERROR) {
return curr;
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 82c4fb9..ec81933 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -244,14 +244,10 @@
return retval;
}
-log_time LogBufferElement::flushTo(SocketClient* reader, LogBuffer* parent,
- bool privileged, bool lastSame) {
- struct logger_entry_v4 entry;
+log_time LogBufferElement::flushTo(SocketClient* reader, LogBuffer* parent, bool lastSame) {
+ struct logger_entry entry = {};
- memset(&entry, 0, sizeof(struct logger_entry_v4));
-
- entry.hdr_size = privileged ? sizeof(struct logger_entry_v4)
- : sizeof(struct logger_entry_v3);
+ entry.hdr_size = sizeof(struct logger_entry);
entry.lid = mLogId;
entry.pid = mPid;
entry.tid = mTid;
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index da4991b..fd790e4 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LOGD_LOG_BUFFER_ELEMENT_H__
-#define _LOGD_LOG_BUFFER_ELEMENT_H__
+#pragma once
#include <stdatomic.h>
#include <stdint.h>
@@ -96,8 +95,5 @@
}
static const log_time FLUSH_ERROR;
- log_time flushTo(SocketClient* writer, LogBuffer* parent, bool privileged,
- bool lastSame);
+ log_time flushTo(SocketClient* writer, LogBuffer* parent, bool lastSame);
};
-
-#endif
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index 443570f..ba61042 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -41,8 +41,7 @@
}
// + 1 to ensure null terminator if MAX_PAYLOAD buffer is received
- char buffer[sizeof_log_id_t + sizeof(uint16_t) + sizeof(log_time) +
- LOGGER_ENTRY_MAX_PAYLOAD + 1];
+ char buffer[sizeof(android_log_header_t) + LOGGER_ENTRY_MAX_PAYLOAD + 1];
struct iovec iov = { buffer, sizeof(buffer) - 1 };
alignas(4) char control[CMSG_SPACE(sizeof(struct ucred))];
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index f19e7b0..0cc7886 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -311,9 +311,7 @@
if (log_msg.entry.len <= sizeof(uint32_t)) continue;
uint32_t Tag = get4LE(msg);
if (Tag != TAG_DEF_LOG_TAG) continue;
- uid_t uid = (log_msg.entry.hdr_size >= sizeof(logger_entry_v4))
- ? log_msg.entry.uid
- : AID_ROOT;
+ uid_t uid = log_msg.entry.uid;
std::string Name;
std::string Format;
diff --git a/libnativeloader/test/api_test.c b/logd/fuzz/Android.bp
similarity index 63%
rename from libnativeloader/test/api_test.c
rename to logd/fuzz/Android.bp
index e7025fd..299242d 100644
--- a/libnativeloader/test/api_test.c
+++ b/logd/fuzz/Android.bp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright 2019 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.
@@ -13,13 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-/* The main purpose of this test is to ensure this C header compiles in C, so
- * that no C++ features inadvertently leak into the C ABI. */
-#include "nativeloader/native_loader.h"
-
-int main(int argc, char** argv) {
- (void)argc;
- (void)argv;
- return 0;
+cc_fuzz {
+ name: "log_buffer_log_fuzzer",
+ srcs: [
+ "log_buffer_log_fuzzer.cpp",
+ ],
+ static_libs: [
+ "libbase",
+ "libcutils",
+ "libselinux",
+ "liblog",
+ "liblogd",
+ "libcutils",
+ ],
+ cflags: ["-Werror"],
}
diff --git a/logd/fuzz/log_buffer_log_fuzzer.cpp b/logd/fuzz/log_buffer_log_fuzzer.cpp
new file mode 100644
index 0000000..4d1589b
--- /dev/null
+++ b/logd/fuzz/log_buffer_log_fuzzer.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2019 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.
+ */
+#include <string>
+
+#include "../LogBuffer.h"
+#include "../LogTimes.h"
+
+// We don't want to waste a lot of entropy on messages
+#define MAX_MSG_LENGTH 5
+
+// Tag IDs usually start at 1000, we only want to try 1000 through 1009
+#define MIN_TAG_ID 1000
+#define TAG_MOD 10
+
+namespace android {
+struct LogInput {
+ public:
+ log_id_t log_id;
+ log_time realtime;
+ uid_t uid;
+ pid_t pid;
+ pid_t tid;
+ unsigned int log_mask;
+};
+
+int write_log_messages(const uint8_t** pdata, size_t* data_left, LogBuffer* log_buffer) {
+ const uint8_t* data = *pdata;
+ const LogInput* logInput = reinterpret_cast<const LogInput*>(data);
+ data += sizeof(LogInput);
+ *data_left -= sizeof(LogInput);
+
+ uint32_t tag = MIN_TAG_ID + data[0] % TAG_MOD;
+ uint8_t msg_length = data[1] % MAX_MSG_LENGTH;
+ if (msg_length < 2) {
+ // Not enough data for message
+ return 0;
+ }
+
+ data += 2 * sizeof(uint8_t);
+ *data_left -= 2 * sizeof(uint8_t);
+
+ if (*data_left < msg_length) {
+ // Not enough data for tag and message
+ *pdata = data;
+ return 0;
+ }
+
+ // We need nullterm'd strings
+ char msg[sizeof(uint32_t) + MAX_MSG_LENGTH + sizeof(char)];
+ char* msg_only = msg + sizeof(uint32_t);
+ memcpy(msg, &tag, sizeof(uint32_t));
+ memcpy(msg_only, data, msg_length);
+ msg_only[msg_length] = '\0';
+ data += msg_length;
+ *data_left -= msg_length;
+
+ // Other elements not in enum.
+ log_id_t log_id = static_cast<log_id_t>(unsigned(logInput->log_id) % (LOG_ID_MAX + 1));
+ log_buffer->log(log_id, logInput->realtime, logInput->uid, logInput->pid, logInput->tid, msg,
+ sizeof(uint32_t) + msg_length + 1);
+ log_buffer->formatStatistics(logInput->uid, logInput->pid, logInput->log_mask);
+ *pdata = data;
+ return 1;
+}
+
+// Because system/core/logd/main.cpp redefines these.
+void prdebug(char const* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+char* uidToName(uid_t) {
+ return strdup("fake");
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ // We want a random tag length and a random remaining message length
+ if (data == nullptr || size < sizeof(LogInput) + 2 * sizeof(uint8_t)) {
+ return 0;
+ }
+
+ LastLogTimes times;
+ LogBuffer log_buffer(×);
+ size_t data_left = size;
+ const uint8_t** pdata = &data;
+
+ log_buffer.enableStatistics();
+ log_buffer.initPrune(nullptr);
+ // We want to get pruning code to get called.
+ log_id_for_each(i) { log_buffer.setSize(i, 10000); }
+
+ while (data_left >= sizeof(LogInput) + 2 * sizeof(uint8_t)) {
+ if (!write_log_messages(pdata, &data_left, &log_buffer)) {
+ return 0;
+ }
+ }
+
+ log_id_for_each(i) { log_buffer.clear(i); }
+ return 0;
+}
+} // namespace android
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 80625a7..f47bee1 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -241,47 +241,18 @@
static void caught_signal(int /* signum */) {
}
-static void dump_log_msg(const char* prefix, log_msg* msg, unsigned int version,
- int lid) {
+static void dump_log_msg(const char* prefix, log_msg* msg, int lid) {
std::cout << std::flush;
std::cerr << std::flush;
fflush(stdout);
fflush(stderr);
- switch (msg->entry.hdr_size) {
- case 0:
- version = 1;
- break;
+ EXPECT_EQ(sizeof(logger_entry), msg->entry.hdr_size);
- case sizeof(msg->entry_v2): /* PLUS case sizeof(msg->entry_v3): */
- if (version == 0) {
- version = (msg->entry_v3.lid < LOG_ID_MAX) ? 3 : 2;
- }
- break;
-
- case sizeof(msg->entry_v4):
- if (version == 0) {
- version = 4;
- }
- break;
- }
-
- fprintf(stderr, "%s: v%u[%u] ", prefix, version, msg->len());
- if (version != 1) {
- fprintf(stderr, "hdr_size=%u ", msg->entry.hdr_size);
- }
- fprintf(stderr, "pid=%u tid=%u %u.%09u ", msg->entry.pid, msg->entry.tid,
- msg->entry.sec, msg->entry.nsec);
- switch (version) {
- case 1:
- break;
- case 2:
- fprintf(stderr, "euid=%u ", msg->entry_v2.euid);
- break;
- case 3:
- default:
- lid = msg->entry.lid;
- break;
- }
+ fprintf(stderr, "%s: [%u] ", prefix, msg->len());
+ fprintf(stderr, "hdr_size=%u ", msg->entry.hdr_size);
+ fprintf(stderr, "pid=%u tid=%u %u.%09u ", msg->entry.pid, msg->entry.tid, msg->entry.sec,
+ msg->entry.nsec);
+ lid = msg->entry.lid;
switch (lid) {
case 0:
@@ -584,11 +555,11 @@
}
if (content_wrap) {
- dump_log_msg("wrap", &msg_wrap, 3, -1);
+ dump_log_msg("wrap", &msg_wrap, -1);
}
if (content_timeout) {
- dump_log_msg("timeout", &msg_timeout, 3, -1);
+ dump_log_msg("timeout", &msg_timeout, -1);
}
EXPECT_TRUE(written);
@@ -721,11 +692,11 @@
}
if (content_wrap) {
- dump_log_msg("wrap", &msg_wrap, 3, -1);
+ dump_log_msg("wrap", &msg_wrap, -1);
}
if (content_timeout) {
- dump_log_msg("timeout", &msg_timeout, 3, -1);
+ dump_log_msg("timeout", &msg_timeout, -1);
}
if (content_wrap || !content_timeout) {
@@ -776,7 +747,7 @@
EXPECT_TRUE(read_one);
if (read_one) {
- dump_log_msg("user", &msg, 3, -1);
+ dump_log_msg("user", &msg, -1);
}
fprintf(stderr, "Sleep for >%d seconds logd SO_SNDTIMEO ...\n", sndtimeo);
@@ -794,7 +765,7 @@
EXPECT_EQ(0, recv_ret);
if (recv_ret > 0) {
- dump_log_msg("user", &msg, 3, -1);
+ dump_log_msg("user", &msg, -1);
}
EXPECT_EQ(0, save_errno);
diff --git a/reboot/Android.bp b/reboot/Android.bp
index 805fd9a..cc71723 100644
--- a/reboot/Android.bp
+++ b/reboot/Android.bp
@@ -5,4 +5,5 @@
srcs: ["reboot.c"],
shared_libs: ["libcutils"],
cflags: ["-Werror"],
+ recovery_available: true,
}
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index a705071..5241730 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -260,35 +260,6 @@
endif # ifeq ($(_enforce_vndk_at_runtime),true)
-# ld.config.txt for VNDK versions older than PLATFORM_VNDK_VERSION
-# are built with the VNDK libraries lists under /prebuilts/vndk.
-#
-# ld.config.$(VER).txt is built and installed for all VNDK versions
-# listed in PRODUCT_EXTRA_VNDK_VERSIONS.
-#
-# $(1): VNDK version
-define build_versioned_ld_config
-include $(CLEAR_VARS)
-LOCAL_MODULE := ld.config.$(1).txt
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-LOCAL_MODULE_STEM := $$(LOCAL_MODULE)
-include $(BUILD_SYSTEM)/base_rules.mk
-ld_config_template := $(LOCAL_PATH)/etc/ld.config.txt
-vndk_version := $(1)
-lib_list_from_prebuilts := true
-include $(LOCAL_PATH)/update_and_install_ld_config.mk
-endef
-
-vndk_snapshots := $(wildcard prebuilts/vndk/*)
-supported_vndk_snapshot_versions := \
- $(strip $(patsubst prebuilts/vndk/v%,%,$(vndk_snapshots)))
-$(foreach ver,$(supported_vndk_snapshot_versions),\
- $(eval $(call build_versioned_ld_config,$(ver))))
-
-vndk_snapshots :=
-supported_vndk_snapshot_versions :=
-
#######################################
# ld.config.vndk_lite.txt
#
@@ -355,7 +326,7 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_SAMEPROCESS_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_SAMEPROCESS_LIBRARIES),.vendor)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_SAMEPROCESS_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_SAMEPROCESS_LIBRARIES),.com.android.vndk.current)
$(LOCAL_BUILT_MODULE):
@echo "Generate: $@"
@mkdir -p $(dir $@)
@@ -371,7 +342,7 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_CORE_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_CORE_LIBRARIES),.vendor)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_CORE_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_CORE_LIBRARIES),.com.android.vndk.current)
$(LOCAL_BUILT_MODULE):
@echo "Generate: $@"
@mkdir -p $(dir $@)
@@ -387,7 +358,7 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_PRIVATE_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_PRIVATE_LIBRARIES),.vendor)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_PRIVATE_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_PRIVATE_LIBRARIES),.com.android.vndk.current)
$(LOCAL_BUILT_MODULE):
@echo "Generate: $@"
@mkdir -p $(dir $@)
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index bb8d4d0..a99756a 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -42,29 +42,29 @@
# APEX related namespaces.
###############################################################################
-additional.namespaces = runtime,conscrypt,media,neuralnetworks,resolv
+additional.namespaces = art,conscrypt,media,neuralnetworks,resolv
# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
# If a shared library or an executable requests a shared library that
# cannot be loaded into the default namespace, the dynamic linker tries
-# to load the shared library from the runtime namespace. And then, if the
-# shared library cannot be loaded from the runtime namespace either, the
+# to load the shared library from the art namespace. And then, if the
+# shared library cannot be loaded from the art namespace either, the
# dynamic linker tries to load the shared library from the resolv namespace.
# Finally, if all attempts fail, the dynamic linker returns an error.
-namespace.default.links = runtime,resolv,neuralnetworks
-namespace.default.asan.links = runtime,resolv,neuralnetworks
-namespace.default.link.runtime.shared_libs = libandroidicu.so
-namespace.default.link.runtime.shared_libs += libdexfile_external.so
-namespace.default.link.runtime.shared_libs += libdexfiled_external.so
+namespace.default.links = art,resolv,neuralnetworks
+namespace.default.asan.links = art,resolv,neuralnetworks
+namespace.default.link.art.shared_libs = libandroidicu.so
+namespace.default.link.art.shared_libs += libdexfile_external.so
+namespace.default.link.art.shared_libs += libdexfiled_external.so
# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
-namespace.default.link.runtime.shared_libs += libicui18n.so
-namespace.default.link.runtime.shared_libs += libicuuc.so
-namespace.default.link.runtime.shared_libs += libnativebridge.so
-namespace.default.link.runtime.shared_libs += libnativehelper.so
-namespace.default.link.runtime.shared_libs += libnativeloader.so
+namespace.default.link.art.shared_libs += libicui18n.so
+namespace.default.link.art.shared_libs += libicuuc.so
+namespace.default.link.art.shared_libs += libnativebridge.so
+namespace.default.link.art.shared_libs += libnativehelper.so
+namespace.default.link.art.shared_libs += libnativeloader.so
# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
-namespace.default.link.runtime.shared_libs += libpac.so
+namespace.default.link.art.shared_libs += libpac.so
# When libnetd_resolv.so can't be found in the default namespace, search for it
# in the resolv namespace. Don't allow any other libraries from the resolv namespace
@@ -75,25 +75,25 @@
namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
###############################################################################
-# "runtime" APEX namespace
+# "art" APEX namespace
#
-# This namespace exposes externally accessible libraries from the Runtime APEX.
-# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
+# This namespace exposes externally accessible libraries from the ART APEX.
+# Keep in sync with the "art" namespace in art/build/apex/ld.config.txt.
###############################################################################
-# TODO(b/139408016): Rename this namespace to "art".
-namespace.runtime.isolated = true
+namespace.art.isolated = true
# Visible to allow links to be created at runtime, e.g. through
# android_link_namespaces in libnativeloader.
-namespace.runtime.visible = true
+namespace.art.visible = true
-namespace.runtime.search.paths = /apex/com.android.art/${LIB}
-namespace.runtime.asan.search.paths = /apex/com.android.art/${LIB}
-namespace.runtime.links = default
+namespace.art.search.paths = /apex/com.android.art/${LIB}
+namespace.art.asan.search.paths = /apex/com.android.art/${LIB}
+namespace.art.links = default,neuralnetworks
# Need allow_all_shared_libs because libart.so can dlopen oat files in
# /system/framework and /data.
# TODO(b/130340935): Use a dynamically created linker namespace similar to
# classloader-namespace for oat files, and tighten this up.
-namespace.runtime.link.default.allow_all_shared_libs = true
+namespace.art.link.default.allow_all_shared_libs = true
+namespace.art.link.neuralnetworks.shared_libs = libneuralnetworks.so
###############################################################################
# "media" APEX namespace
@@ -136,8 +136,8 @@
namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
-namespace.conscrypt.links = runtime,default
-namespace.conscrypt.link.runtime.shared_libs = libandroidio.so
+namespace.conscrypt.links = art,default
+namespace.conscrypt.link.art.shared_libs = libandroidio.so
namespace.conscrypt.link.default.shared_libs = libc.so
namespace.conscrypt.link.default.shared_libs += libm.so
namespace.conscrypt.link.default.shared_libs += libdl.so
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 7c8b3e7..9c9f4a9 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -35,7 +35,7 @@
dir.system = /data
[system]
-additional.namespaces = runtime,conscrypt,media,neuralnetworks,resolv,sphal,vndk,rs
+additional.namespaces = art,conscrypt,media,neuralnetworks,resolv,sphal,vndk,rs
###############################################################################
# "default" namespace
@@ -68,23 +68,23 @@
# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
# If a shared library or an executable requests a shared library that
# cannot be loaded into the default namespace, the dynamic linker tries
-# to load the shared library from the runtime namespace. And then, if the
-# shared library cannot be loaded from the runtime namespace either, the
+# to load the shared library from the art namespace. And then, if the
+# shared library cannot be loaded from the art namespace either, the
# dynamic linker tries to load the shared library from the resolv namespace.
# Finally, if all attempts fail, the dynamic linker returns an error.
-namespace.default.links = runtime,resolv,neuralnetworks
-namespace.default.link.runtime.shared_libs = libandroidicu.so
-namespace.default.link.runtime.shared_libs += libdexfile_external.so
-namespace.default.link.runtime.shared_libs += libdexfiled_external.so
+namespace.default.links = art,resolv,neuralnetworks
+namespace.default.link.art.shared_libs = libandroidicu.so
+namespace.default.link.art.shared_libs += libdexfile_external.so
+namespace.default.link.art.shared_libs += libdexfiled_external.so
# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
-namespace.default.link.runtime.shared_libs += libicui18n.so
-namespace.default.link.runtime.shared_libs += libicuuc.so
-namespace.default.link.runtime.shared_libs += libnativebridge.so
-namespace.default.link.runtime.shared_libs += libnativehelper.so
-namespace.default.link.runtime.shared_libs += libnativeloader.so
+namespace.default.link.art.shared_libs += libicui18n.so
+namespace.default.link.art.shared_libs += libicuuc.so
+namespace.default.link.art.shared_libs += libnativebridge.so
+namespace.default.link.art.shared_libs += libnativehelper.so
+namespace.default.link.art.shared_libs += libnativeloader.so
# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
-namespace.default.link.runtime.shared_libs += libpac.so
+namespace.default.link.art.shared_libs += libpac.so
# When libnetd_resolv.so can't be found in the default namespace, search for it
# in the resolv namespace. Don't allow any other libraries from the resolv namespace
@@ -95,26 +95,25 @@
namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
###############################################################################
-# "runtime" APEX namespace
+# "art" APEX namespace
#
-# This namespace pulls in externally accessible libs from the Runtime APEX.
-# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
+# This namespace pulls in externally accessible libs from the ART APEX.
+# Keep in sync with the "art" namespace in art/build/apex/ld.config.txt.
###############################################################################
-# TODO(b/139408016): Rename this namespace to "art".
-namespace.runtime.isolated = true
+namespace.art.isolated = true
# Visible to allow links to be created at runtime, e.g. through
# android_link_namespaces in libnativeloader.
-namespace.runtime.visible = true
+namespace.art.visible = true
-namespace.runtime.search.paths = /apex/com.android.art/${LIB}
-namespace.runtime.asan.search.paths = /apex/com.android.art/${LIB}
-namespace.runtime.links = default,neuralnetworks
+namespace.art.search.paths = /apex/com.android.art/${LIB}
+namespace.art.asan.search.paths = /apex/com.android.art/${LIB}
+namespace.art.links = default,neuralnetworks
# Need allow_all_shared_libs because libart.so can dlopen oat files in
# /system/framework and /data.
# TODO(b/130340935): Use a dynamically created linker namespace similar to
# classloader-namespace for oat files, and tighten this up.
-namespace.runtime.link.default.allow_all_shared_libs = true
-namespace.runtime.link.neuralnetworks.shared_libs = libneuralnetworks.so
+namespace.art.link.default.allow_all_shared_libs = true
+namespace.art.link.neuralnetworks.shared_libs = libneuralnetworks.so
###############################################################################
# "media" APEX namespace
@@ -149,12 +148,13 @@
namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
-namespace.conscrypt.links = runtime,default
-namespace.conscrypt.link.runtime.shared_libs = libandroidio.so
+namespace.conscrypt.links = art,default
+namespace.conscrypt.link.art.shared_libs = libandroidio.so
namespace.conscrypt.link.default.shared_libs = libc.so
namespace.conscrypt.link.default.shared_libs += libm.so
namespace.conscrypt.link.default.shared_libs += libdl.so
namespace.conscrypt.link.default.shared_libs += liblog.so
+namespace.conscrypt.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
###############################################################################
# "resolv" APEX namespace
@@ -174,6 +174,7 @@
namespace.resolv.link.default.shared_libs += libbinder_ndk.so
namespace.resolv.link.default.shared_libs += liblog.so
namespace.resolv.link.default.shared_libs += libvndksupport.so
+namespace.resolv.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
###############################################################################
# "sphal" namespace
@@ -348,7 +349,7 @@
namespace.neuralnetworks.link.default.shared_libs += libneuralnetworks_packageinfo.so
namespace.neuralnetworks.link.default.shared_libs += libsync.so
namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
-
+namespace.neuralnetworks.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
###############################################################################
# Namespace config for vendor processes. In O, no restriction is enforced for
@@ -357,7 +358,7 @@
# (LL-NDK only) access.
###############################################################################
[vendor]
-additional.namespaces = runtime,neuralnetworks
+additional.namespaces = art,neuralnetworks
namespace.default.isolated = false
@@ -399,36 +400,35 @@
namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk%VNDK_VER%
namespace.default.asan.search.paths += /system/${LIB}/vndk%VNDK_VER%
-namespace.default.links = runtime,neuralnetworks
-namespace.default.link.runtime.shared_libs = libdexfile_external.so
-namespace.default.link.runtime.shared_libs += libdexfiled_external.so
+namespace.default.links = art,neuralnetworks
+namespace.default.link.art.shared_libs = libdexfile_external.so
+namespace.default.link.art.shared_libs += libdexfiled_external.so
# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
-namespace.default.link.runtime.shared_libs += libicui18n.so
-namespace.default.link.runtime.shared_libs += libicuuc.so
-namespace.default.link.runtime.shared_libs += libnativebridge.so
-namespace.default.link.runtime.shared_libs += libnativehelper.so
-namespace.default.link.runtime.shared_libs += libnativeloader.so
+namespace.default.link.art.shared_libs += libicui18n.so
+namespace.default.link.art.shared_libs += libicuuc.so
+namespace.default.link.art.shared_libs += libnativebridge.so
+namespace.default.link.art.shared_libs += libnativehelper.so
+namespace.default.link.art.shared_libs += libnativeloader.so
# Workaround for b/124772622
-namespace.default.link.runtime.shared_libs += libandroidicu.so
+namespace.default.link.art.shared_libs += libandroidicu.so
# LLNDK library moved into apex
namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
###############################################################################
-# "runtime" APEX namespace
+# "art" APEX namespace
#
-# This namespace exposes externally accessible libraries from the Runtime APEX.
-# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
+# This namespace exposes externally accessible libraries from the ART APEX.
+# Keep in sync with the "art" namespace in art/build/apex/ld.config.txt.
###############################################################################
-# TODO(b/139408016): Rename this namespace to "art".
-namespace.runtime.isolated = true
+namespace.art.isolated = true
-namespace.runtime.search.paths = /apex/com.android.art/${LIB}
-namespace.runtime.asan.search.paths = /apex/com.android.art/${LIB}
-namespace.runtime.links = default
+namespace.art.search.paths = /apex/com.android.art/${LIB}
+namespace.art.asan.search.paths = /apex/com.android.art/${LIB}
+namespace.art.links = default
# TODO(b/130340935): Use a dynamically created linker namespace similar to
# classloader-namespace for oat files, and tighten this up.
-namespace.runtime.link.default.allow_all_shared_libs = true
+namespace.art.link.default.allow_all_shared_libs = true
###############################################################################
# "neuralnetworks" APEX namespace
@@ -450,7 +450,7 @@
namespace.neuralnetworks.link.default.shared_libs += libneuralnetworks_packageinfo.so
namespace.neuralnetworks.link.default.shared_libs += libsync.so
namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
-
+namespace.neuralnetworks.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
###############################################################################
# Namespace config for native tests that need access to both system and vendor
@@ -459,7 +459,7 @@
# includes the requisite namespace setup for APEXes.
###############################################################################
[unrestricted]
-additional.namespaces = runtime,media,conscrypt,resolv,neuralnetworks
+additional.namespaces = art,media,conscrypt,resolv,neuralnetworks
# Visible to allow links to be created at runtime, e.g. through
# android_link_namespaces in libnativeloader.
@@ -477,20 +477,19 @@
namespace.default.asan.search.paths += /vendor/${LIB}
# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
-namespace.default.links = runtime,resolv,neuralnetworks
-namespace.default.link.runtime.shared_libs = libandroidicu.so
-namespace.default.link.runtime.shared_libs += libdexfile_external.so
-namespace.default.link.runtime.shared_libs += libdexfiled_external.so
+namespace.default.links = art,resolv,neuralnetworks
+namespace.default.link.art.shared_libs = libandroidicu.so
+namespace.default.link.art.shared_libs += libdexfile_external.so
+namespace.default.link.art.shared_libs += libdexfiled_external.so
# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
-namespace.default.link.runtime.shared_libs += libicui18n.so
-namespace.default.link.runtime.shared_libs += libicuuc.so
-namespace.default.link.runtime.shared_libs += libnativebridge.so
-namespace.default.link.runtime.shared_libs += libnativehelper.so
-namespace.default.link.runtime.shared_libs += libnativeloader.so
+namespace.default.link.art.shared_libs += libicui18n.so
+namespace.default.link.art.shared_libs += libicuuc.so
+namespace.default.link.art.shared_libs += libnativebridge.so
+namespace.default.link.art.shared_libs += libnativehelper.so
+namespace.default.link.art.shared_libs += libnativeloader.so
# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
-namespace.default.link.runtime.shared_libs += libpac.so
-namespace.default.link.runtime.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+namespace.default.link.art.shared_libs += libpac.so
namespace.default.link.resolv.shared_libs = libnetd_resolv.so
@@ -498,22 +497,22 @@
namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
###############################################################################
-# "runtime" APEX namespace
+# "art" APEX namespace
#
-# This namespace exposes externally accessible libraries from the Runtime APEX.
-# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
+# This namespace exposes externally accessible libraries from the ART APEX.
+# Keep in sync with the "art" namespace in art/build/apex/ld.config.txt.
###############################################################################
-# TODO(b/139408016): Rename this namespace to "art".
-namespace.runtime.isolated = true
+namespace.art.isolated = true
# Visible to allow links to be created at runtime, e.g. through
# android_link_namespaces in libnativeloader.
-namespace.runtime.visible = true
+namespace.art.visible = true
-namespace.runtime.search.paths = /apex/com.android.art/${LIB}
-namespace.runtime.asan.search.paths = /apex/com.android.art/${LIB}
-namespace.runtime.links = default
+namespace.art.search.paths = /apex/com.android.art/${LIB}
+namespace.art.asan.search.paths = /apex/com.android.art/${LIB}
+namespace.art.links = default
# TODO(b/130340935): Use a dynamically created linker namespace similar to
# classloader-namespace for oat files, and tighten this up.
+namespace.runtime.link.default.allow_all_shared_libs = true
###############################################################################
# "media" APEX namespace
@@ -548,11 +547,12 @@
namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
-namespace.conscrypt.links = runtime,default
-namespace.conscrypt.link.runtime.shared_libs = libandroidio.so
+namespace.conscrypt.links = art,default
+namespace.conscrypt.link.art.shared_libs = libandroidio.so
namespace.conscrypt.link.default.shared_libs = libc.so
namespace.conscrypt.link.default.shared_libs += libm.so
namespace.conscrypt.link.default.shared_libs += libdl.so
+namespace.conscrypt.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
###############################################################################
# "resolv" APEX namespace
@@ -570,6 +570,7 @@
namespace.resolv.link.default.shared_libs += libm.so
namespace.resolv.link.default.shared_libs += libdl.so
namespace.resolv.link.default.shared_libs += libbinder_ndk.so
+namespace.resolv.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
###############################################################################
# "neuralnetworks" APEX namespace
@@ -591,7 +592,7 @@
namespace.neuralnetworks.link.default.shared_libs += libneuralnetworks_packageinfo.so
namespace.neuralnetworks.link.default.shared_libs += libsync.so
namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
-
+namespace.neuralnetworks.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
###############################################################################
# Namespace config for binaries under /postinstall.
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
index 27e855f..405f5a9 100644
--- a/rootdir/etc/public.libraries.android.txt
+++ b/rootdir/etc/public.libraries.android.txt
@@ -17,7 +17,7 @@
libmediandk.so
libm.so
libnativewindow.so
-libneuralnetworks.so
+libneuralnetworks.so nopreload
libOpenMAXAL.so
libOpenSLES.so
libRS.so
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 3b64b82..9b77ce2 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -754,6 +754,7 @@
# to make it too large, since it may bring userdata loss, if they
# are not aware of using fsync()/sync() to prepare sudden power-cut.
write /sys/fs/f2fs/${dev.mnt.blk.data}/cp_interval 200
+ write /sys/fs/f2fs/${dev.mnt.blk.data}/gc_urgent_sleep_time 50
# Permissions for System Server and daemons.
chown system system /sys/power/autosleep
@@ -917,3 +918,26 @@
on init && property:ro.debuggable=1
start console
+
+on userspace-reboot
+ # TODO(b/135984674): reset all necessary properties here.
+ setprop sys.init.userspace_reboot_in_progress 1
+ setprop sys.boot_completed 0
+ setprop sys.init.updatable_crashing 0
+ setprop apexd.status 0
+
+on userspace-reboot-fs-remount
+ # Make sure that vold is running.
+ # This is mostly a precaution measure in case vold for some reason wasn't running when
+ # userspace reboot was initiated.
+ start vold
+ exec - system system -- /system/bin/vdc checkpoint resetCheckpoint
+ exec - system system -- /system/bin/vdc checkpoint markBootAttempt
+ remount_userdata
+
+on userspace-reboot-resume
+ trigger userspace-reboot-fs-remount
+ trigger post-fs-data
+ trigger zygote-start
+ trigger early-boot
+ trigger boot
diff --git a/rootdir/update_and_install_ld_config.mk b/rootdir/update_and_install_ld_config.mk
index c4b8e4e..44f7b65 100644
--- a/rootdir/update_and_install_ld_config.mk
+++ b/rootdir/update_and_install_ld_config.mk
@@ -38,8 +38,8 @@
llndk_libraries_file := $(library_lists_dir)/llndk.libraries.$(vndk_version).txt
vndksp_libraries_file := $(library_lists_dir)/vndksp.libraries.$(vndk_version).txt
-vndkcore_libraries_file := $(library_lists_dir)/vndkcore.libraries.txt
-vndkprivate_libraries_file := $(library_lists_dir)/vndkprivate.libraries.txt
+vndkcore_libraries_file := $(library_lists_dir)/vndkcore.libraries.$(vndk_version).txt
+vndkprivate_libraries_file := $(library_lists_dir)/vndkprivate.libraries.$(vndk_version).txt
llndk_moved_to_apex_libraries_file := $(library_lists_dir)/llndkinapex.libraries.txt
ifeq ($(my_vndk_use_core_variant),true)
vndk_using_core_variant_libraries_file := $(library_lists_dir)/vndk_using_core_variant.libraries.$(vndk_version).txt
diff --git a/libnativebridge/.clang-format b/set-verity-state/.clang-format
similarity index 100%
rename from libnativebridge/.clang-format
rename to set-verity-state/.clang-format
diff --git a/set-verity-state/Android.bp b/set-verity-state/Android.bp
new file mode 100644
index 0000000..cd8c8c5
--- /dev/null
+++ b/set-verity-state/Android.bp
@@ -0,0 +1,24 @@
+// Copyright 2019 The Android Open Source Project
+
+cc_binary {
+ name: "set-verity-state",
+ srcs: ["set-verity-state.cpp"],
+ shared_libs: [
+ "libbase",
+ "libcrypto",
+ "libcrypto_utils",
+ "libcutils",
+ "libfec",
+ "libfs_mgr",
+ "liblog",
+ ],
+ static_libs: [
+ "libavb_user",
+ ],
+
+ cflags: ["-Werror"],
+ symlinks: [
+ "enable-verity",
+ "disable-verity",
+ ],
+}
diff --git a/set-verity-state/set-verity-state.cpp b/set-verity-state/set-verity-state.cpp
new file mode 100644
index 0000000..0a26aba
--- /dev/null
+++ b/set-verity-state/set-verity-state.cpp
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libavb_user/libavb_user.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <fs_mgr.h>
+#include <fs_mgr_overlayfs.h>
+#include <fstab/fstab.h>
+#include <log/log_properties.h>
+
+#include "fec/io.h"
+
+#ifdef ALLOW_DISABLE_VERITY
+static const bool kAllowDisableVerity = true;
+#else
+static const bool kAllowDisableVerity = false;
+#endif
+
+using android::base::unique_fd;
+
+static void suggest_run_adb_root() {
+ if (getuid() != 0) printf("Maybe run adb root?\n");
+}
+
+static bool make_block_device_writable(const std::string& dev) {
+ unique_fd fd(open(dev.c_str(), O_RDONLY | O_CLOEXEC));
+ if (fd == -1) {
+ return false;
+ }
+
+ int OFF = 0;
+ bool result = (ioctl(fd.get(), BLKROSET, &OFF) != -1);
+ return result;
+}
+
+/* Turn verity on/off */
+static bool set_verity_enabled_state(const char* block_device, const char* mount_point,
+ bool enable) {
+ if (!make_block_device_writable(block_device)) {
+ printf("Could not make block device %s writable (%s).\n", block_device, strerror(errno));
+ return false;
+ }
+
+ fec::io fh(block_device, O_RDWR);
+
+ if (!fh) {
+ printf("Could not open block device %s (%s).\n", block_device, strerror(errno));
+ suggest_run_adb_root();
+ return false;
+ }
+
+ fec_verity_metadata metadata;
+
+ if (!fh.get_verity_metadata(metadata)) {
+ printf("Couldn't find verity metadata!\n");
+ return false;
+ }
+
+ if (!enable && metadata.disabled) {
+ printf("Verity already disabled on %s\n", mount_point);
+ return false;
+ }
+
+ if (enable && !metadata.disabled) {
+ printf("Verity already enabled on %s\n", mount_point);
+ return false;
+ }
+
+ if (!fh.set_verity_status(enable)) {
+ printf("Could not set verity %s flag on device %s with error %s\n",
+ enable ? "enabled" : "disabled", block_device, strerror(errno));
+ return false;
+ }
+
+ auto change = false;
+ errno = 0;
+ if (enable ? fs_mgr_overlayfs_teardown(mount_point, &change)
+ : fs_mgr_overlayfs_setup(nullptr, mount_point, &change)) {
+ if (change) {
+ printf("%s overlayfs for %s\n", enable ? "disabling" : "using", mount_point);
+ }
+ } else if (errno) {
+ int expected_errno = enable ? EBUSY : ENOENT;
+ if (errno != expected_errno) {
+ printf("Overlayfs %s for %s failed with error %s\n", enable ? "teardown" : "setup",
+ mount_point, strerror(errno));
+ }
+ }
+ printf("Verity %s on %s\n", enable ? "enabled" : "disabled", mount_point);
+ return true;
+}
+
+/* Helper function to get A/B suffix, if any. If the device isn't
+ * using A/B the empty string is returned. Otherwise either "_a",
+ * "_b", ... is returned.
+ */
+static std::string get_ab_suffix() {
+ return android::base::GetProperty("ro.boot.slot_suffix", "");
+}
+
+static bool is_avb_device_locked() {
+ return android::base::GetProperty("ro.boot.vbmeta.device_state", "") == "locked";
+}
+
+static bool overlayfs_setup(bool enable) {
+ auto change = false;
+ errno = 0;
+ if (enable ? fs_mgr_overlayfs_teardown(nullptr, &change)
+ : fs_mgr_overlayfs_setup(nullptr, nullptr, &change)) {
+ if (change) {
+ printf("%s overlayfs\n", enable ? "disabling" : "using");
+ }
+ } else if (errno) {
+ printf("Overlayfs %s failed with error %s\n", enable ? "teardown" : "setup", strerror(errno));
+ suggest_run_adb_root();
+ }
+ return change;
+}
+
+/* Use AVB to turn verity on/off */
+static bool set_avb_verity_enabled_state(AvbOps* ops, bool enable_verity) {
+ std::string ab_suffix = get_ab_suffix();
+ bool verity_enabled;
+
+ if (is_avb_device_locked()) {
+ printf("Device is locked. Please unlock the device first\n");
+ return false;
+ }
+
+ if (!avb_user_verity_get(ops, ab_suffix.c_str(), &verity_enabled)) {
+ printf("Error getting verity state. Try adb root first?\n");
+ return false;
+ }
+
+ if ((verity_enabled && enable_verity) || (!verity_enabled && !enable_verity)) {
+ printf("verity is already %s\n", verity_enabled ? "enabled" : "disabled");
+ return false;
+ }
+
+ if (!avb_user_verity_set(ops, ab_suffix.c_str(), enable_verity)) {
+ printf("Error setting verity\n");
+ return false;
+ }
+
+ overlayfs_setup(enable_verity);
+ printf("Successfully %s verity\n", enable_verity ? "enabled" : "disabled");
+ return true;
+}
+
+int main(int argc, char* argv[]) {
+ if (argc == 0) {
+ LOG(FATAL) << "set-verity-state called with empty argv";
+ }
+
+ std::optional<bool> enable_opt;
+ std::string procname = android::base::Basename(argv[0]);
+ if (procname == "enable-verity") {
+ enable_opt = true;
+ } else if (procname == "disable-verity") {
+ enable_opt = false;
+ }
+
+ if (!enable_opt.has_value()) {
+ if (argc != 2) {
+ printf("usage: %s [1|0]\n", argv[0]);
+ return 1;
+ }
+
+ if (strcmp(argv[1], "1") == 0) {
+ enable_opt = true;
+ } else if (strcmp(argv[1], "0") == 0) {
+ enable_opt = false;
+ } else {
+ printf("usage: %s [1|0]\n", argv[0]);
+ return 1;
+ }
+ }
+
+ bool enable = enable_opt.value();
+
+ bool any_changed = false;
+
+ // Figure out if we're using VB1.0 or VB2.0 (aka AVB) - by
+ // contract, androidboot.vbmeta.digest is set by the bootloader
+ // when using AVB).
+ bool using_avb = !android::base::GetProperty("ro.boot.vbmeta.digest", "").empty();
+
+ // If using AVB, dm-verity is used on any build so we want it to
+ // be possible to disable/enable on any build (except USER). For
+ // VB1.0 dm-verity is only enabled on certain builds.
+ if (!using_avb) {
+ if (!kAllowDisableVerity) {
+ printf("%s only works for userdebug builds\n", argv[0]);
+ }
+
+ if (!android::base::GetBoolProperty("ro.secure", false)) {
+ overlayfs_setup(enable);
+ printf("verity not enabled - ENG build\n");
+ return 0;
+ }
+ }
+
+ // Should never be possible to disable dm-verity on a USER build
+ // regardless of using AVB or VB1.0.
+ if (!__android_log_is_debuggable()) {
+ printf("verity cannot be disabled/enabled - USER build\n");
+ return 0;
+ }
+
+ if (using_avb) {
+ // Yep, the system is using AVB.
+ AvbOps* ops = avb_ops_user_new();
+ if (ops == nullptr) {
+ printf("Error getting AVB ops\n");
+ return 1;
+ }
+ if (set_avb_verity_enabled_state(ops, enable)) {
+ any_changed = true;
+ }
+ avb_ops_user_free(ops);
+ } else {
+ // Not using AVB - assume VB1.0.
+
+ // read all fstab entries at once from all sources
+ android::fs_mgr::Fstab fstab;
+ if (!android::fs_mgr::ReadDefaultFstab(&fstab)) {
+ printf("Failed to read fstab\n");
+ suggest_run_adb_root();
+ return 0;
+ }
+
+ // Loop through entries looking for ones that verity manages.
+ for (const auto& entry : fstab) {
+ if (entry.fs_mgr_flags.verify) {
+ if (set_verity_enabled_state(entry.blk_device.c_str(), entry.mount_point.c_str(), enable)) {
+ any_changed = true;
+ }
+ }
+ }
+ }
+ if (!any_changed) any_changed = overlayfs_setup(enable);
+
+ if (any_changed) {
+ printf("Now reboot your device for settings to take effect\n");
+ }
+
+ return 0;
+}
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 694b50e..ec4f6ab 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -25,7 +25,7 @@
"tcpdump",
"toolbox",
"toybox",
- "unzip",
+ "ziptool",
],
}