Merge "Build adbd for recovery"
diff --git a/adb/adb_unique_fd.h b/adb/adb_unique_fd.h
index 7d2354d..d1dc9d1 100644
--- a/adb/adb_unique_fd.h
+++ b/adb/adb_unique_fd.h
@@ -16,6 +16,7 @@
#pragma once
+#include <errno.h>
#include <unistd.h>
#include <android-base/unique_fd.h>
@@ -48,14 +49,18 @@
if (flags & O_CLOEXEC) {
if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 ||
fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
- PLOG(FATAL) << "failed to set FD_CLOEXEC on newly created pipe";
+ close(pipefd[0]);
+ close(pipefd[1]);
+ return false;
}
}
if (flags & O_NONBLOCK) {
if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 ||
fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) {
- PLOG(FATAL) << "failed to set O_NONBLOCK on newly created pipe";
+ close(pipefd[0]);
+ close(pipefd[1]);
+ return false;
}
}
#endif
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 6493262..5aa87d9 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -316,7 +316,8 @@
static int show_help() {
// clang-format off
fprintf(stdout,
-/* 1234567890123456789012345678901234567890123456789012345678901234567890123456 */
+// 1 2 3 4 5 6 7 8
+// 12345678901234567890123456789012345678901234567890123456789012345678901234567890
"usage: fastboot [OPTION...] COMMAND...\n"
"\n"
"flashing:\n"
@@ -324,8 +325,8 @@
" flashall Flash all partitions from $ANDROID_PRODUCT_OUT.\n"
" On A/B devices, flashed slot is set as active.\n"
" Secondary images may be flashed to inactive slot.\n"
- " flash PARTITION [FILENAME]\n"
- " Flash given partition only.\n"
+ " flash PARTITION [FILENAME] Flash given partition, using the image from\n"
+ " $ANDROID_PRODUCT_OUT if no filename is given.\n"
"\n"
"basics:\n"
" devices [-l] List devices in bootloader (-l: with device paths).\n"
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 741fde0..d1c427d 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -95,6 +95,11 @@
void CreateSerializedPropertyInfo();
+struct PropertyAuditData {
+ const ucred* cr;
+ const char* name;
+};
+
void property_init() {
mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
CreateSerializedPropertyInfo();
@@ -111,7 +116,7 @@
return false;
}
- property_audit_data audit_data;
+ PropertyAuditData audit_data;
audit_data.name = name.c_str();
audit_data.cr = &cr;
@@ -393,6 +398,35 @@
DISALLOW_IMPLICIT_CONSTRUCTORS(SocketConnection);
};
+bool CheckControlPropertyPerms(const std::string& name, const std::string& value,
+ const std::string& source_context, const ucred& cr) {
+ // We check the legacy method first but these properties are dontaudit, so we only log an audit
+ // if the newer method fails as well. We only do this with the legacy ctl. properties.
+ if (name == "ctl.start" || name == "ctl.stop" || name == "ctl.restart") {
+ // The legacy permissions model is that ctl. properties have their name ctl.<action> and
+ // their value is the name of the service to apply that action to. Permissions for these
+ // actions are based on the service, so we must create a fake name of ctl.<service> to
+ // check permissions.
+ auto control_string_legacy = "ctl." + value;
+ const char* target_context_legacy = nullptr;
+ const char* type_legacy = nullptr;
+ property_info_area->GetPropertyInfo(control_string_legacy.c_str(), &target_context_legacy,
+ &type_legacy);
+
+ if (CheckMacPerms(control_string_legacy, target_context_legacy, source_context.c_str(), cr)) {
+ return true;
+ }
+ }
+
+ auto control_string_full = name + "$" + value;
+ const char* target_context_full = nullptr;
+ const char* type_full = nullptr;
+ property_info_area->GetPropertyInfo(control_string_full.c_str(), &target_context_full,
+ &type_full);
+
+ return CheckMacPerms(control_string_full, target_context_full, source_context.c_str(), cr);
+}
+
// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
const std::string& source_context, const ucred& cr, std::string* error) {
@@ -402,15 +436,9 @@
}
if (StartsWith(name, "ctl.")) {
- // ctl. properties have their name ctl.<action> and their value is the name of the service
- // to apply that action to. Permissions for these actions are based on the service, so we
- // must create a fake name of ctl.<service> to check permissions.
- auto control_string = "ctl." + value;
- const char* target_context = nullptr;
- const char* type = nullptr;
- property_info_area->GetPropertyInfo(control_string.c_str(), &target_context, &type);
- if (!CheckMacPerms(control_string, target_context, source_context.c_str(), cr)) {
- *error = StringPrintf("Unable to '%s' service %s", name.c_str() + 4, value.c_str());
+ if (!CheckControlPropertyPerms(name, value, source_context, cr)) {
+ *error = StringPrintf("Invalid permissions to perform '%s' on '%s'", name.c_str() + 4,
+ value.c_str());
return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
}
@@ -742,7 +770,7 @@
}
static int SelinuxAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
- property_audit_data* d = reinterpret_cast<property_audit_data*>(data);
+ auto* d = reinterpret_cast<PropertyAuditData*>(data);
if (!d || !d->name || !d->cr) {
LOG(ERROR) << "AuditCallback invoked with null data arguments!";
diff --git a/init/property_service.h b/init/property_service.h
index 4a354c2..cacd987 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -26,11 +26,6 @@
namespace android {
namespace init {
-struct property_audit_data {
- const ucred* cr;
- const char* name;
-};
-
extern uint32_t (*property_set)(const std::string& name, const std::string& value);
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 1e3d379..f78a31f 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -46,6 +46,7 @@
#include <android-base/macros.h>
#include <android-base/stringprintf.h>
+#include <android-base/test_utils.h>
#include <android-base/unique_fd.h>
#include <cutils/atomic.h>
#include <cutils/threads.h>
@@ -1186,49 +1187,45 @@
ASSERT_TRUE(expected_functions.empty()) << "Not all functions found in shared library.";
}
-static const char* CopySharedLibrary() {
-#if defined(__LP64__)
- const char* lib_name = "lib64";
-#else
- const char* lib_name = "lib";
-#endif
+static void CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {
+ std::string system_dir;
#if defined(__BIONIC__)
- const char* tmp_so_name = "/data/local/tmp/libbacktrace_test.so";
- std::string cp_cmd = android::base::StringPrintf("cp /system/%s/libbacktrace_test.so %s",
- lib_name, tmp_so_name);
+ system_dir = "/system/lib";
#else
- const char* tmp_so_name = "/tmp/libbacktrace_test.so";
- if (getenv("ANDROID_HOST_OUT") == NULL) {
- fprintf(stderr, "ANDROID_HOST_OUT not set, make sure you run lunch.");
- return nullptr;
- }
- std::string cp_cmd = android::base::StringPrintf("cp %s/%s/libbacktrace_test.so %s",
- getenv("ANDROID_HOST_OUT"), lib_name,
- tmp_so_name);
+ const char* host_out_env = getenv("ANDROID_HOST_OUT");
+ ASSERT_TRUE(host_out_env != nullptr);
+ system_dir = std::string(host_out_env) + "/lib";
#endif
- // Copy the shared so to a tempory directory.
- system(cp_cmd.c_str());
+#if defined(__LP64__)
+ system_dir += "64";
+#endif
- return tmp_so_name;
+ *tmp_so_name = std::string(tmp_dir) + "/libbacktrace_test.so";
+ std::string cp_cmd =
+ android::base::StringPrintf("cp %s/libbacktrace_test.so %s", system_dir.c_str(), tmp_dir);
+
+ // Copy the shared so to a tempory directory.
+ ASSERT_EQ(0, system(cp_cmd.c_str()));
}
TEST(libbacktrace, check_unreadable_elf_local) {
- const char* tmp_so_name = CopySharedLibrary();
- ASSERT_TRUE(tmp_so_name != nullptr);
+ TemporaryDir td;
+ std::string tmp_so_name;
+ ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
struct stat buf;
- ASSERT_TRUE(stat(tmp_so_name, &buf) != -1);
+ ASSERT_TRUE(stat(tmp_so_name.c_str(), &buf) != -1);
uint64_t map_size = buf.st_size;
- int fd = open(tmp_so_name, O_RDONLY);
+ int fd = open(tmp_so_name.c_str(), O_RDONLY);
ASSERT_TRUE(fd != -1);
void* map = mmap(nullptr, map_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
ASSERT_TRUE(map != MAP_FAILED);
close(fd);
- ASSERT_TRUE(unlink(tmp_so_name) != -1);
+ ASSERT_TRUE(unlink(tmp_so_name.c_str()) != -1);
std::vector<std::string> found_functions;
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS,
@@ -1256,32 +1253,33 @@
}
TEST(libbacktrace, check_unreadable_elf_remote) {
- const char* tmp_so_name = CopySharedLibrary();
- ASSERT_TRUE(tmp_so_name != nullptr);
+ TemporaryDir td;
+ std::string tmp_so_name;
+ ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
g_ready = 0;
struct stat buf;
- ASSERT_TRUE(stat(tmp_so_name, &buf) != -1);
+ ASSERT_TRUE(stat(tmp_so_name.c_str(), &buf) != -1);
uint64_t map_size = buf.st_size;
pid_t pid;
if ((pid = fork()) == 0) {
- int fd = open(tmp_so_name, O_RDONLY);
+ int fd = open(tmp_so_name.c_str(), O_RDONLY);
if (fd == -1) {
- fprintf(stderr, "Failed to open file %s: %s\n", tmp_so_name, strerror(errno));
- unlink(tmp_so_name);
+ fprintf(stderr, "Failed to open file %s: %s\n", tmp_so_name.c_str(), strerror(errno));
+ unlink(tmp_so_name.c_str());
exit(0);
}
void* map = mmap(nullptr, map_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
if (map == MAP_FAILED) {
fprintf(stderr, "Failed to map in memory: %s\n", strerror(errno));
- unlink(tmp_so_name);
+ unlink(tmp_so_name.c_str());
exit(0);
}
close(fd);
- if (unlink(tmp_so_name) == -1) {
+ if (unlink(tmp_so_name.c_str()) == -1) {
fprintf(stderr, "Failed to unlink: %s\n", strerror(errno));
exit(0);
}
@@ -1394,11 +1392,13 @@
typedef int (*test_func_t)(int, int, int, int, void (*)(void*), void*);
TEST(libbacktrace, unwind_through_unreadable_elf_local) {
- const char* tmp_so_name = CopySharedLibrary();
- ASSERT_TRUE(tmp_so_name != nullptr);
- void* lib_handle = dlopen(tmp_so_name, RTLD_NOW);
+ TemporaryDir td;
+ std::string tmp_so_name;
+ ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
+
+ void* lib_handle = dlopen(tmp_so_name.c_str(), RTLD_NOW);
ASSERT_TRUE(lib_handle != nullptr);
- ASSERT_TRUE(unlink(tmp_so_name) != -1);
+ ASSERT_TRUE(unlink(tmp_so_name.c_str()) != -1);
test_func_t test_func;
test_func = reinterpret_cast<test_func_t>(dlsym(lib_handle, "test_level_one"));
@@ -1411,11 +1411,13 @@
}
TEST(libbacktrace, unwind_through_unreadable_elf_remote) {
- const char* tmp_so_name = CopySharedLibrary();
- ASSERT_TRUE(tmp_so_name != nullptr);
- void* lib_handle = dlopen(tmp_so_name, RTLD_NOW);
+ TemporaryDir td;
+ std::string tmp_so_name;
+ ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
+
+ void* lib_handle = dlopen(tmp_so_name.c_str(), RTLD_NOW);
ASSERT_TRUE(lib_handle != nullptr);
- ASSERT_TRUE(unlink(tmp_so_name) != -1);
+ ASSERT_TRUE(unlink(tmp_so_name.c_str()) != -1);
test_func_t test_func;
test_func = reinterpret_cast<test_func_t>(dlsym(lib_handle, "test_level_one"));
@@ -1444,7 +1446,8 @@
size_t frame_num;
if (FindFuncFrameInBacktrace(backtrace.get(), reinterpret_cast<uint64_t>(test_func),
- &frame_num)) {
+ &frame_num) &&
+ frame_num != 0) {
VerifyUnreadableElfFrame(backtrace.get(), reinterpret_cast<uint64_t>(test_func), frame_num);
done = true;
}
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 1bd796a..4a165a0 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -83,8 +83,8 @@
},
android_arm: {
// TODO: This is to work around b/24465209. Remove after root cause is fixed
+ pack_relocations: false,
ldflags: ["-Wl,--hash-style=both"],
- use_clang_lld: false,
},
windows: {
srcs: ["uio.c"],
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 0ebb226..7fef106 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -46,6 +46,8 @@
"%s:%d: %s CHECK '" #predicate "' failed.",\
__FILE__, __LINE__, __FUNCTION__)
+using namespace std::string_literals;
+
namespace android {
#if defined(__ANDROID__)
@@ -236,10 +238,15 @@
// Different name is useful for debugging
namespace_name = kVendorClassloaderNamespaceName;
ALOGD("classloader namespace configured for unbundled vendor apk. library_path=%s", library_path.c_str());
- } else if (!oem_public_libraries_.empty()) {
- // oem_public_libraries are NOT available to vendor apks, otherwise it
+ } else {
+ // oem and product public libraries are NOT available to vendor apks, otherwise it
// would be system->vendor violation.
- system_exposed_libraries = system_exposed_libraries + ":" + oem_public_libraries_.c_str();
+ if (!oem_public_libraries_.empty()) {
+ system_exposed_libraries = system_exposed_libraries + ':' + oem_public_libraries_;
+ }
+ if (!product_public_libraries_.empty()) {
+ system_exposed_libraries = system_exposed_libraries + ':' + product_public_libraries_;
+ }
}
NativeLoaderNamespace native_loader_ns;
@@ -351,6 +358,8 @@
std::string vndksp_native_libraries_system_config =
root_dir + kVndkspNativeLibrariesSystemConfigPathFromRoot;
+ std::string product_public_native_libraries_dir = "/product/etc";
+
std::string error_msg;
LOG_ALWAYS_FATAL_IF(
!ReadConfig(public_native_libraries_system_config, &sonames, always_true, &error_msg),
@@ -373,7 +382,7 @@
//
// 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
+ // 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 : sonames) {
@@ -387,48 +396,15 @@
// system libs that are exposed to apps. The libs in the txt files must be
// named as lib<name>.<companyname>.so.
sonames.clear();
- std::string dirname = base::Dirname(public_native_libraries_system_config);
- std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname.c_str()), closedir);
- if (dir != nullptr) {
- // Failing to opening the dir is not an error, which can happen in
- // webview_zygote.
- struct dirent* ent;
- while ((ent = readdir(dir.get())) != nullptr) {
- if (ent->d_type != DT_REG && ent->d_type != DT_LNK) {
- continue;
- }
- const std::string filename(ent->d_name);
- if (android::base::StartsWith(filename, kPublicNativeLibrariesExtensionConfigPrefix) &&
- android::base::EndsWith(filename, kPublicNativeLibrariesExtensionConfigSuffix)) {
- const size_t start = kPublicNativeLibrariesExtensionConfigPrefixLen;
- const size_t end = filename.size() - kPublicNativeLibrariesExtensionConfigSuffixLen;
- const std::string company_name = filename.substr(start, end - start);
- const std::string config_file_path = dirname + "/" + 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());
- LOG_ALWAYS_FATAL_IF(
- !ReadConfig(
- config_file_path, &sonames,
- [&company_name](const std::string& soname, std::string* error_msg) {
- if (android::base::StartsWith(soname, "lib") &&
- android::base::EndsWith(soname, "." + company_name + ".so")) {
- return true;
- } else {
- *error_msg = "Library name \"" + soname +
- "\" does not end with the company name: " + company_name + ".";
- return false;
- }
- },
- &error_msg),
- "Error reading public native library list from \"%s\": %s", config_file_path.c_str(),
- error_msg.c_str());
- }
- }
- }
+ ReadExtensionLibraries(base::Dirname(public_native_libraries_system_config).c_str(), &sonames);
oem_public_libraries_ = base::Join(sonames, ':');
+ // read /product/etc/public.libraries-<companyname>.txt which contain partner defined
+ // product libs that are exposed to apps.
+ sonames.clear();
+ ReadExtensionLibraries(product_public_native_libraries_dir.c_str(), &sonames);
+ product_public_libraries_ = base::Join(sonames, ':');
+
// Insert VNDK version to llndk and vndksp config file names.
insert_vndk_version_str(&llndk_native_libraries_system_config);
insert_vndk_version_str(&vndksp_native_libraries_system_config);
@@ -448,11 +424,54 @@
vendor_public_libraries_ = base::Join(sonames, ':');
}
- void Reset() {
- namespaces_.clear();
- }
+ void Reset() { namespaces_.clear(); }
private:
+ 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);
+ if (android::base::StartsWith(filename, kPublicNativeLibrariesExtensionConfigPrefix) &&
+ android::base::EndsWith(filename, kPublicNativeLibrariesExtensionConfigSuffix)) {
+ const size_t start = kPublicNativeLibrariesExtensionConfigPrefixLen;
+ const size_t end = filename.size() - kPublicNativeLibrariesExtensionConfigSuffixLen;
+ const std::string company_name = filename.substr(start, end - start);
+ 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());
+
+ std::string error_msg;
+
+ LOG_ALWAYS_FATAL_IF(
+ !ReadConfig(
+ config_file_path, sonames,
+ [&company_name](const std::string& soname, std::string* error_msg) {
+ if (android::base::StartsWith(soname, "lib") &&
+ android::base::EndsWith(soname, "." + company_name + ".so")) {
+ return true;
+ } else {
+ *error_msg = "Library name \"" + soname +
+ "\" does not end with the company name: " + company_name + ".";
+ return false;
+ }
+ },
+ &error_msg),
+ "Error reading public native library list from \"%s\": %s", config_file_path.c_str(),
+ error_msg.c_str());
+ }
+ }
+ }
+ }
+
+
bool ReadConfig(const std::string& configFile, std::vector<std::string>* sonames,
const std::function<bool(const std::string& /* soname */,
std::string* /* error_msg */)>& check_soname,
@@ -559,6 +578,7 @@
std::string system_public_libraries_;
std::string vendor_public_libraries_;
std::string oem_public_libraries_;
+ std::string product_public_libraries_;
std::string system_llndk_libraries_;
std::string system_vndksp_libraries_;
diff --git a/libnativeloader/test/Android.bp b/libnativeloader/test/Android.bp
index 5cf88b0..d528f30 100644
--- a/libnativeloader/test/Android.bp
+++ b/libnativeloader/test/Android.bp
@@ -49,3 +49,23 @@
"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",
+ ],
+}
diff --git a/libnativeloader/test/Android.mk b/libnativeloader/test/Android.mk
index e625454..65e7b09 100644
--- a/libnativeloader/test/Android.mk
+++ b/libnativeloader/test/Android.mk
@@ -30,6 +30,13 @@
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
diff --git a/libnativeloader/test/public.libraries-product1.txt b/libnativeloader/test/public.libraries-product1.txt
new file mode 100644
index 0000000..358154c
--- /dev/null
+++ b/libnativeloader/test/public.libraries-product1.txt
@@ -0,0 +1,2 @@
+libfoo.product1.so
+libbar.product1.so
diff --git a/libnativeloader/test/src/android/test/app/TestActivity.java b/libnativeloader/test/src/android/test/app/TestActivity.java
index 214892d..a7a455d 100644
--- a/libnativeloader/test/src/android/test/app/TestActivity.java
+++ b/libnativeloader/test/src/android/test/app/TestActivity.java
@@ -29,6 +29,8 @@
tryLoadingLib("bar.oem1");
tryLoadingLib("foo.oem2");
tryLoadingLib("bar.oem2");
+ tryLoadingLib("foo.product1");
+ tryLoadingLib("bar.product1");
}
private void tryLoadingLib(String name) {
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 6dfa697..58295fa 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -39,12 +39,18 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#ifdef __ANDROID__
+#include <android-base/properties.h>
+#endif
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <private/android_filesystem_config.h>
#include <processgroup/processgroup.h>
+#ifdef __ANDROID__
+using android::base::GetBoolProperty;
+#endif
using android::base::StartsWith;
using android::base::StringPrintf;
using android::base::WriteStringToFile;
@@ -62,12 +68,25 @@
static const std::string& GetCgroupRootPath() {
static std::string cgroup_root_path;
std::call_once(init_path_flag, [&]() {
- // Check if mem cgroup is mounted, only then check for write-access to avoid
- // SELinux denials
+#ifdef __ANDROID__
+ // low-ram devices use per-app memcg by default, unlike high-end ones
+ bool low_ram_device = GetBoolProperty("ro.config.low_ram", false);
+ bool per_app_memcg =
+ GetBoolProperty("ro.config.per_app_memcg", low_ram_device);
+#else
+ // host does not support Android properties
+ bool per_app_memcg = false;
+#endif
+ if (per_app_memcg) {
+ // Check if mem cgroup is mounted, only then check for
+ // write-access to avoid SELinux denials
cgroup_root_path =
- (access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ? ACCT_CGROUP_PATH
- : MEM_CGROUP_PATH);
- });
+ (access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ?
+ ACCT_CGROUP_PATH : MEM_CGROUP_PATH);
+ } else {
+ cgroup_root_path = ACCT_CGROUP_PATH;
+ }
+ });
return cgroup_root_path;
}
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 6bcbbb8..26be64d 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -63,6 +63,7 @@
"MapInfo.cpp",
"Maps.cpp",
"Memory.cpp",
+ "LocalUnwinder.cpp",
"Regs.cpp",
"RegsArm.cpp",
"RegsArm64.cpp",
@@ -134,6 +135,21 @@
//-------------------------------------------------------------------------
// Unit Tests
//-------------------------------------------------------------------------
+cc_test_library {
+ name: "libunwindstack_local",
+ defaults: ["libunwindstack_flags"],
+ srcs: ["tests/TestLocal.cpp"],
+
+ cflags: [
+ "-O0",
+ "-g",
+ ],
+
+ shared_libs: [
+ "libunwindstack",
+ ],
+}
+
cc_test {
name: "libunwindstack_test",
defaults: ["libunwindstack_flags"],
@@ -160,6 +176,7 @@
"tests/ElfTest.cpp",
"tests/ElfTestUtils.cpp",
"tests/JitDebugTest.cpp",
+ "tests/LocalUnwinderTest.cpp",
"tests/LogFake.cpp",
"tests/MapInfoGetElfTest.cpp",
"tests/MapInfoGetLoadBiasTest.cpp",
diff --git a/libunwindstack/LocalUnwinder.cpp b/libunwindstack/LocalUnwinder.cpp
new file mode 100644
index 0000000..952b332
--- /dev/null
+++ b/libunwindstack/LocalUnwinder.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <pthread.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/LocalUnwinder.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+
+namespace unwindstack {
+
+bool LocalUnwinder::Init() {
+ pthread_rwlock_init(&maps_rwlock_, nullptr);
+
+ // Create the maps.
+ maps_.reset(new unwindstack::LocalUpdatableMaps());
+ if (!maps_->Parse()) {
+ maps_.reset();
+ return false;
+ }
+
+ process_memory_ = unwindstack::Memory::CreateProcessMemory(getpid());
+
+ return true;
+}
+
+bool LocalUnwinder::ShouldSkipLibrary(const std::string& map_name) {
+ for (const std::string& skip_library : skip_libraries_) {
+ if (skip_library == map_name) {
+ return true;
+ }
+ }
+ return false;
+}
+
+MapInfo* LocalUnwinder::GetMapInfo(uint64_t pc) {
+ pthread_rwlock_rdlock(&maps_rwlock_);
+ MapInfo* map_info = maps_->Find(pc);
+ pthread_rwlock_unlock(&maps_rwlock_);
+
+ if (map_info == nullptr) {
+ pthread_rwlock_wrlock(&maps_rwlock_);
+ // This is guaranteed not to invalidate any previous MapInfo objects so
+ // we don't need to worry about any MapInfo* values already in use.
+ if (maps_->Reparse()) {
+ map_info = maps_->Find(pc);
+ }
+ pthread_rwlock_unlock(&maps_rwlock_);
+ }
+
+ return map_info;
+}
+
+bool LocalUnwinder::Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames) {
+ std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
+ unwindstack::RegsGetLocal(regs.get());
+
+ size_t num_frames = 0;
+ bool adjust_pc = false;
+ while (true) {
+ uint64_t cur_pc = regs->pc();
+ uint64_t cur_sp = regs->sp();
+
+ MapInfo* map_info = GetMapInfo(cur_pc);
+ if (map_info == nullptr) {
+ break;
+ }
+
+ Elf* elf = map_info->GetElf(process_memory_, true);
+ uint64_t rel_pc = elf->GetRelPc(cur_pc, map_info);
+ uint64_t step_pc = rel_pc;
+ uint64_t pc_adjustment;
+ if (adjust_pc) {
+ pc_adjustment = regs->GetPcAdjustment(rel_pc, elf);
+ } else {
+ pc_adjustment = 0;
+ }
+ step_pc -= pc_adjustment;
+ // Skip any locations that are within this library.
+ if (num_frames != 0 || !ShouldSkipLibrary(map_info->name)) {
+ // Add frame information.
+ std::string func_name;
+ uint64_t func_offset;
+ if (elf->GetFunctionName(rel_pc, &func_name, &func_offset)) {
+ frame_info->emplace_back(map_info, cur_pc - pc_adjustment, rel_pc - pc_adjustment,
+ func_name, func_offset);
+ } else {
+ frame_info->emplace_back(map_info, cur_pc - pc_adjustment, rel_pc - pc_adjustment, "", 0);
+ }
+ num_frames++;
+ }
+ if (!elf->valid()) {
+ break;
+ }
+ if (frame_info->size() == max_frames) {
+ break;
+ }
+
+ adjust_pc = true;
+ bool finished;
+ if (!elf->Step(rel_pc, step_pc, regs.get(), process_memory_.get(), &finished) || finished) {
+ break;
+ }
+ // pc and sp are the same, terminate the unwind.
+ if (cur_pc == regs->pc() && cur_sp == regs->sp()) {
+ break;
+ }
+ }
+ return num_frames != 0;
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index bb682ea..e676a5a 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -105,4 +105,83 @@
return "/proc/" + std::to_string(pid_) + "/maps";
}
+const std::string LocalUpdatableMaps::GetMapsFile() const {
+ return "/proc/self/maps";
+}
+
+bool LocalUpdatableMaps::Reparse() {
+ // New maps will be added at the end without deleting the old ones.
+ size_t last_map_idx = maps_.size();
+ if (!Parse()) {
+ // Delete any maps added by the Parse call.
+ for (size_t i = last_map_idx; i < maps_.size(); i++) {
+ delete maps_[i];
+ }
+ maps_.resize(last_map_idx);
+ return false;
+ }
+
+ size_t total_entries = maps_.size();
+ size_t search_map_idx = 0;
+ for (size_t new_map_idx = last_map_idx; new_map_idx < maps_.size(); new_map_idx++) {
+ MapInfo* new_map_info = maps_[new_map_idx];
+ uint64_t start = new_map_info->start;
+ uint64_t end = new_map_info->end;
+ uint64_t flags = new_map_info->flags;
+ std::string* name = &new_map_info->name;
+ for (size_t old_map_idx = search_map_idx; old_map_idx < last_map_idx; old_map_idx++) {
+ MapInfo* info = maps_[old_map_idx];
+ if (start == info->start && end == info->end && flags == info->flags && *name == info->name) {
+ // No need to check
+ search_map_idx = old_map_idx + 1;
+ delete new_map_info;
+ maps_[new_map_idx] = nullptr;
+ total_entries--;
+ break;
+ } else if (info->start > start) {
+ // Stop, there isn't going to be a match.
+ search_map_idx = old_map_idx;
+ break;
+ }
+
+ // Never delete these maps, they may be in use. The assumption is
+ // that there will only every be a handfull of these so waiting
+ // to destroy them is not too expensive.
+ saved_maps_.push_back(info);
+ maps_[old_map_idx] = nullptr;
+ total_entries--;
+ }
+ if (search_map_idx >= last_map_idx) {
+ break;
+ }
+ }
+
+ // Now move out any of the maps that never were found.
+ for (size_t i = search_map_idx; i < last_map_idx; i++) {
+ saved_maps_.push_back(maps_[i]);
+ maps_[i] = nullptr;
+ total_entries--;
+ }
+
+ // Sort all of the values such that the nullptrs wind up at the end, then
+ // resize them away.
+ std::sort(maps_.begin(), maps_.end(), [](const auto* a, const auto* b) {
+ if (a == nullptr) {
+ return false;
+ } else if (b == nullptr) {
+ return true;
+ }
+ return a->start < b->start;
+ });
+ maps_.resize(total_entries);
+
+ return true;
+}
+
+LocalUpdatableMaps::~LocalUpdatableMaps() {
+ for (auto map_info : saved_maps_) {
+ delete map_info;
+ }
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/LocalUnwinder.h b/libunwindstack/include/unwindstack/LocalUnwinder.h
new file mode 100644
index 0000000..80bb53e
--- /dev/null
+++ b/libunwindstack/include/unwindstack/LocalUnwinder.h
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBUNWINDSTACK_LOCAL_UNWINDER_H
+#define _LIBUNWINDSTACK_LOCAL_UNWINDER_H
+
+#include <pthread.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Error.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Elf;
+struct MapInfo;
+
+struct LocalFrameData {
+ LocalFrameData(MapInfo* map_info, uint64_t pc, uint64_t rel_pc, const std::string& function_name,
+ uint64_t function_offset)
+ : map_info(map_info),
+ pc(pc),
+ rel_pc(rel_pc),
+ function_name(function_name),
+ function_offset(function_offset) {}
+
+ MapInfo* map_info;
+ uint64_t pc;
+ uint64_t rel_pc;
+ std::string function_name;
+ uint64_t function_offset;
+};
+
+// This is a specialized class that should only be used for doing local unwinds.
+// The Unwind call can be made as multiple times on the same object, and it can
+// be called by multiple threads at the same time.
+// It is designed to be used in debugging circumstances to get a stack trace
+// as fast as possible.
+class LocalUnwinder {
+ public:
+ LocalUnwinder() = default;
+ LocalUnwinder(const std::vector<std::string>& skip_libraries) : skip_libraries_(skip_libraries) {}
+ ~LocalUnwinder() = default;
+
+ bool Init();
+
+ bool Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames);
+
+ bool ShouldSkipLibrary(const std::string& map_name);
+
+ MapInfo* GetMapInfo(uint64_t pc);
+
+ ErrorCode LastErrorCode() { return last_error_.code; }
+ uint64_t LastErrorAddress() { return last_error_.address; }
+
+ private:
+ pthread_rwlock_t maps_rwlock_;
+ std::unique_ptr<LocalUpdatableMaps> maps_ = nullptr;
+ std::shared_ptr<Memory> process_memory_;
+ std::vector<std::string> skip_libraries_;
+ ErrorData last_error_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_LOCAL_UNWINDER_H
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
index 74e5c47..67fbed2 100644
--- a/libunwindstack/include/unwindstack/Maps.h
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -87,6 +87,19 @@
virtual ~LocalMaps() = default;
};
+class LocalUpdatableMaps : public Maps {
+ public:
+ LocalUpdatableMaps() : Maps() {}
+ virtual ~LocalUpdatableMaps();
+
+ bool Reparse();
+
+ const std::string GetMapsFile() const override;
+
+ private:
+ std::vector<MapInfo*> saved_maps_;
+};
+
class BufferMaps : public Maps {
public:
BufferMaps(const char* buffer) : buffer_(buffer) {}
diff --git a/libunwindstack/tests/LocalUnwinderTest.cpp b/libunwindstack/tests/LocalUnwinderTest.cpp
new file mode 100644
index 0000000..56a18cd
--- /dev/null
+++ b/libunwindstack/tests/LocalUnwinderTest.cpp
@@ -0,0 +1,207 @@
+/*
+ * 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.
+ */
+
+#include <dlfcn.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <android-base/stringprintf.h>
+
+#include <unwindstack/LocalUnwinder.h>
+
+namespace unwindstack {
+
+static std::vector<LocalFrameData>* g_frame_info;
+static LocalUnwinder* g_unwinder;
+
+extern "C" void SignalLocalInnerFunction() {
+ g_unwinder->Unwind(g_frame_info, 256);
+}
+
+extern "C" void SignalLocalMiddleFunction() {
+ SignalLocalInnerFunction();
+}
+
+extern "C" void SignalLocalOuterFunction() {
+ SignalLocalMiddleFunction();
+}
+
+static void SignalLocalCallerHandler(int, siginfo_t*, void*) {
+ SignalLocalOuterFunction();
+}
+
+static std::string ErrorMsg(const std::vector<const char*>& function_names,
+ const std::vector<LocalFrameData>& frame_info) {
+ std::string unwind;
+ size_t i = 0;
+ for (const auto& frame : frame_info) {
+ unwind += android::base::StringPrintf("#%02zu pc 0x%" PRIx64 " rel_pc 0x%" PRIx64, i++,
+ frame.pc, frame.rel_pc);
+ if (frame.map_info != nullptr) {
+ if (!frame.map_info->name.empty()) {
+ unwind += " " + frame.map_info->name;
+ } else {
+ unwind += android::base::StringPrintf(" 0x%" PRIx64 "-0x%" PRIx64, frame.map_info->start,
+ frame.map_info->end);
+ }
+ if (frame.map_info->offset != 0) {
+ unwind += android::base::StringPrintf(" offset 0x%" PRIx64, frame.map_info->offset);
+ }
+ }
+ if (!frame.function_name.empty()) {
+ unwind += " " + frame.function_name;
+ if (frame.function_offset != 0) {
+ unwind += android::base::StringPrintf("+%" PRId64, frame.function_offset);
+ }
+ }
+ unwind += '\n';
+ }
+
+ return std::string(
+ "Unwind completed without finding all frames\n"
+ " Looking for function: ") +
+ function_names.front() + "\n" + "Unwind data:\n" + unwind;
+}
+
+// This test assumes that this code is compiled with optimizations turned
+// off. If this doesn't happen, then all of the calls will be optimized
+// away.
+extern "C" void LocalInnerFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
+ std::vector<LocalFrameData> frame_info;
+ g_frame_info = &frame_info;
+ g_unwinder = unwinder;
+ std::vector<const char*> expected_function_names;
+
+ if (unwind_through_signal) {
+ struct sigaction act, oldact;
+ memset(&act, 0, sizeof(act));
+ act.sa_sigaction = SignalLocalCallerHandler;
+ act.sa_flags = SA_RESTART | SA_ONSTACK;
+ ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
+
+ raise(SIGUSR1);
+
+ ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr));
+
+ expected_function_names = {"LocalOuterFunction", "LocalMiddleFunction",
+ "LocalInnerFunction", "SignalLocalOuterFunction",
+ "SignalLocalMiddleFunction", "SignalLocalInnerFunction"};
+ } else {
+ ASSERT_TRUE(unwinder->Unwind(&frame_info, 256));
+
+ expected_function_names = {"LocalOuterFunction", "LocalMiddleFunction", "LocalInnerFunction"};
+ }
+
+ for (auto& frame : frame_info) {
+ if (frame.function_name == expected_function_names.back()) {
+ expected_function_names.pop_back();
+ if (expected_function_names.empty()) {
+ break;
+ }
+ }
+ }
+
+ ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info);
+}
+
+extern "C" void LocalMiddleFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
+ LocalInnerFunction(unwinder, unwind_through_signal);
+}
+
+extern "C" void LocalOuterFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
+ LocalMiddleFunction(unwinder, unwind_through_signal);
+}
+
+class LocalUnwinderTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ unwinder_.reset(new LocalUnwinder);
+ ASSERT_TRUE(unwinder_->Init());
+ }
+
+ std::unique_ptr<LocalUnwinder> unwinder_;
+};
+
+TEST_F(LocalUnwinderTest, local) {
+ LocalOuterFunction(unwinder_.get(), false);
+}
+
+TEST_F(LocalUnwinderTest, local_signal) {
+ LocalOuterFunction(unwinder_.get(), true);
+}
+
+TEST_F(LocalUnwinderTest, local_multiple) {
+ ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
+
+ ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true));
+
+ ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
+
+ ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true));
+}
+
+// This test verifies that doing an unwind before and after a dlopen
+// works. It's verifying that the maps read during the first unwind
+// do not cause a problem when doing the unwind using the code in
+// the dlopen'd code.
+TEST_F(LocalUnwinderTest, unwind_after_dlopen) {
+ // Prime the maps data.
+ ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
+
+ std::string testlib(testing::internal::GetArgvs()[0]);
+ auto const value = testlib.find_last_of('/');
+ if (value == std::string::npos) {
+ testlib = "../";
+ } else {
+ testlib = testlib.substr(0, value + 1) + "../";
+ }
+ testlib += "libunwindstack_local.so";
+
+ void* handle = dlopen(testlib.c_str(), RTLD_NOW);
+ ASSERT_TRUE(handle != nullptr);
+
+ void (*unwind_function)(void*, void*) =
+ reinterpret_cast<void (*)(void*, void*)>(dlsym(handle, "TestlibLevel1"));
+ ASSERT_TRUE(unwind_function != nullptr);
+
+ std::vector<LocalFrameData> frame_info;
+ unwind_function(unwinder_.get(), &frame_info);
+
+ ASSERT_EQ(0, dlclose(handle));
+
+ std::vector<const char*> expected_function_names{"TestlibLevel1", "TestlibLevel2",
+ "TestlibLevel3", "TestlibLevel4"};
+
+ for (auto& frame : frame_info) {
+ if (frame.function_name == expected_function_names.back()) {
+ expected_function_names.pop_back();
+ if (expected_function_names.empty()) {
+ break;
+ }
+ }
+ }
+
+ ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/TestLocal.cpp b/libunwindstack/tests/TestLocal.cpp
new file mode 100644
index 0000000..fa0baff
--- /dev/null
+++ b/libunwindstack/tests/TestLocal.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#include <unwindstack/LocalUnwinder.h>
+
+#include <vector>
+
+extern "C" void TestlibLevel4(void* unwinder_data, void* frame_data) {
+ unwindstack::LocalUnwinder* unwinder =
+ reinterpret_cast<unwindstack::LocalUnwinder*>(unwinder_data);
+ std::vector<unwindstack::LocalFrameData>* frame_info =
+ reinterpret_cast<std::vector<unwindstack::LocalFrameData>*>(frame_data);
+ unwinder->Unwind(frame_info, 256);
+}
+
+extern "C" void TestlibLevel3(void* unwinder_data, void* frame_data) {
+ TestlibLevel4(unwinder_data, frame_data);
+}
+
+extern "C" void TestlibLevel2(void* unwinder_data, void* frame_data) {
+ TestlibLevel3(unwinder_data, frame_data);
+}
+
+extern "C" void TestlibLevel1(void* unwinder_data, void* frame_data) {
+ TestlibLevel2(unwinder_data, frame_data);
+}
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index 242cc6a..83695bb 100644
--- a/libunwindstack/tests/UnwindTest.cpp
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -106,15 +106,12 @@
Unwinder unwinder(512, maps, regs, process_memory);
unwinder.Unwind();
- std::string expected_function = expected_function_names.back();
- expected_function_names.pop_back();
for (auto& frame : unwinder.frames()) {
- if (frame.function_name == expected_function) {
+ if (frame.function_name == expected_function_names.back()) {
+ expected_function_names.pop_back();
if (expected_function_names.empty()) {
break;
}
- expected_function = expected_function_names.back();
- expected_function_names.pop_back();
}
}
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index ba96cc8..51e3f9e 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -37,7 +37,8 @@
###############################################################################
namespace.default.isolated = true
-namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /product/${LIB}
# We can't have entire /system/${LIB} as permitted paths because doing so
# makes it possible to load libs in /system/${LIB}/vndk* directories by
@@ -49,6 +50,7 @@
namespace.default.permitted.paths = /system/${LIB}/drm
namespace.default.permitted.paths += /system/${LIB}/extractors
namespace.default.permitted.paths += /system/${LIB}/hw
+namespace.default.permitted.paths += /product/${LIB}
# These are where odex files are located. libart has to be able to dlopen the files
namespace.default.permitted.paths += /system/framework
namespace.default.permitted.paths += /system/app
@@ -68,6 +70,8 @@
namespace.default.asan.search.paths = /data/asan/system/${LIB}
namespace.default.asan.search.paths += /system/${LIB}
+namespace.default.asan.search.paths += /data/asan/product/${LIB}
+namespace.default.asan.search.paths += /product/${LIB}
namespace.default.asan.permitted.paths = /data
namespace.default.asan.permitted.paths += /system/${LIB}/drm
@@ -83,6 +87,7 @@
namespace.default.asan.permitted.paths += /odm/app
namespace.default.asan.permitted.paths += /odm/priv-app
namespace.default.asan.permitted.paths += /oem/app
+namespace.default.asan.permitted.paths += /product/${LIB}
namespace.default.asan.permitted.paths += /product/framework
namespace.default.asan.permitted.paths += /product/app
namespace.default.asan.permitted.paths += /product/priv-app
@@ -320,10 +325,13 @@
###############################################################################
namespace.system.isolated = false
-namespace.system.search.paths = /system/${LIB}
+namespace.system.search.paths = /system/${LIB}
+namespace.system.search.paths += /product/${LIB}
namespace.system.asan.search.paths = /data/asan/system/${LIB}
namespace.system.asan.search.paths += /system/${LIB}
+namespace.system.asan.search.paths += /data/asan/product/${LIB}
+namespace.system.asan.search.paths += /product/${LIB}
###############################################################################
# Namespace config for binaries under /postinstall.
@@ -335,4 +343,5 @@
###############################################################################
[postinstall]
namespace.default.isolated = false
-namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /product/${LIB}
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 1fd4195..ab03755 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -40,6 +40,7 @@
namespace.default.search.paths = /system/${LIB}
namespace.default.search.paths += /odm/${LIB}
namespace.default.search.paths += /vendor/${LIB}
+namespace.default.search.paths += /product/${LIB}
namespace.default.asan.search.paths = /data/asan/system/${LIB}
namespace.default.asan.search.paths += /system/${LIB}
@@ -47,6 +48,8 @@
namespace.default.asan.search.paths += /odm/${LIB}
namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
namespace.default.asan.search.paths += /vendor/${LIB}
+namespace.default.asan.search.paths += /data/asan/product/${LIB}
+namespace.default.asan.search.paths += /product/${LIB}
###############################################################################
# "sphal" namespace
@@ -205,6 +208,7 @@
namespace.default.search.paths += /system/${LIB}/vndk%VNDK_VER%
namespace.default.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
namespace.default.search.paths += /system/${LIB}
+namespace.default.search.paths += /product/${LIB}
namespace.default.asan.search.paths = /data/asan/odm/${LIB}
namespace.default.asan.search.paths += /odm/${LIB}
@@ -224,6 +228,8 @@
namespace.default.asan.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
namespace.default.asan.search.paths += /data/asan/system/${LIB}
namespace.default.asan.search.paths += /system/${LIB}
+namespace.default.asan.search.paths += /data/asan/product/${LIB}
+namespace.default.asan.search.paths += /product/${LIB}
###############################################################################
# Namespace config for binaries under /postinstall.
@@ -235,4 +241,5 @@
###############################################################################
[postinstall]
namespace.default.isolated = false
-namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /product/${LIB}