Merge changes I4d6da40d,I91c7ced5,I7b9f6d18
* changes:
adb: Add a test for emulator connection
adb: Improve test_adb a bit more
adb: Add a way to reconnect TCP transports
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index 7876368..eb46903 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -21,6 +21,7 @@
#include <errno.h>
#include <fcntl.h>
#include <mntent.h>
+#include <spawn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -106,6 +107,41 @@
return (info.feat_ro_compat & EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS) != 0;
}
+static bool can_unshare_blocks(int fd, const char* dev) {
+ const char* E2FSCK_BIN = "/system/bin/e2fsck";
+ if (access(E2FSCK_BIN, X_OK)) {
+ WriteFdFmt(fd, "e2fsck is not available, cannot undo deduplication on %s\n", dev);
+ return false;
+ }
+
+ pid_t child;
+ char* env[] = {nullptr};
+ const char* argv[] = {E2FSCK_BIN, "-n", "-E", "unshare_blocks", dev, nullptr};
+ if (posix_spawn(&child, E2FSCK_BIN, nullptr, nullptr, const_cast<char**>(argv), env)) {
+ WriteFdFmt(fd, "failed to e2fsck to check deduplication: %s\n", strerror(errno));
+ return false;
+ }
+ int status = 0;
+ int ret = TEMP_FAILURE_RETRY(waitpid(child, &status, 0));
+ if (ret < 0) {
+ WriteFdFmt(fd, "failed to get e2fsck status: %s\n", strerror(errno));
+ return false;
+ }
+ if (!WIFEXITED(status)) {
+ WriteFdFmt(fd, "e2fsck exited abnormally with status %d\n", status);
+ return false;
+ }
+ int rc = WEXITSTATUS(status);
+ if (rc != 0) {
+ WriteFdFmt(fd,
+ "%s is deduplicated, and an e2fsck check failed. It might not "
+ "have enough free-space to be remounted as writable.\n",
+ dev);
+ return false;
+ }
+ return true;
+}
+
static bool remount_partition(int fd, const char* dir, std::vector<std::string>& dedup) {
if (!directory_exists(dir)) {
return true;
@@ -133,6 +169,9 @@
}
if (mount(dev.c_str(), dir, "none", MS_REMOUNT, nullptr) == -1) {
if (errno == EROFS && fs_has_shared_blocks(dev.c_str())) {
+ if (!can_unshare_blocks(fd, dev.c_str())) {
+ return false;
+ }
// We return true so remount_service() can detect that the only
// failure was deduplicated filesystems.
dedup.push_back(dev);
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index ab11bdd..c724b11 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -234,6 +234,10 @@
for (unsigned i = 0; i < USB_FFS_NUM_BUFS; i++) {
aiob->iocbs[i] = &aiob->iocb[i];
}
+ memset(&aiob->ctx, 0, sizeof(aiob->ctx));
+ if (io_setup(USB_FFS_NUM_BUFS, &aiob->ctx)) {
+ D("[ aio: got error on io_setup (%d) ]", errno);
+ }
}
static int getMaxPacketSize(int ffs_fd) {
@@ -312,13 +316,6 @@
goto err;
}
- memset(&h->read_aiob.ctx, 0, sizeof(h->read_aiob.ctx));
- memset(&h->write_aiob.ctx, 0, sizeof(h->write_aiob.ctx));
- if (io_setup(USB_FFS_NUM_BUFS, &h->read_aiob.ctx) ||
- io_setup(USB_FFS_NUM_BUFS, &h->write_aiob.ctx)) {
- D("[ aio: got error on io_setup (%d) ]", errno);
- }
-
h->read_aiob.fd = h->bulk_out;
h->write_aiob.fd = h->bulk_in;
return true;
@@ -439,23 +436,29 @@
num_bufs += 1;
}
- if (io_submit(aiob->ctx, num_bufs, aiob->iocbs.data()) < num_bufs) {
- D("[ aio: got error submitting %s (%d) ]", read ? "read" : "write", errno);
- return -1;
- }
- if (TEMP_FAILURE_RETRY(
- io_getevents(aiob->ctx, num_bufs, num_bufs, aiob->events.data(), nullptr)) < num_bufs) {
- D("[ aio: got error waiting %s (%d) ]", read ? "read" : "write", errno);
- return -1;
- }
- for (int i = 0; i < num_bufs; i++) {
- if (aiob->events[i].res < 0) {
- errno = aiob->events[i].res;
- D("[ aio: got error event on %s (%d) ]", read ? "read" : "write", errno);
+ while (true) {
+ if (TEMP_FAILURE_RETRY(io_submit(aiob->ctx, num_bufs, aiob->iocbs.data())) < num_bufs) {
+ PLOG(ERROR) << "aio: got error submitting " << (read ? "read" : "write");
return -1;
}
+ if (TEMP_FAILURE_RETRY(io_getevents(aiob->ctx, num_bufs, num_bufs, aiob->events.data(),
+ nullptr)) < num_bufs) {
+ PLOG(ERROR) << "aio: got error waiting " << (read ? "read" : "write");
+ return -1;
+ }
+ if (num_bufs == 1 && aiob->events[0].res == -EINTR) {
+ continue;
+ }
+ for (int i = 0; i < num_bufs; i++) {
+ if (aiob->events[i].res < 0) {
+ errno = -aiob->events[i].res;
+ PLOG(ERROR) << "aio: got error event on " << (read ? "read" : "write")
+ << " total bufs " << num_bufs;
+ return -1;
+ }
+ }
+ return 0;
}
- return 0;
}
static int usb_ffs_aio_read(usb_handle* h, void* data, int len) {
@@ -494,8 +497,6 @@
h->kicked = false;
adb_close(h->bulk_out);
adb_close(h->bulk_in);
- io_destroy(h->read_aiob.ctx);
- io_destroy(h->write_aiob.ctx);
// Notify usb_adb_open_thread to open a new connection.
h->lock.lock();
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index dea2e17..079a574 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -304,7 +304,16 @@
crash_mutex.lock();
if (lock_count++ > 0) {
- async_safe_format_log(ANDROID_LOG_ERROR, "libc", "recursed signal handler call, exiting");
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "recursed signal handler call, aborting");
+ signal(SIGABRT, SIG_DFL);
+ raise(SIGABRT);
+ sigset_t sigset;
+ sigemptyset(&sigset);
+ sigaddset(&sigset, SIGABRT);
+ sigprocmask(SIG_UNBLOCK, &sigset, nullptr);
+
+ // Just in case...
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "abort didn't exit, exiting");
_exit(1);
}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 472ab59..af4d6c1 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -686,6 +686,49 @@
return a;
}
+/* Extracts <device>s from the by-name symlinks specified in a fstab:
+ * /dev/block/<type>/<device>/by-name/<partition>
+ *
+ * <type> can be: platform, pci or vbd.
+ *
+ * For example, given the following entries in the input fstab:
+ * /dev/block/platform/soc/1da4000.ufshc/by-name/system
+ * /dev/block/pci/soc.0/f9824900.sdhci/by-name/vendor
+ * it returns a set { "soc/1da4000.ufshc", "soc.0/f9824900.sdhci" }.
+ */
+static std::set<std::string> extract_boot_devices(const fstab& fstab) {
+ std::set<std::string> boot_devices;
+
+ for (int i = 0; i < fstab.num_entries; i++) {
+ std::string blk_device(fstab.recs[i].blk_device);
+ // Skips blk_device that doesn't conform to the format.
+ if (!android::base::StartsWith(blk_device, "/dev/block") ||
+ android::base::StartsWith(blk_device, "/dev/block/by-name") ||
+ android::base::StartsWith(blk_device, "/dev/block/bootdevice/by-name")) {
+ continue;
+ }
+ // Skips non-by_name blk_device.
+ // /dev/block/<type>/<device>/by-name/<partition>
+ // ^ slash_by_name
+ auto slash_by_name = blk_device.find("/by-name");
+ if (slash_by_name == std::string::npos) continue;
+ blk_device.erase(slash_by_name); // erases /by-name/<partition>
+
+ // Erases /dev/block/, now we have <type>/<device>
+ blk_device.erase(0, std::string("/dev/block/").size());
+
+ // <type>/<device>
+ // ^ first_slash
+ auto first_slash = blk_device.find('/');
+ if (first_slash == std::string::npos) continue;
+
+ auto boot_device = blk_device.substr(first_slash + 1);
+ if (!boot_device.empty()) boot_devices.insert(std::move(boot_device));
+ }
+
+ return boot_devices;
+}
+
struct fstab *fs_mgr_read_fstab(const char *fstab_path)
{
FILE *fstab_file;
@@ -863,6 +906,23 @@
return nullptr;
}
+std::set<std::string> fs_mgr_get_boot_devices() {
+ // boot_devices can be specified in device tree.
+ std::string dt_value;
+ std::string file_name = get_android_dt_dir() + "/boot_devices";
+ if (read_dt_file(file_name, &dt_value)) {
+ auto boot_devices = android::base::Split(dt_value, ",");
+ return std::set<std::string>(boot_devices.begin(), boot_devices.end());
+ }
+
+ // Fallback to extract boot devices from fstab.
+ std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+ fs_mgr_free_fstab);
+ if (fstab) return extract_boot_devices(*fstab);
+
+ return {};
+}
+
int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab)
{
return fstab->fs_mgr_flags & MF_VOLDMANAGED;
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 04ccfc5..d232cca 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -22,6 +22,7 @@
#include <stdint.h>
#include <stdio.h>
+#include <set>
#include <string>
/*
@@ -89,5 +90,6 @@
int fs_mgr_has_sysfs_path(const struct fstab_rec* fstab);
std::string fs_mgr_get_slot_suffix();
+std::set<std::string> fs_mgr_get_boot_devices();
#endif /* __CORE_FS_TAB_H */
diff --git a/init/README.md b/init/README.md
index c08b07a..550ef05 100644
--- a/init/README.md
+++ b/init/README.md
@@ -195,6 +195,10 @@
> This service will not automatically start with its class.
It must be explicitly started by name or by interface name.
+`enter_namespace <type> <path>`
+> Enters the namespace of type _type_ located at _path_. Only network namespaces are supported with
+ _type_ set to "net". Note that only one namespace of a given _type_ may be entered.
+
`file <path> <type>`
> Open a file path and pass its fd to the launched process. _type_ must be
"r", "w" or "rw". For native executables see libcutils
diff --git a/init/devices.cpp b/init/devices.cpp
index 8d27f4f..ada1e28 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -329,10 +329,10 @@
<< partition_name_sanitized << "'";
}
links.emplace_back(link_path + "/by-name/" + partition_name_sanitized);
- }
-
- if (uevent.partition_num >= 0) {
- links.emplace_back(link_path + "/by-num/p" + std::to_string(uevent.partition_num));
+ // Adds symlink: /dev/block/by-name/<partition_name>.
+ if (boot_devices_.find(device) != boot_devices_.end()) {
+ links.emplace_back("/dev/block/by-name/" + partition_name_sanitized);
+ }
}
auto last_slash = uevent.path.rfind('/');
@@ -350,8 +350,14 @@
PLOG(ERROR) << "Failed to create directory " << Dirname(link);
}
- if (symlink(devpath.c_str(), link.c_str()) && errno != EEXIST) {
- PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link;
+ if (symlink(devpath.c_str(), link.c_str())) {
+ if (errno != EEXIST) {
+ PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link;
+ } else if (std::string link_path;
+ Readlink(link, &link_path) && link_path != devpath) {
+ PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link
+ << ", which already links to: " << link_path;
+ }
}
}
}
@@ -415,16 +421,18 @@
DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
std::vector<SysfsPermissions> sysfs_permissions,
- std::vector<Subsystem> subsystems, bool skip_restorecon)
+ std::vector<Subsystem> subsystems, std::set<std::string> boot_devices,
+ bool skip_restorecon)
: dev_permissions_(std::move(dev_permissions)),
sysfs_permissions_(std::move(sysfs_permissions)),
subsystems_(std::move(subsystems)),
+ boot_devices_(std::move(boot_devices)),
skip_restorecon_(skip_restorecon),
sysfs_mount_point_("/sys") {}
DeviceHandler::DeviceHandler()
: DeviceHandler(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
- std::vector<Subsystem>{}, false) {}
+ std::vector<Subsystem>{}, std::set<std::string>{}, false) {}
} // namespace init
} // namespace android
diff --git a/init/devices.h b/init/devices.h
index 1f8f1e8..f9035da 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -21,6 +21,7 @@
#include <sys/types.h>
#include <algorithm>
+#include <set>
#include <string>
#include <vector>
@@ -103,8 +104,8 @@
DeviceHandler();
DeviceHandler(std::vector<Permissions> dev_permissions,
- std::vector<SysfsPermissions> sysfs_permissions,
- std::vector<Subsystem> subsystems, bool skip_restorecon);
+ std::vector<SysfsPermissions> sysfs_permissions, std::vector<Subsystem> subsystems,
+ std::set<std::string> boot_devices, bool skip_restorecon);
~DeviceHandler(){};
void HandleDeviceEvent(const Uevent& uevent);
@@ -125,6 +126,7 @@
std::vector<Permissions> dev_permissions_;
std::vector<SysfsPermissions> sysfs_permissions_;
std::vector<Subsystem> subsystems_;
+ std::set<std::string> boot_devices_;
bool skip_restorecon_;
std::string sysfs_mount_point_;
};
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
index eba00cb..d658f4d 100644
--- a/init/devices_test.cpp
+++ b/init/devices_test.cpp
@@ -84,7 +84,6 @@
};
std::vector<std::string> expected_result{
"/dev/block/platform/soc.0/f9824900.sdhci/by-name/modem",
- "/dev/block/platform/soc.0/f9824900.sdhci/by-num/p1",
"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
};
@@ -100,7 +99,6 @@
.partition_num = 1,
};
std::vector<std::string> expected_result{
- "/dev/block/platform/soc.0/f9824900.sdhci/by-num/p1",
"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
};
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index a5ba647..34de640 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -77,7 +77,7 @@
std::unique_ptr<LogicalPartitionTable> dm_linear_table_;
std::vector<fstab_rec*> mount_fstab_recs_;
std::set<std::string> required_devices_partition_names_;
- DeviceHandler device_handler_;
+ std::unique_ptr<DeviceHandler> device_handler_;
UeventListener uevent_listener_;
};
@@ -147,6 +147,11 @@
if (IsDmLinearEnabled()) {
dm_linear_table_ = android::fs_mgr::LoadPartitionsFromDeviceTree();
}
+
+ auto boot_devices = fs_mgr_get_boot_devices();
+ device_handler_ =
+ std::make_unique<DeviceHandler>(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
+ std::vector<Subsystem>{}, std::move(boot_devices), false);
}
std::unique_ptr<FirstStageMount> FirstStageMount::Create() {
@@ -205,7 +210,7 @@
bool found = false;
auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
if (uevent.path == dm_path) {
- device_handler_.HandleDeviceEvent(uevent);
+ device_handler_->HandleDeviceEvent(uevent);
found = true;
return ListenerAction::kStop;
}
@@ -262,7 +267,7 @@
if (iter != required_devices_partition_names_.end()) {
LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
required_devices_partition_names_.erase(iter);
- device_handler_.HandleDeviceEvent(uevent);
+ device_handler_->HandleDeviceEvent(uevent);
if (required_devices_partition_names_.empty()) {
return ListenerAction::kStop;
} else {
@@ -299,7 +304,7 @@
auto verity_callback = [&device_name, &dm_device, this, &found](const Uevent& uevent) {
if (uevent.device_name == device_name) {
LOG(VERBOSE) << "Creating device-mapper device : " << dm_device;
- device_handler_.HandleDeviceEvent(uevent);
+ device_handler_->HandleDeviceEvent(uevent);
found = true;
return ListenerAction::kStop;
}
@@ -468,9 +473,8 @@
// the content of uevent. by-name symlink will be at [0] if uevent->partition_name
// is not empty. e.g.,
// - /dev/block/platform/soc.0/f9824900.sdhci/by-name/modem
- // - /dev/block/platform/soc.0/f9824900.sdhci/by-num/p1
// - /dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1
- std::vector<std::string> links = device_handler_.GetBlockDeviceSymlinks(uevent);
+ std::vector<std::string> links = device_handler_->GetBlockDeviceSymlinks(uevent);
if (!links.empty()) {
auto[it, inserted] = by_name_symlink_map_.emplace(uevent.partition_name, links[0]);
if (!inserted) {
diff --git a/init/service.cpp b/init/service.cpp
index 03c2cee..0e08d9b 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -34,6 +34,7 @@
#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
#include <hidl-util/FQName.h>
#include <processgroup/processgroup.h>
#include <selinux/selinux.h>
@@ -59,13 +60,13 @@
using android::base::ParseInt;
using android::base::StartsWith;
using android::base::StringPrintf;
+using android::base::unique_fd;
using android::base::WriteStringToFile;
namespace android {
namespace init {
-static Result<std::string> ComputeContextFromExecutable(std::string& service_name,
- const std::string& service_path) {
+static Result<std::string> ComputeContextFromExecutable(const std::string& service_path) {
std::string computed_context;
char* raw_con = nullptr;
@@ -101,36 +102,49 @@
return computed_context;
}
-static void SetUpPidNamespace(const std::string& service_name) {
+Result<Success> Service::SetUpMountNamespace() const {
constexpr unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID;
- // It's OK to LOG(FATAL) in this function since it's running in the first
- // child process.
-
// Recursively remount / as slave like zygote does so unmounting and mounting /proc
// doesn't interfere with the parent namespace's /proc mount. This will also
// prevent any other mounts/unmounts initiated by the service from interfering
// with the parent namespace but will still allow mount events from the parent
// namespace to propagate to the child.
if (mount("rootfs", "/", nullptr, (MS_SLAVE | MS_REC), nullptr) == -1) {
- PLOG(FATAL) << "couldn't remount(/) recursively as slave for " << service_name;
- }
- // umount() then mount() /proc.
- // Note that it is not sufficient to mount with MS_REMOUNT.
- if (umount("/proc") == -1) {
- PLOG(FATAL) << "couldn't umount(/proc) for " << service_name;
- }
- if (mount("", "/proc", "proc", kSafeFlags, "") == -1) {
- PLOG(FATAL) << "couldn't mount(/proc) for " << service_name;
+ return ErrnoError() << "Could not remount(/) recursively as slave";
}
- if (prctl(PR_SET_NAME, service_name.c_str()) == -1) {
- PLOG(FATAL) << "couldn't set name for " << service_name;
+ // umount() then mount() /proc and/or /sys
+ // Note that it is not sufficient to mount with MS_REMOUNT.
+ if (namespace_flags_ & CLONE_NEWPID) {
+ if (umount("/proc") == -1) {
+ return ErrnoError() << "Could not umount(/proc)";
+ }
+ if (mount("", "/proc", "proc", kSafeFlags, "") == -1) {
+ return ErrnoError() << "Could not mount(/proc)";
+ }
+ }
+ bool remount_sys = std::any_of(namespaces_to_enter_.begin(), namespaces_to_enter_.end(),
+ [](const auto& entry) { return entry.first == CLONE_NEWNET; });
+ if (remount_sys) {
+ if (umount2("/sys", MNT_DETACH) == -1) {
+ return ErrnoError() << "Could not umount(/sys)";
+ }
+ if (mount("", "/sys", "sys", kSafeFlags, "") == -1) {
+ return ErrnoError() << "Could not mount(/sys)";
+ }
+ }
+ return Success();
+}
+
+Result<Success> Service::SetUpPidNamespace() const {
+ if (prctl(PR_SET_NAME, name_.c_str()) == -1) {
+ return ErrnoError() << "Could not set name";
}
pid_t child_pid = fork();
if (child_pid == -1) {
- PLOG(FATAL) << "couldn't fork init inside the PID namespace for " << service_name;
+ return ErrnoError() << "Could not fork init inside the PID namespace";
}
if (child_pid > 0) {
@@ -153,6 +167,20 @@
}
_exit(WEXITSTATUS(init_exitstatus));
}
+ return Success();
+}
+
+Result<Success> Service::EnterNamespaces() const {
+ for (const auto& [nstype, path] : namespaces_to_enter_) {
+ auto fd = unique_fd{open(path.c_str(), O_RDONLY | O_CLOEXEC)};
+ if (!fd) {
+ return ErrnoError() << "Could not open namespace at " << path;
+ }
+ if (setns(fd, nstype) == -1) {
+ return ErrnoError() << "Could not setns() namespace at " << path;
+ }
+ }
+ return Success();
}
static bool ExpandArgsAndExecv(const std::vector<std::string>& args, bool sigstop) {
@@ -422,6 +450,20 @@
return Success();
}
+Result<Success> Service::ParseEnterNamespace(const std::vector<std::string>& args) {
+ if (args[1] != "net") {
+ return Error() << "Init only supports entering network namespaces";
+ }
+ if (!namespaces_to_enter_.empty()) {
+ return Error() << "Only one network namespace may be entered";
+ }
+ // Network namespaces require that /sys is remounted, otherwise the old adapters will still be
+ // present. Therefore, they also require mount namespaces.
+ namespace_flags_ |= CLONE_NEWNS;
+ namespaces_to_enter_.emplace_back(CLONE_NEWNET, args[2]);
+ return Success();
+}
+
Result<Success> Service::ParseGroup(const std::vector<std::string>& args) {
auto gid = DecodeUid(args[1]);
if (!gid) {
@@ -691,6 +733,8 @@
{"console", {0, 1, &Service::ParseConsole}},
{"critical", {0, 0, &Service::ParseCritical}},
{"disabled", {0, 0, &Service::ParseDisabled}},
+ {"enter_namespace",
+ {2, 2, &Service::ParseEnterNamespace}},
{"file", {2, 2, &Service::ParseFile}},
{"group", {1, NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}},
{"interface", {2, 2, &Service::ParseInterface}},
@@ -793,7 +837,7 @@
if (!seclabel_.empty()) {
scon = seclabel_;
} else {
- auto result = ComputeContextFromExecutable(name_, args_[0]);
+ auto result = ComputeContextFromExecutable(args_[0]);
if (!result) {
return result.error();
}
@@ -812,10 +856,24 @@
if (pid == 0) {
umask(077);
+ if (auto result = EnterNamespaces(); !result) {
+ LOG(FATAL) << "Service '" << name_ << "' could not enter namespaces: " << result.error();
+ }
+
+ if (namespace_flags_ & CLONE_NEWNS) {
+ if (auto result = SetUpMountNamespace(); !result) {
+ LOG(FATAL) << "Service '" << name_
+ << "' could not set up mount namespace: " << result.error();
+ }
+ }
+
if (namespace_flags_ & CLONE_NEWPID) {
// This will fork again to run an init process inside the PID
// namespace.
- SetUpPidNamespace(name_);
+ if (auto result = SetUpPidNamespace(); !result) {
+ LOG(FATAL) << "Service '" << name_
+ << "' could not set up PID namespace: " << result.error();
+ }
}
for (const auto& [key, value] : environment_vars_) {
diff --git a/init/service.h b/init/service.h
index 9cb35b8..cbfd52f 100644
--- a/init/service.h
+++ b/init/service.h
@@ -125,6 +125,9 @@
using OptionParser = Result<Success> (Service::*)(const std::vector<std::string>& args);
class OptionParserMap;
+ Result<Success> SetUpMountNamespace() const;
+ Result<Success> SetUpPidNamespace() const;
+ Result<Success> EnterNamespaces() const;
void NotifyStateChange(const std::string& new_state) const;
void StopOrReset(int how);
void ZapStdio() const;
@@ -137,6 +140,7 @@
Result<Success> ParseConsole(const std::vector<std::string>& args);
Result<Success> ParseCritical(const std::vector<std::string>& args);
Result<Success> ParseDisabled(const std::vector<std::string>& args);
+ Result<Success> ParseEnterNamespace(const std::vector<std::string>& args);
Result<Success> ParseGroup(const std::vector<std::string>& args);
Result<Success> ParsePriority(const std::vector<std::string>& args);
Result<Success> ParseInterface(const std::vector<std::string>& args);
@@ -181,6 +185,8 @@
std::vector<gid_t> supp_gids_;
CapSet capabilities_;
unsigned namespace_flags_;
+ // Pair of namespace type, path to namespace.
+ std::vector<std::pair<int, std::string>> namespaces_to_enter_;
std::string seclabel_;
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 1435d82..a284203 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -30,6 +30,7 @@
#include <android-base/chrono_utils.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
+#include <fstab/fstab.h>
#include <selinux/android.h>
#include <selinux/selinux.h>
@@ -242,8 +243,9 @@
std::string hardware = android::base::GetProperty("ro.hardware", "");
parser.ParseConfig("/ueventd." + hardware + ".rc");
+ auto boot_devices = fs_mgr_get_boot_devices();
return DeviceHandler(std::move(dev_permissions), std::move(sysfs_permissions),
- std::move(subsystems), true);
+ std::move(subsystems), std::move(boot_devices), true);
}
int ueventd_main(int argc, char** argv) {
diff --git a/libmemunreachable/README.md b/libmemunreachable/README.md
index ae8fa94..6d9141a 100644
--- a/libmemunreachable/README.md
+++ b/libmemunreachable/README.md
@@ -12,6 +12,27 @@
Usage
-------
+### In Android apps ###
+
+libmemunreachble is loaded by zygote and can be triggered with `dumpsys -t 600 meminfo --unreachable [process]`.
+
+To enable malloc\_debug backtraces on allocations for a single app process on a userdebug device, use:
+```
+adb root
+adb shell setprop libc.debug.malloc.program app_process
+adb shell setprop wrap.[process] "\$\@"
+adb shell setprop libc.debug.malloc.options backtrace=4
+```
+
+Kill and restart the app, trigger the leak, and then run `dumpsys -t 600 meminfo --unreachable [process]`.
+
+To disable malloc\_debug:
+```
+adb shell setprop libc.debug.malloc.options "''"
+adb shell setprop libc.debug.malloc.program "''"
+adb shell setprop wrap.[process] "''"
+```
+
### C interface ###
#### `bool LogUnreachableMemory(bool log_contents, size_t limit)` ####
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 5d996e9..82f2e73 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -202,6 +202,7 @@
"tests/files/offline/jit_debug_x86/*",
"tests/files/offline/jit_map_arm/*",
"tests/files/offline/gnu_debugdata_arm/*",
+ "tests/files/offline/offset_arm/*",
"tests/files/offline/straddle_arm/*",
"tests/files/offline/straddle_arm64/*",
],
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 02f8a9a..3762107 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -160,14 +160,14 @@
}
// The relative pc is always relative to the start of the map from which it comes.
-bool Elf::Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs,
- Memory* process_memory, bool* finished) {
+bool Elf::Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, Regs* regs, Memory* process_memory,
+ bool* finished) {
if (!valid_) {
return false;
}
// The relative pc expectd by StepIfSignalHandler is relative to the start of the elf.
- if (regs->StepIfSignalHandler(rel_pc + elf_offset, this, process_memory)) {
+ if (regs->StepIfSignalHandler(rel_pc, this, process_memory)) {
*finished = false;
return true;
}
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 9a6c6df..099cc9e 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -220,8 +220,7 @@
in_device_map = true;
} else {
bool finished;
- stepped = elf->Step(rel_pc, step_pc, map_info->elf_offset, regs_, process_memory_.get(),
- &finished);
+ stepped = elf->Step(rel_pc, step_pc, regs_, process_memory_.get(), &finished);
elf->GetLastError(&last_error_);
if (stepped && finished) {
break;
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index 385973e..f4cdbda 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -65,8 +65,8 @@
uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
- bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs,
- Memory* process_memory, bool* finished);
+ bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, Regs* regs, Memory* process_memory,
+ bool* finished);
ElfInterface* CreateInterfaceFromMemory(Memory* memory);
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index f9028c4..aecbf6d 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -133,7 +133,7 @@
ASSERT_FALSE(elf.GetFunctionName(0, &name, &func_offset));
bool finished;
- ASSERT_FALSE(elf.Step(0, 0, 0, nullptr, nullptr, &finished));
+ ASSERT_FALSE(elf.Step(0, 0, nullptr, nullptr, &finished));
}
TEST_F(ElfTest, elf32_invalid_machine) {
@@ -330,7 +330,7 @@
elf.FakeSetValid(true);
elf.FakeSetLoadBias(0);
bool finished;
- ASSERT_TRUE(elf.Step(0x1000, 0x1000, 0x2000, ®s, &process_memory, &finished));
+ ASSERT_TRUE(elf.Step(0x3000, 0x1000, ®s, &process_memory, &finished));
EXPECT_FALSE(finished);
EXPECT_EQ(15U, regs.pc());
EXPECT_EQ(13U, regs.sp());
@@ -370,7 +370,7 @@
EXPECT_CALL(*interface, Step(0x1000, 0, ®s, &process_memory, &finished))
.WillOnce(::testing::Return(true));
- ASSERT_TRUE(elf.Step(0x1004, 0x1000, 0x2000, ®s, &process_memory, &finished));
+ ASSERT_TRUE(elf.Step(0x1004, 0x1000, ®s, &process_memory, &finished));
}
TEST_F(ElfTest, step_in_interface_non_zero_load_bias) {
@@ -388,7 +388,7 @@
EXPECT_CALL(*interface, Step(0x7300, 0x4000, ®s, &process_memory, &finished))
.WillOnce(::testing::Return(true));
- ASSERT_TRUE(elf.Step(0x7304, 0x7300, 0x2000, ®s, &process_memory, &finished));
+ ASSERT_TRUE(elf.Step(0x7304, 0x7300, ®s, &process_memory, &finished));
}
TEST_F(ElfTest, get_global_invalid_elf) {
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 2b8f0c2..285fc9e 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -46,6 +46,12 @@
namespace unwindstack {
+static void AddMemory(std::string file_name, MemoryOfflineParts* parts) {
+ MemoryOffline* memory = new MemoryOffline;
+ ASSERT_TRUE(memory->Init(file_name.c_str(), 0));
+ parts->Add(memory);
+}
+
class UnwindOfflineTest : public ::testing::Test {
protected:
void TearDown() override {
@@ -64,9 +70,24 @@
maps_.reset(new BufferMaps(data.c_str()));
ASSERT_TRUE(maps_->Parse());
- std::unique_ptr<MemoryOffline> stack_memory(new MemoryOffline);
- ASSERT_TRUE(stack_memory->Init((dir_ + "stack.data").c_str(), 0));
- process_memory_.reset(stack_memory.release());
+ std::string stack_name(dir_ + "stack.data");
+ struct stat st;
+ if (stat(stack_name.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
+ std::unique_ptr<MemoryOffline> stack_memory(new MemoryOffline);
+ ASSERT_TRUE(stack_memory->Init((dir_ + "stack.data").c_str(), 0));
+ process_memory_.reset(stack_memory.release());
+ } else {
+ std::unique_ptr<MemoryOfflineParts> stack_memory(new MemoryOfflineParts);
+ for (size_t i = 0;; i++) {
+ stack_name = dir_ + "stack" + std::to_string(i) + ".data";
+ if (stat(stack_name.c_str(), &st) == -1 || !S_ISREG(st.st_mode)) {
+ ASSERT_TRUE(i != 0) << "No stack data files found.";
+ break;
+ }
+ AddMemory(stack_name, stack_memory.get());
+ }
+ process_memory_.reset(stack_memory.release());
+ }
switch (arch) {
case ARCH_ARM: {
@@ -180,7 +201,7 @@
}
TEST_F(UnwindOfflineTest, pc_straddle_arm) {
- Init("straddle_arm/", ARCH_ARM);
+ ASSERT_NO_FATAL_FAILURE(Init("straddle_arm/", ARCH_ARM));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@@ -204,7 +225,7 @@
}
TEST_F(UnwindOfflineTest, pc_in_gnu_debugdata_arm) {
- Init("gnu_debugdata_arm/", ARCH_ARM);
+ ASSERT_NO_FATAL_FAILURE(Init("gnu_debugdata_arm/", ARCH_ARM));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@@ -224,7 +245,7 @@
}
TEST_F(UnwindOfflineTest, pc_straddle_arm64) {
- Init("straddle_arm64/", ARCH_ARM64);
+ ASSERT_NO_FATAL_FAILURE(Init("straddle_arm64/", ARCH_ARM64));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@@ -255,14 +276,8 @@
EXPECT_EQ(0x7fe0d84110U, unwinder.frames()[5].sp);
}
-static void AddMemory(std::string file_name, MemoryOfflineParts* parts) {
- MemoryOffline* memory = new MemoryOffline;
- ASSERT_TRUE(memory->Init(file_name.c_str(), 0));
- parts->Add(memory);
-}
-
TEST_F(UnwindOfflineTest, jit_debug_x86) {
- Init("jit_debug_x86/", ARCH_X86);
+ ASSERT_NO_FATAL_FAILURE(Init("jit_debug_x86/", ARCH_X86));
MemoryOfflineParts* memory = new MemoryOfflineParts;
AddMemory(dir_ + "descriptor.data", memory);
@@ -555,7 +570,7 @@
}
TEST_F(UnwindOfflineTest, jit_debug_arm) {
- Init("jit_debug_arm/", ARCH_ARM);
+ ASSERT_NO_FATAL_FAILURE(Init("jit_debug_arm/", ARCH_ARM));
MemoryOfflineParts* memory = new MemoryOfflineParts;
AddMemory(dir_ + "descriptor.data", memory);
@@ -873,7 +888,7 @@
// fallback to iterating over the cies/fdes and ignore the eh_frame_hdr.
// No .gnu_debugdata section in the elf file, so no symbols.
TEST_F(UnwindOfflineTest, bad_eh_frame_hdr_arm64) {
- Init("bad_eh_frame_hdr_arm64/", ARCH_ARM64);
+ ASSERT_NO_FATAL_FAILURE(Init("bad_eh_frame_hdr_arm64/", ARCH_ARM64));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@@ -902,7 +917,7 @@
// The elf has bad eh_frame unwind information for the pcs. If eh_frame
// is used first, the unwind will not match the expected output.
TEST_F(UnwindOfflineTest, debug_frame_first_x86) {
- Init("debug_frame_first_x86/", ARCH_X86);
+ ASSERT_NO_FATAL_FAILURE(Init("debug_frame_first_x86/", ARCH_X86));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@@ -930,7 +945,7 @@
// Make sure that a pc that is at the beginning of an fde unwinds correctly.
TEST_F(UnwindOfflineTest, eh_frame_hdr_begin_x86_64) {
- Init("eh_frame_hdr_begin_x86_64/", ARCH_X86_64);
+ ASSERT_NO_FATAL_FAILURE(Init("eh_frame_hdr_begin_x86_64/", ARCH_X86_64));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@@ -957,7 +972,7 @@
}
TEST_F(UnwindOfflineTest, art_quick_osr_stub_arm) {
- Init("art_quick_osr_stub_arm/", ARCH_ARM);
+ ASSERT_NO_FATAL_FAILURE(Init("art_quick_osr_stub_arm/", ARCH_ARM));
MemoryOfflineParts* memory = new MemoryOfflineParts;
AddMemory(dir_ + "descriptor.data", memory);
@@ -1072,7 +1087,7 @@
}
TEST_F(UnwindOfflineTest, jit_map_arm) {
- Init("jit_map_arm/", ARCH_ARM);
+ ASSERT_NO_FATAL_FAILURE(Init("jit_map_arm/", ARCH_ARM));
maps_->Add(0xd025c788, 0xd025c9f0, 0, PROT_READ | PROT_EXEC | MAPS_FLAGS_JIT_SYMFILE_MAP,
"jit_map0.so", 0);
@@ -1111,4 +1126,78 @@
EXPECT_EQ(0xcd4ff960U, unwinder.frames()[5].sp);
}
+TEST_F(UnwindOfflineTest, offset_arm) {
+ ASSERT_NO_FATAL_FAILURE(Init("offset_arm/", ARCH_ARM));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(19U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 0032bfa0 (offset 0x42000) libunwindstack_test (SignalInnerFunction+40)\n"
+ " #01 pc 0032bfeb (offset 0x42000) libunwindstack_test (SignalMiddleFunction+2)\n"
+ " #02 pc 0032bff3 (offset 0x42000) libunwindstack_test (SignalOuterFunction+2)\n"
+ " #03 pc 0032fed3 (offset 0x42000) libunwindstack_test "
+ "(_ZN11unwindstackL19SignalCallerHandlerEiP7siginfoPv+26)\n"
+ " #04 pc 00026528 (offset 0x25000) libc.so\n"
+ " #05 pc 00000000 <unknown>\n"
+ " #06 pc 0032c2d9 (offset 0x42000) libunwindstack_test (InnerFunction+736)\n"
+ " #07 pc 0032cc4f (offset 0x42000) libunwindstack_test (MiddleFunction+42)\n"
+ " #08 pc 0032cc81 (offset 0x42000) libunwindstack_test (OuterFunction+42)\n"
+ " #09 pc 0032e547 (offset 0x42000) libunwindstack_test "
+ "(_ZN11unwindstackL19RemoteThroughSignalEij+270)\n"
+ " #10 pc 0032ed99 (offset 0x42000) libunwindstack_test "
+ "(_ZN11unwindstack55UnwindTest_remote_through_signal_with_invalid_func_Test8TestBodyEv+16)\n"
+ " #11 pc 00354453 (offset 0x42000) libunwindstack_test (_ZN7testing4Test3RunEv+154)\n"
+ " #12 pc 00354de7 (offset 0x42000) libunwindstack_test (_ZN7testing8TestInfo3RunEv+194)\n"
+ " #13 pc 00355105 (offset 0x42000) libunwindstack_test (_ZN7testing8TestCase3RunEv+180)\n"
+ " #14 pc 0035a215 (offset 0x42000) libunwindstack_test "
+ "(_ZN7testing8internal12UnitTestImpl11RunAllTestsEv+664)\n"
+ " #15 pc 00359f4f (offset 0x42000) libunwindstack_test (_ZN7testing8UnitTest3RunEv+110)\n"
+ " #16 pc 0034d3db (offset 0x42000) libunwindstack_test (main+38)\n"
+ " #17 pc 00092c0d (offset 0x25000) libc.so (__libc_init+48)\n"
+ " #18 pc 0004202f (offset 0x42000) libunwindstack_test (_start_main+38)\n",
+ frame_info);
+
+ EXPECT_EQ(0x2e55fa0U, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xf43d2cccU, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x2e55febU, unwinder.frames()[1].pc);
+ EXPECT_EQ(0xf43d2ce0U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x2e55ff3U, unwinder.frames()[2].pc);
+ EXPECT_EQ(0xf43d2ce8U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x2e59ed3U, unwinder.frames()[3].pc);
+ EXPECT_EQ(0xf43d2cf0U, unwinder.frames()[3].sp);
+ EXPECT_EQ(0xf4136528U, unwinder.frames()[4].pc);
+ EXPECT_EQ(0xf43d2d10U, unwinder.frames()[4].sp);
+ EXPECT_EQ(0U, unwinder.frames()[5].pc);
+ EXPECT_EQ(0xffcc0ee0U, unwinder.frames()[5].sp);
+ EXPECT_EQ(0x2e562d9U, unwinder.frames()[6].pc);
+ EXPECT_EQ(0xffcc0ee0U, unwinder.frames()[6].sp);
+ EXPECT_EQ(0x2e56c4fU, unwinder.frames()[7].pc);
+ EXPECT_EQ(0xffcc1060U, unwinder.frames()[7].sp);
+ EXPECT_EQ(0x2e56c81U, unwinder.frames()[8].pc);
+ EXPECT_EQ(0xffcc1078U, unwinder.frames()[8].sp);
+ EXPECT_EQ(0x2e58547U, unwinder.frames()[9].pc);
+ EXPECT_EQ(0xffcc1090U, unwinder.frames()[9].sp);
+ EXPECT_EQ(0x2e58d99U, unwinder.frames()[10].pc);
+ EXPECT_EQ(0xffcc1438U, unwinder.frames()[10].sp);
+ EXPECT_EQ(0x2e7e453U, unwinder.frames()[11].pc);
+ EXPECT_EQ(0xffcc1448U, unwinder.frames()[11].sp);
+ EXPECT_EQ(0x2e7ede7U, unwinder.frames()[12].pc);
+ EXPECT_EQ(0xffcc1458U, unwinder.frames()[12].sp);
+ EXPECT_EQ(0x2e7f105U, unwinder.frames()[13].pc);
+ EXPECT_EQ(0xffcc1490U, unwinder.frames()[13].sp);
+ EXPECT_EQ(0x2e84215U, unwinder.frames()[14].pc);
+ EXPECT_EQ(0xffcc14c0U, unwinder.frames()[14].sp);
+ EXPECT_EQ(0x2e83f4fU, unwinder.frames()[15].pc);
+ EXPECT_EQ(0xffcc1510U, unwinder.frames()[15].sp);
+ EXPECT_EQ(0x2e773dbU, unwinder.frames()[16].pc);
+ EXPECT_EQ(0xffcc1528U, unwinder.frames()[16].sp);
+ EXPECT_EQ(0xf41a2c0dU, unwinder.frames()[17].pc);
+ EXPECT_EQ(0xffcc1540U, unwinder.frames()[17].sp);
+ EXPECT_EQ(0x2b6c02fU, unwinder.frames()[18].pc);
+ EXPECT_EQ(0xffcc1558U, unwinder.frames()[18].sp);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/files/offline/offset_arm/libc.so b/libunwindstack/tests/files/offline/offset_arm/libc.so
new file mode 100644
index 0000000..9f5c8ca
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test b/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test
new file mode 100644
index 0000000..7a30bfa
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/maps.txt b/libunwindstack/tests/files/offline/offset_arm/maps.txt
new file mode 100644
index 0000000..6224464
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/maps.txt
@@ -0,0 +1,2 @@
+2b6c000-2e92000 r-xp 42000 00:00 0 libunwindstack_test
+f4135000-f41a9000 r-xp 25000 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/offset_arm/regs.txt b/libunwindstack/tests/files/offline/offset_arm/regs.txt
new file mode 100644
index 0000000..1f4ac8f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/regs.txt
@@ -0,0 +1,16 @@
+r0: 5
+r1: 5
+r2: 4
+r3: 1
+r4: 73804b6b
+r5: f3c9c000
+r6: 2ea09ac
+r7: 10624dd3
+r8: f41b5d8c
+r9: f3c9c000
+r10: 6f17
+r11: f3c94048
+ip: 2ea0807
+sp: f43d2ccc
+lr: 2e55fef
+pc: 2e55fa0
diff --git a/libunwindstack/tests/files/offline/offset_arm/stack0.data b/libunwindstack/tests/files/offline/offset_arm/stack0.data
new file mode 100644
index 0000000..23a9874
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/stack1.data b/libunwindstack/tests/files/offline/offset_arm/stack1.data
new file mode 100644
index 0000000..49bdd1e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/stack1.data
Binary files differ
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
index 74868d4..589731d 100644
--- a/libunwindstack/tools/unwind_for_offline.cpp
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -30,6 +30,7 @@
#include <memory>
#include <string>
#include <unordered_map>
+#include <utility>
#include <vector>
#include <unwindstack/Elf.h>
@@ -78,30 +79,44 @@
return true;
}
-bool SaveStack(pid_t pid, uint64_t sp_start, uint64_t sp_end) {
- std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("stack.data", "w+"), &fclose);
- if (fp == nullptr) {
- printf("Failed to create stack.data.\n");
- return false;
- }
+bool SaveStack(pid_t pid, const std::vector<std::pair<uint64_t, uint64_t>>& stacks) {
+ for (size_t i = 0; i < stacks.size(); i++) {
+ std::string file_name;
+ if (stacks.size() != 1) {
+ file_name = "stack" + std::to_string(i) + ".data";
+ } else {
+ file_name = "stack.data";
+ }
- size_t bytes = fwrite(&sp_start, 1, sizeof(sp_start), fp.get());
- if (bytes != sizeof(sp_start)) {
- perror("Failed to write all data.");
- return false;
- }
+ // Do this first, so if it fails, we don't create the file.
+ uint64_t sp_start = stacks[i].first;
+ uint64_t sp_end = stacks[i].second;
+ std::vector<uint8_t> buffer(sp_end - sp_start);
+ auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
+ if (!process_memory->Read(sp_start, buffer.data(), buffer.size())) {
+ printf("Unable to read stack data.\n");
+ return false;
+ }
- std::vector<uint8_t> buffer(sp_end - sp_start);
- auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
- if (!process_memory->Read(sp_start, buffer.data(), buffer.size())) {
- printf("Unable to read stack data.\n");
- return false;
- }
+ printf("Saving the stack 0x%" PRIx64 "-0x%" PRIx64 "\n", sp_start, sp_end);
- bytes = fwrite(buffer.data(), 1, buffer.size(), fp.get());
- if (bytes != buffer.size()) {
- printf("Failed to write all stack data: stack size %zu, written %zu\n", buffer.size(), bytes);
- return 1;
+ std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(file_name.c_str(), "w+"), &fclose);
+ if (fp == nullptr) {
+ printf("Failed to create stack.data.\n");
+ return false;
+ }
+
+ size_t bytes = fwrite(&sp_start, 1, sizeof(sp_start), fp.get());
+ if (bytes != sizeof(sp_start)) {
+ perror("Failed to write all data.");
+ return false;
+ }
+
+ bytes = fwrite(buffer.data(), 1, buffer.size(), fp.get());
+ if (bytes != buffer.size()) {
+ printf("Failed to write all stack data: stack size %zu, written %zu\n", buffer.size(), bytes);
+ return false;
+ }
}
return true;
@@ -110,17 +125,11 @@
bool CreateElfFromMemory(std::shared_ptr<unwindstack::Memory>& memory, map_info_t* info) {
std::string cur_name;
if (info->name.empty()) {
- cur_name = android::base::StringPrintf("anonymous:%" PRIx64, info->start);
+ cur_name = android::base::StringPrintf("anonymous_%" PRIx64, info->start);
} else {
- cur_name = basename(info->name.c_str());
- cur_name = android::base::StringPrintf("%s:%" PRIx64, basename(info->name.c_str()), info->start);
+ cur_name = android::base::StringPrintf("%s_%" PRIx64, basename(info->name.c_str()), info->start);
}
- std::unique_ptr<FILE, decltype(&fclose)> output(fopen(cur_name.c_str(), "w+"), &fclose);
- if (output == nullptr) {
- printf("Cannot create %s\n", cur_name.c_str());
- return false;
- }
std::vector<uint8_t> buffer(info->end - info->start);
// If this is a mapped in file, it might not be possible to read the entire
// map, so read all that is readable.
@@ -129,6 +138,13 @@
printf("Cannot read data from address %" PRIx64 " length %zu\n", info->start, buffer.size());
return false;
}
+
+ std::unique_ptr<FILE, decltype(&fclose)> output(fopen(cur_name.c_str(), "w+"), &fclose);
+ if (output == nullptr) {
+ printf("Cannot create %s\n", cur_name.c_str());
+ return false;
+ }
+
size_t bytes_written = fwrite(buffer.data(), 1, bytes, output.get());
if (bytes_written != bytes) {
printf("Failed to write all data to file: bytes read %zu, written %zu\n", bytes, bytes_written);
@@ -198,9 +214,21 @@
unwinder.Unwind();
std::unordered_map<uint64_t, map_info_t> maps_by_start;
- uint64_t last_sp;
+ std::vector<std::pair<uint64_t, uint64_t>> stacks;
+ uint64_t sp_map_start = 0;
+ unwindstack::MapInfo* map_info = maps.Find(sp);
+ if (map_info != nullptr) {
+ stacks.emplace_back(std::make_pair(sp, map_info->end));
+ sp_map_start = map_info->start;
+ }
+
for (auto frame : unwinder.frames()) {
- last_sp = frame.sp;
+ map_info = maps.Find(frame.sp);
+ if (map_info != nullptr && sp_map_start != map_info->start) {
+ stacks.emplace_back(std::make_pair(frame.sp, map_info->end));
+ sp_map_start = map_info->start;
+ }
+
if (maps_by_start.count(frame.map_start) == 0) {
auto info = &maps_by_start[frame.map_start];
info->start = frame.map_start;
@@ -211,7 +239,12 @@
// Try to create the elf from memory, this will handle cases where
// the data only exists in memory such as vdso data on x86.
if (!CreateElfFromMemory(process_memory, info)) {
- return 1;
+ printf("Ignoring map ");
+ if (!info->name.empty()) {
+ printf("%s\n", info->name.c_str());
+ } else {
+ printf("anonymous:%" PRIx64 "\n", info->start);
+ }
}
}
}
@@ -221,7 +254,7 @@
printf("%s\n", unwinder.FormatFrame(i).c_str());
}
- if (!SaveStack(pid, sp, last_sp)) {
+ if (!SaveStack(pid, stacks)) {
return 1;
}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index d3504ad..197047d 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -419,6 +419,7 @@
mkdir /data/misc/radio 0770 system radio
mkdir /data/misc/sms 0770 system radio
mkdir /data/misc/carrierid 0770 system radio
+ mkdir /data/misc/apns 0770 system radio
mkdir /data/misc/zoneinfo 0775 system system
mkdir /data/misc/textclassifier 0771 system system
mkdir /data/misc/vpn 0770 system vpn