Merge "Relax host init parser to work with the build system"
diff --git a/.clang-format-4 b/.clang-format-4
index ae4a451..9127163 100644
--- a/.clang-format-4
+++ b/.clang-format-4
@@ -5,6 +5,7 @@
CommentPragmas: NOLINT:.*
DerivePointerAlignment: false
IndentWidth: 4
+ContinuationIndentWidth: 8
PointerAlignment: Left
TabWidth: 4
UseTab: Never
diff --git a/CleanSpec.mk b/CleanSpec.mk
index dc45959..0e43dae 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -74,3 +74,7 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/ld.config.txt)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/llndk.libraries.txt)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/vndksp.libraries.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/ld.config.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/llndk.libraries.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/vndksp.libraries.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/)
diff --git a/adb/Android.bp b/adb/Android.bp
index 553473f..53bf7e3 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -302,8 +302,6 @@
name: "adbd",
defaults: ["adb_defaults"],
- // adbd must be static, as it is copied into the recovery image.
- static_executable: true,
recovery_available: true,
srcs: [
@@ -344,7 +342,6 @@
"libselinux",
"libsquashfs_utils",
"libqemu_pipe",
- "libdebuggerd_handler",
"libbase",
"libcutils",
@@ -371,6 +368,7 @@
"liblog",
"libusb",
"libmdnssd",
+ "libselinux",
],
test_suites: ["device-tests"],
}
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 39983ab..06fcf16 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -1136,8 +1136,7 @@
cmd += " " + escape_arg(*argv++);
}
- // No need for shell protocol with logcat, always disable for simplicity.
- return send_shell_command(cmd, true);
+ return send_shell_command(cmd);
}
static void write_zeros(int bytes, int fd) {
@@ -1861,7 +1860,7 @@
cmd += " " + escape_arg(*argv++);
}
- return send_shell_command(cmd, false);
+ return send_shell_command(cmd);
}
static int install_app(int argc, const char** argv) {
@@ -2049,7 +2048,7 @@
cmd += " " + escape_arg(*argv++);
}
- return send_shell_command(cmd, false);
+ return send_shell_command(cmd);
}
static int uninstall_app_legacy(int argc, const char** argv) {
@@ -2073,7 +2072,7 @@
static int delete_file(const std::string& filename) {
std::string cmd = "rm -f " + escape_arg(filename);
- return send_shell_command(cmd, false);
+ return send_shell_command(cmd);
}
static int install_app_legacy(int argc, const char** argv) {
diff --git a/adb/client/commandline.h b/adb/client/commandline.h
index 36cd798..3d10030 100644
--- a/adb/client/commandline.h
+++ b/adb/client/commandline.h
@@ -92,7 +92,7 @@
// resulting output.
// if |callback| is non-null, stdout/stderr output will be handled by it.
int send_shell_command(
- const std::string& command, bool disable_shell_protocol,
- StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
+ const std::string& command, bool disable_shell_protocol = false,
+ StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
#endif // COMMANDLINE_H
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 232d9c5..c02cafa 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -38,7 +38,6 @@
#include <scoped_minijail.h>
#include <private/android_filesystem_config.h>
-#include "debuggerd/handler.h"
#include "selinux/android.h"
#include "adb.h"
@@ -274,7 +273,6 @@
close_stdin();
- debuggerd_init(nullptr);
adb_trace_init(argv);
D("Handling main()");
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index 401c99c..56d647a 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -98,6 +98,7 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <private/android_logger.h>
+#include <selinux/android.h>
#include "adb.h"
#include "adb_io.h"
@@ -343,6 +344,24 @@
adb_write(oom_score_adj_fd, oom_score_adj_value, strlen(oom_score_adj_value)));
}
+#ifdef __ANDROID_RECOVERY__
+ // Special routine for recovery. Switch to shell domain when adbd is
+ // is running with dropped privileged (i.e. not running as root) and
+ // is built for the recovery mode. This is required because recovery
+ // rootfs is not labeled and everything is labeled just as rootfs.
+ char* con = nullptr;
+ if (getcon(&con) == 0) {
+ if (!strcmp(con, "u:r:adbd:s0")) {
+ if (selinux_android_setcon("u:r:shell:s0") < 0) {
+ LOG(FATAL) << "Could not set SELinux context for subprocess";
+ }
+ }
+ freecon(con);
+ } else {
+ LOG(FATAL) << "Failed to get SELinux context";
+ }
+#endif
+
if (command_.empty()) {
// Spawn a login shell if we don't have a command.
execle(_PATH_BSHELL, "-" _PATH_BSHELL, nullptr, cenv.data());
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 8ed92a6..ddd1caf 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -480,23 +480,25 @@
- (wait until screen is up, boot has completed)
- adb shell getprop ro.boot.bootreason (bootloader reason)
- adb shell getprop persist.sys.boot.reason (last reason)
+- adb shell getprop sys.boot.reason.last (last last reason)
- adb shell getprop sys.boot.reason (system reason)
- NB: all should have a value that is compliant with our known set." ]
test_properties() {
duration_test 1
wait_for_screen
retval=0
- check_set="ro.boot.bootreason persist.sys.boot.reason sys.boot.reason"
+ check_set="ro.boot.bootreason sys.boot.reason.last sys.boot.reason"
bootloader=""
# NB: this test could fail if performed _after_ optional_factory_reset test
# and will report
# ERROR: expected "reboot" got ""
- # for Android property persist.sys.boot.reason
+ # for Android property sys.boot.reason.last
# following is mitigation for the persist.sys.boot.reason, skip it
if [ "reboot,factory_reset" = "`validate_property ro.boot_bootreason`" ]; then
check_set="ro.boot.bootreason sys.boot.reason"
bootloader="bootloader"
fi
+ EXPECT_PROPERTY persist.sys.boot.reason ""
for prop in ${check_set}; do
reason=`validate_property ${prop}`
EXPECT_PROPERTY ${prop} ${reason} || retval=${?}
@@ -554,7 +556,8 @@
popd >&2
wait_for_screen
EXPECT_PROPERTY sys.boot.reason "\(reboot,ota\|bootloader\)"
- EXPECT_PROPERTY persist.sys.boot.reason bootloader
+ EXPECT_PROPERTY sys.boot.reason.last bootloader
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs reboot,ota bootloader
}
@@ -568,7 +571,8 @@
adb reboot ota
wait_for_screen
EXPECT_PROPERTY sys.boot.reason reboot,ota
- EXPECT_PROPERTY persist.sys.boot.reason reboot,ota
+ EXPECT_PROPERTY sys.boot.reason.last reboot,ota
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs reboot,ota
}
@@ -593,9 +597,15 @@
wait_for_screen
bootloader_reason=`validate_property ro.boot.bootreason`
EXPECT_PROPERTY ro.boot.bootreason ${bootloader_reason}
- EXPECT_PROPERTY sys.boot.reason ${reason}
- EXPECT_PROPERTY persist.sys.boot.reason ${reason}
- report_bootstat_logs ${reason}
+ # to make sys.boot.reason report user friendly
+ reasons=${reason}
+ if [ "${bootloader_reason}" != "${reason}" -a -n "${bootloader_reason}" ]; then
+ reasons="\(${reason}\|${bootloader_reason}\)"
+ fi
+ EXPECT_PROPERTY sys.boot.reason ${reasons}
+ EXPECT_PROPERTY sys.boot.reason.last ${reason}
+ EXPECT_PROPERTY persist.sys.boot.reason ""
+ report_bootstat_logs ${reason} ${bootloader_reason}
}
[ "USAGE: test_cold
@@ -627,7 +637,8 @@
adb reboot >&2
wait_for_screen
EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
- EXPECT_PROPERTY persist.sys.boot.reason "reboot,.*"
+ EXPECT_PROPERTY sys.boot.reason.last "reboot,.*"
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs reboot,factory_reset reboot, reboot,adb \
"-bootstat: Failed to read /data/misc/bootstat/build_date: No such file or directory" \
"-bootstat: Failed to parse boot time record: /data/misc/bootstat/build_date"
@@ -658,6 +669,7 @@
wait_for_screen
( exit ${save_ret} ) # because one can not just do ?=${save_ret}
EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
+ EXPECT_PROPERTY sys.boot.reason.last ""
EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs reboot,factory_reset bootloader \
"-bootstat: Failed to read /data/misc/bootstat/last_boot_time_utc: No such file or directory" \
@@ -731,7 +743,8 @@
)
EXPECT_PROPERTY sys.boot.reason shutdown,battery
- EXPECT_PROPERTY persist.sys.boot.reason cold
+ EXPECT_PROPERTY sys.boot.reason.last cold
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs shutdown,battery "-bootstat: Battery level at shutdown 2%"
exitPstore
}
@@ -752,7 +765,8 @@
echo -n "WARNING: Please power device back up, waiting ... " >&2
wait_for_screen -n >&2
EXPECT_PROPERTY sys.boot.reason shutdown,battery
- EXPECT_PROPERTY persist.sys.boot.reason shutdown,battery
+ EXPECT_PROPERTY sys.boot.reason.last shutdown,battery
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs shutdown,battery
}
@@ -772,7 +786,8 @@
echo -n "WARNING: Please power device back up, waiting ... " >&2
wait_for_screen -n >&2
EXPECT_PROPERTY sys.boot.reason shutdown,thermal,battery
- EXPECT_PROPERTY persist.sys.boot.reason shutdown,thermal,battery
+ EXPECT_PROPERTY sys.boot.reason.last shutdown,thermal,battery
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs shutdown,thermal,battery
}
@@ -808,7 +823,8 @@
echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
wait_for_screen
EXPECT_PROPERTY sys.boot.reason ${panic_msg}
- EXPECT_PROPERTY persist.sys.boot.reason ${panic_msg}
+ EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs kernel_panic,sysrq
exitPstore
}
@@ -835,7 +851,8 @@
echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
wait_for_screen
EXPECT_PROPERTY sys.boot.reason ${panic_msg}
- EXPECT_PROPERTY persist.sys.boot.reason ${panic_msg}
+ EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs kernel_panic,sysrq,test \
"-bootstat: Unknown boot reason: kernel_panic,sysrq,test"
exitPstore
@@ -865,7 +882,8 @@
adb reboot warm
wait_for_screen
EXPECT_PROPERTY sys.boot.reason ${panic_msg}
- EXPECT_PROPERTY persist.sys.boot.reason ${panic_msg}
+ EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs kernel_panic,hung
exitPstore
}
@@ -897,7 +915,8 @@
echo -n "WARNING: Please power device back up, waiting ... " >&2
wait_for_screen -n >&2
EXPECT_PROPERTY sys.boot.reason shutdown,thermal
- EXPECT_PROPERTY persist.sys.boot.reason shutdown,thermal
+ EXPECT_PROPERTY sys.boot.reason.last shutdown,thermal
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs shutdown,thermal
}
@@ -917,7 +936,8 @@
echo -n "WARNING: Please power device back up, waiting ... " >&2
wait_for_screen -n >&2
EXPECT_PROPERTY sys.boot.reason shutdown,userrequested
- EXPECT_PROPERTY persist.sys.boot.reason shutdown,userrequested
+ EXPECT_PROPERTY sys.boot.reason.last shutdown,userrequested
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs shutdown,userrequested
}
@@ -933,7 +953,8 @@
adb shell reboot
wait_for_screen
EXPECT_PROPERTY sys.boot.reason reboot,shell
- EXPECT_PROPERTY persist.sys.boot.reason reboot,shell
+ EXPECT_PROPERTY sys.boot.reason.last reboot,shell
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs reboot,shell
}
@@ -949,7 +970,8 @@
adb reboot
wait_for_screen
EXPECT_PROPERTY sys.boot.reason reboot,adb
- EXPECT_PROPERTY persist.sys.boot.reason reboot,adb
+ EXPECT_PROPERTY sys.boot.reason.last reboot,adb
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs reboot,adb
}
@@ -984,23 +1006,8 @@
adb shell 'reboot "Its Just So Hard"'
wait_for_screen
EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard
- EXPECT_PROPERTY persist.sys.boot.reason "reboot,Its Just So Hard"
- # Do not leave this test with an illegal value in persist.sys.boot.reason
- save_ret=${?} # hold on to error code from above two lines
- if isDebuggable; then # can do this easy, or we can do this hard.
- adb shell su root setprop persist.sys.boot.reason reboot,its_just_so_hard
- ( exit ${save_ret} ) # because one can not just do ?=${save_ret}
- else
- report_bootstat_logs reboot,its_just_so_hard # report what we have so far
- # user build mitigation
- adb shell reboot its_just_so_hard
- wait_for_screen
- ( exit ${save_ret} ) # because one can not just do ?=${save_ret}
- EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard
- fi
- # Ensure persist.sys.boot.reason now valid, failure here acts as a signal
- # that we could choke up following tests. For example test_properties.
- EXPECT_PROPERTY persist.sys.boot.reason reboot,its_just_so_hard ${flag}
+ EXPECT_PROPERTY sys.boot.reason.last reboot,its_just_so_hard
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs reboot,its_just_so_hard
}
@@ -1049,7 +1056,8 @@
# Check values
EXPECT_PROPERTY ro.boot.bootreason "${bootloader_expected}"
EXPECT_PROPERTY sys.boot.reason "${sys_expected}"
- EXPECT_PROPERTY persist.sys.boot.reason "${last_expected}"
+ EXPECT_PROPERTY sys.boot.reason.last "${last_expected}"
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs "${sys_expected}"
}
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index d5fb047..9fe25fd 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -310,6 +310,7 @@
{"kernel_panic,adsp", 165},
{"kernel_panic,dsps", 166},
{"kernel_panic,wcnss", 167},
+ {"kernel_panic,_sde_encoder_phys_cmd_handle_ppdone_timeout", 168},
};
// Converts a string value representing the reason the system booted to an
@@ -734,6 +735,7 @@
const char system_reboot_reason_property[] = "sys.boot.reason";
const char last_reboot_reason_property[] = LAST_REBOOT_REASON_PROPERTY;
+const char last_last_reboot_reason_property[] = "sys.boot.reason.last";
const char bootloader_reboot_reason_property[] = "ro.boot.bootreason";
// Scrub, Sanitize, Standardize and Enhance the boot reason string supplied.
@@ -925,27 +927,10 @@
}
android_logcat_pclose(&ctx, fp);
static const char logcat_battery[] = "W/healthd ( 0): battery l=";
- const char* match = logcat_battery;
- if (content == "") {
- // Service logd.klog not running, go to smaller buffer in the kernel.
- int rc = klogctl(KLOG_SIZE_BUFFER, nullptr, 0);
- if (rc > 0) {
- ssize_t len = rc + 1024; // 1K Margin should it grow between calls.
- std::unique_ptr<char[]> buf(new char[len]);
- rc = klogctl(KLOG_READ_ALL, buf.get(), len);
- if (rc < len) {
- len = rc + 1;
- }
- buf[--len] = '\0';
- content = buf.get();
- }
- match = battery;
- }
-
- pos = content.find(match); // The first one it finds.
+ pos = content.find(logcat_battery); // The first one it finds.
if (pos != std::string::npos) {
- digits = content.substr(pos + strlen(match), strlen("100 "));
+ digits = content.substr(pos + strlen(logcat_battery), strlen("100 "));
}
endptr = digits.c_str();
level = 0;
@@ -1001,10 +986,6 @@
}
LOG(INFO) << "Canonical boot reason: " << ret;
- if (isKernelRebootReason(ret) && (GetProperty(last_reboot_reason_property) != "")) {
- // Rewrite as it must be old news, kernel reasons trump user space.
- SetProperty(last_reboot_reason_property, ret);
- }
return ret;
}
@@ -1134,6 +1115,15 @@
const std::string system_boot_reason(BootReasonStrToReason(bootloader_boot_reason));
// Record the scrubbed system_boot_reason to the property
SetProperty(system_reboot_reason_property, system_boot_reason);
+ // Shift last_reboot_reason_property to last_last_reboot_reason_property
+ std::string last_boot_reason(GetProperty(last_reboot_reason_property));
+ if (last_boot_reason.empty() || isKernelRebootReason(system_boot_reason)) {
+ last_boot_reason = system_boot_reason;
+ } else {
+ transformReason(last_boot_reason);
+ }
+ SetProperty(last_last_reboot_reason_property, last_boot_reason);
+ SetProperty(last_reboot_reason_property, "");
}
// Records several metrics related to the time it takes to boot the device,
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 0b13662..1b20157 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -47,6 +47,7 @@
cc_library_static {
name: "libtombstoned_client_static",
defaults: ["debuggerd_defaults"],
+ recovery_available: true,
srcs: [
"tombstoned/tombstoned_client.cpp",
"util.cpp",
@@ -90,7 +91,6 @@
cc_library_static {
name: "libdebuggerd_handler",
defaults: ["debuggerd_defaults"],
- recovery_available: true,
srcs: ["handler/debuggerd_fallback_nop.cpp"],
whole_static_libs: [
@@ -104,6 +104,7 @@
cc_library_static {
name: "libdebuggerd_handler_fallback",
defaults: ["debuggerd_defaults"],
+ recovery_available: true,
srcs: [
"handler/debuggerd_fallback.cpp",
],
@@ -120,6 +121,12 @@
"liblzma",
"libcutils",
],
+ target: {
+ recovery: {
+ cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+ exclude_static_libs: ["libdexfile"],
+ },
+ },
export_include_dirs: ["include"],
}
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index b016e23..360ea95 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -20,6 +20,7 @@
#include <string.h>
#include <limits>
+#include <string_view>
#include <thread>
#include <android-base/file.h>
@@ -33,9 +34,10 @@
using android::base::unique_fd;
static void usage(int exit_code) {
- fprintf(stderr, "usage: debuggerd [-b] PID\n");
+ fprintf(stderr, "usage: debuggerd [-bj] PID\n");
fprintf(stderr, "\n");
fprintf(stderr, "-b, --backtrace just a backtrace rather than a full tombstone\n");
+ fprintf(stderr, "-j collect java traces\n");
_exit(exit_code);
}
@@ -58,8 +60,19 @@
int main(int argc, char* argv[]) {
if (argc <= 1) usage(0);
if (argc > 3) usage(1);
- if (argc == 3 && strcmp(argv[1], "-b") != 0 && strcmp(argv[1], "--backtrace") != 0) usage(1);
- bool backtrace_only = argc == 3;
+
+ DebuggerdDumpType dump_type = kDebuggerdTombstone;
+
+ if (argc == 3) {
+ std::string_view flag = argv[1];
+ if (flag == "-b" || flag == "--backtrace") {
+ dump_type = kDebuggerdNativeBacktrace;
+ } else if (flag == "-j") {
+ dump_type = kDebuggerdJavaBacktrace;
+ } else {
+ usage(1);
+ }
+ }
pid_t pid;
if (!android::base::ParseInt(argv[argc - 1], &pid, 1, std::numeric_limits<pid_t>::max())) {
@@ -90,8 +103,7 @@
}
std::thread redirect_thread = spawn_redirect_thread(std::move(piperead));
- if (!debuggerd_trigger_dump(pid, backtrace_only ? kDebuggerdNativeBacktrace : kDebuggerdTombstone,
- 0, std::move(pipewrite))) {
+ if (!debuggerd_trigger_dump(pid, dump_type, 0, std::move(pipewrite))) {
redirect_thread.join();
errx(1, "failed to dump process %d", pid);
}
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index bc3b04b..923442f 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -56,12 +56,15 @@
"libselinux",
"libavb",
"libfstab",
+ "libdm",
],
export_static_lib_headers: [
"libfstab",
+ "libdm",
],
whole_static_libs: [
"liblogwrap",
+ "libdm",
"libfstab",
],
cppflags: [
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 6e9ffba..9856126 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1190,27 +1190,6 @@
return 0;
}
-int fs_mgr_unmount_all(struct fstab *fstab)
-{
- int i = 0;
- int ret = 0;
-
- if (!fstab) {
- return -1;
- }
-
- while (fstab->recs[i].blk_device) {
- if (umount(fstab->recs[i].mount_point)) {
- LERROR << "Cannot unmount filesystem at "
- << fstab->recs[i].mount_point;
- ret = -1;
- }
- i++;
- }
-
- return ret;
-}
-
/* This must be called after mount_all, because the mkswap command needs to be
* available.
*/
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index 5a9cb65..2020fa6 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -139,38 +139,23 @@
};
std::unique_ptr<FsManagerAvbVerifier> FsManagerAvbVerifier::Create() {
- std::string cmdline;
- if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
- PERROR << "Failed to read /proc/cmdline";
- return nullptr;
- }
-
std::unique_ptr<FsManagerAvbVerifier> avb_verifier(new FsManagerAvbVerifier());
if (!avb_verifier) {
LERROR << "Failed to create unique_ptr<FsManagerAvbVerifier>";
return nullptr;
}
- std::string digest;
- std::string hash_alg;
- for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
- std::vector<std::string> pieces = android::base::Split(entry, "=");
- const std::string& key = pieces[0];
- const std::string& value = pieces[1];
-
- if (key == "androidboot.vbmeta.hash_alg") {
- hash_alg = value;
- } else if (key == "androidboot.vbmeta.size") {
- if (!android::base::ParseUint(value.c_str(), &avb_verifier->vbmeta_size_)) {
- return nullptr;
- }
- } else if (key == "androidboot.vbmeta.digest") {
- digest = value;
- }
+ std::string value;
+ if (!fs_mgr_get_boot_config("vbmeta.size", &value) ||
+ !android::base::ParseUint(value.c_str(), &avb_verifier->vbmeta_size_)) {
+ LERROR << "Invalid hash size: " << value.c_str();
+ return nullptr;
}
// Reads hash algorithm.
size_t expected_digest_size = 0;
+ std::string hash_alg;
+ fs_mgr_get_boot_config("vbmeta.hash_alg", &hash_alg);
if (hash_alg == "sha256") {
expected_digest_size = SHA256_DIGEST_LENGTH * 2;
avb_verifier->hash_alg_ = kSHA256;
@@ -183,6 +168,8 @@
}
// Reads digest.
+ std::string digest;
+ fs_mgr_get_boot_config("vbmeta.digest", &digest);
if (digest.size() != expected_digest_size) {
LERROR << "Unexpected digest size: " << digest.size()
<< " (expected: " << expected_digest_size << ")";
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index 9c5d3f3..733ad55 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -14,7 +14,10 @@
* limitations under the License.
*/
+#include <algorithm>
+#include <iterator>
#include <string>
+#include <vector>
#include <android-base/file.h>
#include <android-base/stringprintf.h>
@@ -23,46 +26,71 @@
#include "fs_mgr_priv.h"
-// Tries to get the given boot config value from kernel cmdline.
-// Returns true if successfully found, false otherwise.
-bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val) {
+std::vector<std::pair<std::string, std::string>> fs_mgr_parse_boot_config(const std::string& cmdline) {
+ static constexpr char quote = '"';
+
+ std::vector<std::pair<std::string, std::string>> result;
+ size_t base = 0;
+ while (true) {
+ // skip quoted spans
+ auto found = base;
+ while (((found = cmdline.find_first_of(" \"", found)) != cmdline.npos) &&
+ (cmdline[found] == quote)) {
+ // unbalanced quote is ok
+ if ((found = cmdline.find(quote, found + 1)) == cmdline.npos) break;
+ ++found;
+ }
+ std::string piece;
+ auto source = cmdline.substr(base, found - base);
+ std::remove_copy(source.begin(), source.end(),
+ std::back_insert_iterator<std::string>(piece), quote);
+ auto equal_sign = piece.find('=');
+ if (equal_sign == piece.npos) {
+ if (!piece.empty()) {
+ // no difference between <key> and <key>=
+ result.emplace_back(std::move(piece), "");
+ }
+ } else {
+ result.emplace_back(piece.substr(0, equal_sign), piece.substr(equal_sign + 1));
+ }
+ if (found == cmdline.npos) break;
+ base = found + 1;
+ }
+
+ return result;
+}
+
+bool fs_mgr_get_boot_config_from_kernel(const std::string& cmdline, const std::string& android_key,
+ std::string* out_val) {
FS_MGR_CHECK(out_val != nullptr);
- std::string cmdline;
- std::string cmdline_key("androidboot." + key);
- if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
- for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
- std::vector<std::string> pieces = android::base::Split(entry, "=");
- if (pieces.size() == 2) {
- if (pieces[0] == cmdline_key) {
- *out_val = pieces[1];
- return true;
- }
- }
+ const std::string cmdline_key("androidboot." + android_key);
+ for (const auto& [key, value] : fs_mgr_parse_boot_config(cmdline)) {
+ if (key == cmdline_key) {
+ *out_val = value;
+ return true;
}
}
+ *out_val = "";
return false;
}
-// Tries to get the boot config value in properties, kernel cmdline and
-// device tree (in that order). returns 'true' if successfully found, 'false'
-// otherwise
+// Tries to get the given boot config value from kernel cmdline.
+// Returns true if successfully found, false otherwise.
+bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val) {
+ std::string cmdline;
+ if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) return false;
+ return fs_mgr_get_boot_config_from_kernel(cmdline, key, out_val);
+}
+
+// Tries to get the boot config value in device tree, properties and
+// kernel cmdline (in that order). Returns 'true' if successfully
+// found, 'false' otherwise.
bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val) {
FS_MGR_CHECK(out_val != nullptr);
- // first check if we have "ro.boot" property already
- *out_val = android::base::GetProperty("ro.boot." + key, "");
- if (!out_val->empty()) {
- return true;
- }
-
- // fallback to kernel cmdline, properties may not be ready yet
- if (fs_mgr_get_boot_config_from_kernel_cmdline(key, out_val)) {
- return true;
- }
-
- // lastly, check the device tree
+ // firstly, check the device tree
if (is_dt_compatible()) {
std::string file_name = get_android_dt_dir() + "/" + key;
if (android::base::ReadFileToString(file_name, out_val)) {
@@ -73,5 +101,16 @@
}
}
+ // next, check if we have "ro.boot" property already
+ *out_val = android::base::GetProperty("ro.boot." + key, "");
+ if (!out_val->empty()) {
+ return true;
+ }
+
+ // finally, fallback to kernel cmdline, properties may not be ready yet
+ if (fs_mgr_get_boot_config_from_kernel_cmdline(key, out_val)) {
+ return true;
+ }
+
return false;
}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index af4d6c1..a14dba3 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -679,7 +679,6 @@
// We can't call fs_mgr_free_fstab because a->recs still references the
// memory allocated by strdup.
free(b->recs);
- free(b->fstab_filename);
free(b);
a->num_entries = total_entries;
@@ -741,9 +740,7 @@
}
fstab = fs_mgr_read_fstab_file(fstab_file);
- if (fstab) {
- fstab->fstab_filename = strdup(fstab_path);
- } else {
+ if (!fstab) {
LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << fstab_path << "'";
}
@@ -854,9 +851,6 @@
/* Free the fstab_recs array created by calloc(3) */
free(fstab->recs);
- /* Free the fstab filename */
- free(fstab->fstab_filename);
-
/* Free fstab */
free(fstab);
}
diff --git a/fs_mgr/fs_mgr_priv_boot_config.h b/fs_mgr/fs_mgr_priv_boot_config.h
index d98dc02..417fb38 100644
--- a/fs_mgr/fs_mgr_priv_boot_config.h
+++ b/fs_mgr/fs_mgr_priv_boot_config.h
@@ -19,7 +19,13 @@
#include <sys/cdefs.h>
#include <string>
+#include <utility>
+#include <vector>
+std::vector<std::pair<std::string, std::string>> fs_mgr_parse_boot_config(const std::string& cmdline);
+
+bool fs_mgr_get_boot_config_from_kernel(const std::string& cmdline, const std::string& key,
+ std::string* out_val);
bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val);
bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val);
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 72f019e..c1b2ed9 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -70,7 +70,6 @@
char *tmp_mount_point);
int fs_mgr_do_mount_one(struct fstab_rec *rec);
int fs_mgr_do_tmpfs_mount(const char *n_name);
-int fs_mgr_unmount_all(struct fstab *fstab);
struct fstab_rec const* fs_mgr_get_crypt_entry(struct fstab const* fstab);
void fs_mgr_get_crypt_info(struct fstab* fstab, char* key_loc, char* real_blk_device, size_t size);
bool fs_mgr_load_verity_state(int* mode);
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index d232cca..b1ee328 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -33,7 +33,6 @@
struct fstab {
int num_entries;
struct fstab_rec* recs;
- char* fstab_filename;
};
struct fstab_rec {
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
new file mode 100644
index 0000000..672d401
--- /dev/null
+++ b/fs_mgr/libdm/Android.bp
@@ -0,0 +1,37 @@
+//
+// 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.
+//
+
+cc_library_static {
+ name: "libdm",
+ recovery_available: true,
+
+ export_include_dirs: ["include"],
+ cflags: [
+ // TODO(b/110035986): Allows us to create a skeleton of required classes
+ "-Wno-unused-private-field",
+ ],
+
+ srcs: [
+ "dm_table.cpp",
+ "dm_target.cpp",
+ "dm.cpp"
+ ],
+
+ header_libs: [
+ "libbase_headers",
+ "liblog_headers",
+ ],
+}
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
new file mode 100644
index 0000000..57c1270
--- /dev/null
+++ b/fs_mgr/libdm/dm.cpp
@@ -0,0 +1,270 @@
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <linux/dm-ioctl.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/unique_fd.h>
+
+#include "dm.h"
+
+namespace android {
+namespace dm {
+
+DeviceMapper& DeviceMapper::Instance() {
+ static DeviceMapper instance;
+ return instance;
+}
+// Creates a new device mapper device
+bool DeviceMapper::CreateDevice(const std::string& name) {
+ if (name.empty()) {
+ LOG(ERROR) << "Unnamed device mapper device creation is not supported";
+ return false;
+ }
+
+ if (name.size() >= DM_NAME_LEN) {
+ LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
+ return false;
+ }
+
+ std::unique_ptr<struct dm_ioctl, decltype(&free)> io(
+ static_cast<struct dm_ioctl*>(malloc(sizeof(struct dm_ioctl))), free);
+ if (io == nullptr) {
+ LOG(ERROR) << "Failed to allocate dm_ioctl";
+ return false;
+ }
+ InitIo(io.get(), name);
+
+ if (ioctl(fd_, DM_DEV_CREATE, io.get())) {
+ PLOG(ERROR) << "DM_DEV_CREATE failed to create [" << name << "]";
+ return false;
+ }
+
+ // Check to make sure the newly created device doesn't already have targets
+ // added or opened by someone
+ CHECK(io->target_count == 0) << "Unexpected targets for newly created [" << name << "] device";
+ CHECK(io->open_count == 0) << "Unexpected opens for newly created [" << name << "] device";
+
+ // Creates a new device mapper device with the name passed in
+ return true;
+}
+
+bool DeviceMapper::DeleteDevice(const std::string& name) {
+ if (name.empty()) {
+ LOG(ERROR) << "Unnamed device mapper device creation is not supported";
+ return false;
+ }
+
+ if (name.size() >= DM_NAME_LEN) {
+ LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
+ return false;
+ }
+
+ std::unique_ptr<struct dm_ioctl, decltype(&free)> io(
+ static_cast<struct dm_ioctl*>(malloc(sizeof(struct dm_ioctl))), free);
+ if (io == nullptr) {
+ LOG(ERROR) << "Failed to allocate dm_ioctl";
+ return false;
+ }
+ InitIo(io.get(), name);
+
+ if (ioctl(fd_, DM_DEV_REMOVE, io.get())) {
+ PLOG(ERROR) << "DM_DEV_REMOVE failed to create [" << name << "]";
+ return false;
+ }
+
+ // Check to make sure appropriate uevent is generated so ueventd will
+ // do the right thing and remove the corresponding device node and symlinks.
+ CHECK(io->flags & DM_UEVENT_GENERATED_FLAG)
+ << "Didn't generate uevent for [" << name << "] removal";
+
+ return true;
+}
+
+const std::unique_ptr<DmTable> DeviceMapper::table(const std::string& /* name */) const {
+ // TODO(b/110035986): Return the table, as read from the kernel instead
+ return nullptr;
+}
+
+DmDeviceState DeviceMapper::state(const std::string& /* name */) const {
+ // TODO(b/110035986): Return the state, as read from the kernel instead
+ return DmDeviceState::INVALID;
+}
+
+bool DeviceMapper::LoadTableAndActivate(const std::string& /* name */, const DmTable& /* table */) {
+ return false;
+}
+
+// Reads all the available device mapper targets and their corresponding
+// versions from the kernel and returns in a vector
+bool DeviceMapper::GetAvailableTargets(std::vector<DmTarget>* targets) {
+ targets->clear();
+
+ // calculate the space needed to read a maximum of kMaxPossibleDmTargets
+ uint32_t payload_size = sizeof(struct dm_target_versions);
+ payload_size += DM_MAX_TYPE_NAME;
+ // device mapper wants every target spec to be aligned at 8-byte boundary
+ payload_size = DM_ALIGN(payload_size);
+ payload_size *= kMaxPossibleDmTargets;
+
+ uint32_t data_size = sizeof(struct dm_ioctl) + payload_size;
+ auto buffer = std::unique_ptr<void, void (*)(void*)>(calloc(1, data_size), free);
+ if (buffer == nullptr) {
+ LOG(ERROR) << "failed to allocate memory";
+ return false;
+ }
+
+ // Sets appropriate data size and data_start to make sure we tell kernel
+ // about the total size of the buffer we are passing and where to start
+ // writing the list of targets.
+ struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
+ InitIo(io);
+ io->data_size = data_size;
+ io->data_start = sizeof(*io);
+
+ if (ioctl(fd_, DM_LIST_VERSIONS, io)) {
+ PLOG(ERROR) << "Failed to get DM_LIST_VERSIONS from kernel";
+ return false;
+ }
+
+ // If the provided buffer wasn't enough to list all targets, note that
+ // any data beyond sizeof(*io) must not be read in this case
+ if (io->flags & DM_BUFFER_FULL_FLAG) {
+ LOG(INFO) << data_size << " is not enough memory to list all dm targets";
+ return false;
+ }
+
+ // if there are no targets registered, return success with empty vector
+ if (io->data_size == sizeof(*io)) {
+ return true;
+ }
+
+ // Parse each target and list the name and version
+ // TODO(b/110035986): Templatize this
+ uint32_t next = sizeof(*io);
+ data_size = io->data_size - next;
+ struct dm_target_versions* vers =
+ reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) + next);
+ while (next && data_size) {
+ targets->emplace_back((vers));
+ if (vers->next == 0) {
+ break;
+ }
+ next += vers->next;
+ data_size -= vers->next;
+ vers = reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) + next);
+ }
+
+ return true;
+}
+
+bool DeviceMapper::GetAvailableDevices(std::vector<DmBlockDevice>* devices) {
+ devices->clear();
+
+ // calculate the space needed to read a maximum of 256 targets, each with
+ // name with maximum length of 16 bytes
+ uint32_t payload_size = sizeof(struct dm_name_list);
+ // 128-bytes for the name
+ payload_size += DM_NAME_LEN;
+ // dm wants every device spec to be aligned at 8-byte boundary
+ payload_size = DM_ALIGN(payload_size);
+ payload_size *= kMaxPossibleDmDevices;
+ uint32_t data_size = sizeof(struct dm_ioctl) + payload_size;
+ auto buffer = std::unique_ptr<void, void (*)(void*)>(calloc(1, data_size), free);
+ if (buffer == nullptr) {
+ LOG(ERROR) << "failed to allocate memory";
+ return false;
+ }
+
+ // Sets appropriate data size and data_start to make sure we tell kernel
+ // about the total size of the buffer we are passing and where to start
+ // writing the list of targets.
+ struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
+ InitIo(io);
+ io->data_size = data_size;
+ io->data_start = sizeof(*io);
+
+ if (ioctl(fd_, DM_LIST_DEVICES, io)) {
+ PLOG(ERROR) << "Failed to get DM_LIST_DEVICES from kernel";
+ return false;
+ }
+
+ // If the provided buffer wasn't enough to list all devices any data
+ // beyond sizeof(*io) must not be read.
+ if (io->flags & DM_BUFFER_FULL_FLAG) {
+ LOG(INFO) << data_size << " is not enough memory to list all dm devices";
+ return false;
+ }
+
+ // if there are no devices created yet, return success with empty vector
+ if (io->data_size == sizeof(*io)) {
+ return true;
+ }
+
+ // Parse each device and add a new DmBlockDevice to the vector
+ // created from the kernel data.
+ uint32_t next = sizeof(*io);
+ data_size = io->data_size - next;
+ struct dm_name_list* dm_dev =
+ reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);
+
+ while (next && data_size) {
+ devices->emplace_back((dm_dev));
+ if (dm_dev->next == 0) {
+ break;
+ }
+ next += dm_dev->next;
+ data_size -= dm_dev->next;
+ dm_dev = reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);
+ }
+
+ return true;
+}
+
+// Accepts a device mapper device name (like system_a, vendor_b etc) and
+// returns the path to it's device node (or symlink to the device node)
+std::string DeviceMapper::GetDmDevicePathByName(const std::string& /* name */) {
+ return "";
+}
+
+// private methods of DeviceMapper
+void DeviceMapper::InitIo(struct dm_ioctl* io, const std::string& name) const {
+ CHECK(io != nullptr) << "nullptr passed to dm_ioctl initialization";
+ memset(io, 0, sizeof(*io));
+
+ io->version[0] = DM_VERSION0;
+ io->version[1] = DM_VERSION1;
+ io->version[2] = DM_VERSION2;
+ io->data_size = sizeof(*io);
+ io->data_start = 0;
+ if (!name.empty()) {
+ strlcpy(io->name, name.c_str(), sizeof(io->name));
+ }
+}
+
+} // namespace dm
+} // namespace android
diff --git a/fs_mgr/libdm/dm_table.cpp b/fs_mgr/libdm/dm_table.cpp
new file mode 100644
index 0000000..14b3932
--- /dev/null
+++ b/fs_mgr/libdm/dm_table.cpp
@@ -0,0 +1,55 @@
+/*
+ * 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 <android-base/logging.h>
+#include <android-base/macros.h>
+
+#include <string>
+#include <vector>
+
+#include "dm_table.h"
+
+namespace android {
+namespace dm {
+
+bool DmTable::AddTarget(std::unique_ptr<DmTarget>&& /* target */) {
+ return true;
+}
+
+bool DmTable::RemoveTarget(std::unique_ptr<DmTarget>&& /* target */) {
+ return true;
+}
+
+bool DmTable::valid() const {
+ return true;
+}
+
+uint64_t DmTable::size() const {
+ return valid() ? size_ : 0;
+}
+
+// Returns a string represnetation of the table that is ready to be passed
+// down to the kernel for loading
+//
+// Implementation must verify there are no gaps in the table, table starts
+// with sector == 0, and iterate over each target to get its table
+// serialized.
+std::string DmTable::Serialize() const {
+ return "";
+}
+
+} // namespace dm
+} // namespace android
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
new file mode 100644
index 0000000..8bcd526
--- /dev/null
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 <android-base/logging.h>
+#include <android-base/macros.h>
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "dm_target.h"
+
+namespace android {
+namespace dm {} // namespace dm
+} // namespace android
diff --git a/fs_mgr/libdm/include/dm.h b/fs_mgr/libdm/include/dm.h
new file mode 100644
index 0000000..52a9a11
--- /dev/null
+++ b/fs_mgr/libdm/include/dm.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2018 Google, Inc
+ *
+ * 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 _LIBDM_DM_H_
+#define _LIBDM_DM_H_
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/dm-ioctl.h>
+#include <linux/kdev_t.h>
+#include <sys/sysmacros.h>
+#include <unistd.h>
+
+#include <memory>
+
+#include <android-base/logging.h>
+
+#include <dm_table.h>
+
+// The minimum expected device mapper major.minor version
+#define DM_VERSION0 (4)
+#define DM_VERSION1 (0)
+#define DM_VERSION2 (0)
+
+#define DM_ALIGN_MASK (7)
+#define DM_ALIGN(x) ((x + DM_ALIGN_MASK) & ~DM_ALIGN_MASK)
+
+namespace android {
+namespace dm {
+
+enum class DmDeviceState { INVALID, SUSPENDED, ACTIVE };
+
+class DeviceMapper final {
+ public:
+ class DmBlockDevice final {
+ public:
+ // only allow creating this with dm_name_list
+ DmBlockDevice() = delete;
+
+ explicit DmBlockDevice(struct dm_name_list* d) : name_(d->name), dev_(d->dev){};
+
+ // Returs device mapper name associated with the block device
+ const std::string& name() const { return name_; }
+
+ // Return major number for the block device
+ uint32_t Major() const { return major(dev_); }
+
+ // Return minor number for the block device
+ uint32_t Minor() const { return minor(dev_); }
+ ~DmBlockDevice() = default;
+
+ private:
+ std::string name_;
+ uint64_t dev_;
+ };
+
+ // Creates a device mapper device with given name.
+ // Return 'true' on success and 'false' on failure to
+ // create OR if a device mapper device with the same name already
+ // exists.
+ // TODO(b/110035986): Make this method private and to be only
+ // called through LoadTableAndActivate() below.
+ bool CreateDevice(const std::string& name);
+
+ // Removes a device mapper device with the given name.
+ // Returns 'true' on success, false otherwise.
+ bool DeleteDevice(const std::string& name);
+
+ // Reads the device mapper table from the device with given anme and
+ // returns it in a DmTable object.
+ const std::unique_ptr<DmTable> table(const std::string& name) const;
+
+ // Returns the current state of the underlying device mapper device
+ // with given name.
+ // One of INVALID, SUSPENDED or ACTIVE.
+ DmDeviceState state(const std::string& name) const;
+
+ // Loads the device mapper table from parameter into the underlying
+ // device mapper device with given name and activate / resumes the device in the process.
+ // If a device mapper device with the 'name', doesn't exist, it will be created.
+ // Returns 'true' on success, false otherwise.
+ bool LoadTableAndActivate(const std::string& name, const DmTable& table);
+
+ // Returns true if a list of available device mapper targets registered in the kernel was
+ // successfully read and stored in 'targets'. Returns 'false' otherwise.
+ bool GetAvailableTargets(std::vector<DmTarget>* targets);
+
+ // Return 'true' if it can successfully read the list of device mapper block devices
+ // currently created. 'devices' will be empty if the kernel interactions
+ // were successful and there are no block devices at the moment. Returns
+ // 'false' in case of any failure along the way.
+ bool GetAvailableDevices(std::vector<DmBlockDevice>* devices);
+
+ // Returns the path to the device mapper device node in '/dev' corresponding to
+ // 'name'.
+ std::string GetDmDevicePathByName(const std::string& name);
+
+ // The only way to create a DeviceMapper object.
+ static DeviceMapper& Instance();
+
+ ~DeviceMapper() {
+ if (fd_ != -1) {
+ ::close(fd_);
+ }
+ }
+
+ private:
+ // Maximum possible device mapper targets registered in the kernel.
+ // This is only used to read the list of targets from kernel so we allocate
+ // a finite amount of memory. This limit is in no way enforced by the kernel.
+ static constexpr uint32_t kMaxPossibleDmTargets = 256;
+
+ // Maximum possible device mapper created block devices. Note that this is restricted by
+ // the minor numbers (that used to be 8 bits) that can be range from 0 to 2^20-1 in newer
+ // kernels. In Android systems however, we never expect these to grow beyond the artificial
+ // limit we are imposing here of 256.
+ static constexpr uint32_t kMaxPossibleDmDevices = 256;
+
+ void InitIo(struct dm_ioctl* io, const std::string& name = std::string()) const;
+
+ DeviceMapper() : fd_(-1) {
+ fd_ = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
+ if (fd_ < 0) {
+ PLOG(ERROR) << "Failed to open device-mapper";
+ }
+ }
+
+ int fd_;
+ // Non-copyable & Non-movable
+ DeviceMapper(const DeviceMapper&) = delete;
+ DeviceMapper& operator=(const DeviceMapper&) = delete;
+ DeviceMapper& operator=(DeviceMapper&&) = delete;
+ DeviceMapper(DeviceMapper&&) = delete;
+};
+
+} // namespace dm
+} // namespace android
+
+#endif /* _LIBDM_DM_H_ */
diff --git a/fs_mgr/libdm/include/dm_table.h b/fs_mgr/libdm/include/dm_table.h
new file mode 100644
index 0000000..0b1685d
--- /dev/null
+++ b/fs_mgr/libdm/include/dm_table.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2018 Google, Inc
+ *
+ * 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 _LIBDM_DMTABLE_H_
+#define _LIBDM_DMTABLE_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "dm_target.h"
+
+namespace android {
+namespace dm {
+
+class DmTable {
+ public:
+ DmTable() : size_(0){};
+
+ // Adds a target to the device mapper table for a range specified in the target object.
+ // The function will return 'true' if the target was successfully added and doesn't overlap with
+ // any of the existing targets in the table. Gaps are allowed. The final check, including
+ // overlaps and gaps are done before loading the table. Returns 'false' on failure.
+ bool AddTarget(std::unique_ptr<DmTarget>&& target);
+
+ // Removes a target from the table for the range specified in the target object. Returns 'false'
+ // if the target name doesn't match with the one in the table. Returns 'true' if target is
+ // successfully removed.
+ bool RemoveTarget(std::unique_ptr<DmTarget>&& target);
+
+ // Checks the table to make sure it is valid. i.e. Checks for range overlaps, range gaps
+ // and returns 'true' if the table is ready to be loaded into kernel. Returns 'false' if the
+ // table is malformed.
+ bool valid() const;
+
+ // Returns the total size represented by the table in terms of number of 512-byte sectors.
+ // NOTE: This function will overlook if there are any gaps in the targets added in the table.
+ uint64_t size() const;
+
+ // Returns the string represntation of the table that is ready to be passed into the kernel
+ // as part of the DM_TABLE_LOAD ioctl.
+ std::string Serialize() const;
+
+ ~DmTable() = default;
+
+ private:
+ // list of targets defined in this table sorted by
+ // their start and end sectors.
+ // Note: Overlapping targets MUST never be added in this list.
+ std::vector<std::unique_ptr<DmTarget>> targets_;
+
+ // Total size in terms of # of sectors, as calculated by looking at the last and the first
+ // target in 'target_'.
+ uint64_t size_;
+};
+
+} // namespace dm
+} // namespace android
+
+#endif /* _LIBDM_DMTABLE_H_ */
diff --git a/fs_mgr/libdm/include/dm_target.h b/fs_mgr/libdm/include/dm_target.h
new file mode 100644
index 0000000..31b0cb6
--- /dev/null
+++ b/fs_mgr/libdm/include/dm_target.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2018 Google, Inc
+ *
+ * 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 _LIBDM_DMTARGET_H_
+#define _LIBDM_DMTARGET_H_
+
+#include <linux/dm-ioctl.h>
+#include <stdint.h>
+
+#include <android-base/logging.h>
+
+#include <string>
+
+namespace android {
+namespace dm {
+
+class DmTarget {
+ public:
+ DmTarget(const std::string& name, uint64_t start = 0, uint64_t length = 0)
+ : name_(name), v0_(0), v1_(0), v2_(0), start_(start), length_(length){};
+
+ // Creates a DmTarget object from dm_target_version as read from kernel
+ // with DM_LIST_VERSION ioctl.
+ DmTarget(const struct dm_target_versions* vers) : start_(0), length_(0) {
+ CHECK(vers != nullptr) << "Can't create DmTarget with dm_target_versions set to nullptr";
+ v0_ = vers->version[0];
+ v1_ = vers->version[1];
+ v2_ = vers->version[2];
+ name_ = vers->name;
+ }
+
+ virtual ~DmTarget() = default;
+
+ // Returns name of the target.
+ const std::string& name() const { return name_; }
+
+ // Returns size in number of sectors when this target is part of
+ // a DmTable, return 0 otherwise.
+ uint64_t size() const { return length_; }
+
+ // Return string representation of the device mapper target version.
+ std::string version() const {
+ return std::to_string(v0_) + "." + std::to_string(v1_) + "." + std::to_string(v2_);
+ }
+
+ // Function that converts this object to a string of arguments that can
+ // be passed to the kernel for adding this target in a table. Each target (e.g. verity, linear)
+ // must implement this, for it to be used on a device.
+ virtual std::string Serialize() const { return ""; }
+
+ private:
+ // Name of the target.
+ std::string name_;
+ // Target version.
+ uint32_t v0_, v1_, v2_;
+ // logical sector number start and total length (in terms of 512-byte sectors) represented
+ // by this target within a DmTable.
+ uint64_t start_, length_;
+};
+
+} // namespace dm
+} // namespace android
+
+#endif /* _LIBDM_DMTARGET_H_ */
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
new file mode 100644
index 0000000..5497223
--- /dev/null
+++ b/fs_mgr/tests/Android.bp
@@ -0,0 +1,36 @@
+// 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.
+
+cc_test {
+ name: "fs_mgr_unit_test",
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+ static_libs: [
+ "libfs_mgr",
+ "libfstab",
+ ],
+
+ srcs: [
+ "fs_mgr_test.cpp",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+}
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
new file mode 100644
index 0000000..2e76752
--- /dev/null
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -0,0 +1,131 @@
+/*
+ * 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 <algorithm>
+#include <iterator>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+
+#include "../fs_mgr_priv_boot_config.h"
+
+namespace {
+
+const std::string cmdline =
+ "rcupdate.rcu_expedited=1 rootwait ro "
+ "init=/init androidboot.bootdevice=1d84000.ufshc "
+ "androidboot.baseband=sdy androidboot.keymaster=1 skip_initramfs "
+ "androidboot.serialno=BLAHBLAHBLAH androidboot.slot_suffix=_a "
+ "androidboot.hardware.platform=sdw813 androidboot.hardware=foo "
+ "androidboot.revision=EVT1.0 androidboot.bootloader=burp-0.1-7521 "
+ "androidboot.hardware.sku=mary androidboot.hardware.radio.subtype=0 "
+ "androidboot.dtbo_idx=2 androidboot.mode=normal "
+ "androidboot.hardware.ddr=1GB,combuchi,LPDDR4X "
+ "androidboot.ddr_info=combuchiandroidboot.ddr_size=2GB "
+ "androidboot.hardware.ufs=2GB,combushi "
+ "androidboot.boottime=0BLE:58,1BLL:22,1BLE:571,2BLL:105,ODT:0,AVB:123 "
+ "androidboot.ramdump=disabled "
+ "dm=\"1 vroot none ro 1,0 10416 verity 1 624684 fec_start 624684\" "
+ "root=/dev/dm-0 "
+ "androidboot.vbmeta.device=PARTUUID=aa08f1a4-c7c9-402e-9a66-9707cafa9ceb "
+ "androidboot.vbmeta.avb_version=\"1.1\" "
+ "androidboot.vbmeta.device_state=unlocked "
+ "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=5248 "
+ "androidboot.vbmeta.digest="
+ "ac13147e959861c20f2a6da97d25fe79e60e902c022a371c5c039d31e7c68860 "
+ "androidboot.vbmeta.invalidate_on_error=yes "
+ "androidboot.veritymode=enforcing androidboot.verifiedbootstate=orange "
+ "androidboot.space=\"sha256 5248 androidboot.nospace=nope\" "
+ "printk.devkmsg=on msm_rtb.filter=0x237 ehci-hcd.park=3 "
+ "\"string =\"\"string '\" "
+ "service_locator.enable=1 firmware_class.path=/vendor/firmware "
+ "cgroup.memory=nokmem lpm_levels.sleep_disabled=1 "
+ "buildvariant=userdebug console=null "
+ "terminator=\"truncated";
+
+const std::vector<std::pair<std::string, std::string>> result_space = {
+ {"rcupdate.rcu_expedited", "1"},
+ {"rootwait", ""},
+ {"ro", ""},
+ {"init", "/init"},
+ {"androidboot.bootdevice", "1d84000.ufshc"},
+ {"androidboot.baseband", "sdy"},
+ {"androidboot.keymaster", "1"},
+ {"skip_initramfs", ""},
+ {"androidboot.serialno", "BLAHBLAHBLAH"},
+ {"androidboot.slot_suffix", "_a"},
+ {"androidboot.hardware.platform", "sdw813"},
+ {"androidboot.hardware", "foo"},
+ {"androidboot.revision", "EVT1.0"},
+ {"androidboot.bootloader", "burp-0.1-7521"},
+ {"androidboot.hardware.sku", "mary"},
+ {"androidboot.hardware.radio.subtype", "0"},
+ {"androidboot.dtbo_idx", "2"},
+ {"androidboot.mode", "normal"},
+ {"androidboot.hardware.ddr", "1GB,combuchi,LPDDR4X"},
+ {"androidboot.ddr_info", "combuchiandroidboot.ddr_size=2GB"},
+ {"androidboot.hardware.ufs", "2GB,combushi"},
+ {"androidboot.boottime", "0BLE:58,1BLL:22,1BLE:571,2BLL:105,ODT:0,AVB:123"},
+ {"androidboot.ramdump", "disabled"},
+ {"dm", "1 vroot none ro 1,0 10416 verity 1 624684 fec_start 624684"},
+ {"root", "/dev/dm-0"},
+ {"androidboot.vbmeta.device", "PARTUUID=aa08f1a4-c7c9-402e-9a66-9707cafa9ceb"},
+ {"androidboot.vbmeta.avb_version", "1.1"},
+ {"androidboot.vbmeta.device_state", "unlocked"},
+ {"androidboot.vbmeta.hash_alg", "sha256"},
+ {"androidboot.vbmeta.size", "5248"},
+ {"androidboot.vbmeta.digest",
+ "ac13147e959861c20f2a6da97d25fe79e60e902c022a371c5c039d31e7c68860"},
+ {"androidboot.vbmeta.invalidate_on_error", "yes"},
+ {"androidboot.veritymode", "enforcing"},
+ {"androidboot.verifiedbootstate", "orange"},
+ {"androidboot.space", "sha256 5248 androidboot.nospace=nope"},
+ {"printk.devkmsg", "on"},
+ {"msm_rtb.filter", "0x237"},
+ {"ehci-hcd.park", "3"},
+ {"string ", "string '"},
+ {"service_locator.enable", "1"},
+ {"firmware_class.path", "/vendor/firmware"},
+ {"cgroup.memory", "nokmem"},
+ {"lpm_levels.sleep_disabled", "1"},
+ {"buildvariant", "userdebug"},
+ {"console", "null"},
+ {"terminator", "truncated"},
+};
+
+} // namespace
+
+TEST(fs_mgr, fs_mgr_parse_boot_config) {
+ EXPECT_EQ(result_space, fs_mgr_parse_boot_config(cmdline));
+}
+
+TEST(fs_mgr, fs_mgr_get_boot_config_from_kernel_cmdline) {
+ std::string content;
+ for (const auto& entry : result_space) {
+ static constexpr char androidboot[] = "androidboot.";
+ if (!android::base::StartsWith(entry.first, androidboot)) continue;
+ auto key = entry.first.substr(strlen(androidboot));
+ EXPECT_TRUE(fs_mgr_get_boot_config_from_kernel(cmdline, key, &content)) << " for " << key;
+ EXPECT_EQ(entry.second, content);
+ }
+ EXPECT_FALSE(fs_mgr_get_boot_config_from_kernel(cmdline, "vbmeta.avb_versio", &content));
+ EXPECT_TRUE(content.empty()) << content;
+ EXPECT_FALSE(fs_mgr_get_boot_config_from_kernel(cmdline, "nospace", &content));
+ EXPECT_TRUE(content.empty()) << content;
+}
diff --git a/fs_mgr/tools/Android.bp b/fs_mgr/tools/Android.bp
new file mode 100644
index 0000000..4d4aae4
--- /dev/null
+++ b/fs_mgr/tools/Android.bp
@@ -0,0 +1,31 @@
+//
+// 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.
+//
+
+cc_binary {
+ name: "dmctl",
+ srcs: ["dmctl.cpp"],
+
+ static_libs: [
+ "libfs_mgr",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+
+ cflags: ["-Werror"],
+}
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
new file mode 100644
index 0000000..b40b83a
--- /dev/null
+++ b/fs_mgr/tools/dmctl.cpp
@@ -0,0 +1,174 @@
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <linux/dm-ioctl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/unique_fd.h>
+#include <dm.h>
+
+#include <functional>
+#include <iomanip>
+#include <ios>
+#include <iostream>
+#include <map>
+#include <string>
+#include <vector>
+
+using DeviceMapper = ::android::dm::DeviceMapper;
+using DmTarget = ::android::dm::DmTarget;
+using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
+
+static int Usage(void) {
+ std::cerr << "usage: dmctl <command> [command options]" << std::endl;
+ std::cerr << "commands:" << std::endl;
+ std::cerr << " create <dm-name> [<dm-target> [-lo <filename>] <dm-target-args>]" << std::endl;
+ std::cerr << " delete <dm-name>" << std::endl;
+ std::cerr << " list <devices | targets>" << std::endl;
+ std::cerr << " help" << std::endl;
+ return -EINVAL;
+}
+
+static int DmCreateCmdHandler(int argc, char** argv) {
+ if (argc < 1) {
+ std::cerr << "DmCreateCmdHandler: atleast 'name' MUST be provided for target device";
+ return -EINVAL;
+ }
+
+ std::string name = argv[0];
+ DeviceMapper& dm = DeviceMapper::Instance();
+ if (!dm.CreateDevice(name)) {
+ std::cerr << "DmCreateCmdHandler: Failed to create " << name << " device";
+ return -EIO;
+ }
+
+ // if we also have target specified
+ if (argc > 1) {
+ // fall through for now. This will eventually create a DmTarget() based on the target name
+ // passing it the table that is specified at the command line
+ }
+
+ return 0;
+}
+
+static int DmDeleteCmdHandler(int argc, char** argv) {
+ if (argc < 1) {
+ std::cerr << "DmCreateCmdHandler: atleast 'name' MUST be provided for target device";
+ return -EINVAL;
+ }
+
+ std::string name = argv[0];
+ DeviceMapper& dm = DeviceMapper::Instance();
+ if (!dm.DeleteDevice(name)) {
+ std::cerr << "DmCreateCmdHandler: Failed to create " << name << " device";
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int DmListTargets(DeviceMapper& dm) {
+ std::vector<DmTarget> targets;
+ if (!dm.GetAvailableTargets(&targets)) {
+ std::cerr << "Failed to read available device mapper targets" << std::endl;
+ return -errno;
+ }
+
+ std::cout << "Available Device Mapper Targets:" << std::endl;
+ if (targets.empty()) {
+ std::cout << " <empty>" << std::endl;
+ return 0;
+ }
+
+ for (const auto& target : targets) {
+ std::cout << std::left << std::setw(20) << target.name() << " : " << target.version()
+ << std::endl;
+ }
+
+ return 0;
+}
+
+static int DmListDevices(DeviceMapper& dm) {
+ std::vector<DmBlockDevice> devices;
+ if (!dm.GetAvailableDevices(&devices)) {
+ std::cerr << "Failed to read available device mapper devices" << std::endl;
+ return -errno;
+ }
+ std::cout << "Available Device Mapper Devices:" << std::endl;
+ if (devices.empty()) {
+ std::cout << " <empty>" << std::endl;
+ return 0;
+ }
+
+ for (const auto& dev : devices) {
+ std::cout << std::left << std::setw(20) << dev.name() << " : " << dev.Major() << ":"
+ << dev.Minor() << std::endl;
+ }
+
+ return 0;
+}
+
+static const std::map<std::string, std::function<int(DeviceMapper&)>> listmap = {
+ {"targets", DmListTargets},
+ {"devices", DmListDevices},
+};
+
+static int DmListCmdHandler(int argc, char** argv) {
+ if (argc < 1) {
+ std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
+ return -EINVAL;
+ }
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ for (const auto& l : listmap) {
+ if (l.first == argv[0]) return l.second(dm);
+ }
+
+ std::cerr << "Invalid argument to \'dmctl list\': " << argv[0] << std::endl;
+ return -EINVAL;
+}
+
+static int HelpCmdHandler(int /* argc */, char** /* argv */) {
+ Usage();
+ return 0;
+}
+
+static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
+ {"create", DmCreateCmdHandler},
+ {"delete", DmDeleteCmdHandler},
+ {"list", DmListCmdHandler},
+ {"help", HelpCmdHandler},
+};
+
+int main(int argc, char** argv) {
+ android::base::InitLogging(argv, &android::base::StderrLogger);
+ if (argc < 2) {
+ return Usage();
+ }
+
+ for (const auto& cmd : cmdmap) {
+ if (cmd.first == argv[1]) {
+ return cmd.second(argc - 2, argv + 2);
+ }
+ }
+
+ return Usage();
+}
diff --git a/init/Android.bp b/init/Android.bp
index 25877c0..7d863c8 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -67,24 +67,26 @@
"libsquashfs_utils",
"liblogwrap",
"libext4_utils",
- "libcutils",
- "libbase",
- "libc",
"libseccomp_policy",
- "libselinux",
- "liblog",
"libcrypto_utils",
- "libcrypto",
- "libc++_static",
- "libdl",
"libsparse",
- "libz",
"libprocessgroup",
"libavb",
"libkeyutils",
"libprotobuf-cpp-lite",
"libpropertyinfoserializer",
"libpropertyinfoparser",
+ "libselinux",
+ ],
+ shared_libs: [
+ "libcutils",
+ "libbase",
+ "libc",
+ "liblog",
+ "libcrypto",
+ "libc++",
+ "libdl",
+ "libz",
],
}
@@ -166,7 +168,6 @@
cc_test {
name: "init_tests",
defaults: ["init_defaults"],
- static_executable: true,
srcs: [
"devices_test.cpp",
"init_test.cpp",
@@ -187,7 +188,6 @@
cc_benchmark {
name: "init_benchmarks",
- static_executable: true,
defaults: ["init_defaults"],
srcs: [
"subcontext_benchmark.cpp",
diff --git a/init/Android.mk b/init/Android.mk
index c4a6a50..da27a73 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -45,7 +45,6 @@
LOCAL_MODULE:= init
-LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
@@ -59,18 +58,10 @@
libsquashfs_utils \
liblogwrap \
libext4_utils \
- libcutils \
- libbase \
- libc \
libseccomp_policy \
- libselinux \
- liblog \
libcrypto_utils \
- libcrypto \
- libc++_static \
- libdl \
libsparse \
- libz \
+ libselinux \
libprocessgroup \
libavb \
libkeyutils \
@@ -78,6 +69,25 @@
libpropertyinfoserializer \
libpropertyinfoparser \
+shared_libs := \
+ libcutils \
+ libbase \
+ liblog \
+ libcrypto \
+ libdl \
+ libz \
+
+ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
+# init is static executable for non-system-as-root devices, because the dynamic linker
+# and shared libs are not available before /system is mounted, but init has to run
+# before the partition is mounted.
+LOCAL_STATIC_LIBRARIES += $(shared_libs) libc++_static
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+else
+LOCAL_SHARED_LIBRARIES := $(shared_libs) libc++
+endif
+shared_libs :=
+
LOCAL_REQUIRED_MODULES := \
e2fsdroid \
mke2fs \
diff --git a/init/init.cpp b/init/init.cpp
index 82648d9..b494bcc 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -604,47 +604,61 @@
if (is_first_stage) {
boot_clock::time_point start_time = boot_clock::now();
+ std::vector<std::pair<std::string, int>> errors;
+#define CHECKCALL(x) \
+ if (x != 0) errors.emplace_back(#x " failed", errno);
+
// Clear the umask.
umask(0);
- clearenv();
- setenv("PATH", _PATH_DEFPATH, 1);
+ CHECKCALL(clearenv());
+ CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
// Get the basic filesystem setup we need put together in the initramdisk
// on / and then we'll let the rc file figure out the rest.
- mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
- mkdir("/dev/pts", 0755);
- mkdir("/dev/socket", 0755);
- mount("devpts", "/dev/pts", "devpts", 0, NULL);
- #define MAKE_STR(x) __STRING(x)
- mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
+ CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
+ CHECKCALL(mkdir("/dev/pts", 0755));
+ CHECKCALL(mkdir("/dev/socket", 0755));
+ CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
+#define MAKE_STR(x) __STRING(x)
+ CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
+#undef MAKE_STR
// Don't expose the raw commandline to unprivileged processes.
- chmod("/proc/cmdline", 0440);
+ CHECKCALL(chmod("/proc/cmdline", 0440));
gid_t groups[] = { AID_READPROC };
- setgroups(arraysize(groups), groups);
- mount("sysfs", "/sys", "sysfs", 0, NULL);
- mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
+ CHECKCALL(setgroups(arraysize(groups), groups));
+ CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
+ CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
- mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
+ CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));
if constexpr (WORLD_WRITABLE_KMSG) {
- mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11));
+ CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
}
- mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
- mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));
+ CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
+ CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));
// Mount staging areas for devices managed by vold
// See storage config details at http://source.android.com/devices/storage/
- mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
- "mode=0755,uid=0,gid=1000");
+ CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+ "mode=0755,uid=0,gid=1000"));
// /mnt/vendor is used to mount vendor-specific partitions that can not be
// part of the vendor partition, e.g. because they are mounted read-write.
- mkdir("/mnt/vendor", 0755);
+ CHECKCALL(mkdir("/mnt/vendor", 0755));
+
+#undef CHECKCALL
// Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
// talk to the outside world...
InitKernelLogging(argv);
+ if (!errors.empty()) {
+ for (const auto& [error_string, error_errno] : errors) {
+ LOG(ERROR) << error_string << " " << strerror(error_errno);
+ }
+ LOG(FATAL) << "Init encountered errors starting first stage, aborting";
+ }
+
LOG(INFO) << "init first stage started!";
if (!DoFirstStageMount()) {
diff --git a/init/parser.cpp b/init/parser.cpp
index ee6ee06..4f1cac4 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -70,24 +70,23 @@
case T_EOF:
end_section();
return;
- case T_NEWLINE:
+ case T_NEWLINE: {
state.line++;
if (args.empty()) break;
// If we have a line matching a prefix we recognize, call its callback and unset any
// current section parsers. This is meant for /sys/ and /dev/ line entries for
// uevent.
- for (const auto& [prefix, callback] : line_callbacks_) {
- if (android::base::StartsWith(args[0], prefix)) {
- end_section();
+ auto line_callback = std::find_if(
+ line_callbacks_.begin(), line_callbacks_.end(),
+ [&args](const auto& c) { return android::base::StartsWith(args[0], c.first); });
+ if (line_callback != line_callbacks_.end()) {
+ end_section();
- if (auto result = callback(std::move(args)); !result) {
- parse_error_count_++;
- LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
- }
- break;
+ if (auto result = line_callback->second(std::move(args)); !result) {
+ parse_error_count_++;
+ LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
}
- }
- if (section_parsers_.count(args[0])) {
+ } else if (section_parsers_.count(args[0])) {
end_section();
section_parser = section_parsers_[args[0]].get();
section_start_line = state.line;
@@ -111,6 +110,7 @@
}
args.clear();
break;
+ }
case T_TEXT:
args.emplace_back(state.text);
break;
diff --git a/libcutils/OWNERS b/libcutils/OWNERS
new file mode 100644
index 0000000..c18ed51
--- /dev/null
+++ b/libcutils/OWNERS
@@ -0,0 +1,4 @@
+cferris@google.com
+enh@google.com
+jmgao@google.com
+tomcherry@google.com
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 6169324..267b7b3 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -154,6 +154,7 @@
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/crash_dump64" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/debuggerd" },
{ 00750, AID_ROOT, AID_ROOT, 0, "system/bin/install-recovery.sh" },
+ { 00550, AID_LOGD, AID_LOGD, 0, "system/bin/logd" },
{ 00700, AID_ROOT, AID_ROOT, 0, "system/bin/secilc" },
{ 00750, AID_ROOT, AID_ROOT, 0, "system/bin/uncrypt" },
{ 00600, AID_ROOT, AID_ROOT, 0, "system/build.prop" },
@@ -179,12 +180,6 @@
// in user builds.
{ 00700, AID_SYSTEM, AID_SHELL, CAP_MASK_LONG(CAP_BLOCK_SUSPEND),
"system/bin/inputflinger" },
- { 00550, AID_LOGD, AID_LOGD, CAP_MASK_LONG(CAP_SYSLOG) |
- CAP_MASK_LONG(CAP_AUDIT_CONTROL) |
- CAP_MASK_LONG(CAP_SETGID),
- "system/bin/logd" },
- { 00550, AID_SYSTEM, AID_LOG, CAP_MASK_LONG(CAP_SYSLOG),
- "system/bin/bootstat" },
{ 00750, AID_ROOT, AID_SHELL, CAP_MASK_LONG(CAP_SETUID) |
CAP_MASK_LONG(CAP_SETGID),
"system/bin/run-as" },
diff --git a/libsparse/.clang-format b/libsparse/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libsparse/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index c7c089f..8ad339f 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -6,11 +6,11 @@
recovery_available: true,
unique_host_soname: true,
srcs: [
- "backed_block.c",
- "output_file.c",
- "sparse.c",
- "sparse_crc32.c",
- "sparse_err.c",
+ "backed_block.cpp",
+ "output_file.cpp",
+ "sparse.cpp",
+ "sparse_crc32.cpp",
+ "sparse_err.cpp",
"sparse_read.cpp",
],
cflags: ["-Werror"],
@@ -31,8 +31,8 @@
name: "simg2img",
host_supported: true,
srcs: [
- "simg2img.c",
- "sparse_crc32.c",
+ "simg2img.cpp",
+ "sparse_crc32.cpp",
],
static_libs: [
"libsparse",
@@ -46,7 +46,7 @@
cc_binary {
name: "img2simg",
host_supported: true,
- srcs: ["img2simg.c"],
+ srcs: ["img2simg.cpp"],
static_libs: [
"libsparse",
"libz",
@@ -58,7 +58,7 @@
cc_binary_host {
name: "append2simg",
- srcs: ["append2simg.c"],
+ srcs: ["append2simg.cpp"],
static_libs: [
"libsparse",
"libz",
diff --git a/libsparse/OWNERS b/libsparse/OWNERS
new file mode 100644
index 0000000..70b375f
--- /dev/null
+++ b/libsparse/OWNERS
@@ -0,0 +1 @@
+ccross@google.com
diff --git a/libsparse/append2simg.c b/libsparse/append2simg.c
deleted file mode 100644
index eef8764..0000000
--- a/libsparse/append2simg.c
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define _FILE_OFFSET_BITS 64
-#define _LARGEFILE64_SOURCE 1
-#define _GNU_SOURCE
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <sparse/sparse.h>
-#include "sparse_file.h"
-#include "backed_block.h"
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-#if defined(__APPLE__) && defined(__MACH__)
-#define lseek64 lseek
-#endif
-#if defined(__APPLE__) && defined(__MACH__)
-#define lseek64 lseek
-#define off64_t off_t
-#endif
-
-void usage()
-{
- fprintf(stderr, "Usage: append2simg <output> <input>\n");
-}
-
-int main(int argc, char *argv[])
-{
- int output;
- int output_block;
- char *output_path;
- struct sparse_file *sparse_output;
-
- int input;
- char *input_path;
- off64_t input_len;
-
- int tmp_fd;
- char *tmp_path;
-
- int ret;
-
- if (argc == 3) {
- output_path = argv[1];
- input_path = argv[2];
- } else {
- usage();
- exit(-1);
- }
-
- ret = asprintf(&tmp_path, "%s.append2simg", output_path);
- if (ret < 0) {
- fprintf(stderr, "Couldn't allocate filename\n");
- exit(-1);
- }
-
- output = open(output_path, O_RDWR | O_BINARY);
- if (output < 0) {
- fprintf(stderr, "Couldn't open output file (%s)\n", strerror(errno));
- exit(-1);
- }
-
- sparse_output = sparse_file_import_auto(output, false, true);
- if (!sparse_output) {
- fprintf(stderr, "Couldn't import output file\n");
- exit(-1);
- }
-
- input = open(input_path, O_RDONLY | O_BINARY);
- if (input < 0) {
- fprintf(stderr, "Couldn't open input file (%s)\n", strerror(errno));
- exit(-1);
- }
-
- input_len = lseek64(input, 0, SEEK_END);
- if (input_len < 0) {
- fprintf(stderr, "Couldn't get input file length (%s)\n", strerror(errno));
- exit(-1);
- } else if (input_len % sparse_output->block_size) {
- fprintf(stderr, "Input file is not a multiple of the output file's block size");
- exit(-1);
- }
- lseek64(input, 0, SEEK_SET);
-
- output_block = sparse_output->len / sparse_output->block_size;
- if (sparse_file_add_fd(sparse_output, input, 0, input_len, output_block) < 0) {
- fprintf(stderr, "Couldn't add input file\n");
- exit(-1);
- }
- sparse_output->len += input_len;
-
- tmp_fd = open(tmp_path, O_WRONLY | O_CREAT | O_BINARY, 0664);
- if (tmp_fd < 0) {
- fprintf(stderr, "Couldn't open temporary file (%s)\n", strerror(errno));
- exit(-1);
- }
-
- lseek64(output, 0, SEEK_SET);
- if (sparse_file_write(sparse_output, tmp_fd, false, true, false) < 0) {
- fprintf(stderr, "Failed to write sparse file\n");
- exit(-1);
- }
-
- sparse_file_destroy(sparse_output);
- close(tmp_fd);
- close(output);
- close(input);
-
- ret = rename(tmp_path, output_path);
- if (ret < 0) {
- fprintf(stderr, "Failed to rename temporary file (%s)\n", strerror(errno));
- exit(-1);
- }
-
- free(tmp_path);
-
- exit(0);
-}
diff --git a/libsparse/append2simg.cpp b/libsparse/append2simg.cpp
new file mode 100644
index 0000000..99f4339
--- /dev/null
+++ b/libsparse/append2simg.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sparse/sparse.h>
+#include "backed_block.h"
+#include "sparse_file.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#endif
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#define off64_t off_t
+#endif
+
+void usage() {
+ fprintf(stderr, "Usage: append2simg <output> <input>\n");
+}
+
+int main(int argc, char* argv[]) {
+ int output;
+ int output_block;
+ char* output_path;
+ struct sparse_file* sparse_output;
+
+ int input;
+ char* input_path;
+ off64_t input_len;
+
+ int tmp_fd;
+ char* tmp_path;
+
+ int ret;
+
+ if (argc == 3) {
+ output_path = argv[1];
+ input_path = argv[2];
+ } else {
+ usage();
+ exit(-1);
+ }
+
+ ret = asprintf(&tmp_path, "%s.append2simg", output_path);
+ if (ret < 0) {
+ fprintf(stderr, "Couldn't allocate filename\n");
+ exit(-1);
+ }
+
+ output = open(output_path, O_RDWR | O_BINARY);
+ if (output < 0) {
+ fprintf(stderr, "Couldn't open output file (%s)\n", strerror(errno));
+ exit(-1);
+ }
+
+ sparse_output = sparse_file_import_auto(output, false, true);
+ if (!sparse_output) {
+ fprintf(stderr, "Couldn't import output file\n");
+ exit(-1);
+ }
+
+ input = open(input_path, O_RDONLY | O_BINARY);
+ if (input < 0) {
+ fprintf(stderr, "Couldn't open input file (%s)\n", strerror(errno));
+ exit(-1);
+ }
+
+ input_len = lseek64(input, 0, SEEK_END);
+ if (input_len < 0) {
+ fprintf(stderr, "Couldn't get input file length (%s)\n", strerror(errno));
+ exit(-1);
+ } else if (input_len % sparse_output->block_size) {
+ fprintf(stderr, "Input file is not a multiple of the output file's block size");
+ exit(-1);
+ }
+ lseek64(input, 0, SEEK_SET);
+
+ output_block = sparse_output->len / sparse_output->block_size;
+ if (sparse_file_add_fd(sparse_output, input, 0, input_len, output_block) < 0) {
+ fprintf(stderr, "Couldn't add input file\n");
+ exit(-1);
+ }
+ sparse_output->len += input_len;
+
+ tmp_fd = open(tmp_path, O_WRONLY | O_CREAT | O_BINARY, 0664);
+ if (tmp_fd < 0) {
+ fprintf(stderr, "Couldn't open temporary file (%s)\n", strerror(errno));
+ exit(-1);
+ }
+
+ lseek64(output, 0, SEEK_SET);
+ if (sparse_file_write(sparse_output, tmp_fd, false, true, false) < 0) {
+ fprintf(stderr, "Failed to write sparse file\n");
+ exit(-1);
+ }
+
+ sparse_file_destroy(sparse_output);
+ close(tmp_fd);
+ close(output);
+ close(input);
+
+ ret = rename(tmp_path, output_path);
+ if (ret < 0) {
+ fprintf(stderr, "Failed to rename temporary file (%s)\n", strerror(errno));
+ exit(-1);
+ }
+
+ free(tmp_path);
+
+ exit(0);
+}
diff --git a/libsparse/backed_block.c b/libsparse/backed_block.c
deleted file mode 100644
index 794cd6b..0000000
--- a/libsparse/backed_block.c
+++ /dev/null
@@ -1,404 +0,0 @@
-/*
- * Copyright (C) 2010 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 <assert.h>
-#include <errno.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "backed_block.h"
-#include "sparse_defs.h"
-
-struct backed_block {
- unsigned int block;
- unsigned int len;
- enum backed_block_type type;
- union {
- struct {
- void *data;
- } data;
- struct {
- char *filename;
- int64_t offset;
- } file;
- struct {
- int fd;
- int64_t offset;
- } fd;
- struct {
- uint32_t val;
- } fill;
- };
- struct backed_block *next;
-};
-
-struct backed_block_list {
- struct backed_block *data_blocks;
- struct backed_block *last_used;
- unsigned int block_size;
-};
-
-struct backed_block *backed_block_iter_new(struct backed_block_list *bbl)
-{
- return bbl->data_blocks;
-}
-
-struct backed_block *backed_block_iter_next(struct backed_block *bb)
-{
- return bb->next;
-}
-
-unsigned int backed_block_len(struct backed_block *bb)
-{
- return bb->len;
-}
-
-unsigned int backed_block_block(struct backed_block *bb)
-{
- return bb->block;
-}
-
-void *backed_block_data(struct backed_block *bb)
-{
- assert(bb->type == BACKED_BLOCK_DATA);
- return bb->data.data;
-}
-
-const char *backed_block_filename(struct backed_block *bb)
-{
- assert(bb->type == BACKED_BLOCK_FILE);
- return bb->file.filename;
-}
-
-int backed_block_fd(struct backed_block *bb)
-{
- assert(bb->type == BACKED_BLOCK_FD);
- return bb->fd.fd;
-}
-
-int64_t backed_block_file_offset(struct backed_block *bb)
-{
- assert(bb->type == BACKED_BLOCK_FILE || bb->type == BACKED_BLOCK_FD);
- if (bb->type == BACKED_BLOCK_FILE) {
- return bb->file.offset;
- } else { /* bb->type == BACKED_BLOCK_FD */
- return bb->fd.offset;
- }
-}
-
-uint32_t backed_block_fill_val(struct backed_block *bb)
-{
- assert(bb->type == BACKED_BLOCK_FILL);
- return bb->fill.val;
-}
-
-enum backed_block_type backed_block_type(struct backed_block *bb)
-{
- return bb->type;
-}
-
-void backed_block_destroy(struct backed_block *bb)
-{
- if (bb->type == BACKED_BLOCK_FILE) {
- free(bb->file.filename);
- }
-
- free(bb);
-}
-
-struct backed_block_list *backed_block_list_new(unsigned int block_size)
-{
- struct backed_block_list *b = calloc(sizeof(struct backed_block_list), 1);
- b->block_size = block_size;
- return b;
-}
-
-void backed_block_list_destroy(struct backed_block_list *bbl)
-{
- if (bbl->data_blocks) {
- struct backed_block *bb = bbl->data_blocks;
- while (bb) {
- struct backed_block *next = bb->next;
- backed_block_destroy(bb);
- bb = next;
- }
- }
-
- free(bbl);
-}
-
-void backed_block_list_move(struct backed_block_list *from,
- struct backed_block_list *to, struct backed_block *start,
- struct backed_block *end)
-{
- struct backed_block *bb;
-
- if (start == NULL) {
- start = from->data_blocks;
- }
-
- if (!end) {
- for (end = start; end && end->next; end = end->next)
- ;
- }
-
- if (start == NULL || end == NULL) {
- return;
- }
-
- from->last_used = NULL;
- to->last_used = NULL;
- if (from->data_blocks == start) {
- from->data_blocks = end->next;
- } else {
- for (bb = from->data_blocks; bb; bb = bb->next) {
- if (bb->next == start) {
- bb->next = end->next;
- break;
- }
- }
- }
-
- if (!to->data_blocks) {
- to->data_blocks = start;
- end->next = NULL;
- } else {
- for (bb = to->data_blocks; bb; bb = bb->next) {
- if (!bb->next || bb->next->block > start->block) {
- end->next = bb->next;
- bb->next = start;
- break;
- }
- }
- }
-}
-
-/* may free b */
-static int merge_bb(struct backed_block_list *bbl,
- struct backed_block *a, struct backed_block *b)
-{
- unsigned int block_len;
-
- /* Block doesn't exist (possible if one block is the last block) */
- if (!a || !b) {
- return -EINVAL;
- }
-
- assert(a->block < b->block);
-
- /* Blocks are of different types */
- if (a->type != b->type) {
- return -EINVAL;
- }
-
- /* Blocks are not adjacent */
- block_len = a->len / bbl->block_size; /* rounds down */
- if (a->block + block_len != b->block) {
- return -EINVAL;
- }
-
- switch (a->type) {
- case BACKED_BLOCK_DATA:
- /* Don't support merging data for now */
- return -EINVAL;
- case BACKED_BLOCK_FILL:
- if (a->fill.val != b->fill.val) {
- return -EINVAL;
- }
- break;
- case BACKED_BLOCK_FILE:
- /* Already make sure b->type is BACKED_BLOCK_FILE */
- if (strcmp(a->file.filename, b->file.filename) ||
- a->file.offset + a->len != b->file.offset) {
- return -EINVAL;
- }
- break;
- case BACKED_BLOCK_FD:
- if (a->fd.fd != b->fd.fd ||
- a->fd.offset + a->len != b->fd.offset) {
- return -EINVAL;
- }
- break;
- }
-
- /* Blocks are compatible and adjacent, with a before b. Merge b into a,
- * and free b */
- a->len += b->len;
- a->next = b->next;
-
- backed_block_destroy(b);
-
- return 0;
-}
-
-static int queue_bb(struct backed_block_list *bbl, struct backed_block *new_bb)
-{
- struct backed_block *bb;
-
- if (bbl->data_blocks == NULL) {
- bbl->data_blocks = new_bb;
- return 0;
- }
-
- if (bbl->data_blocks->block > new_bb->block) {
- new_bb->next = bbl->data_blocks;
- bbl->data_blocks = new_bb;
- return 0;
- }
-
- /* Optimization: blocks are mostly queued in sequence, so save the
- pointer to the last bb that was added, and start searching from
- there if the next block number is higher */
- if (bbl->last_used && new_bb->block > bbl->last_used->block)
- bb = bbl->last_used;
- else
- bb = bbl->data_blocks;
- bbl->last_used = new_bb;
-
- for (; bb->next && bb->next->block < new_bb->block; bb = bb->next)
- ;
-
- if (bb->next == NULL) {
- bb->next = new_bb;
- } else {
- new_bb->next = bb->next;
- bb->next = new_bb;
- }
-
- merge_bb(bbl, new_bb, new_bb->next);
- if (!merge_bb(bbl, bb, new_bb)) {
- /* new_bb destroyed, point to retained as last_used */
- bbl->last_used = bb;
- }
-
- return 0;
-}
-
-/* Queues a fill block of memory to be written to the specified data blocks */
-int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val,
- unsigned int len, unsigned int block)
-{
- struct backed_block *bb = calloc(1, sizeof(struct backed_block));
- if (bb == NULL) {
- return -ENOMEM;
- }
-
- bb->block = block;
- bb->len = len;
- bb->type = BACKED_BLOCK_FILL;
- bb->fill.val = fill_val;
- bb->next = NULL;
-
- return queue_bb(bbl, bb);
-}
-
-/* Queues a block of memory to be written to the specified data blocks */
-int backed_block_add_data(struct backed_block_list *bbl, void *data,
- unsigned int len, unsigned int block)
-{
- struct backed_block *bb = calloc(1, sizeof(struct backed_block));
- if (bb == NULL) {
- return -ENOMEM;
- }
-
- bb->block = block;
- bb->len = len;
- bb->type = BACKED_BLOCK_DATA;
- bb->data.data = data;
- bb->next = NULL;
-
- return queue_bb(bbl, bb);
-}
-
-/* Queues a chunk of a file on disk to be written to the specified data blocks */
-int backed_block_add_file(struct backed_block_list *bbl, const char *filename,
- int64_t offset, unsigned int len, unsigned int block)
-{
- struct backed_block *bb = calloc(1, sizeof(struct backed_block));
- if (bb == NULL) {
- return -ENOMEM;
- }
-
- bb->block = block;
- bb->len = len;
- bb->type = BACKED_BLOCK_FILE;
- bb->file.filename = strdup(filename);
- bb->file.offset = offset;
- bb->next = NULL;
-
- return queue_bb(bbl, bb);
-}
-
-/* Queues a chunk of a fd to be written to the specified data blocks */
-int backed_block_add_fd(struct backed_block_list *bbl, int fd, int64_t offset,
- unsigned int len, unsigned int block)
-{
- struct backed_block *bb = calloc(1, sizeof(struct backed_block));
- if (bb == NULL) {
- return -ENOMEM;
- }
-
- bb->block = block;
- bb->len = len;
- bb->type = BACKED_BLOCK_FD;
- bb->fd.fd = fd;
- bb->fd.offset = offset;
- bb->next = NULL;
-
- return queue_bb(bbl, bb);
-}
-
-int backed_block_split(struct backed_block_list *bbl, struct backed_block *bb,
- unsigned int max_len)
-{
- struct backed_block *new_bb;
-
- max_len = ALIGN_DOWN(max_len, bbl->block_size);
-
- if (bb->len <= max_len) {
- return 0;
- }
-
- new_bb = malloc(sizeof(struct backed_block));
- if (new_bb == NULL) {
- return -ENOMEM;
- }
-
- *new_bb = *bb;
-
- new_bb->len = bb->len - max_len;
- new_bb->block = bb->block + max_len / bbl->block_size;
- new_bb->next = bb->next;
- bb->next = new_bb;
- bb->len = max_len;
-
- switch (bb->type) {
- case BACKED_BLOCK_DATA:
- new_bb->data.data = (char *)bb->data.data + max_len;
- break;
- case BACKED_BLOCK_FILE:
- new_bb->file.offset += max_len;
- break;
- case BACKED_BLOCK_FD:
- new_bb->fd.offset += max_len;
- break;
- case BACKED_BLOCK_FILL:
- break;
- }
-
- return 0;
-}
diff --git a/libsparse/backed_block.cpp b/libsparse/backed_block.cpp
new file mode 100644
index 0000000..7f5632e
--- /dev/null
+++ b/libsparse/backed_block.cpp
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2010 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 <assert.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "backed_block.h"
+#include "sparse_defs.h"
+
+struct backed_block {
+ unsigned int block;
+ unsigned int len;
+ enum backed_block_type type;
+ union {
+ struct {
+ void* data;
+ } data;
+ struct {
+ char* filename;
+ int64_t offset;
+ } file;
+ struct {
+ int fd;
+ int64_t offset;
+ } fd;
+ struct {
+ uint32_t val;
+ } fill;
+ };
+ struct backed_block* next;
+};
+
+struct backed_block_list {
+ struct backed_block* data_blocks;
+ struct backed_block* last_used;
+ unsigned int block_size;
+};
+
+struct backed_block* backed_block_iter_new(struct backed_block_list* bbl) {
+ return bbl->data_blocks;
+}
+
+struct backed_block* backed_block_iter_next(struct backed_block* bb) {
+ return bb->next;
+}
+
+unsigned int backed_block_len(struct backed_block* bb) {
+ return bb->len;
+}
+
+unsigned int backed_block_block(struct backed_block* bb) {
+ return bb->block;
+}
+
+void* backed_block_data(struct backed_block* bb) {
+ assert(bb->type == BACKED_BLOCK_DATA);
+ return bb->data.data;
+}
+
+const char* backed_block_filename(struct backed_block* bb) {
+ assert(bb->type == BACKED_BLOCK_FILE);
+ return bb->file.filename;
+}
+
+int backed_block_fd(struct backed_block* bb) {
+ assert(bb->type == BACKED_BLOCK_FD);
+ return bb->fd.fd;
+}
+
+int64_t backed_block_file_offset(struct backed_block* bb) {
+ assert(bb->type == BACKED_BLOCK_FILE || bb->type == BACKED_BLOCK_FD);
+ if (bb->type == BACKED_BLOCK_FILE) {
+ return bb->file.offset;
+ } else { /* bb->type == BACKED_BLOCK_FD */
+ return bb->fd.offset;
+ }
+}
+
+uint32_t backed_block_fill_val(struct backed_block* bb) {
+ assert(bb->type == BACKED_BLOCK_FILL);
+ return bb->fill.val;
+}
+
+enum backed_block_type backed_block_type(struct backed_block* bb) {
+ return bb->type;
+}
+
+void backed_block_destroy(struct backed_block* bb) {
+ if (bb->type == BACKED_BLOCK_FILE) {
+ free(bb->file.filename);
+ }
+
+ free(bb);
+}
+
+struct backed_block_list* backed_block_list_new(unsigned int block_size) {
+ struct backed_block_list* b =
+ reinterpret_cast<backed_block_list*>(calloc(sizeof(struct backed_block_list), 1));
+ b->block_size = block_size;
+ return b;
+}
+
+void backed_block_list_destroy(struct backed_block_list* bbl) {
+ if (bbl->data_blocks) {
+ struct backed_block* bb = bbl->data_blocks;
+ while (bb) {
+ struct backed_block* next = bb->next;
+ backed_block_destroy(bb);
+ bb = next;
+ }
+ }
+
+ free(bbl);
+}
+
+void backed_block_list_move(struct backed_block_list* from, struct backed_block_list* to,
+ struct backed_block* start, struct backed_block* end) {
+ struct backed_block* bb;
+
+ if (start == NULL) {
+ start = from->data_blocks;
+ }
+
+ if (!end) {
+ for (end = start; end && end->next; end = end->next)
+ ;
+ }
+
+ if (start == NULL || end == NULL) {
+ return;
+ }
+
+ from->last_used = NULL;
+ to->last_used = NULL;
+ if (from->data_blocks == start) {
+ from->data_blocks = end->next;
+ } else {
+ for (bb = from->data_blocks; bb; bb = bb->next) {
+ if (bb->next == start) {
+ bb->next = end->next;
+ break;
+ }
+ }
+ }
+
+ if (!to->data_blocks) {
+ to->data_blocks = start;
+ end->next = NULL;
+ } else {
+ for (bb = to->data_blocks; bb; bb = bb->next) {
+ if (!bb->next || bb->next->block > start->block) {
+ end->next = bb->next;
+ bb->next = start;
+ break;
+ }
+ }
+ }
+}
+
+/* may free b */
+static int merge_bb(struct backed_block_list* bbl, struct backed_block* a, struct backed_block* b) {
+ unsigned int block_len;
+
+ /* Block doesn't exist (possible if one block is the last block) */
+ if (!a || !b) {
+ return -EINVAL;
+ }
+
+ assert(a->block < b->block);
+
+ /* Blocks are of different types */
+ if (a->type != b->type) {
+ return -EINVAL;
+ }
+
+ /* Blocks are not adjacent */
+ block_len = a->len / bbl->block_size; /* rounds down */
+ if (a->block + block_len != b->block) {
+ return -EINVAL;
+ }
+
+ switch (a->type) {
+ case BACKED_BLOCK_DATA:
+ /* Don't support merging data for now */
+ return -EINVAL;
+ case BACKED_BLOCK_FILL:
+ if (a->fill.val != b->fill.val) {
+ return -EINVAL;
+ }
+ break;
+ case BACKED_BLOCK_FILE:
+ /* Already make sure b->type is BACKED_BLOCK_FILE */
+ if (strcmp(a->file.filename, b->file.filename) || a->file.offset + a->len != b->file.offset) {
+ return -EINVAL;
+ }
+ break;
+ case BACKED_BLOCK_FD:
+ if (a->fd.fd != b->fd.fd || a->fd.offset + a->len != b->fd.offset) {
+ return -EINVAL;
+ }
+ break;
+ }
+
+ /* Blocks are compatible and adjacent, with a before b. Merge b into a,
+ * and free b */
+ a->len += b->len;
+ a->next = b->next;
+
+ backed_block_destroy(b);
+
+ return 0;
+}
+
+static int queue_bb(struct backed_block_list* bbl, struct backed_block* new_bb) {
+ struct backed_block* bb;
+
+ if (bbl->data_blocks == NULL) {
+ bbl->data_blocks = new_bb;
+ return 0;
+ }
+
+ if (bbl->data_blocks->block > new_bb->block) {
+ new_bb->next = bbl->data_blocks;
+ bbl->data_blocks = new_bb;
+ return 0;
+ }
+
+ /* Optimization: blocks are mostly queued in sequence, so save the
+ pointer to the last bb that was added, and start searching from
+ there if the next block number is higher */
+ if (bbl->last_used && new_bb->block > bbl->last_used->block)
+ bb = bbl->last_used;
+ else
+ bb = bbl->data_blocks;
+ bbl->last_used = new_bb;
+
+ for (; bb->next && bb->next->block < new_bb->block; bb = bb->next)
+ ;
+
+ if (bb->next == NULL) {
+ bb->next = new_bb;
+ } else {
+ new_bb->next = bb->next;
+ bb->next = new_bb;
+ }
+
+ merge_bb(bbl, new_bb, new_bb->next);
+ if (!merge_bb(bbl, bb, new_bb)) {
+ /* new_bb destroyed, point to retained as last_used */
+ bbl->last_used = bb;
+ }
+
+ return 0;
+}
+
+/* Queues a fill block of memory to be written to the specified data blocks */
+int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, unsigned int len,
+ unsigned int block) {
+ struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
+ if (bb == NULL) {
+ return -ENOMEM;
+ }
+
+ bb->block = block;
+ bb->len = len;
+ bb->type = BACKED_BLOCK_FILL;
+ bb->fill.val = fill_val;
+ bb->next = NULL;
+
+ return queue_bb(bbl, bb);
+}
+
+/* Queues a block of memory to be written to the specified data blocks */
+int backed_block_add_data(struct backed_block_list* bbl, void* data, unsigned int len,
+ unsigned int block) {
+ struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
+ if (bb == NULL) {
+ return -ENOMEM;
+ }
+
+ bb->block = block;
+ bb->len = len;
+ bb->type = BACKED_BLOCK_DATA;
+ bb->data.data = data;
+ bb->next = NULL;
+
+ return queue_bb(bbl, bb);
+}
+
+/* Queues a chunk of a file on disk to be written to the specified data blocks */
+int backed_block_add_file(struct backed_block_list* bbl, const char* filename, int64_t offset,
+ unsigned int len, unsigned int block) {
+ struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
+ if (bb == NULL) {
+ return -ENOMEM;
+ }
+
+ bb->block = block;
+ bb->len = len;
+ bb->type = BACKED_BLOCK_FILE;
+ bb->file.filename = strdup(filename);
+ bb->file.offset = offset;
+ bb->next = NULL;
+
+ return queue_bb(bbl, bb);
+}
+
+/* Queues a chunk of a fd to be written to the specified data blocks */
+int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, unsigned int len,
+ unsigned int block) {
+ struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
+ if (bb == NULL) {
+ return -ENOMEM;
+ }
+
+ bb->block = block;
+ bb->len = len;
+ bb->type = BACKED_BLOCK_FD;
+ bb->fd.fd = fd;
+ bb->fd.offset = offset;
+ bb->next = NULL;
+
+ return queue_bb(bbl, bb);
+}
+
+int backed_block_split(struct backed_block_list* bbl, struct backed_block* bb,
+ unsigned int max_len) {
+ struct backed_block* new_bb;
+
+ max_len = ALIGN_DOWN(max_len, bbl->block_size);
+
+ if (bb->len <= max_len) {
+ return 0;
+ }
+
+ new_bb = reinterpret_cast<backed_block*>(malloc(sizeof(struct backed_block)));
+ if (new_bb == NULL) {
+ return -ENOMEM;
+ }
+
+ *new_bb = *bb;
+
+ new_bb->len = bb->len - max_len;
+ new_bb->block = bb->block + max_len / bbl->block_size;
+ new_bb->next = bb->next;
+ bb->next = new_bb;
+ bb->len = max_len;
+
+ switch (bb->type) {
+ case BACKED_BLOCK_DATA:
+ new_bb->data.data = (char*)bb->data.data + max_len;
+ break;
+ case BACKED_BLOCK_FILE:
+ new_bb->file.offset += max_len;
+ break;
+ case BACKED_BLOCK_FD:
+ new_bb->fd.offset += max_len;
+ break;
+ case BACKED_BLOCK_FILL:
+ break;
+ }
+
+ return 0;
+}
diff --git a/libsparse/backed_block.h b/libsparse/backed_block.h
index 1a159be..3a75460 100644
--- a/libsparse/backed_block.h
+++ b/libsparse/backed_block.h
@@ -23,42 +23,40 @@
struct backed_block;
enum backed_block_type {
- BACKED_BLOCK_DATA,
- BACKED_BLOCK_FILE,
- BACKED_BLOCK_FD,
- BACKED_BLOCK_FILL,
+ BACKED_BLOCK_DATA,
+ BACKED_BLOCK_FILE,
+ BACKED_BLOCK_FD,
+ BACKED_BLOCK_FILL,
};
-int backed_block_add_data(struct backed_block_list *bbl, void *data,
- unsigned int len, unsigned int block);
-int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val,
- unsigned int len, unsigned int block);
-int backed_block_add_file(struct backed_block_list *bbl, const char *filename,
- int64_t offset, unsigned int len, unsigned int block);
-int backed_block_add_fd(struct backed_block_list *bbl, int fd,
- int64_t offset, unsigned int len, unsigned int block);
+int backed_block_add_data(struct backed_block_list* bbl, void* data, unsigned int len,
+ unsigned int block);
+int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, unsigned int len,
+ unsigned int block);
+int backed_block_add_file(struct backed_block_list* bbl, const char* filename, int64_t offset,
+ unsigned int len, unsigned int block);
+int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, unsigned int len,
+ unsigned int block);
-struct backed_block *backed_block_iter_new(struct backed_block_list *bbl);
-struct backed_block *backed_block_iter_next(struct backed_block *bb);
-unsigned int backed_block_len(struct backed_block *bb);
-unsigned int backed_block_block(struct backed_block *bb);
-void *backed_block_data(struct backed_block *bb);
-const char *backed_block_filename(struct backed_block *bb);
-int backed_block_fd(struct backed_block *bb);
-int64_t backed_block_file_offset(struct backed_block *bb);
-uint32_t backed_block_fill_val(struct backed_block *bb);
-enum backed_block_type backed_block_type(struct backed_block *bb);
-int backed_block_split(struct backed_block_list *bbl, struct backed_block *bb,
- unsigned int max_len);
+struct backed_block* backed_block_iter_new(struct backed_block_list* bbl);
+struct backed_block* backed_block_iter_next(struct backed_block* bb);
+unsigned int backed_block_len(struct backed_block* bb);
+unsigned int backed_block_block(struct backed_block* bb);
+void* backed_block_data(struct backed_block* bb);
+const char* backed_block_filename(struct backed_block* bb);
+int backed_block_fd(struct backed_block* bb);
+int64_t backed_block_file_offset(struct backed_block* bb);
+uint32_t backed_block_fill_val(struct backed_block* bb);
+enum backed_block_type backed_block_type(struct backed_block* bb);
+int backed_block_split(struct backed_block_list* bbl, struct backed_block* bb, unsigned int max_len);
-struct backed_block *backed_block_iter_new(struct backed_block_list *bbl);
-struct backed_block *backed_block_iter_next(struct backed_block *bb);
+struct backed_block* backed_block_iter_new(struct backed_block_list* bbl);
+struct backed_block* backed_block_iter_next(struct backed_block* bb);
-struct backed_block_list *backed_block_list_new(unsigned int block_size);
-void backed_block_list_destroy(struct backed_block_list *bbl);
+struct backed_block_list* backed_block_list_new(unsigned int block_size);
+void backed_block_list_destroy(struct backed_block_list* bbl);
-void backed_block_list_move(struct backed_block_list *from,
- struct backed_block_list *to, struct backed_block *start,
- struct backed_block *end);
+void backed_block_list_move(struct backed_block_list* from, struct backed_block_list* to,
+ struct backed_block* start, struct backed_block* end);
#endif
diff --git a/libsparse/defs.h b/libsparse/defs.h
index 34e63c5..28e5cab 100644
--- a/libsparse/defs.h
+++ b/libsparse/defs.h
@@ -17,7 +17,7 @@
#ifndef _LIBSPARSE_DEFS_H_
#ifndef __unused
-#define __unused __attribute__((__unused__))
+#define __unused __attribute__((__unused__))
#endif
#endif /* _LIBSPARSE_DEFS_H_ */
diff --git a/libsparse/img2simg.c b/libsparse/img2simg.c
deleted file mode 100644
index a0db36f..0000000
--- a/libsparse/img2simg.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2012 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 _FILE_OFFSET_BITS 64
-#define _LARGEFILE64_SOURCE 1
-
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <sparse/sparse.h>
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-#if defined(__APPLE__) && defined(__MACH__)
-#define lseek64 lseek
-#define off64_t off_t
-#endif
-
-void usage()
-{
- fprintf(stderr, "Usage: img2simg <raw_image_file> <sparse_image_file> [<block_size>]\n");
-}
-
-int main(int argc, char *argv[])
-{
- int in;
- int out;
- int ret;
- struct sparse_file *s;
- unsigned int block_size = 4096;
- off64_t len;
-
- if (argc < 3 || argc > 4) {
- usage();
- exit(-1);
- }
-
- if (argc == 4) {
- block_size = atoi(argv[3]);
- }
-
- if (block_size < 1024 || block_size % 4 != 0) {
- usage();
- exit(-1);
- }
-
- if (strcmp(argv[1], "-") == 0) {
- in = STDIN_FILENO;
- } else {
- in = open(argv[1], O_RDONLY | O_BINARY);
- if (in < 0) {
- fprintf(stderr, "Cannot open input file %s\n", argv[1]);
- exit(-1);
- }
- }
-
- if (strcmp(argv[2], "-") == 0) {
- out = STDOUT_FILENO;
- } else {
- out = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
- if (out < 0) {
- fprintf(stderr, "Cannot open output file %s\n", argv[2]);
- exit(-1);
- }
- }
-
- len = lseek64(in, 0, SEEK_END);
- lseek64(in, 0, SEEK_SET);
-
- s = sparse_file_new(block_size, len);
- if (!s) {
- fprintf(stderr, "Failed to create sparse file\n");
- exit(-1);
- }
-
- sparse_file_verbose(s);
- ret = sparse_file_read(s, in, false, false);
- if (ret) {
- fprintf(stderr, "Failed to read file\n");
- exit(-1);
- }
-
- ret = sparse_file_write(s, out, false, true, false);
- if (ret) {
- fprintf(stderr, "Failed to write sparse file\n");
- exit(-1);
- }
-
- close(in);
- close(out);
-
- exit(0);
-}
diff --git a/libsparse/img2simg.cpp b/libsparse/img2simg.cpp
new file mode 100644
index 0000000..4c2c6ca
--- /dev/null
+++ b/libsparse/img2simg.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2012 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 _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <sparse/sparse.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#define off64_t off_t
+#endif
+
+void usage() {
+ fprintf(stderr, "Usage: img2simg <raw_image_file> <sparse_image_file> [<block_size>]\n");
+}
+
+int main(int argc, char* argv[]) {
+ int in;
+ int out;
+ int ret;
+ struct sparse_file* s;
+ unsigned int block_size = 4096;
+ off64_t len;
+
+ if (argc < 3 || argc > 4) {
+ usage();
+ exit(-1);
+ }
+
+ if (argc == 4) {
+ block_size = atoi(argv[3]);
+ }
+
+ if (block_size < 1024 || block_size % 4 != 0) {
+ usage();
+ exit(-1);
+ }
+
+ if (strcmp(argv[1], "-") == 0) {
+ in = STDIN_FILENO;
+ } else {
+ in = open(argv[1], O_RDONLY | O_BINARY);
+ if (in < 0) {
+ fprintf(stderr, "Cannot open input file %s\n", argv[1]);
+ exit(-1);
+ }
+ }
+
+ if (strcmp(argv[2], "-") == 0) {
+ out = STDOUT_FILENO;
+ } else {
+ out = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
+ if (out < 0) {
+ fprintf(stderr, "Cannot open output file %s\n", argv[2]);
+ exit(-1);
+ }
+ }
+
+ len = lseek64(in, 0, SEEK_END);
+ lseek64(in, 0, SEEK_SET);
+
+ s = sparse_file_new(block_size, len);
+ if (!s) {
+ fprintf(stderr, "Failed to create sparse file\n");
+ exit(-1);
+ }
+
+ sparse_file_verbose(s);
+ ret = sparse_file_read(s, in, false, false);
+ if (ret) {
+ fprintf(stderr, "Failed to read file\n");
+ exit(-1);
+ }
+
+ ret = sparse_file_write(s, out, false, true, false);
+ if (ret) {
+ fprintf(stderr, "Failed to write sparse file\n");
+ exit(-1);
+ }
+
+ close(in);
+ close(out);
+
+ exit(0);
+}
diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h
index 1b91ead..3d5fb0c 100644
--- a/libsparse/include/sparse/sparse.h
+++ b/libsparse/include/sparse/sparse.h
@@ -246,9 +246,24 @@
int sparse_file_read(struct sparse_file *s, int fd, bool sparse, bool crc);
/**
- * sparse_file_import - import an existing sparse file
+ * sparse_file_read_buf - read a buffer into a sparse file cookie
*
* @s - sparse file cookie
+ * @buf - buffer to read from
+ * @crc - verify the crc of a file in the Android sparse file format
+ *
+ * Reads a buffer into a sparse file cookie. The buffer must remain
+ * valid until the sparse file cookie is freed. If crc is true, the
+ * crc of the sparse file will be verified.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int sparse_file_read_buf(struct sparse_file *s, char *buf, bool crc);
+
+/**
+ * sparse_file_import - import an existing sparse file
+ *
+ * @fd - file descriptor to read from
* @verbose - print verbose errors while reading the sparse file
* @crc - verify the crc of a file in the Android sparse file format
*
@@ -261,6 +276,21 @@
struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc);
/**
+ * sparse_file_import_buf - import an existing sparse file from a buffer
+ *
+ * @buf - buffer to read from
+ * @verbose - print verbose errors while reading the sparse file
+ * @crc - verify the crc of a file in the Android sparse file format
+ *
+ * Reads existing sparse file data into a sparse file cookie, recreating the same
+ * sparse cookie that was used to write it. If verbose is true, prints verbose
+ * errors when the sparse file is formatted incorrectly.
+ *
+ * Returns a new sparse file cookie on success, NULL on error.
+ */
+struct sparse_file *sparse_file_import_buf(char* buf, bool verbose, bool crc);
+
+/**
* sparse_file_import_auto - import an existing sparse or normal file
*
* @fd - file descriptor to read from
diff --git a/libsparse/output_file.c b/libsparse/output_file.c
deleted file mode 100644
index 002ad27..0000000
--- a/libsparse/output_file.c
+++ /dev/null
@@ -1,776 +0,0 @@
-/*
- * Copyright (C) 2010 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 _FILE_OFFSET_BITS 64
-#define _LARGEFILE64_SOURCE 1
-
-#include <fcntl.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <zlib.h>
-
-#include "defs.h"
-#include "output_file.h"
-#include "sparse_crc32.h"
-#include "sparse_format.h"
-
-#ifndef _WIN32
-#include <sys/mman.h>
-#define O_BINARY 0
-#else
-#define ftruncate64 ftruncate
-#endif
-
-#if defined(__APPLE__) && defined(__MACH__)
-#define lseek64 lseek
-#define ftruncate64 ftruncate
-#define mmap64 mmap
-#define off64_t off_t
-#endif
-
-#define min(a, b) \
- ({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; })
-
-#define SPARSE_HEADER_MAJOR_VER 1
-#define SPARSE_HEADER_MINOR_VER 0
-#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
-#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
-
-#define container_of(inner, outer_t, elem) \
- ((outer_t *)((char *)(inner) - offsetof(outer_t, elem)))
-
-struct output_file_ops {
- int (*open)(struct output_file *, int fd);
- int (*skip)(struct output_file *, int64_t);
- int (*pad)(struct output_file *, int64_t);
- int (*write)(struct output_file *, void *, size_t);
- void (*close)(struct output_file *);
-};
-
-struct sparse_file_ops {
- int (*write_data_chunk)(struct output_file *out, unsigned int len,
- void *data);
- int (*write_fill_chunk)(struct output_file *out, unsigned int len,
- uint32_t fill_val);
- int (*write_skip_chunk)(struct output_file *out, int64_t len);
- int (*write_end_chunk)(struct output_file *out);
-};
-
-struct output_file {
- int64_t cur_out_ptr;
- unsigned int chunk_cnt;
- uint32_t crc32;
- struct output_file_ops *ops;
- struct sparse_file_ops *sparse_ops;
- int use_crc;
- unsigned int block_size;
- int64_t len;
- char *zero_buf;
- uint32_t *fill_buf;
- char *buf;
-};
-
-struct output_file_gz {
- struct output_file out;
- gzFile gz_fd;
-};
-
-#define to_output_file_gz(_o) \
- container_of((_o), struct output_file_gz, out)
-
-struct output_file_normal {
- struct output_file out;
- int fd;
-};
-
-#define to_output_file_normal(_o) \
- container_of((_o), struct output_file_normal, out)
-
-struct output_file_callback {
- struct output_file out;
- void *priv;
- int (*write)(void *priv, const void *buf, size_t len);
-};
-
-#define to_output_file_callback(_o) \
- container_of((_o), struct output_file_callback, out)
-
-static int file_open(struct output_file *out, int fd)
-{
- struct output_file_normal *outn = to_output_file_normal(out);
-
- outn->fd = fd;
- return 0;
-}
-
-static int file_skip(struct output_file *out, int64_t cnt)
-{
- off64_t ret;
- struct output_file_normal *outn = to_output_file_normal(out);
-
- ret = lseek64(outn->fd, cnt, SEEK_CUR);
- if (ret < 0) {
- error_errno("lseek64");
- return -1;
- }
- return 0;
-}
-
-static int file_pad(struct output_file *out, int64_t len)
-{
- int ret;
- struct output_file_normal *outn = to_output_file_normal(out);
-
- ret = ftruncate64(outn->fd, len);
- if (ret < 0) {
- return -errno;
- }
-
- return 0;
-}
-
-static int file_write(struct output_file *out, void *data, size_t len)
-{
- ssize_t ret;
- struct output_file_normal *outn = to_output_file_normal(out);
-
- while (len > 0) {
- ret = write(outn->fd, data, len);
- if (ret < 0) {
- if (errno == EINTR) {
- continue;
- }
- error_errno("write");
- return -1;
- }
-
- data = (char *)data + ret;
- len -= ret;
- }
-
- return 0;
-}
-
-static void file_close(struct output_file *out)
-{
- struct output_file_normal *outn = to_output_file_normal(out);
-
- free(outn);
-}
-
-static struct output_file_ops file_ops = {
- .open = file_open,
- .skip = file_skip,
- .pad = file_pad,
- .write = file_write,
- .close = file_close,
-};
-
-static int gz_file_open(struct output_file *out, int fd)
-{
- struct output_file_gz *outgz = to_output_file_gz(out);
-
- outgz->gz_fd = gzdopen(fd, "wb9");
- if (!outgz->gz_fd) {
- error_errno("gzopen");
- return -errno;
- }
-
- return 0;
-}
-
-
-static int gz_file_skip(struct output_file *out, int64_t cnt)
-{
- off64_t ret;
- struct output_file_gz *outgz = to_output_file_gz(out);
-
- ret = gzseek(outgz->gz_fd, cnt, SEEK_CUR);
- if (ret < 0) {
- error_errno("gzseek");
- return -1;
- }
- return 0;
-}
-
-static int gz_file_pad(struct output_file *out, int64_t len)
-{
- off64_t ret;
- struct output_file_gz *outgz = to_output_file_gz(out);
-
- ret = gztell(outgz->gz_fd);
- if (ret < 0) {
- return -1;
- }
-
- if (ret >= len) {
- return 0;
- }
-
- ret = gzseek(outgz->gz_fd, len - 1, SEEK_SET);
- if (ret < 0) {
- return -1;
- }
-
- gzwrite(outgz->gz_fd, "", 1);
-
- return 0;
-}
-
-static int gz_file_write(struct output_file *out, void *data, size_t len)
-{
- int ret;
- struct output_file_gz *outgz = to_output_file_gz(out);
-
- while (len > 0) {
- ret = gzwrite(outgz->gz_fd, data,
- min(len, (unsigned int)INT_MAX));
- if (ret == 0) {
- error("gzwrite %s", gzerror(outgz->gz_fd, NULL));
- return -1;
- }
- len -= ret;
- data = (char *)data + ret;
- }
-
- return 0;
-}
-
-static void gz_file_close(struct output_file *out)
-{
- struct output_file_gz *outgz = to_output_file_gz(out);
-
- gzclose(outgz->gz_fd);
- free(outgz);
-}
-
-static struct output_file_ops gz_file_ops = {
- .open = gz_file_open,
- .skip = gz_file_skip,
- .pad = gz_file_pad,
- .write = gz_file_write,
- .close = gz_file_close,
-};
-
-static int callback_file_open(struct output_file *out __unused, int fd __unused)
-{
- return 0;
-}
-
-static int callback_file_skip(struct output_file *out, int64_t off)
-{
- struct output_file_callback *outc = to_output_file_callback(out);
- int to_write;
- int ret;
-
- while (off > 0) {
- to_write = min(off, (int64_t)INT_MAX);
- ret = outc->write(outc->priv, NULL, to_write);
- if (ret < 0) {
- return ret;
- }
- off -= to_write;
- }
-
- return 0;
-}
-
-static int callback_file_pad(struct output_file *out __unused, int64_t len __unused)
-{
- return -1;
-}
-
-static int callback_file_write(struct output_file *out, void *data, size_t len)
-{
- struct output_file_callback *outc = to_output_file_callback(out);
-
- return outc->write(outc->priv, data, len);
-}
-
-static void callback_file_close(struct output_file *out)
-{
- struct output_file_callback *outc = to_output_file_callback(out);
-
- free(outc);
-}
-
-static struct output_file_ops callback_file_ops = {
- .open = callback_file_open,
- .skip = callback_file_skip,
- .pad = callback_file_pad,
- .write = callback_file_write,
- .close = callback_file_close,
-};
-
-int read_all(int fd, void *buf, size_t len)
-{
- size_t total = 0;
- int ret;
- char *ptr = buf;
-
- while (total < len) {
- ret = read(fd, ptr, len - total);
-
- if (ret < 0)
- return -errno;
-
- if (ret == 0)
- return -EINVAL;
-
- ptr += ret;
- total += ret;
- }
-
- return 0;
-}
-
-static int write_sparse_skip_chunk(struct output_file *out, int64_t skip_len)
-{
- chunk_header_t chunk_header;
- int ret;
-
- if (skip_len % out->block_size) {
- error("don't care size %"PRIi64" is not a multiple of the block size %u",
- skip_len, out->block_size);
- return -1;
- }
-
- /* We are skipping data, so emit a don't care chunk. */
- chunk_header.chunk_type = CHUNK_TYPE_DONT_CARE;
- chunk_header.reserved1 = 0;
- chunk_header.chunk_sz = skip_len / out->block_size;
- chunk_header.total_sz = CHUNK_HEADER_LEN;
- ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
- if (ret < 0)
- return -1;
-
- out->cur_out_ptr += skip_len;
- out->chunk_cnt++;
-
- return 0;
-}
-
-static int write_sparse_fill_chunk(struct output_file *out, unsigned int len,
- uint32_t fill_val)
-{
- chunk_header_t chunk_header;
- int rnd_up_len, count;
- int ret;
-
- /* Round up the fill length to a multiple of the block size */
- rnd_up_len = ALIGN(len, out->block_size);
-
- /* Finally we can safely emit a chunk of data */
- chunk_header.chunk_type = CHUNK_TYPE_FILL;
- chunk_header.reserved1 = 0;
- chunk_header.chunk_sz = rnd_up_len / out->block_size;
- chunk_header.total_sz = CHUNK_HEADER_LEN + sizeof(fill_val);
- ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
-
- if (ret < 0)
- return -1;
- ret = out->ops->write(out, &fill_val, sizeof(fill_val));
- if (ret < 0)
- return -1;
-
- if (out->use_crc) {
- count = out->block_size / sizeof(uint32_t);
- while (count--)
- out->crc32 = sparse_crc32(out->crc32, &fill_val, sizeof(uint32_t));
- }
-
- out->cur_out_ptr += rnd_up_len;
- out->chunk_cnt++;
-
- return 0;
-}
-
-static int write_sparse_data_chunk(struct output_file *out, unsigned int len,
- void *data)
-{
- chunk_header_t chunk_header;
- int rnd_up_len, zero_len;
- int ret;
-
- /* Round up the data length to a multiple of the block size */
- rnd_up_len = ALIGN(len, out->block_size);
- zero_len = rnd_up_len - len;
-
- /* Finally we can safely emit a chunk of data */
- chunk_header.chunk_type = CHUNK_TYPE_RAW;
- chunk_header.reserved1 = 0;
- chunk_header.chunk_sz = rnd_up_len / out->block_size;
- chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len;
- ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
-
- if (ret < 0)
- return -1;
- ret = out->ops->write(out, data, len);
- if (ret < 0)
- return -1;
- if (zero_len) {
- ret = out->ops->write(out, out->zero_buf, zero_len);
- if (ret < 0)
- return -1;
- }
-
- if (out->use_crc) {
- out->crc32 = sparse_crc32(out->crc32, data, len);
- if (zero_len)
- out->crc32 = sparse_crc32(out->crc32, out->zero_buf, zero_len);
- }
-
- out->cur_out_ptr += rnd_up_len;
- out->chunk_cnt++;
-
- return 0;
-}
-
-int write_sparse_end_chunk(struct output_file *out)
-{
- chunk_header_t chunk_header;
- int ret;
-
- if (out->use_crc) {
- chunk_header.chunk_type = CHUNK_TYPE_CRC32;
- chunk_header.reserved1 = 0;
- chunk_header.chunk_sz = 0;
- chunk_header.total_sz = CHUNK_HEADER_LEN + 4;
-
- ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
- if (ret < 0) {
- return ret;
- }
- out->ops->write(out, &out->crc32, 4);
- if (ret < 0) {
- return ret;
- }
-
- out->chunk_cnt++;
- }
-
- return 0;
-}
-
-static struct sparse_file_ops sparse_file_ops = {
- .write_data_chunk = write_sparse_data_chunk,
- .write_fill_chunk = write_sparse_fill_chunk,
- .write_skip_chunk = write_sparse_skip_chunk,
- .write_end_chunk = write_sparse_end_chunk,
-};
-
-static int write_normal_data_chunk(struct output_file *out, unsigned int len,
- void *data)
-{
- int ret;
- unsigned int rnd_up_len = ALIGN(len, out->block_size);
-
- ret = out->ops->write(out, data, len);
- if (ret < 0) {
- return ret;
- }
-
- if (rnd_up_len > len) {
- ret = out->ops->skip(out, rnd_up_len - len);
- }
-
- return ret;
-}
-
-static int write_normal_fill_chunk(struct output_file *out, unsigned int len,
- uint32_t fill_val)
-{
- int ret;
- unsigned int i;
- unsigned int write_len;
-
- /* Initialize fill_buf with the fill_val */
- for (i = 0; i < out->block_size / sizeof(uint32_t); i++) {
- out->fill_buf[i] = fill_val;
- }
-
- while (len) {
- write_len = min(len, out->block_size);
- ret = out->ops->write(out, out->fill_buf, write_len);
- if (ret < 0) {
- return ret;
- }
-
- len -= write_len;
- }
-
- return 0;
-}
-
-static int write_normal_skip_chunk(struct output_file *out, int64_t len)
-{
- return out->ops->skip(out, len);
-}
-
-int write_normal_end_chunk(struct output_file *out)
-{
- return out->ops->pad(out, out->len);
-}
-
-static struct sparse_file_ops normal_file_ops = {
- .write_data_chunk = write_normal_data_chunk,
- .write_fill_chunk = write_normal_fill_chunk,
- .write_skip_chunk = write_normal_skip_chunk,
- .write_end_chunk = write_normal_end_chunk,
-};
-
-void output_file_close(struct output_file *out)
-{
- out->sparse_ops->write_end_chunk(out);
- out->ops->close(out);
-}
-
-static int output_file_init(struct output_file *out, int block_size,
- int64_t len, bool sparse, int chunks, bool crc)
-{
- int ret;
-
- out->len = len;
- out->block_size = block_size;
- out->cur_out_ptr = 0ll;
- out->chunk_cnt = 0;
- out->crc32 = 0;
- out->use_crc = crc;
-
- out->zero_buf = calloc(block_size, 1);
- if (!out->zero_buf) {
- error_errno("malloc zero_buf");
- return -ENOMEM;
- }
-
- out->fill_buf = calloc(block_size, 1);
- if (!out->fill_buf) {
- error_errno("malloc fill_buf");
- ret = -ENOMEM;
- goto err_fill_buf;
- }
-
- if (sparse) {
- out->sparse_ops = &sparse_file_ops;
- } else {
- out->sparse_ops = &normal_file_ops;
- }
-
- if (sparse) {
- sparse_header_t sparse_header = {
- .magic = SPARSE_HEADER_MAGIC,
- .major_version = SPARSE_HEADER_MAJOR_VER,
- .minor_version = SPARSE_HEADER_MINOR_VER,
- .file_hdr_sz = SPARSE_HEADER_LEN,
- .chunk_hdr_sz = CHUNK_HEADER_LEN,
- .blk_sz = out->block_size,
- .total_blks = DIV_ROUND_UP(out->len, out->block_size),
- .total_chunks = chunks,
- .image_checksum = 0
- };
-
- if (out->use_crc) {
- sparse_header.total_chunks++;
- }
-
- ret = out->ops->write(out, &sparse_header, sizeof(sparse_header));
- if (ret < 0) {
- goto err_write;
- }
- }
-
- return 0;
-
-err_write:
- free(out->fill_buf);
-err_fill_buf:
- free(out->zero_buf);
- return ret;
-}
-
-static struct output_file *output_file_new_gz(void)
-{
- struct output_file_gz *outgz = calloc(1, sizeof(struct output_file_gz));
- if (!outgz) {
- error_errno("malloc struct outgz");
- return NULL;
- }
-
- outgz->out.ops = &gz_file_ops;
-
- return &outgz->out;
-}
-
-static struct output_file *output_file_new_normal(void)
-{
- struct output_file_normal *outn = calloc(1, sizeof(struct output_file_normal));
- if (!outn) {
- error_errno("malloc struct outn");
- return NULL;
- }
-
- outn->out.ops = &file_ops;
-
- return &outn->out;
-}
-
-struct output_file *output_file_open_callback(
- int (*write)(void *, const void *, size_t),
- void *priv, unsigned int block_size, int64_t len,
- int gz __unused, int sparse, int chunks, int crc)
-{
- int ret;
- struct output_file_callback *outc;
-
- outc = calloc(1, sizeof(struct output_file_callback));
- if (!outc) {
- error_errno("malloc struct outc");
- return NULL;
- }
-
- outc->out.ops = &callback_file_ops;
- outc->priv = priv;
- outc->write = write;
-
- ret = output_file_init(&outc->out, block_size, len, sparse, chunks, crc);
- if (ret < 0) {
- free(outc);
- return NULL;
- }
-
- return &outc->out;
-}
-
-struct output_file *output_file_open_fd(int fd, unsigned int block_size, int64_t len,
- int gz, int sparse, int chunks, int crc)
-{
- int ret;
- struct output_file *out;
-
- if (gz) {
- out = output_file_new_gz();
- } else {
- out = output_file_new_normal();
- }
- if (!out) {
- return NULL;
- }
-
- out->ops->open(out, fd);
-
- ret = output_file_init(out, block_size, len, sparse, chunks, crc);
- if (ret < 0) {
- free(out);
- return NULL;
- }
-
- return out;
-}
-
-/* Write a contiguous region of data blocks from a memory buffer */
-int write_data_chunk(struct output_file *out, unsigned int len, void *data)
-{
- return out->sparse_ops->write_data_chunk(out, len, data);
-}
-
-/* Write a contiguous region of data blocks with a fill value */
-int write_fill_chunk(struct output_file *out, unsigned int len,
- uint32_t fill_val)
-{
- return out->sparse_ops->write_fill_chunk(out, len, fill_val);
-}
-
-int write_fd_chunk(struct output_file *out, unsigned int len,
- int fd, int64_t offset)
-{
- int ret;
- int64_t aligned_offset;
- int aligned_diff;
- uint64_t buffer_size;
- char *ptr;
-
- aligned_offset = offset & ~(4096 - 1);
- aligned_diff = offset - aligned_offset;
- buffer_size = (uint64_t)len + (uint64_t)aligned_diff;
-
-#ifndef _WIN32
- if (buffer_size > SIZE_MAX)
- return -E2BIG;
- char *data = mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, fd,
- aligned_offset);
- if (data == MAP_FAILED) {
- return -errno;
- }
- ptr = data + aligned_diff;
-#else
- off64_t pos;
- char *data = malloc(len);
- if (!data) {
- return -errno;
- }
- pos = lseek64(fd, offset, SEEK_SET);
- if (pos < 0) {
- free(data);
- return -errno;
- }
- ret = read_all(fd, data, len);
- if (ret < 0) {
- free(data);
- return ret;
- }
- ptr = data;
-#endif
-
- ret = out->sparse_ops->write_data_chunk(out, len, ptr);
-
-#ifndef _WIN32
- munmap(data, buffer_size);
-#else
- free(data);
-#endif
-
- return ret;
-}
-
-/* Write a contiguous region of data blocks from a file */
-int write_file_chunk(struct output_file *out, unsigned int len,
- const char *file, int64_t offset)
-{
- int ret;
-
- int file_fd = open(file, O_RDONLY | O_BINARY);
- if (file_fd < 0) {
- return -errno;
- }
-
- ret = write_fd_chunk(out, len, file_fd, offset);
-
- close(file_fd);
-
- return ret;
-}
-
-int write_skip_chunk(struct output_file *out, int64_t len)
-{
- return out->sparse_ops->write_skip_chunk(out, len);
-}
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
new file mode 100644
index 0000000..5388e77
--- /dev/null
+++ b/libsparse/output_file.cpp
@@ -0,0 +1,720 @@
+/*
+ * Copyright (C) 2010 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 _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <zlib.h>
+
+#include "defs.h"
+#include "output_file.h"
+#include "sparse_crc32.h"
+#include "sparse_format.h"
+
+#ifndef _WIN32
+#include <sys/mman.h>
+#define O_BINARY 0
+#else
+#define ftruncate64 ftruncate
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#define ftruncate64 ftruncate
+#define mmap64 mmap
+#define off64_t off_t
+#endif
+
+#define min(a, b) \
+ ({ \
+ typeof(a) _a = (a); \
+ typeof(b) _b = (b); \
+ (_a < _b) ? _a : _b; \
+ })
+
+#define SPARSE_HEADER_MAJOR_VER 1
+#define SPARSE_HEADER_MINOR_VER 0
+#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
+#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
+
+#define container_of(inner, outer_t, elem) ((outer_t*)((char*)(inner)-offsetof(outer_t, elem)))
+
+struct output_file_ops {
+ int (*open)(struct output_file*, int fd);
+ int (*skip)(struct output_file*, int64_t);
+ int (*pad)(struct output_file*, int64_t);
+ int (*write)(struct output_file*, void*, size_t);
+ void (*close)(struct output_file*);
+};
+
+struct sparse_file_ops {
+ int (*write_data_chunk)(struct output_file* out, unsigned int len, void* data);
+ int (*write_fill_chunk)(struct output_file* out, unsigned int len, uint32_t fill_val);
+ int (*write_skip_chunk)(struct output_file* out, int64_t len);
+ int (*write_end_chunk)(struct output_file* out);
+};
+
+struct output_file {
+ int64_t cur_out_ptr;
+ unsigned int chunk_cnt;
+ uint32_t crc32;
+ struct output_file_ops* ops;
+ struct sparse_file_ops* sparse_ops;
+ int use_crc;
+ unsigned int block_size;
+ int64_t len;
+ char* zero_buf;
+ uint32_t* fill_buf;
+ char* buf;
+};
+
+struct output_file_gz {
+ struct output_file out;
+ gzFile gz_fd;
+};
+
+#define to_output_file_gz(_o) container_of((_o), struct output_file_gz, out)
+
+struct output_file_normal {
+ struct output_file out;
+ int fd;
+};
+
+#define to_output_file_normal(_o) container_of((_o), struct output_file_normal, out)
+
+struct output_file_callback {
+ struct output_file out;
+ void* priv;
+ int (*write)(void* priv, const void* buf, size_t len);
+};
+
+#define to_output_file_callback(_o) container_of((_o), struct output_file_callback, out)
+
+static int file_open(struct output_file* out, int fd) {
+ struct output_file_normal* outn = to_output_file_normal(out);
+
+ outn->fd = fd;
+ return 0;
+}
+
+static int file_skip(struct output_file* out, int64_t cnt) {
+ off64_t ret;
+ struct output_file_normal* outn = to_output_file_normal(out);
+
+ ret = lseek64(outn->fd, cnt, SEEK_CUR);
+ if (ret < 0) {
+ error_errno("lseek64");
+ return -1;
+ }
+ return 0;
+}
+
+static int file_pad(struct output_file* out, int64_t len) {
+ int ret;
+ struct output_file_normal* outn = to_output_file_normal(out);
+
+ ret = ftruncate64(outn->fd, len);
+ if (ret < 0) {
+ return -errno;
+ }
+
+ return 0;
+}
+
+static int file_write(struct output_file* out, void* data, size_t len) {
+ ssize_t ret;
+ struct output_file_normal* outn = to_output_file_normal(out);
+
+ while (len > 0) {
+ ret = write(outn->fd, data, len);
+ if (ret < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ error_errno("write");
+ return -1;
+ }
+
+ data = (char*)data + ret;
+ len -= ret;
+ }
+
+ return 0;
+}
+
+static void file_close(struct output_file* out) {
+ struct output_file_normal* outn = to_output_file_normal(out);
+
+ free(outn);
+}
+
+static struct output_file_ops file_ops = {
+ .open = file_open,
+ .skip = file_skip,
+ .pad = file_pad,
+ .write = file_write,
+ .close = file_close,
+};
+
+static int gz_file_open(struct output_file* out, int fd) {
+ struct output_file_gz* outgz = to_output_file_gz(out);
+
+ outgz->gz_fd = gzdopen(fd, "wb9");
+ if (!outgz->gz_fd) {
+ error_errno("gzopen");
+ return -errno;
+ }
+
+ return 0;
+}
+
+static int gz_file_skip(struct output_file* out, int64_t cnt) {
+ off64_t ret;
+ struct output_file_gz* outgz = to_output_file_gz(out);
+
+ ret = gzseek(outgz->gz_fd, cnt, SEEK_CUR);
+ if (ret < 0) {
+ error_errno("gzseek");
+ return -1;
+ }
+ return 0;
+}
+
+static int gz_file_pad(struct output_file* out, int64_t len) {
+ off64_t ret;
+ struct output_file_gz* outgz = to_output_file_gz(out);
+
+ ret = gztell(outgz->gz_fd);
+ if (ret < 0) {
+ return -1;
+ }
+
+ if (ret >= len) {
+ return 0;
+ }
+
+ ret = gzseek(outgz->gz_fd, len - 1, SEEK_SET);
+ if (ret < 0) {
+ return -1;
+ }
+
+ gzwrite(outgz->gz_fd, "", 1);
+
+ return 0;
+}
+
+static int gz_file_write(struct output_file* out, void* data, size_t len) {
+ int ret;
+ struct output_file_gz* outgz = to_output_file_gz(out);
+
+ while (len > 0) {
+ ret = gzwrite(outgz->gz_fd, data, min(len, (unsigned int)INT_MAX));
+ if (ret == 0) {
+ error("gzwrite %s", gzerror(outgz->gz_fd, NULL));
+ return -1;
+ }
+ len -= ret;
+ data = (char*)data + ret;
+ }
+
+ return 0;
+}
+
+static void gz_file_close(struct output_file* out) {
+ struct output_file_gz* outgz = to_output_file_gz(out);
+
+ gzclose(outgz->gz_fd);
+ free(outgz);
+}
+
+static struct output_file_ops gz_file_ops = {
+ .open = gz_file_open,
+ .skip = gz_file_skip,
+ .pad = gz_file_pad,
+ .write = gz_file_write,
+ .close = gz_file_close,
+};
+
+static int callback_file_open(struct output_file* out __unused, int fd __unused) {
+ return 0;
+}
+
+static int callback_file_skip(struct output_file* out, int64_t off) {
+ struct output_file_callback* outc = to_output_file_callback(out);
+ int to_write;
+ int ret;
+
+ while (off > 0) {
+ to_write = min(off, (int64_t)INT_MAX);
+ ret = outc->write(outc->priv, NULL, to_write);
+ if (ret < 0) {
+ return ret;
+ }
+ off -= to_write;
+ }
+
+ return 0;
+}
+
+static int callback_file_pad(struct output_file* out __unused, int64_t len __unused) {
+ return -1;
+}
+
+static int callback_file_write(struct output_file* out, void* data, size_t len) {
+ struct output_file_callback* outc = to_output_file_callback(out);
+
+ return outc->write(outc->priv, data, len);
+}
+
+static void callback_file_close(struct output_file* out) {
+ struct output_file_callback* outc = to_output_file_callback(out);
+
+ free(outc);
+}
+
+static struct output_file_ops callback_file_ops = {
+ .open = callback_file_open,
+ .skip = callback_file_skip,
+ .pad = callback_file_pad,
+ .write = callback_file_write,
+ .close = callback_file_close,
+};
+
+int read_all(int fd, void* buf, size_t len) {
+ size_t total = 0;
+ int ret;
+ char* ptr = reinterpret_cast<char*>(buf);
+
+ while (total < len) {
+ ret = read(fd, ptr, len - total);
+
+ if (ret < 0) return -errno;
+
+ if (ret == 0) return -EINVAL;
+
+ ptr += ret;
+ total += ret;
+ }
+
+ return 0;
+}
+
+static int write_sparse_skip_chunk(struct output_file* out, int64_t skip_len) {
+ chunk_header_t chunk_header;
+ int ret;
+
+ if (skip_len % out->block_size) {
+ error("don't care size %" PRIi64 " is not a multiple of the block size %u", skip_len,
+ out->block_size);
+ return -1;
+ }
+
+ /* We are skipping data, so emit a don't care chunk. */
+ chunk_header.chunk_type = CHUNK_TYPE_DONT_CARE;
+ chunk_header.reserved1 = 0;
+ chunk_header.chunk_sz = skip_len / out->block_size;
+ chunk_header.total_sz = CHUNK_HEADER_LEN;
+ ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+ if (ret < 0) return -1;
+
+ out->cur_out_ptr += skip_len;
+ out->chunk_cnt++;
+
+ return 0;
+}
+
+static int write_sparse_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
+ chunk_header_t chunk_header;
+ int rnd_up_len, count;
+ int ret;
+
+ /* Round up the fill length to a multiple of the block size */
+ rnd_up_len = ALIGN(len, out->block_size);
+
+ /* Finally we can safely emit a chunk of data */
+ chunk_header.chunk_type = CHUNK_TYPE_FILL;
+ chunk_header.reserved1 = 0;
+ chunk_header.chunk_sz = rnd_up_len / out->block_size;
+ chunk_header.total_sz = CHUNK_HEADER_LEN + sizeof(fill_val);
+ ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+
+ if (ret < 0) return -1;
+ ret = out->ops->write(out, &fill_val, sizeof(fill_val));
+ if (ret < 0) return -1;
+
+ if (out->use_crc) {
+ count = out->block_size / sizeof(uint32_t);
+ while (count--) out->crc32 = sparse_crc32(out->crc32, &fill_val, sizeof(uint32_t));
+ }
+
+ out->cur_out_ptr += rnd_up_len;
+ out->chunk_cnt++;
+
+ return 0;
+}
+
+static int write_sparse_data_chunk(struct output_file* out, unsigned int len, void* data) {
+ chunk_header_t chunk_header;
+ int rnd_up_len, zero_len;
+ int ret;
+
+ /* Round up the data length to a multiple of the block size */
+ rnd_up_len = ALIGN(len, out->block_size);
+ zero_len = rnd_up_len - len;
+
+ /* Finally we can safely emit a chunk of data */
+ chunk_header.chunk_type = CHUNK_TYPE_RAW;
+ chunk_header.reserved1 = 0;
+ chunk_header.chunk_sz = rnd_up_len / out->block_size;
+ chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len;
+ ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+
+ if (ret < 0) return -1;
+ ret = out->ops->write(out, data, len);
+ if (ret < 0) return -1;
+ if (zero_len) {
+ ret = out->ops->write(out, out->zero_buf, zero_len);
+ if (ret < 0) return -1;
+ }
+
+ if (out->use_crc) {
+ out->crc32 = sparse_crc32(out->crc32, data, len);
+ if (zero_len) out->crc32 = sparse_crc32(out->crc32, out->zero_buf, zero_len);
+ }
+
+ out->cur_out_ptr += rnd_up_len;
+ out->chunk_cnt++;
+
+ return 0;
+}
+
+int write_sparse_end_chunk(struct output_file* out) {
+ chunk_header_t chunk_header;
+ int ret;
+
+ if (out->use_crc) {
+ chunk_header.chunk_type = CHUNK_TYPE_CRC32;
+ chunk_header.reserved1 = 0;
+ chunk_header.chunk_sz = 0;
+ chunk_header.total_sz = CHUNK_HEADER_LEN + 4;
+
+ ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+ if (ret < 0) {
+ return ret;
+ }
+ out->ops->write(out, &out->crc32, 4);
+ if (ret < 0) {
+ return ret;
+ }
+
+ out->chunk_cnt++;
+ }
+
+ return 0;
+}
+
+static struct sparse_file_ops sparse_file_ops = {
+ .write_data_chunk = write_sparse_data_chunk,
+ .write_fill_chunk = write_sparse_fill_chunk,
+ .write_skip_chunk = write_sparse_skip_chunk,
+ .write_end_chunk = write_sparse_end_chunk,
+};
+
+static int write_normal_data_chunk(struct output_file* out, unsigned int len, void* data) {
+ int ret;
+ unsigned int rnd_up_len = ALIGN(len, out->block_size);
+
+ ret = out->ops->write(out, data, len);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (rnd_up_len > len) {
+ ret = out->ops->skip(out, rnd_up_len - len);
+ }
+
+ return ret;
+}
+
+static int write_normal_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
+ int ret;
+ unsigned int i;
+ unsigned int write_len;
+
+ /* Initialize fill_buf with the fill_val */
+ for (i = 0; i < out->block_size / sizeof(uint32_t); i++) {
+ out->fill_buf[i] = fill_val;
+ }
+
+ while (len) {
+ write_len = min(len, out->block_size);
+ ret = out->ops->write(out, out->fill_buf, write_len);
+ if (ret < 0) {
+ return ret;
+ }
+
+ len -= write_len;
+ }
+
+ return 0;
+}
+
+static int write_normal_skip_chunk(struct output_file* out, int64_t len) {
+ return out->ops->skip(out, len);
+}
+
+int write_normal_end_chunk(struct output_file* out) {
+ return out->ops->pad(out, out->len);
+}
+
+static struct sparse_file_ops normal_file_ops = {
+ .write_data_chunk = write_normal_data_chunk,
+ .write_fill_chunk = write_normal_fill_chunk,
+ .write_skip_chunk = write_normal_skip_chunk,
+ .write_end_chunk = write_normal_end_chunk,
+};
+
+void output_file_close(struct output_file* out) {
+ out->sparse_ops->write_end_chunk(out);
+ out->ops->close(out);
+}
+
+static int output_file_init(struct output_file* out, int block_size, int64_t len, bool sparse,
+ int chunks, bool crc) {
+ int ret;
+
+ out->len = len;
+ out->block_size = block_size;
+ out->cur_out_ptr = 0ll;
+ out->chunk_cnt = 0;
+ out->crc32 = 0;
+ out->use_crc = crc;
+
+ out->zero_buf = reinterpret_cast<char*>(calloc(block_size, 1));
+ if (!out->zero_buf) {
+ error_errno("malloc zero_buf");
+ return -ENOMEM;
+ }
+
+ out->fill_buf = reinterpret_cast<uint32_t*>(calloc(block_size, 1));
+ if (!out->fill_buf) {
+ error_errno("malloc fill_buf");
+ ret = -ENOMEM;
+ goto err_fill_buf;
+ }
+
+ if (sparse) {
+ out->sparse_ops = &sparse_file_ops;
+ } else {
+ out->sparse_ops = &normal_file_ops;
+ }
+
+ if (sparse) {
+ sparse_header_t sparse_header = {
+ .magic = SPARSE_HEADER_MAGIC,
+ .major_version = SPARSE_HEADER_MAJOR_VER,
+ .minor_version = SPARSE_HEADER_MINOR_VER,
+ .file_hdr_sz = SPARSE_HEADER_LEN,
+ .chunk_hdr_sz = CHUNK_HEADER_LEN,
+ .blk_sz = out->block_size,
+ .total_blks = static_cast<unsigned>(DIV_ROUND_UP(out->len, out->block_size)),
+ .total_chunks = static_cast<unsigned>(chunks),
+ .image_checksum = 0};
+
+ if (out->use_crc) {
+ sparse_header.total_chunks++;
+ }
+
+ ret = out->ops->write(out, &sparse_header, sizeof(sparse_header));
+ if (ret < 0) {
+ goto err_write;
+ }
+ }
+
+ return 0;
+
+err_write:
+ free(out->fill_buf);
+err_fill_buf:
+ free(out->zero_buf);
+ return ret;
+}
+
+static struct output_file* output_file_new_gz(void) {
+ struct output_file_gz* outgz =
+ reinterpret_cast<struct output_file_gz*>(calloc(1, sizeof(struct output_file_gz)));
+ if (!outgz) {
+ error_errno("malloc struct outgz");
+ return NULL;
+ }
+
+ outgz->out.ops = &gz_file_ops;
+
+ return &outgz->out;
+}
+
+static struct output_file* output_file_new_normal(void) {
+ struct output_file_normal* outn =
+ reinterpret_cast<struct output_file_normal*>(calloc(1, sizeof(struct output_file_normal)));
+ if (!outn) {
+ error_errno("malloc struct outn");
+ return NULL;
+ }
+
+ outn->out.ops = &file_ops;
+
+ return &outn->out;
+}
+
+struct output_file* output_file_open_callback(int (*write)(void*, const void*, size_t), void* priv,
+ unsigned int block_size, int64_t len, int gz __unused,
+ int sparse, int chunks, int crc) {
+ int ret;
+ struct output_file_callback* outc;
+
+ outc =
+ reinterpret_cast<struct output_file_callback*>(calloc(1, sizeof(struct output_file_callback)));
+ if (!outc) {
+ error_errno("malloc struct outc");
+ return NULL;
+ }
+
+ outc->out.ops = &callback_file_ops;
+ outc->priv = priv;
+ outc->write = write;
+
+ ret = output_file_init(&outc->out, block_size, len, sparse, chunks, crc);
+ if (ret < 0) {
+ free(outc);
+ return NULL;
+ }
+
+ return &outc->out;
+}
+
+struct output_file* output_file_open_fd(int fd, unsigned int block_size, int64_t len, int gz,
+ int sparse, int chunks, int crc) {
+ int ret;
+ struct output_file* out;
+
+ if (gz) {
+ out = output_file_new_gz();
+ } else {
+ out = output_file_new_normal();
+ }
+ if (!out) {
+ return NULL;
+ }
+
+ out->ops->open(out, fd);
+
+ ret = output_file_init(out, block_size, len, sparse, chunks, crc);
+ if (ret < 0) {
+ free(out);
+ return NULL;
+ }
+
+ return out;
+}
+
+/* Write a contiguous region of data blocks from a memory buffer */
+int write_data_chunk(struct output_file* out, unsigned int len, void* data) {
+ return out->sparse_ops->write_data_chunk(out, len, data);
+}
+
+/* Write a contiguous region of data blocks with a fill value */
+int write_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
+ return out->sparse_ops->write_fill_chunk(out, len, fill_val);
+}
+
+int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset) {
+ int ret;
+ int64_t aligned_offset;
+ int aligned_diff;
+ uint64_t buffer_size;
+ char* ptr;
+
+ aligned_offset = offset & ~(4096 - 1);
+ aligned_diff = offset - aligned_offset;
+ buffer_size = (uint64_t)len + (uint64_t)aligned_diff;
+
+#ifndef _WIN32
+ if (buffer_size > SIZE_MAX) return -E2BIG;
+ char* data =
+ reinterpret_cast<char*>(mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, fd, aligned_offset));
+ if (data == MAP_FAILED) {
+ return -errno;
+ }
+ ptr = data + aligned_diff;
+#else
+ off64_t pos;
+ char* data = reinterpret_cast<char*>(malloc(len));
+ if (!data) {
+ return -errno;
+ }
+ pos = lseek64(fd, offset, SEEK_SET);
+ if (pos < 0) {
+ free(data);
+ return -errno;
+ }
+ ret = read_all(fd, data, len);
+ if (ret < 0) {
+ free(data);
+ return ret;
+ }
+ ptr = data;
+#endif
+
+ ret = out->sparse_ops->write_data_chunk(out, len, ptr);
+
+#ifndef _WIN32
+ munmap(data, buffer_size);
+#else
+ free(data);
+#endif
+
+ return ret;
+}
+
+/* Write a contiguous region of data blocks from a file */
+int write_file_chunk(struct output_file* out, unsigned int len, const char* file, int64_t offset) {
+ int ret;
+
+ int file_fd = open(file, O_RDONLY | O_BINARY);
+ if (file_fd < 0) {
+ return -errno;
+ }
+
+ ret = write_fd_chunk(out, len, file_fd, offset);
+
+ close(file_fd);
+
+ return ret;
+}
+
+int write_skip_chunk(struct output_file* out, int64_t len) {
+ return out->sparse_ops->write_skip_chunk(out, len);
+}
diff --git a/libsparse/output_file.h b/libsparse/output_file.h
index 690f610..278430b 100644
--- a/libsparse/output_file.h
+++ b/libsparse/output_file.h
@@ -25,23 +25,19 @@
struct output_file;
-struct output_file *output_file_open_fd(int fd, unsigned int block_size, int64_t len,
- int gz, int sparse, int chunks, int crc);
-struct output_file *output_file_open_callback(
- int (*write)(void *, const void *, size_t),
- void *priv, unsigned int block_size, int64_t len, int gz, int sparse,
- int chunks, int crc);
-int write_data_chunk(struct output_file *out, unsigned int len, void *data);
-int write_fill_chunk(struct output_file *out, unsigned int len,
- uint32_t fill_val);
-int write_file_chunk(struct output_file *out, unsigned int len,
- const char *file, int64_t offset);
-int write_fd_chunk(struct output_file *out, unsigned int len,
- int fd, int64_t offset);
-int write_skip_chunk(struct output_file *out, int64_t len);
-void output_file_close(struct output_file *out);
+struct output_file* output_file_open_fd(int fd, unsigned int block_size, int64_t len, int gz,
+ int sparse, int chunks, int crc);
+struct output_file* output_file_open_callback(int (*write)(void*, const void*, size_t), void* priv,
+ unsigned int block_size, int64_t len, int gz,
+ int sparse, int chunks, int crc);
+int write_data_chunk(struct output_file* out, unsigned int len, void* data);
+int write_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val);
+int write_file_chunk(struct output_file* out, unsigned int len, const char* file, int64_t offset);
+int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset);
+int write_skip_chunk(struct output_file* out, int64_t len);
+void output_file_close(struct output_file* out);
-int read_all(int fd, void *buf, size_t len);
+int read_all(int fd, void* buf, size_t len);
#ifdef __cplusplus
}
diff --git a/libsparse/simg2img.c b/libsparse/simg2img.c
deleted file mode 100644
index b9b438e..0000000
--- a/libsparse/simg2img.c
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2010 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 <sparse/sparse.h>
-
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-void usage()
-{
- fprintf(stderr, "Usage: simg2img <sparse_image_files> <raw_image_file>\n");
-}
-
-int main(int argc, char *argv[])
-{
- int in;
- int out;
- int i;
- struct sparse_file *s;
-
- if (argc < 3) {
- usage();
- exit(-1);
- }
-
- out = open(argv[argc - 1], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
- if (out < 0) {
- fprintf(stderr, "Cannot open output file %s\n", argv[argc - 1]);
- exit(-1);
- }
-
- for (i = 1; i < argc - 1; i++) {
- if (strcmp(argv[i], "-") == 0) {
- in = STDIN_FILENO;
- } else {
- in = open(argv[i], O_RDONLY | O_BINARY);
- if (in < 0) {
- fprintf(stderr, "Cannot open input file %s\n", argv[i]);
- exit(-1);
- }
- }
-
- s = sparse_file_import(in, true, false);
- if (!s) {
- fprintf(stderr, "Failed to read sparse file\n");
- exit(-1);
- }
-
- if (lseek(out, 0, SEEK_SET) == -1) {
- perror("lseek failed");
- exit(EXIT_FAILURE);
- }
-
- if (sparse_file_write(s, out, false, false, false) < 0) {
- fprintf(stderr, "Cannot write output file\n");
- exit(-1);
- }
- sparse_file_destroy(s);
- close(in);
- }
-
- close(out);
-
- exit(0);
-}
-
diff --git a/libsparse/simg2img.cpp b/libsparse/simg2img.cpp
new file mode 100644
index 0000000..8ba5f69
--- /dev/null
+++ b/libsparse/simg2img.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2010 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 <sparse/sparse.h>
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+void usage() {
+ fprintf(stderr, "Usage: simg2img <sparse_image_files> <raw_image_file>\n");
+}
+
+int main(int argc, char* argv[]) {
+ int in;
+ int out;
+ int i;
+ struct sparse_file* s;
+
+ if (argc < 3) {
+ usage();
+ exit(-1);
+ }
+
+ out = open(argv[argc - 1], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
+ if (out < 0) {
+ fprintf(stderr, "Cannot open output file %s\n", argv[argc - 1]);
+ exit(-1);
+ }
+
+ for (i = 1; i < argc - 1; i++) {
+ if (strcmp(argv[i], "-") == 0) {
+ in = STDIN_FILENO;
+ } else {
+ in = open(argv[i], O_RDONLY | O_BINARY);
+ if (in < 0) {
+ fprintf(stderr, "Cannot open input file %s\n", argv[i]);
+ exit(-1);
+ }
+ }
+
+ s = sparse_file_import(in, true, false);
+ if (!s) {
+ fprintf(stderr, "Failed to read sparse file\n");
+ exit(-1);
+ }
+
+ if (lseek(out, 0, SEEK_SET) == -1) {
+ perror("lseek failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if (sparse_file_write(s, out, false, false, false) < 0) {
+ fprintf(stderr, "Cannot write output file\n");
+ exit(-1);
+ }
+ sparse_file_destroy(s);
+ close(in);
+ }
+
+ close(out);
+
+ exit(0);
+}
diff --git a/libsparse/simg2simg.c b/libsparse/simg2simg.c
deleted file mode 100644
index 5f9ccf6..0000000
--- a/libsparse/simg2simg.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2012 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 _FILE_OFFSET_BITS 64
-#define _LARGEFILE64_SOURCE 1
-#define _GNU_SOURCE
-
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <sparse/sparse.h>
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-void usage()
-{
- fprintf(stderr, "Usage: simg2simg <sparse image file> <sparse_image_file> <max_size>\n");
-}
-
-int main(int argc, char *argv[])
-{
- int in;
- int out;
- int i;
- int ret;
- struct sparse_file *s;
- int64_t max_size;
- struct sparse_file **out_s;
- int files;
- char filename[4096];
-
- if (argc != 4) {
- usage();
- exit(-1);
- }
-
- max_size = atoll(argv[3]);
-
- in = open(argv[1], O_RDONLY | O_BINARY);
- if (in < 0) {
- fprintf(stderr, "Cannot open input file %s\n", argv[1]);
- exit(-1);
- }
-
- s = sparse_file_import(in, true, false);
- if (!s) {
- fprintf(stderr, "Failed to import sparse file\n");
- exit(-1);
- }
-
- files = sparse_file_resparse(s, max_size, NULL, 0);
- if (files < 0) {
- fprintf(stderr, "Failed to resparse\n");
- exit(-1);
- }
-
- out_s = calloc(sizeof(struct sparse_file *), files);
- if (!out_s) {
- fprintf(stderr, "Failed to allocate sparse file array\n");
- exit(-1);
- }
-
- files = sparse_file_resparse(s, max_size, out_s, files);
- if (files < 0) {
- fprintf(stderr, "Failed to resparse\n");
- exit(-1);
- }
-
- for (i = 0; i < files; i++) {
- ret = snprintf(filename, sizeof(filename), "%s.%d", argv[2], i);
- if (ret >= (int)sizeof(filename)) {
- fprintf(stderr, "Filename too long\n");
- exit(-1);
- }
-
- out = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
- if (out < 0) {
- fprintf(stderr, "Cannot open output file %s\n", argv[2]);
- exit(-1);
- }
-
- ret = sparse_file_write(out_s[i], out, false, true, false);
- if (ret) {
- fprintf(stderr, "Failed to write sparse file\n");
- exit(-1);
- }
- close(out);
- }
-
- close(in);
-
- exit(0);
-}
diff --git a/libsparse/simg2simg.cpp b/libsparse/simg2simg.cpp
new file mode 100644
index 0000000..7e65701
--- /dev/null
+++ b/libsparse/simg2simg.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2012 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 _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <sparse/sparse.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+void usage() {
+ fprintf(stderr, "Usage: simg2simg <sparse image file> <sparse_image_file> <max_size>\n");
+}
+
+int main(int argc, char* argv[]) {
+ int in;
+ int out;
+ int i;
+ int ret;
+ struct sparse_file* s;
+ int64_t max_size;
+ struct sparse_file** out_s;
+ int files;
+ char filename[4096];
+
+ if (argc != 4) {
+ usage();
+ exit(-1);
+ }
+
+ max_size = atoll(argv[3]);
+
+ in = open(argv[1], O_RDONLY | O_BINARY);
+ if (in < 0) {
+ fprintf(stderr, "Cannot open input file %s\n", argv[1]);
+ exit(-1);
+ }
+
+ s = sparse_file_import(in, true, false);
+ if (!s) {
+ fprintf(stderr, "Failed to import sparse file\n");
+ exit(-1);
+ }
+
+ files = sparse_file_resparse(s, max_size, NULL, 0);
+ if (files < 0) {
+ fprintf(stderr, "Failed to resparse\n");
+ exit(-1);
+ }
+
+ out_s = calloc(sizeof(struct sparse_file*), files);
+ if (!out_s) {
+ fprintf(stderr, "Failed to allocate sparse file array\n");
+ exit(-1);
+ }
+
+ files = sparse_file_resparse(s, max_size, out_s, files);
+ if (files < 0) {
+ fprintf(stderr, "Failed to resparse\n");
+ exit(-1);
+ }
+
+ for (i = 0; i < files; i++) {
+ ret = snprintf(filename, sizeof(filename), "%s.%d", argv[2], i);
+ if (ret >= (int)sizeof(filename)) {
+ fprintf(stderr, "Filename too long\n");
+ exit(-1);
+ }
+
+ out = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
+ if (out < 0) {
+ fprintf(stderr, "Cannot open output file %s\n", argv[2]);
+ exit(-1);
+ }
+
+ ret = sparse_file_write(out_s[i], out, false, true, false);
+ if (ret) {
+ fprintf(stderr, "Failed to write sparse file\n");
+ exit(-1);
+ }
+ close(out);
+ }
+
+ close(in);
+
+ exit(0);
+}
diff --git a/libsparse/sparse.c b/libsparse/sparse.c
deleted file mode 100644
index 466435f..0000000
--- a/libsparse/sparse.c
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
- * Copyright (C) 2012 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 <assert.h>
-#include <stdlib.h>
-
-#include <sparse/sparse.h>
-
-#include "defs.h"
-#include "sparse_file.h"
-
-#include "output_file.h"
-#include "backed_block.h"
-#include "sparse_defs.h"
-#include "sparse_format.h"
-
-struct sparse_file *sparse_file_new(unsigned int block_size, int64_t len)
-{
- struct sparse_file *s = calloc(sizeof(struct sparse_file), 1);
- if (!s) {
- return NULL;
- }
-
- s->backed_block_list = backed_block_list_new(block_size);
- if (!s->backed_block_list) {
- free(s);
- return NULL;
- }
-
- s->block_size = block_size;
- s->len = len;
-
- return s;
-}
-
-void sparse_file_destroy(struct sparse_file *s)
-{
- backed_block_list_destroy(s->backed_block_list);
- free(s);
-}
-
-int sparse_file_add_data(struct sparse_file *s,
- void *data, unsigned int len, unsigned int block)
-{
- return backed_block_add_data(s->backed_block_list, data, len, block);
-}
-
-int sparse_file_add_fill(struct sparse_file *s,
- uint32_t fill_val, unsigned int len, unsigned int block)
-{
- return backed_block_add_fill(s->backed_block_list, fill_val, len, block);
-}
-
-int sparse_file_add_file(struct sparse_file *s,
- const char *filename, int64_t file_offset, unsigned int len,
- unsigned int block)
-{
- return backed_block_add_file(s->backed_block_list, filename, file_offset,
- len, block);
-}
-
-int sparse_file_add_fd(struct sparse_file *s,
- int fd, int64_t file_offset, unsigned int len, unsigned int block)
-{
- return backed_block_add_fd(s->backed_block_list, fd, file_offset,
- len, block);
-}
-unsigned int sparse_count_chunks(struct sparse_file *s)
-{
- struct backed_block *bb;
- unsigned int last_block = 0;
- unsigned int chunks = 0;
-
- for (bb = backed_block_iter_new(s->backed_block_list); bb;
- bb = backed_block_iter_next(bb)) {
- if (backed_block_block(bb) > last_block) {
- /* If there is a gap between chunks, add a skip chunk */
- chunks++;
- }
- chunks++;
- last_block = backed_block_block(bb) +
- DIV_ROUND_UP(backed_block_len(bb), s->block_size);
- }
- if (last_block < DIV_ROUND_UP(s->len, s->block_size)) {
- chunks++;
- }
-
- return chunks;
-}
-
-static int sparse_file_write_block(struct output_file *out,
- struct backed_block *bb)
-{
- int ret = -EINVAL;
-
- switch (backed_block_type(bb)) {
- case BACKED_BLOCK_DATA:
- ret = write_data_chunk(out, backed_block_len(bb), backed_block_data(bb));
- break;
- case BACKED_BLOCK_FILE:
- ret = write_file_chunk(out, backed_block_len(bb),
- backed_block_filename(bb),
- backed_block_file_offset(bb));
- break;
- case BACKED_BLOCK_FD:
- ret = write_fd_chunk(out, backed_block_len(bb),
- backed_block_fd(bb),
- backed_block_file_offset(bb));
- break;
- case BACKED_BLOCK_FILL:
- ret = write_fill_chunk(out, backed_block_len(bb),
- backed_block_fill_val(bb));
- break;
- }
-
- return ret;
-}
-
-static int write_all_blocks(struct sparse_file *s, struct output_file *out)
-{
- struct backed_block *bb;
- unsigned int last_block = 0;
- int64_t pad;
- int ret = 0;
-
- for (bb = backed_block_iter_new(s->backed_block_list); bb;
- bb = backed_block_iter_next(bb)) {
- if (backed_block_block(bb) > last_block) {
- unsigned int blocks = backed_block_block(bb) - last_block;
- write_skip_chunk(out, (int64_t)blocks * s->block_size);
- }
- ret = sparse_file_write_block(out, bb);
- if (ret)
- return ret;
- last_block = backed_block_block(bb) +
- DIV_ROUND_UP(backed_block_len(bb), s->block_size);
- }
-
- pad = s->len - (int64_t)last_block * s->block_size;
- assert(pad >= 0);
- if (pad > 0) {
- write_skip_chunk(out, pad);
- }
-
- return 0;
-}
-
-int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse,
- bool crc)
-{
- int ret;
- int chunks;
- struct output_file *out;
-
- chunks = sparse_count_chunks(s);
- out = output_file_open_fd(fd, s->block_size, s->len, gz, sparse, chunks, crc);
-
- if (!out)
- return -ENOMEM;
-
- ret = write_all_blocks(s, out);
-
- output_file_close(out);
-
- return ret;
-}
-
-int sparse_file_callback(struct sparse_file *s, bool sparse, bool crc,
- int (*write)(void *priv, const void *data, size_t len), void *priv)
-{
- int ret;
- int chunks;
- struct output_file *out;
-
- chunks = sparse_count_chunks(s);
- out = output_file_open_callback(write, priv, s->block_size, s->len, false,
- sparse, chunks, crc);
-
- if (!out)
- return -ENOMEM;
-
- ret = write_all_blocks(s, out);
-
- output_file_close(out);
-
- return ret;
-}
-
-struct chunk_data {
- void *priv;
- unsigned int block;
- unsigned int nr_blocks;
- int (*write)(void *priv, const void *data, size_t len,
- unsigned int block, unsigned int nr_blocks);
-};
-
-static int foreach_chunk_write(void *priv, const void *data, size_t len)
-{
- struct chunk_data *chk = priv;
-
- return chk->write(chk->priv, data, len, chk->block, chk->nr_blocks);
-}
-
-int sparse_file_foreach_chunk(struct sparse_file *s, bool sparse, bool crc,
- int (*write)(void *priv, const void *data, size_t len, unsigned int block,
- unsigned int nr_blocks),
- void *priv)
-{
- int ret;
- int chunks;
- struct chunk_data chk;
- struct output_file *out;
- struct backed_block *bb;
-
- chk.priv = priv;
- chk.write = write;
- chk.block = chk.nr_blocks = 0;
- chunks = sparse_count_chunks(s);
- out = output_file_open_callback(foreach_chunk_write, &chk,
- s->block_size, s->len, false, sparse,
- chunks, crc);
-
- if (!out)
- return -ENOMEM;
-
- for (bb = backed_block_iter_new(s->backed_block_list); bb;
- bb = backed_block_iter_next(bb)) {
- chk.block = backed_block_block(bb);
- chk.nr_blocks = (backed_block_len(bb) - 1) / s->block_size + 1;
- ret = sparse_file_write_block(out, bb);
- if (ret)
- return ret;
- }
-
- output_file_close(out);
-
- return ret;
-}
-
-static int out_counter_write(void *priv, const void *data __unused, size_t len)
-{
- int64_t *count = priv;
- *count += len;
- return 0;
-}
-
-int64_t sparse_file_len(struct sparse_file *s, bool sparse, bool crc)
-{
- int ret;
- int chunks = sparse_count_chunks(s);
- int64_t count = 0;
- struct output_file *out;
-
- out = output_file_open_callback(out_counter_write, &count,
- s->block_size, s->len, false, sparse, chunks, crc);
- if (!out) {
- return -1;
- }
-
- ret = write_all_blocks(s, out);
-
- output_file_close(out);
-
- if (ret < 0) {
- return -1;
- }
-
- return count;
-}
-
-unsigned int sparse_file_block_size(struct sparse_file *s)
-{
- return s->block_size;
-}
-
-static struct backed_block *move_chunks_up_to_len(struct sparse_file *from,
- struct sparse_file *to, unsigned int len)
-{
- int64_t count = 0;
- struct output_file *out_counter;
- struct backed_block *last_bb = NULL;
- struct backed_block *bb;
- struct backed_block *start;
- unsigned int last_block = 0;
- int64_t file_len = 0;
- int ret;
-
- /*
- * overhead is sparse file header, the potential end skip
- * chunk and crc chunk.
- */
- int overhead = sizeof(sparse_header_t) + 2 * sizeof(chunk_header_t) +
- sizeof(uint32_t);
- len -= overhead;
-
- start = backed_block_iter_new(from->backed_block_list);
- out_counter = output_file_open_callback(out_counter_write, &count,
- to->block_size, to->len, false, true, 0, false);
- if (!out_counter) {
- return NULL;
- }
-
- for (bb = start; bb; bb = backed_block_iter_next(bb)) {
- count = 0;
- if (backed_block_block(bb) > last_block)
- count += sizeof(chunk_header_t);
- last_block = backed_block_block(bb) +
- DIV_ROUND_UP(backed_block_len(bb), to->block_size);
-
- /* will call out_counter_write to update count */
- ret = sparse_file_write_block(out_counter, bb);
- if (ret) {
- bb = NULL;
- goto out;
- }
- if (file_len + count > len) {
- /*
- * If the remaining available size is more than 1/8th of the
- * requested size, split the chunk. Results in sparse files that
- * are at least 7/8ths of the requested size
- */
- file_len += sizeof(chunk_header_t);
- if (!last_bb || (len - file_len > (len / 8))) {
- backed_block_split(from->backed_block_list, bb, len - file_len);
- last_bb = bb;
- }
- goto move;
- }
- file_len += count;
- last_bb = bb;
- }
-
-move:
- backed_block_list_move(from->backed_block_list,
- to->backed_block_list, start, last_bb);
-
-out:
- output_file_close(out_counter);
-
- return bb;
-}
-
-int sparse_file_resparse(struct sparse_file *in_s, unsigned int max_len,
- struct sparse_file **out_s, int out_s_count)
-{
- struct backed_block *bb;
- struct sparse_file *s;
- struct sparse_file *tmp;
- int c = 0;
-
- tmp = sparse_file_new(in_s->block_size, in_s->len);
- if (!tmp) {
- return -ENOMEM;
- }
-
- do {
- s = sparse_file_new(in_s->block_size, in_s->len);
-
- bb = move_chunks_up_to_len(in_s, s, max_len);
-
- if (c < out_s_count) {
- out_s[c] = s;
- } else {
- backed_block_list_move(s->backed_block_list, tmp->backed_block_list,
- NULL, NULL);
- sparse_file_destroy(s);
- }
- c++;
- } while (bb);
-
- backed_block_list_move(tmp->backed_block_list, in_s->backed_block_list,
- NULL, NULL);
-
- sparse_file_destroy(tmp);
-
- return c;
-}
-
-void sparse_file_verbose(struct sparse_file *s)
-{
- s->verbose = true;
-}
diff --git a/libsparse/sparse.cpp b/libsparse/sparse.cpp
new file mode 100644
index 0000000..6ff97b6
--- /dev/null
+++ b/libsparse/sparse.cpp
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2012 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 <assert.h>
+#include <stdlib.h>
+
+#include <sparse/sparse.h>
+
+#include "defs.h"
+#include "sparse_file.h"
+
+#include "backed_block.h"
+#include "output_file.h"
+#include "sparse_defs.h"
+#include "sparse_format.h"
+
+struct sparse_file* sparse_file_new(unsigned int block_size, int64_t len) {
+ struct sparse_file* s = reinterpret_cast<sparse_file*>(calloc(sizeof(struct sparse_file), 1));
+ if (!s) {
+ return NULL;
+ }
+
+ s->backed_block_list = backed_block_list_new(block_size);
+ if (!s->backed_block_list) {
+ free(s);
+ return NULL;
+ }
+
+ s->block_size = block_size;
+ s->len = len;
+
+ return s;
+}
+
+void sparse_file_destroy(struct sparse_file* s) {
+ backed_block_list_destroy(s->backed_block_list);
+ free(s);
+}
+
+int sparse_file_add_data(struct sparse_file* s, void* data, unsigned int len, unsigned int block) {
+ return backed_block_add_data(s->backed_block_list, data, len, block);
+}
+
+int sparse_file_add_fill(struct sparse_file* s, uint32_t fill_val, unsigned int len,
+ unsigned int block) {
+ return backed_block_add_fill(s->backed_block_list, fill_val, len, block);
+}
+
+int sparse_file_add_file(struct sparse_file* s, const char* filename, int64_t file_offset,
+ unsigned int len, unsigned int block) {
+ return backed_block_add_file(s->backed_block_list, filename, file_offset, len, block);
+}
+
+int sparse_file_add_fd(struct sparse_file* s, int fd, int64_t file_offset, unsigned int len,
+ unsigned int block) {
+ return backed_block_add_fd(s->backed_block_list, fd, file_offset, len, block);
+}
+unsigned int sparse_count_chunks(struct sparse_file* s) {
+ struct backed_block* bb;
+ unsigned int last_block = 0;
+ unsigned int chunks = 0;
+
+ for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) {
+ if (backed_block_block(bb) > last_block) {
+ /* If there is a gap between chunks, add a skip chunk */
+ chunks++;
+ }
+ chunks++;
+ last_block = backed_block_block(bb) + DIV_ROUND_UP(backed_block_len(bb), s->block_size);
+ }
+ if (last_block < DIV_ROUND_UP(s->len, s->block_size)) {
+ chunks++;
+ }
+
+ return chunks;
+}
+
+static int sparse_file_write_block(struct output_file* out, struct backed_block* bb) {
+ int ret = -EINVAL;
+
+ switch (backed_block_type(bb)) {
+ case BACKED_BLOCK_DATA:
+ ret = write_data_chunk(out, backed_block_len(bb), backed_block_data(bb));
+ break;
+ case BACKED_BLOCK_FILE:
+ ret = write_file_chunk(out, backed_block_len(bb), backed_block_filename(bb),
+ backed_block_file_offset(bb));
+ break;
+ case BACKED_BLOCK_FD:
+ ret = write_fd_chunk(out, backed_block_len(bb), backed_block_fd(bb),
+ backed_block_file_offset(bb));
+ break;
+ case BACKED_BLOCK_FILL:
+ ret = write_fill_chunk(out, backed_block_len(bb), backed_block_fill_val(bb));
+ break;
+ }
+
+ return ret;
+}
+
+static int write_all_blocks(struct sparse_file* s, struct output_file* out) {
+ struct backed_block* bb;
+ unsigned int last_block = 0;
+ int64_t pad;
+ int ret = 0;
+
+ for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) {
+ if (backed_block_block(bb) > last_block) {
+ unsigned int blocks = backed_block_block(bb) - last_block;
+ write_skip_chunk(out, (int64_t)blocks * s->block_size);
+ }
+ ret = sparse_file_write_block(out, bb);
+ if (ret) return ret;
+ last_block = backed_block_block(bb) + DIV_ROUND_UP(backed_block_len(bb), s->block_size);
+ }
+
+ pad = s->len - (int64_t)last_block * s->block_size;
+ assert(pad >= 0);
+ if (pad > 0) {
+ write_skip_chunk(out, pad);
+ }
+
+ return 0;
+}
+
+int sparse_file_write(struct sparse_file* s, int fd, bool gz, bool sparse, bool crc) {
+ int ret;
+ int chunks;
+ struct output_file* out;
+
+ chunks = sparse_count_chunks(s);
+ out = output_file_open_fd(fd, s->block_size, s->len, gz, sparse, chunks, crc);
+
+ if (!out) return -ENOMEM;
+
+ ret = write_all_blocks(s, out);
+
+ output_file_close(out);
+
+ return ret;
+}
+
+int sparse_file_callback(struct sparse_file* s, bool sparse, bool crc,
+ int (*write)(void* priv, const void* data, size_t len), void* priv) {
+ int ret;
+ int chunks;
+ struct output_file* out;
+
+ chunks = sparse_count_chunks(s);
+ out = output_file_open_callback(write, priv, s->block_size, s->len, false, sparse, chunks, crc);
+
+ if (!out) return -ENOMEM;
+
+ ret = write_all_blocks(s, out);
+
+ output_file_close(out);
+
+ return ret;
+}
+
+struct chunk_data {
+ void* priv;
+ unsigned int block;
+ unsigned int nr_blocks;
+ int (*write)(void* priv, const void* data, size_t len, unsigned int block, unsigned int nr_blocks);
+};
+
+static int foreach_chunk_write(void* priv, const void* data, size_t len) {
+ struct chunk_data* chk = reinterpret_cast<chunk_data*>(priv);
+
+ return chk->write(chk->priv, data, len, chk->block, chk->nr_blocks);
+}
+
+int sparse_file_foreach_chunk(struct sparse_file* s, bool sparse, bool crc,
+ int (*write)(void* priv, const void* data, size_t len,
+ unsigned int block, unsigned int nr_blocks),
+ void* priv) {
+ int ret;
+ int chunks;
+ struct chunk_data chk;
+ struct output_file* out;
+ struct backed_block* bb;
+
+ chk.priv = priv;
+ chk.write = write;
+ chk.block = chk.nr_blocks = 0;
+ chunks = sparse_count_chunks(s);
+ out = output_file_open_callback(foreach_chunk_write, &chk, s->block_size, s->len, false, sparse,
+ chunks, crc);
+
+ if (!out) return -ENOMEM;
+
+ for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) {
+ chk.block = backed_block_block(bb);
+ chk.nr_blocks = (backed_block_len(bb) - 1) / s->block_size + 1;
+ ret = sparse_file_write_block(out, bb);
+ if (ret) return ret;
+ }
+
+ output_file_close(out);
+
+ return ret;
+}
+
+static int out_counter_write(void* priv, const void* data __unused, size_t len) {
+ int64_t* count = reinterpret_cast<int64_t*>(priv);
+ *count += len;
+ return 0;
+}
+
+int64_t sparse_file_len(struct sparse_file* s, bool sparse, bool crc) {
+ int ret;
+ int chunks = sparse_count_chunks(s);
+ int64_t count = 0;
+ struct output_file* out;
+
+ out = output_file_open_callback(out_counter_write, &count, s->block_size, s->len, false, sparse,
+ chunks, crc);
+ if (!out) {
+ return -1;
+ }
+
+ ret = write_all_blocks(s, out);
+
+ output_file_close(out);
+
+ if (ret < 0) {
+ return -1;
+ }
+
+ return count;
+}
+
+unsigned int sparse_file_block_size(struct sparse_file* s) {
+ return s->block_size;
+}
+
+static struct backed_block* move_chunks_up_to_len(struct sparse_file* from, struct sparse_file* to,
+ unsigned int len) {
+ int64_t count = 0;
+ struct output_file* out_counter;
+ struct backed_block* last_bb = NULL;
+ struct backed_block* bb;
+ struct backed_block* start;
+ unsigned int last_block = 0;
+ int64_t file_len = 0;
+ int ret;
+
+ /*
+ * overhead is sparse file header, the potential end skip
+ * chunk and crc chunk.
+ */
+ int overhead = sizeof(sparse_header_t) + 2 * sizeof(chunk_header_t) + sizeof(uint32_t);
+ len -= overhead;
+
+ start = backed_block_iter_new(from->backed_block_list);
+ out_counter = output_file_open_callback(out_counter_write, &count, to->block_size, to->len, false,
+ true, 0, false);
+ if (!out_counter) {
+ return NULL;
+ }
+
+ for (bb = start; bb; bb = backed_block_iter_next(bb)) {
+ count = 0;
+ if (backed_block_block(bb) > last_block) count += sizeof(chunk_header_t);
+ last_block = backed_block_block(bb) + DIV_ROUND_UP(backed_block_len(bb), to->block_size);
+
+ /* will call out_counter_write to update count */
+ ret = sparse_file_write_block(out_counter, bb);
+ if (ret) {
+ bb = NULL;
+ goto out;
+ }
+ if (file_len + count > len) {
+ /*
+ * If the remaining available size is more than 1/8th of the
+ * requested size, split the chunk. Results in sparse files that
+ * are at least 7/8ths of the requested size
+ */
+ file_len += sizeof(chunk_header_t);
+ if (!last_bb || (len - file_len > (len / 8))) {
+ backed_block_split(from->backed_block_list, bb, len - file_len);
+ last_bb = bb;
+ }
+ goto move;
+ }
+ file_len += count;
+ last_bb = bb;
+ }
+
+move:
+ backed_block_list_move(from->backed_block_list, to->backed_block_list, start, last_bb);
+
+out:
+ output_file_close(out_counter);
+
+ return bb;
+}
+
+int sparse_file_resparse(struct sparse_file* in_s, unsigned int max_len, struct sparse_file** out_s,
+ int out_s_count) {
+ struct backed_block* bb;
+ struct sparse_file* s;
+ struct sparse_file* tmp;
+ int c = 0;
+
+ tmp = sparse_file_new(in_s->block_size, in_s->len);
+ if (!tmp) {
+ return -ENOMEM;
+ }
+
+ do {
+ s = sparse_file_new(in_s->block_size, in_s->len);
+
+ bb = move_chunks_up_to_len(in_s, s, max_len);
+
+ if (c < out_s_count) {
+ out_s[c] = s;
+ } else {
+ backed_block_list_move(s->backed_block_list, tmp->backed_block_list, NULL, NULL);
+ sparse_file_destroy(s);
+ }
+ c++;
+ } while (bb);
+
+ backed_block_list_move(tmp->backed_block_list, in_s->backed_block_list, NULL, NULL);
+
+ sparse_file_destroy(tmp);
+
+ return c;
+}
+
+void sparse_file_verbose(struct sparse_file* s) {
+ s->verbose = true;
+}
diff --git a/libsparse/sparse_crc32.c b/libsparse/sparse_crc32.c
deleted file mode 100644
index 38bfe4a..0000000
--- a/libsparse/sparse_crc32.c
+++ /dev/null
@@ -1,111 +0,0 @@
-/*-
- * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
- * code or tables extracted from it, as desired without restriction.
- */
-
-/*
- * First, the polynomial itself and its table of feedback terms. The
- * polynomial is
- * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
- *
- * Note that we take it "backwards" and put the highest-order term in
- * the lowest-order bit. The X^32 term is "implied"; the LSB is the
- * X^31 term, etc. The X^0 term (usually shown as "+1") results in
- * the MSB being 1
- *
- * Note that the usual hardware shift register implementation, which
- * is what we're using (we're merely optimizing it by doing eight-bit
- * chunks at a time) shifts bits into the lowest-order term. In our
- * implementation, that means shifting towards the right. Why do we
- * do it this way? Because the calculated CRC must be transmitted in
- * order from highest-order term to lowest-order term. UARTs transmit
- * characters in order from LSB to MSB. By storing the CRC this way
- * we hand it to the UART in the order low-byte to high-byte; the UART
- * sends each low-bit to hight-bit; and the result is transmission bit
- * by bit from highest- to lowest-order term without requiring any bit
- * shuffling on our part. Reception works similarly
- *
- * The feedback terms table consists of 256, 32-bit entries. Notes
- *
- * The table can be generated at runtime if desired; code to do so
- * is shown later. It might not be obvious, but the feedback
- * terms simply represent the results of eight shift/xor opera
- * tions for all combinations of data and CRC register values
- *
- * The values must be right-shifted by eight bits by the "updcrc
- * logic; the shift must be unsigned (bring in zeroes). On some
- * hardware you could probably optimize the shift in assembler by
- * using byte-swap instructions
- * polynomial $edb88320
- *
- *
- * CRC32 code derived from work by Gary S. Brown.
- */
-
-/* Code taken from FreeBSD 8 */
-#include <stdint.h>
-
-static uint32_t crc32_tab[] = {
- 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
- 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
- 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
- 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
- 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
- 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
- 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
- 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
- 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
- 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
- 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
- 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
- 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
- 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
- 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
- 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
- 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
- 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
- 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
- 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
- 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
- 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
- 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
- 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
- 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
- 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
- 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
- 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
- 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
- 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
- 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
- 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
- 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
- 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
- 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
- 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
- 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
- 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
- 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
- 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
- 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
- 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
- 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
-};
-
-/*
- * A function that calculates the CRC-32 based on the table above is
- * given below for documentation purposes. An equivalent implementation
- * of this function that's actually used in the kernel can be found
- * in sys/libkern.h, where it can be inlined.
- */
-
-uint32_t sparse_crc32(uint32_t crc_in, const void *buf, int size)
-{
- const uint8_t *p = buf;
- uint32_t crc;
-
- crc = crc_in ^ ~0U;
- while (size--)
- crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
- return crc ^ ~0U;
-}
-
diff --git a/libsparse/sparse_crc32.cpp b/libsparse/sparse_crc32.cpp
new file mode 100644
index 0000000..267322c
--- /dev/null
+++ b/libsparse/sparse_crc32.cpp
@@ -0,0 +1,97 @@
+/*-
+ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
+ * code or tables extracted from it, as desired without restriction.
+ */
+
+/*
+ * First, the polynomial itself and its table of feedback terms. The
+ * polynomial is
+ * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ * Note that we take it "backwards" and put the highest-order term in
+ * the lowest-order bit. The X^32 term is "implied"; the LSB is the
+ * X^31 term, etc. The X^0 term (usually shown as "+1") results in
+ * the MSB being 1
+ *
+ * Note that the usual hardware shift register implementation, which
+ * is what we're using (we're merely optimizing it by doing eight-bit
+ * chunks at a time) shifts bits into the lowest-order term. In our
+ * implementation, that means shifting towards the right. Why do we
+ * do it this way? Because the calculated CRC must be transmitted in
+ * order from highest-order term to lowest-order term. UARTs transmit
+ * characters in order from LSB to MSB. By storing the CRC this way
+ * we hand it to the UART in the order low-byte to high-byte; the UART
+ * sends each low-bit to hight-bit; and the result is transmission bit
+ * by bit from highest- to lowest-order term without requiring any bit
+ * shuffling on our part. Reception works similarly
+ *
+ * The feedback terms table consists of 256, 32-bit entries. Notes
+ *
+ * The table can be generated at runtime if desired; code to do so
+ * is shown later. It might not be obvious, but the feedback
+ * terms simply represent the results of eight shift/xor opera
+ * tions for all combinations of data and CRC register values
+ *
+ * The values must be right-shifted by eight bits by the "updcrc
+ * logic; the shift must be unsigned (bring in zeroes). On some
+ * hardware you could probably optimize the shift in assembler by
+ * using byte-swap instructions
+ * polynomial $edb88320
+ *
+ *
+ * CRC32 code derived from work by Gary S. Brown.
+ */
+
+/* Code taken from FreeBSD 8 */
+#include <stdint.h>
+#include <stdio.h>
+
+static uint32_t crc32_tab[] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+ 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+ 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+ 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+ 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+ 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+ 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+ 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+ 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+ 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+ 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+ 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+ 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+ 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+ 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+ 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+ 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+ 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+ 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+ 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+ 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+ 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d};
+
+/*
+ * A function that calculates the CRC-32 based on the table above is
+ * given below for documentation purposes. An equivalent implementation
+ * of this function that's actually used in the kernel can be found
+ * in sys/libkern.h, where it can be inlined.
+ */
+
+uint32_t sparse_crc32(uint32_t crc_in, const void* buf, size_t size) {
+ const uint8_t* p = reinterpret_cast<const uint8_t*>(buf);
+ uint32_t crc;
+
+ crc = crc_in ^ ~0U;
+ while (size--) crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
+ return crc ^ ~0U;
+}
diff --git a/libsparse/sparse_crc32.h b/libsparse/sparse_crc32.h
index 50cd9e9..2702c4f 100644
--- a/libsparse/sparse_crc32.h
+++ b/libsparse/sparse_crc32.h
@@ -19,14 +19,6 @@
#include <stdint.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-uint32_t sparse_crc32(uint32_t crc, const void *buf, size_t size);
-
-#ifdef __cplusplus
-}
-#endif
+uint32_t sparse_crc32(uint32_t crc, const void* buf, size_t size);
#endif
diff --git a/libsparse/sparse_defs.h b/libsparse/sparse_defs.h
index b99cfd5..9137805 100644
--- a/libsparse/sparse_defs.h
+++ b/libsparse/sparse_defs.h
@@ -39,11 +39,14 @@
typedef unsigned short int u16;
typedef unsigned char u8;
-#define DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y))
-#define ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y)))
+#define DIV_ROUND_UP(x, y) (((x) + (y)-1) / (y))
+#define ALIGN(x, y) ((y)*DIV_ROUND_UP((x), (y)))
#define ALIGN_DOWN(x, y) ((y) * ((x) / (y)))
-#define error(fmt, args...) do { fprintf(stderr, "error: %s: " fmt "\n", __func__, ## args); } while (0)
+#define error(fmt, args...) \
+ do { \
+ fprintf(stderr, "error: %s: " fmt "\n", __func__, ##args); \
+ } while (0)
#define error_errno(s, args...) error(s ": %s", ##args, strerror(errno))
#endif
diff --git a/libsparse/sparse_err.c b/libsparse/sparse_err.cpp
similarity index 73%
rename from libsparse/sparse_err.c
rename to libsparse/sparse_err.cpp
index 0f392ad..6886d31 100644
--- a/libsparse/sparse_err.c
+++ b/libsparse/sparse_err.cpp
@@ -20,14 +20,13 @@
#include <stdio.h>
#include <unistd.h>
-void sparse_default_print(const char *fmt, ...)
-{
- va_list argp;
+void sparse_default_print(const char* fmt, ...) {
+ va_list argp;
- va_start(argp, fmt);
- vfprintf(stderr, fmt, argp);
- va_end(argp);
+ va_start(argp, fmt);
+ vfprintf(stderr, fmt, argp);
+ va_end(argp);
}
-void (*sparse_print_error)(const char *fmt, ...) = sparse_default_print;
-void (*sparse_print_verbose)(const char *fmt, ...) = sparse_default_print;
+void (*sparse_print_error)(const char* fmt, ...) = sparse_default_print;
+void (*sparse_print_verbose)(const char* fmt, ...) = sparse_default_print;
diff --git a/libsparse/sparse_file.h b/libsparse/sparse_file.h
index 763f43f..e565f63 100644
--- a/libsparse/sparse_file.h
+++ b/libsparse/sparse_file.h
@@ -24,12 +24,12 @@
#include <sparse/sparse.h>
struct sparse_file {
- unsigned int block_size;
- int64_t len;
- bool verbose;
+ unsigned int block_size;
+ int64_t len;
+ bool verbose;
- struct backed_block_list *backed_block_list;
- struct output_file *out;
+ struct backed_block_list* backed_block_list;
+ struct output_file* out;
};
#ifdef __cplusplus
diff --git a/libsparse/sparse_format.h b/libsparse/sparse_format.h
index 779e038..a8a721e 100644
--- a/libsparse/sparse_format.h
+++ b/libsparse/sparse_format.h
@@ -23,31 +23,31 @@
#endif
typedef struct sparse_header {
- __le32 magic; /* 0xed26ff3a */
- __le16 major_version; /* (0x1) - reject images with higher major versions */
- __le16 minor_version; /* (0x0) - allow images with higer minor versions */
- __le16 file_hdr_sz; /* 28 bytes for first revision of the file format */
- __le16 chunk_hdr_sz; /* 12 bytes for first revision of the file format */
- __le32 blk_sz; /* block size in bytes, must be a multiple of 4 (4096) */
- __le32 total_blks; /* total blocks in the non-sparse output image */
- __le32 total_chunks; /* total chunks in the sparse input image */
- __le32 image_checksum; /* CRC32 checksum of the original data, counting "don't care" */
- /* as 0. Standard 802.3 polynomial, use a Public Domain */
- /* table implementation */
+ __le32 magic; /* 0xed26ff3a */
+ __le16 major_version; /* (0x1) - reject images with higher major versions */
+ __le16 minor_version; /* (0x0) - allow images with higer minor versions */
+ __le16 file_hdr_sz; /* 28 bytes for first revision of the file format */
+ __le16 chunk_hdr_sz; /* 12 bytes for first revision of the file format */
+ __le32 blk_sz; /* block size in bytes, must be a multiple of 4 (4096) */
+ __le32 total_blks; /* total blocks in the non-sparse output image */
+ __le32 total_chunks; /* total chunks in the sparse input image */
+ __le32 image_checksum; /* CRC32 checksum of the original data, counting "don't care" */
+ /* as 0. Standard 802.3 polynomial, use a Public Domain */
+ /* table implementation */
} sparse_header_t;
-#define SPARSE_HEADER_MAGIC 0xed26ff3a
+#define SPARSE_HEADER_MAGIC 0xed26ff3a
-#define CHUNK_TYPE_RAW 0xCAC1
-#define CHUNK_TYPE_FILL 0xCAC2
-#define CHUNK_TYPE_DONT_CARE 0xCAC3
-#define CHUNK_TYPE_CRC32 0xCAC4
+#define CHUNK_TYPE_RAW 0xCAC1
+#define CHUNK_TYPE_FILL 0xCAC2
+#define CHUNK_TYPE_DONT_CARE 0xCAC3
+#define CHUNK_TYPE_CRC32 0xCAC4
typedef struct chunk_header {
- __le16 chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */
- __le16 reserved1;
- __le32 chunk_sz; /* in blocks in output image */
- __le32 total_sz; /* in bytes of chunk input file including chunk header and data */
+ __le16 chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */
+ __le16 reserved1;
+ __le32 chunk_sz; /* in blocks in output image */
+ __le32 total_sz; /* in bytes of chunk input file including chunk header and data */
} chunk_header_t;
/* Following a Raw or Fill or CRC32 chunk is data.
diff --git a/libsparse/sparse_read.cpp b/libsparse/sparse_read.cpp
index 4379635..56e2c9a 100644
--- a/libsparse/sparse_read.cpp
+++ b/libsparse/sparse_read.cpp
@@ -17,16 +17,16 @@
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE64_SOURCE 1
-#include <algorithm>
-#include <inttypes.h>
#include <fcntl.h>
+#include <inttypes.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <string>
#include <unistd.h>
+#include <algorithm>
+#include <string>
#include <sparse/sparse.h>
@@ -37,447 +37,541 @@
#include "sparse_file.h"
#include "sparse_format.h"
-
#if defined(__APPLE__) && defined(__MACH__)
#define lseek64 lseek
#define off64_t off_t
#endif
#define SPARSE_HEADER_MAJOR_VER 1
-#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
+#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
static constexpr int64_t COPY_BUF_SIZE = 1024 * 1024;
-static char *copybuf;
+static char* copybuf;
-static std::string ErrorString(int err)
-{
- if (err == -EOVERFLOW) return "EOF while reading file";
- if (err == -EINVAL) return "Invalid sparse file format";
- if (err == -ENOMEM) return "Failed allocation while reading file";
- return android::base::StringPrintf("Unknown error %d", err);
+static std::string ErrorString(int err) {
+ if (err == -EOVERFLOW) return "EOF while reading file";
+ if (err == -EINVAL) return "Invalid sparse file format";
+ if (err == -ENOMEM) return "Failed allocation while reading file";
+ return android::base::StringPrintf("Unknown error %d", err);
}
-static void verbose_error(bool verbose, int err, const char *fmt, ...)
-{
- if (!verbose) return;
+class SparseFileSource {
+ public:
+ /* Seeks the source ahead by the given offset. */
+ virtual void Seek(int64_t offset) = 0;
- std::string msg = ErrorString(err);
- if (fmt) {
- msg += " at ";
- va_list argp;
- va_start(argp, fmt);
- android::base::StringAppendV(&msg, fmt, argp);
- va_end(argp);
- }
- sparse_print_verbose("%s\n", msg.c_str());
+ /* Return the current offset. */
+ virtual int64_t GetOffset() = 0;
+
+ /* Set the current offset. Return 0 if successful. */
+ virtual int SetOffset(int64_t offset) = 0;
+
+ /* Adds the given length from the current offset of the source to the file at the given block.
+ * Return 0 if successful. */
+ virtual int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) = 0;
+
+ /* Get data of fixed size from the current offset and seek len bytes. Return 0 if successful. */
+ virtual int ReadValue(void* ptr, int len) = 0;
+
+ /* Find the crc32 of the next len bytes and seek ahead len bytes. Return 0 if successful. */
+ virtual int GetCrc32(uint32_t* crc32, int64_t len) = 0;
+
+ virtual ~SparseFileSource(){};
+};
+
+class SparseFileFdSource : public SparseFileSource {
+ private:
+ int fd;
+
+ public:
+ SparseFileFdSource(int fd) : fd(fd) {}
+ ~SparseFileFdSource() override {}
+
+ void Seek(int64_t off) override { lseek64(fd, off, SEEK_CUR); }
+
+ int64_t GetOffset() override { return lseek64(fd, 0, SEEK_CUR); }
+
+ int SetOffset(int64_t offset) override {
+ return lseek64(fd, offset, SEEK_SET) == offset ? 0 : -errno;
+ }
+
+ int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) override {
+ return sparse_file_add_fd(s, fd, GetOffset(), len, block);
+ }
+
+ int ReadValue(void* ptr, int len) override { return read_all(fd, ptr, len); }
+
+ int GetCrc32(uint32_t* crc32, int64_t len) override {
+ int chunk;
+ int ret;
+ while (len) {
+ chunk = std::min(len, COPY_BUF_SIZE);
+ ret = read_all(fd, copybuf, chunk);
+ if (ret < 0) {
+ return ret;
+ }
+ *crc32 = sparse_crc32(*crc32, copybuf, chunk);
+ len -= chunk;
+ }
+ return 0;
+ }
+};
+
+class SparseFileBufSource : public SparseFileSource {
+ private:
+ char* buf;
+ int64_t offset;
+
+ public:
+ SparseFileBufSource(char* buf) : buf(buf), offset(0) {}
+ ~SparseFileBufSource() override {}
+
+ void Seek(int64_t off) override {
+ buf += off;
+ offset += off;
+ }
+
+ int64_t GetOffset() override { return offset; }
+
+ int SetOffset(int64_t off) override {
+ buf += off - offset;
+ offset = off;
+ return 0;
+ }
+
+ int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) override {
+ return sparse_file_add_data(s, buf, len, block);
+ }
+
+ int ReadValue(void* ptr, int len) override {
+ memcpy(ptr, buf, len);
+ Seek(len);
+ return 0;
+ }
+
+ int GetCrc32(uint32_t* crc32, int64_t len) override {
+ *crc32 = sparse_crc32(*crc32, buf, len);
+ Seek(len);
+ return 0;
+ }
+};
+
+static void verbose_error(bool verbose, int err, const char* fmt, ...) {
+ if (!verbose) return;
+
+ std::string msg = ErrorString(err);
+ if (fmt) {
+ msg += " at ";
+ va_list argp;
+ va_start(argp, fmt);
+ android::base::StringAppendV(&msg, fmt, argp);
+ va_end(argp);
+ }
+ sparse_print_verbose("%s\n", msg.c_str());
}
-static int process_raw_chunk(struct sparse_file *s, unsigned int chunk_size,
- int fd, int64_t offset, unsigned int blocks, unsigned int block,
- uint32_t *crc32)
-{
- int ret;
- int chunk;
- int64_t len = blocks * s->block_size;
+static int process_raw_chunk(struct sparse_file* s, unsigned int chunk_size,
+ SparseFileSource* source, unsigned int blocks, unsigned int block,
+ uint32_t* crc32) {
+ int ret;
+ int64_t len = blocks * s->block_size;
- if (chunk_size % s->block_size != 0) {
- return -EINVAL;
- }
+ if (chunk_size % s->block_size != 0) {
+ return -EINVAL;
+ }
- if (chunk_size / s->block_size != blocks) {
- return -EINVAL;
- }
+ if (chunk_size / s->block_size != blocks) {
+ return -EINVAL;
+ }
- ret = sparse_file_add_fd(s, fd, offset, len, block);
- if (ret < 0) {
- return ret;
- }
+ ret = source->AddToSparseFile(s, len, block);
+ if (ret < 0) {
+ return ret;
+ }
- if (crc32) {
- while (len) {
- chunk = std::min(len, COPY_BUF_SIZE);
- ret = read_all(fd, copybuf, chunk);
- if (ret < 0) {
- return ret;
- }
- *crc32 = sparse_crc32(*crc32, copybuf, chunk);
- len -= chunk;
- }
- } else {
- lseek64(fd, len, SEEK_CUR);
- }
+ if (crc32) {
+ ret = source->GetCrc32(crc32, len);
+ if (ret < 0) {
+ return ret;
+ }
+ } else {
+ source->Seek(len);
+ }
- return 0;
+ return 0;
}
-static int process_fill_chunk(struct sparse_file *s, unsigned int chunk_size,
- int fd, unsigned int blocks, unsigned int block, uint32_t *crc32)
-{
- int ret;
- int chunk;
- int64_t len = (int64_t)blocks * s->block_size;
- uint32_t fill_val;
- uint32_t *fillbuf;
- unsigned int i;
+static int process_fill_chunk(struct sparse_file* s, unsigned int chunk_size,
+ SparseFileSource* source, unsigned int blocks, unsigned int block,
+ uint32_t* crc32) {
+ int ret;
+ int chunk;
+ int64_t len = (int64_t)blocks * s->block_size;
+ uint32_t fill_val;
+ uint32_t* fillbuf;
+ unsigned int i;
- if (chunk_size != sizeof(fill_val)) {
- return -EINVAL;
- }
+ if (chunk_size != sizeof(fill_val)) {
+ return -EINVAL;
+ }
- ret = read_all(fd, &fill_val, sizeof(fill_val));
- if (ret < 0) {
- return ret;
- }
+ ret = source->ReadValue(&fill_val, sizeof(fill_val));
+ if (ret < 0) {
+ return ret;
+ }
- ret = sparse_file_add_fill(s, fill_val, len, block);
- if (ret < 0) {
- return ret;
- }
+ ret = sparse_file_add_fill(s, fill_val, len, block);
+ if (ret < 0) {
+ return ret;
+ }
- if (crc32) {
- /* Fill copy_buf with the fill value */
- fillbuf = (uint32_t *)copybuf;
- for (i = 0; i < (COPY_BUF_SIZE / sizeof(fill_val)); i++) {
- fillbuf[i] = fill_val;
- }
+ if (crc32) {
+ /* Fill copy_buf with the fill value */
+ fillbuf = (uint32_t*)copybuf;
+ for (i = 0; i < (COPY_BUF_SIZE / sizeof(fill_val)); i++) {
+ fillbuf[i] = fill_val;
+ }
- while (len) {
- chunk = std::min(len, COPY_BUF_SIZE);
- *crc32 = sparse_crc32(*crc32, copybuf, chunk);
- len -= chunk;
- }
- }
+ while (len) {
+ chunk = std::min(len, COPY_BUF_SIZE);
+ *crc32 = sparse_crc32(*crc32, copybuf, chunk);
+ len -= chunk;
+ }
+ }
- return 0;
+ return 0;
}
-static int process_skip_chunk(struct sparse_file *s, unsigned int chunk_size,
- int fd __unused, unsigned int blocks,
- unsigned int block __unused, uint32_t *crc32)
-{
- if (chunk_size != 0) {
- return -EINVAL;
- }
+static int process_skip_chunk(struct sparse_file* s, unsigned int chunk_size,
+ SparseFileSource* source __unused, unsigned int blocks,
+ unsigned int block __unused, uint32_t* crc32) {
+ if (chunk_size != 0) {
+ return -EINVAL;
+ }
- if (crc32) {
- int64_t len = (int64_t)blocks * s->block_size;
- memset(copybuf, 0, COPY_BUF_SIZE);
+ if (crc32) {
+ int64_t len = (int64_t)blocks * s->block_size;
+ memset(copybuf, 0, COPY_BUF_SIZE);
- while (len) {
- int chunk = std::min(len, COPY_BUF_SIZE);
- *crc32 = sparse_crc32(*crc32, copybuf, chunk);
- len -= chunk;
- }
- }
+ while (len) {
+ int chunk = std::min(len, COPY_BUF_SIZE);
+ *crc32 = sparse_crc32(*crc32, copybuf, chunk);
+ len -= chunk;
+ }
+ }
- return 0;
+ return 0;
}
-static int process_crc32_chunk(int fd, unsigned int chunk_size, uint32_t *crc32)
-{
- uint32_t file_crc32;
- int ret;
+static int process_crc32_chunk(SparseFileSource* source, unsigned int chunk_size, uint32_t* crc32) {
+ uint32_t file_crc32;
- if (chunk_size != sizeof(file_crc32)) {
- return -EINVAL;
- }
+ if (chunk_size != sizeof(file_crc32)) {
+ return -EINVAL;
+ }
- ret = read_all(fd, &file_crc32, sizeof(file_crc32));
- if (ret < 0) {
- return ret;
- }
+ int ret = source->ReadValue(&file_crc32, sizeof(file_crc32));
+ if (ret < 0) {
+ return ret;
+ }
- if (crc32 != NULL && file_crc32 != *crc32) {
- return -EINVAL;
- }
+ if (crc32 != NULL && file_crc32 != *crc32) {
+ return -EINVAL;
+ }
- return 0;
+ return 0;
}
-static int process_chunk(struct sparse_file *s, int fd, off64_t offset,
- unsigned int chunk_hdr_sz, chunk_header_t *chunk_header,
- unsigned int cur_block, uint32_t *crc_ptr)
-{
- int ret;
- unsigned int chunk_data_size;
+static int process_chunk(struct sparse_file* s, SparseFileSource* source, unsigned int chunk_hdr_sz,
+ chunk_header_t* chunk_header, unsigned int cur_block, uint32_t* crc_ptr) {
+ int ret;
+ unsigned int chunk_data_size;
+ int64_t offset = source->GetOffset();
- chunk_data_size = chunk_header->total_sz - chunk_hdr_sz;
+ chunk_data_size = chunk_header->total_sz - chunk_hdr_sz;
- switch (chunk_header->chunk_type) {
- case CHUNK_TYPE_RAW:
- ret = process_raw_chunk(s, chunk_data_size, fd, offset,
- chunk_header->chunk_sz, cur_block, crc_ptr);
- if (ret < 0) {
- verbose_error(s->verbose, ret, "data block at %" PRId64, offset);
- return ret;
- }
- return chunk_header->chunk_sz;
- case CHUNK_TYPE_FILL:
- ret = process_fill_chunk(s, chunk_data_size, fd,
- chunk_header->chunk_sz, cur_block, crc_ptr);
- if (ret < 0) {
- verbose_error(s->verbose, ret, "fill block at %" PRId64, offset);
- return ret;
- }
- return chunk_header->chunk_sz;
- case CHUNK_TYPE_DONT_CARE:
- ret = process_skip_chunk(s, chunk_data_size, fd,
- chunk_header->chunk_sz, cur_block, crc_ptr);
- if (chunk_data_size != 0) {
- if (ret < 0) {
- verbose_error(s->verbose, ret, "skip block at %" PRId64, offset);
- return ret;
- }
- }
- return chunk_header->chunk_sz;
- case CHUNK_TYPE_CRC32:
- ret = process_crc32_chunk(fd, chunk_data_size, crc_ptr);
- if (ret < 0) {
- verbose_error(s->verbose, -EINVAL, "crc block at %" PRId64,
- offset);
- return ret;
- }
- return 0;
- default:
- verbose_error(s->verbose, -EINVAL, "unknown block %04X at %" PRId64,
- chunk_header->chunk_type, offset);
- }
+ switch (chunk_header->chunk_type) {
+ case CHUNK_TYPE_RAW:
+ ret =
+ process_raw_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block, crc_ptr);
+ if (ret < 0) {
+ verbose_error(s->verbose, ret, "data block at %" PRId64, offset);
+ return ret;
+ }
+ return chunk_header->chunk_sz;
+ case CHUNK_TYPE_FILL:
+ ret = process_fill_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block,
+ crc_ptr);
+ if (ret < 0) {
+ verbose_error(s->verbose, ret, "fill block at %" PRId64, offset);
+ return ret;
+ }
+ return chunk_header->chunk_sz;
+ case CHUNK_TYPE_DONT_CARE:
+ ret = process_skip_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block,
+ crc_ptr);
+ if (chunk_data_size != 0) {
+ if (ret < 0) {
+ verbose_error(s->verbose, ret, "skip block at %" PRId64, offset);
+ return ret;
+ }
+ }
+ return chunk_header->chunk_sz;
+ case CHUNK_TYPE_CRC32:
+ ret = process_crc32_chunk(source, chunk_data_size, crc_ptr);
+ if (ret < 0) {
+ verbose_error(s->verbose, -EINVAL, "crc block at %" PRId64, offset);
+ return ret;
+ }
+ return 0;
+ default:
+ verbose_error(s->verbose, -EINVAL, "unknown block %04X at %" PRId64, chunk_header->chunk_type,
+ offset);
+ }
- return 0;
+ return 0;
}
-static int sparse_file_read_sparse(struct sparse_file *s, int fd, bool crc)
-{
- int ret;
- unsigned int i;
- sparse_header_t sparse_header;
- chunk_header_t chunk_header;
- uint32_t crc32 = 0;
- uint32_t *crc_ptr = 0;
- unsigned int cur_block = 0;
- off64_t offset;
+static int sparse_file_read_sparse(struct sparse_file* s, SparseFileSource* source, bool crc) {
+ int ret;
+ unsigned int i;
+ sparse_header_t sparse_header;
+ chunk_header_t chunk_header;
+ uint32_t crc32 = 0;
+ uint32_t* crc_ptr = 0;
+ unsigned int cur_block = 0;
- if (!copybuf) {
- copybuf = (char *)malloc(COPY_BUF_SIZE);
- }
+ if (!copybuf) {
+ copybuf = (char*)malloc(COPY_BUF_SIZE);
+ }
- if (!copybuf) {
- return -ENOMEM;
- }
+ if (!copybuf) {
+ return -ENOMEM;
+ }
- if (crc) {
- crc_ptr = &crc32;
- }
+ if (crc) {
+ crc_ptr = &crc32;
+ }
- ret = read_all(fd, &sparse_header, sizeof(sparse_header));
- if (ret < 0) {
- return ret;
- }
+ ret = source->ReadValue(&sparse_header, sizeof(sparse_header));
+ if (ret < 0) {
+ return ret;
+ }
- if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
- return -EINVAL;
- }
+ if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
+ return -EINVAL;
+ }
- if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
- return -EINVAL;
- }
+ if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
+ return -EINVAL;
+ }
- if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
- return -EINVAL;
- }
+ if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
+ return -EINVAL;
+ }
- if (sparse_header.chunk_hdr_sz < sizeof(chunk_header)) {
- return -EINVAL;
- }
+ if (sparse_header.chunk_hdr_sz < sizeof(chunk_header)) {
+ return -EINVAL;
+ }
- if (sparse_header.file_hdr_sz > SPARSE_HEADER_LEN) {
- /* Skip the remaining bytes in a header that is longer than
- * we expected.
- */
- lseek64(fd, sparse_header.file_hdr_sz - SPARSE_HEADER_LEN, SEEK_CUR);
- }
+ if (sparse_header.file_hdr_sz > SPARSE_HEADER_LEN) {
+ /* Skip the remaining bytes in a header that is longer than
+ * we expected.
+ */
+ source->Seek(sparse_header.file_hdr_sz - SPARSE_HEADER_LEN);
+ }
- for (i = 0; i < sparse_header.total_chunks; i++) {
- ret = read_all(fd, &chunk_header, sizeof(chunk_header));
- if (ret < 0) {
- return ret;
- }
+ for (i = 0; i < sparse_header.total_chunks; i++) {
+ ret = source->ReadValue(&chunk_header, sizeof(chunk_header));
+ if (ret < 0) {
+ return ret;
+ }
- if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) {
- /* Skip the remaining bytes in a header that is longer than
- * we expected.
- */
- lseek64(fd, sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN, SEEK_CUR);
- }
+ if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) {
+ /* Skip the remaining bytes in a header that is longer than
+ * we expected.
+ */
+ source->Seek(sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN);
+ }
- offset = lseek64(fd, 0, SEEK_CUR);
+ ret = process_chunk(s, source, sparse_header.chunk_hdr_sz, &chunk_header, cur_block, crc_ptr);
+ if (ret < 0) {
+ return ret;
+ }
- ret = process_chunk(s, fd, offset, sparse_header.chunk_hdr_sz, &chunk_header,
- cur_block, crc_ptr);
- if (ret < 0) {
- return ret;
- }
+ cur_block += ret;
+ }
- cur_block += ret;
- }
+ if (sparse_header.total_blks != cur_block) {
+ return -EINVAL;
+ }
- if (sparse_header.total_blks != cur_block) {
- return -EINVAL;
- }
-
- return 0;
+ return 0;
}
-static int sparse_file_read_normal(struct sparse_file *s, int fd)
-{
- int ret;
- uint32_t *buf = (uint32_t *)malloc(s->block_size);
- unsigned int block = 0;
- int64_t remain = s->len;
- int64_t offset = 0;
- unsigned int to_read;
- unsigned int i;
- bool sparse_block;
+static int sparse_file_read_normal(struct sparse_file* s, int fd) {
+ int ret;
+ uint32_t* buf = (uint32_t*)malloc(s->block_size);
+ unsigned int block = 0;
+ int64_t remain = s->len;
+ int64_t offset = 0;
+ unsigned int to_read;
+ unsigned int i;
+ bool sparse_block;
- if (!buf) {
- return -ENOMEM;
- }
+ if (!buf) {
+ return -ENOMEM;
+ }
- while (remain > 0) {
- to_read = std::min(remain, (int64_t)(s->block_size));
- ret = read_all(fd, buf, to_read);
- if (ret < 0) {
- error("failed to read sparse file");
- free(buf);
- return ret;
- }
+ while (remain > 0) {
+ to_read = std::min(remain, (int64_t)(s->block_size));
+ ret = read_all(fd, buf, to_read);
+ if (ret < 0) {
+ error("failed to read sparse file");
+ free(buf);
+ return ret;
+ }
- if (to_read == s->block_size) {
- sparse_block = true;
- for (i = 1; i < s->block_size / sizeof(uint32_t); i++) {
- if (buf[0] != buf[i]) {
- sparse_block = false;
- break;
- }
- }
- } else {
- sparse_block = false;
- }
+ if (to_read == s->block_size) {
+ sparse_block = true;
+ for (i = 1; i < s->block_size / sizeof(uint32_t); i++) {
+ if (buf[0] != buf[i]) {
+ sparse_block = false;
+ break;
+ }
+ }
+ } else {
+ sparse_block = false;
+ }
- if (sparse_block) {
- /* TODO: add flag to use skip instead of fill for buf[0] == 0 */
- sparse_file_add_fill(s, buf[0], to_read, block);
- } else {
- sparse_file_add_fd(s, fd, offset, to_read, block);
- }
+ if (sparse_block) {
+ /* TODO: add flag to use skip instead of fill for buf[0] == 0 */
+ sparse_file_add_fill(s, buf[0], to_read, block);
+ } else {
+ sparse_file_add_fd(s, fd, offset, to_read, block);
+ }
- remain -= to_read;
- offset += to_read;
- block++;
- }
+ remain -= to_read;
+ offset += to_read;
+ block++;
+ }
- free(buf);
- return 0;
+ free(buf);
+ return 0;
}
-int sparse_file_read(struct sparse_file *s, int fd, bool sparse, bool crc)
-{
- if (crc && !sparse) {
- return -EINVAL;
- }
+int sparse_file_read(struct sparse_file* s, int fd, bool sparse, bool crc) {
+ if (crc && !sparse) {
+ return -EINVAL;
+ }
- if (sparse) {
- return sparse_file_read_sparse(s, fd, crc);
- } else {
- return sparse_file_read_normal(s, fd);
- }
+ if (sparse) {
+ SparseFileFdSource source(fd);
+ return sparse_file_read_sparse(s, &source, crc);
+ } else {
+ return sparse_file_read_normal(s, fd);
+ }
}
-struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc)
-{
- int ret;
- sparse_header_t sparse_header;
- int64_t len;
- struct sparse_file *s;
-
- ret = read_all(fd, &sparse_header, sizeof(sparse_header));
- if (ret < 0) {
- verbose_error(verbose, ret, "header");
- return NULL;
- }
-
- if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
- verbose_error(verbose, -EINVAL, "header magic");
- return NULL;
- }
-
- if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
- verbose_error(verbose, -EINVAL, "header major version");
- return NULL;
- }
-
- if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
- return NULL;
- }
-
- if (sparse_header.chunk_hdr_sz < sizeof(chunk_header_t)) {
- return NULL;
- }
-
- len = (int64_t)sparse_header.total_blks * sparse_header.blk_sz;
- s = sparse_file_new(sparse_header.blk_sz, len);
- if (!s) {
- verbose_error(verbose, -EINVAL, NULL);
- return NULL;
- }
-
- ret = lseek64(fd, 0, SEEK_SET);
- if (ret < 0) {
- verbose_error(verbose, ret, "seeking");
- sparse_file_destroy(s);
- return NULL;
- }
-
- s->verbose = verbose;
-
- ret = sparse_file_read(s, fd, true, crc);
- if (ret < 0) {
- sparse_file_destroy(s);
- return NULL;
- }
-
- return s;
+int sparse_file_read_buf(struct sparse_file* s, char* buf, bool crc) {
+ SparseFileBufSource source(buf);
+ return sparse_file_read_sparse(s, &source, crc);
}
-struct sparse_file *sparse_file_import_auto(int fd, bool crc, bool verbose)
-{
- struct sparse_file *s;
- int64_t len;
- int ret;
+static struct sparse_file* sparse_file_import_source(SparseFileSource* source, bool verbose,
+ bool crc) {
+ int ret;
+ sparse_header_t sparse_header;
+ int64_t len;
+ struct sparse_file* s;
- s = sparse_file_import(fd, verbose, crc);
- if (s) {
- return s;
- }
+ ret = source->ReadValue(&sparse_header, sizeof(sparse_header));
+ if (ret < 0) {
+ verbose_error(verbose, ret, "header");
+ return NULL;
+ }
- len = lseek64(fd, 0, SEEK_END);
- if (len < 0) {
- return NULL;
- }
+ if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
+ verbose_error(verbose, -EINVAL, "header magic");
+ return NULL;
+ }
- lseek64(fd, 0, SEEK_SET);
+ if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
+ verbose_error(verbose, -EINVAL, "header major version");
+ return NULL;
+ }
- s = sparse_file_new(4096, len);
- if (!s) {
- return NULL;
- }
+ if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
+ return NULL;
+ }
- ret = sparse_file_read_normal(s, fd);
- if (ret < 0) {
- sparse_file_destroy(s);
- return NULL;
- }
+ if (sparse_header.chunk_hdr_sz < sizeof(chunk_header_t)) {
+ return NULL;
+ }
- return s;
+ len = (int64_t)sparse_header.total_blks * sparse_header.blk_sz;
+ s = sparse_file_new(sparse_header.blk_sz, len);
+ if (!s) {
+ verbose_error(verbose, -EINVAL, NULL);
+ return NULL;
+ }
+
+ ret = source->SetOffset(0);
+ if (ret < 0) {
+ verbose_error(verbose, ret, "seeking");
+ sparse_file_destroy(s);
+ return NULL;
+ }
+
+ s->verbose = verbose;
+
+ ret = sparse_file_read_sparse(s, source, crc);
+ if (ret < 0) {
+ sparse_file_destroy(s);
+ return NULL;
+ }
+
+ return s;
+}
+
+struct sparse_file* sparse_file_import(int fd, bool verbose, bool crc) {
+ SparseFileFdSource source(fd);
+ return sparse_file_import_source(&source, verbose, crc);
+}
+
+struct sparse_file* sparse_file_import_buf(char* buf, bool verbose, bool crc) {
+ SparseFileBufSource source(buf);
+ return sparse_file_import_source(&source, verbose, crc);
+}
+
+struct sparse_file* sparse_file_import_auto(int fd, bool crc, bool verbose) {
+ struct sparse_file* s;
+ int64_t len;
+ int ret;
+
+ s = sparse_file_import(fd, verbose, crc);
+ if (s) {
+ return s;
+ }
+
+ len = lseek64(fd, 0, SEEK_END);
+ if (len < 0) {
+ return NULL;
+ }
+
+ lseek64(fd, 0, SEEK_SET);
+
+ s = sparse_file_new(4096, len);
+ if (!s) {
+ return NULL;
+ }
+
+ ret = sparse_file_read_normal(s, fd);
+ if (ret < 0) {
+ sparse_file_destroy(s);
+ return NULL;
+ }
+
+ return s;
}
diff --git a/libsync/include/android/sync.h b/libsync/include/android/sync.h
index 68f74a0..32bb878 100644
--- a/libsync/include/android/sync.h
+++ b/libsync/include/android/sync.h
@@ -41,28 +41,8 @@
__BEGIN_DECLS
-struct sync_fence_info_data {
- uint32_t len;
- char name[32];
- int32_t status;
- uint8_t pt_info[0];
-};
-
-struct sync_pt_info {
- uint32_t len;
- char obj_name[32];
- char driver_name[32];
- int32_t status;
- uint64_t timestamp_ns;
- uint8_t driver_data[0];
-};
-
/* timeout in msecs */
int sync_wait(int fd, int timeout);
-struct sync_fence_info_data *sync_fence_info(int fd);
-struct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info,
- struct sync_pt_info *itr);
-void sync_fence_info_free(struct sync_fence_info_data *info);
__END_DECLS
diff --git a/libsync/include/ndk/sync.h b/libsync/include/ndk/sync.h
index a786d3e..49f01e1 100644
--- a/libsync/include/ndk/sync.h
+++ b/libsync/include/ndk/sync.h
@@ -32,8 +32,6 @@
__BEGIN_DECLS
-#if __ANDROID_API__ >= __ANDROID_API_O__
-
/* Fences indicate the status of an asynchronous task. They are initially
* in unsignaled state (0), and make a one-time transition to either signaled
* (1) or error (< 0) state. A sync file is a collection of one or more fences;
@@ -63,14 +61,14 @@
* The original fences remain valid, and the caller is responsible for closing
* them.
*/
-int32_t sync_merge(const char *name, int32_t fd1, int32_t fd2);
+int32_t sync_merge(const char* name, int32_t fd1, int32_t fd2) __INTRODUCED_IN(26);
/**
* Retrieve detailed information about a sync file and its fences.
*
* The returned sync_file_info must be freed by calling sync_file_info_free().
*/
-struct sync_file_info *sync_file_info(int32_t fd);
+struct sync_file_info* sync_file_info(int32_t fd) __INTRODUCED_IN(26);
/**
* Get the array of fence infos from the sync file's info.
@@ -88,9 +86,7 @@
}
/** Free a struct sync_file_info structure */
-void sync_file_info_free(struct sync_file_info *info);
-
-#endif // __ANDROID_API__ >= __ANDROID_API_O__
+void sync_file_info_free(struct sync_file_info* info) __INTRODUCED_IN(26);
__END_DECLS
diff --git a/libsync/sync.c b/libsync/sync.c
index 6b187fa..b8c48c7 100644
--- a/libsync/sync.c
+++ b/libsync/sync.c
@@ -30,6 +30,29 @@
#include <android/sync.h>
+/* Prototypes for deprecated functions that used to be declared in the legacy
+ * android/sync.h. They've been moved here to make sure new code does not use
+ * them, but the functions are still defined to avoid breaking existing
+ * binaries. Eventually they can be removed altogether.
+ */
+struct sync_fence_info_data {
+ uint32_t len;
+ char name[32];
+ int32_t status;
+ uint8_t pt_info[0];
+};
+struct sync_pt_info {
+ uint32_t len;
+ char obj_name[32];
+ char driver_name[32];
+ int32_t status;
+ uint64_t timestamp_ns;
+ uint8_t driver_data[0];
+};
+struct sync_fence_info_data* sync_fence_info(int fd);
+struct sync_pt_info* sync_pt_info(struct sync_fence_info_data* info, struct sync_pt_info* itr);
+void sync_fence_info_free(struct sync_fence_info_data* info);
+
/* Legacy Sync API */
struct sync_legacy_merge_data {
diff --git a/libsync/tests/sync_test.cpp b/libsync/tests/sync_test.cpp
index 0fb86d6..011b09d 100644
--- a/libsync/tests/sync_test.cpp
+++ b/libsync/tests/sync_test.cpp
@@ -15,6 +15,35 @@
#include <random>
#include <unordered_map>
+/* These deprecated declarations were in the legacy android/sync.h. They've been removed to
+ * encourage code to move to the modern equivalents. But they are still implemented in libsync.so
+ * to avoid breaking existing binaries; as long as that's true we should keep testing them here.
+ * That means making local copies of the declarations.
+ */
+extern "C" {
+
+struct sync_fence_info_data {
+ uint32_t len;
+ char name[32];
+ int32_t status;
+ uint8_t pt_info[0];
+};
+
+struct sync_pt_info {
+ uint32_t len;
+ char obj_name[32];
+ char driver_name[32];
+ int32_t status;
+ uint64_t timestamp_ns;
+ uint8_t driver_data[0];
+};
+
+struct sync_fence_info_data* sync_fence_info(int fd);
+struct sync_pt_info* sync_pt_info(struct sync_fence_info_data* info, struct sync_pt_info* itr);
+void sync_fence_info_free(struct sync_fence_info_data* info);
+
+} // extern "C"
+
// TODO: better stress tests?
// Handle more than 64 fd's simultaneously, i.e. fix sync_fence_info's 4k limit.
// Handle wraparound in timelines like nvidia.
diff --git a/libsysutils/include/sysutils/FrameworkClient.h b/libsysutils/include/sysutils/FrameworkClient.h
deleted file mode 100644
index 4a3f0de..0000000
--- a/libsysutils/include/sysutils/FrameworkClient.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef _FRAMEWORK_CLIENT_H
-#define _FRAMEWORK_CLIENT_H
-
-#include "List.h"
-
-#include <pthread.h>
-
-class FrameworkClient {
- int mSocket;
- pthread_mutex_t mWriteMutex;
-
-public:
- FrameworkClient(int sock);
- virtual ~FrameworkClient() {}
-
- int sendMsg(const char *msg);
- int sendMsg(const char *msg, const char *data);
-};
-
-typedef android::sysutils::List<FrameworkClient *> FrameworkClientCollection;
-#endif
diff --git a/libsysutils/src/FrameworkClient.cpp b/libsysutils/src/FrameworkClient.cpp
deleted file mode 100644
index 72b3d0a..0000000
--- a/libsysutils/src/FrameworkClient.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2009-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.
- */
-
-#define LOG_TAG "FrameworkClient"
-
-#include <alloca.h>
-#include <errno.h>
-#include <pthread.h>
-#include <sys/types.h>
-
-#include <android/log.h>
-#include <sysutils/FrameworkClient.h>
-
-FrameworkClient::FrameworkClient(int socket) {
- mSocket = socket;
- pthread_mutex_init(&mWriteMutex, NULL);
-}
-
-int FrameworkClient::sendMsg(const char *msg) {
- int ret;
- if (mSocket < 0) {
- errno = EHOSTUNREACH;
- return -1;
- }
-
- pthread_mutex_lock(&mWriteMutex);
- ret = TEMP_FAILURE_RETRY(write(mSocket, msg, strlen(msg) +1));
- if (ret < 0) {
- SLOGW("Unable to send msg '%s' (%s)", msg, strerror(errno));
- }
- pthread_mutex_unlock(&mWriteMutex);
- return 0;
-}
-
-int FrameworkClient::sendMsg(const char *msg, const char *data) {
- size_t bufflen = strlen(msg) + strlen(data) + 1;
- char *buffer = (char *) alloca(bufflen);
- if (!buffer) {
- errno = -ENOMEM;
- return -1;
- }
- snprintf(buffer, bufflen, "%s%s", msg, data);
- return sendMsg(buffer);
-}
-
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index d4cef7c..df5da65 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -223,6 +223,7 @@
"tests/files/offline/art_quick_osr_stub_arm/*",
"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_hdr_begin_x86_64/*",
"tests/files/offline/jit_debug_arm/*",
"tests/files/offline/jit_debug_x86/*",
diff --git a/libunwindstack/ArmExidx.cpp b/libunwindstack/ArmExidx.cpp
index 6e397e3..818f5d1 100644
--- a/libunwindstack/ArmExidx.cpp
+++ b/libunwindstack/ArmExidx.cpp
@@ -31,6 +31,8 @@
namespace unwindstack {
+static constexpr uint8_t LOG_CFA_REG = 64;
+
void ArmExidx::LogRawData() {
std::string log_str("Raw Data:");
for (const uint8_t data : data_) {
@@ -63,8 +65,10 @@
if (data == 1) {
// This is a CANT UNWIND entry.
status_ = ARM_STATUS_NO_UNWIND;
- if (log_) {
- log(log_indent_, "Raw Data: 0x00 0x00 0x00 0x01");
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ log(log_indent_, "Raw Data: 0x00 0x00 0x00 0x01");
+ }
log(log_indent_, "[cantunwind]");
}
return false;
@@ -86,7 +90,7 @@
// If this didn't end with a finish op, add one.
data_.push_back(ARM_OP_FINISH);
}
- if (log_) {
+ if (log_type_ == ARM_LOG_FULL) {
LogRawData();
}
return true;
@@ -163,7 +167,7 @@
data_.push_back(ARM_OP_FINISH);
}
- if (log_) {
+ if (log_type_ == ARM_LOG_FULL) {
LogRawData();
}
return true;
@@ -190,32 +194,45 @@
registers |= byte;
if (registers == 0) {
// 10000000 00000000: Refuse to unwind
- if (log_) {
+ if (log_type_ != ARM_LOG_NONE) {
log(log_indent_, "Refuse to unwind");
}
status_ = ARM_STATUS_NO_UNWIND;
return false;
}
// 1000iiii iiiiiiii: Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}
- if (log_) {
- bool add_comma = false;
- std::string msg = "pop {";
- for (size_t i = 0; i < 12; i++) {
- if (registers & (1 << i)) {
- if (add_comma) {
- msg += ", ";
+ registers <<= 4;
+
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ bool add_comma = false;
+ std::string msg = "pop {";
+ for (size_t reg = 4; reg < 16; reg++) {
+ if (registers & (1 << reg)) {
+ if (add_comma) {
+ msg += ", ";
+ }
+ msg += android::base::StringPrintf("r%zu", reg);
+ add_comma = true;
}
- msg += android::base::StringPrintf("r%zu", i + 4);
- add_comma = true;
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ uint32_t cfa_offset = __builtin_popcount(registers) * 4;
+ log_cfa_offset_ += cfa_offset;
+ for (size_t reg = 4; reg < 16; reg++) {
+ if (registers & (1 << reg)) {
+ log_regs_[reg] = cfa_offset;
+ cfa_offset -= 4;
+ }
}
}
- log(log_indent_, "%s}", msg.c_str());
+
if (log_skip_execution_) {
return true;
}
}
- registers <<= 4;
for (size_t reg = 4; reg < 16; reg++) {
if (registers & (1 << reg)) {
if (!process_memory_->Read32(cfa_, &(*regs_)[reg])) {
@@ -246,15 +263,20 @@
if (bits == 13 || bits == 15) {
// 10011101: Reserved as prefix for ARM register to register moves
// 10011111: Reserved as prefix for Intel Wireless MMX register to register moves
- if (log_) {
+ if (log_type_ != ARM_LOG_NONE) {
log(log_indent_, "[Reserved]");
}
status_ = ARM_STATUS_RESERVED;
return false;
}
// 1001nnnn: Set vsp = r[nnnn] (nnnn != 13, 15)
- if (log_) {
- log(log_indent_, "vsp = r%d", bits);
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ log(log_indent_, "vsp = r%d", bits);
+ } else {
+ log_regs_[LOG_CFA_REG] = bits;
+ }
+
if (log_skip_execution_) {
return true;
}
@@ -270,17 +292,36 @@
// 10100nnn: Pop r4-r[4+nnn]
// 10101nnn: Pop r4-r[4+nnn], r14
- if (log_) {
- std::string msg = "pop {r4";
+ if (log_type_ != ARM_LOG_NONE) {
uint8_t end_reg = byte & 0x7;
- if (end_reg) {
- msg += android::base::StringPrintf("-r%d", 4 + end_reg);
- }
- if (byte & 0x8) {
- log(log_indent_, "%s, r14}", msg.c_str());
+ if (log_type_ == ARM_LOG_FULL) {
+ std::string msg = "pop {r4";
+ if (end_reg) {
+ msg += android::base::StringPrintf("-r%d", 4 + end_reg);
+ }
+ if (byte & 0x8) {
+ log(log_indent_, "%s, r14}", msg.c_str());
+ } else {
+ log(log_indent_, "%s}", msg.c_str());
+ }
} else {
- log(log_indent_, "%s}", msg.c_str());
+ end_reg += 4;
+ uint32_t cfa_offset = (end_reg - 3) * 4;
+ if (byte & 0x8) {
+ cfa_offset += 4;
+ }
+ log_cfa_offset_ += cfa_offset;
+
+ for (uint8_t reg = 4; reg <= end_reg; reg++) {
+ log_regs_[reg] = cfa_offset;
+ cfa_offset -= 4;
+ }
+
+ if (byte & 0x8) {
+ log_regs_[14] = cfa_offset;
+ }
}
+
if (log_skip_execution_) {
return true;
}
@@ -307,8 +348,11 @@
inline bool ArmExidx::DecodePrefix_10_11_0000() {
// 10110000: Finish
- if (log_) {
- log(log_indent_, "finish");
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ log(log_indent_, "finish");
+ }
+
if (log_skip_execution_) {
status_ = ARM_STATUS_FINISH;
return false;
@@ -326,7 +370,7 @@
if (byte == 0) {
// 10110001 00000000: Spare
- if (log_) {
+ if (log_type_ != ARM_LOG_NONE) {
log(log_indent_, "Spare");
}
status_ = ARM_STATUS_SPARE;
@@ -334,7 +378,7 @@
}
if (byte >> 4) {
// 10110001 xxxxyyyy: Spare (xxxx != 0000)
- if (log_) {
+ if (log_type_ != ARM_LOG_NONE) {
log(log_indent_, "Spare");
}
status_ = ARM_STATUS_SPARE;
@@ -342,19 +386,32 @@
}
// 10110001 0000iiii: Pop integer registers under mask {r3, r2, r1, r0}
- if (log_) {
- bool add_comma = false;
- std::string msg = "pop {";
- for (size_t i = 0; i < 4; i++) {
- if (byte & (1 << i)) {
- if (add_comma) {
- msg += ", ";
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ bool add_comma = false;
+ std::string msg = "pop {";
+ for (size_t i = 0; i < 4; i++) {
+ if (byte & (1 << i)) {
+ if (add_comma) {
+ msg += ", ";
+ }
+ msg += android::base::StringPrintf("r%zu", i);
+ add_comma = true;
}
- msg += android::base::StringPrintf("r%zu", i);
- add_comma = true;
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ byte &= 0xf;
+ uint32_t cfa_offset = __builtin_popcount(byte) * 4;
+ log_cfa_offset_ += cfa_offset;
+ for (size_t reg = 0; reg < 4; reg++) {
+ if (byte & (1 << reg)) {
+ log_regs_[reg] = cfa_offset;
+ cfa_offset -= 4;
+ }
}
}
- log(log_indent_, "%s}", msg.c_str());
+
if (log_skip_execution_) {
return true;
}
@@ -373,6 +430,15 @@
return true;
}
+inline void ArmExidx::AdjustRegisters(int32_t offset) {
+ for (auto& entry : log_regs_) {
+ if (entry.first >= LOG_CFA_REG) {
+ break;
+ }
+ entry.second += offset;
+ }
+}
+
inline bool ArmExidx::DecodePrefix_10_11_0010() {
// 10110010 uleb128: vsp = vsp + 0x204 + (uleb128 << 2)
uint32_t result = 0;
@@ -387,8 +453,15 @@
shift += 7;
} while (byte & 0x80);
result <<= 2;
- if (log_) {
- log(log_indent_, "vsp = vsp + %d", 0x204 + result);
+ if (log_type_ != ARM_LOG_NONE) {
+ int32_t cfa_offset = 0x204 + result;
+ if (log_type_ == ARM_LOG_FULL) {
+ log(log_indent_, "vsp = vsp + %d", cfa_offset);
+ } else {
+ log_cfa_offset_ += cfa_offset;
+ }
+ AdjustRegisters(cfa_offset);
+
if (log_skip_execution_) {
return true;
}
@@ -404,14 +477,20 @@
return false;
}
- if (log_) {
+ if (log_type_ != ARM_LOG_NONE) {
uint8_t start_reg = byte >> 4;
- std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
uint8_t end_reg = start_reg + (byte & 0xf);
- if (end_reg) {
- msg += android::base::StringPrintf("-d%d", end_reg);
+
+ if (log_type_ == ARM_LOG_FULL) {
+ std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
+ if (end_reg) {
+ msg += android::base::StringPrintf("-d%d", end_reg);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ log(log_indent_, "Unsupported DX register display");
}
- log(log_indent_, "%s}", msg.c_str());
+
if (log_skip_execution_) {
return true;
}
@@ -422,7 +501,7 @@
inline bool ArmExidx::DecodePrefix_10_11_01nn() {
// 101101nn: Spare
- if (log_) {
+ if (log_type_ != ARM_LOG_NONE) {
log(log_indent_, "Spare");
}
status_ = ARM_STATUS_SPARE;
@@ -433,13 +512,18 @@
CHECK((byte & ~0x07) == 0xb8);
// 10111nnn: Pop VFP double-precision registers D[8]-D[8+nnn] by FSTMFDX
- if (log_) {
- std::string msg = "pop {d8";
- uint8_t last_reg = (byte & 0x7);
- if (last_reg) {
- msg += android::base::StringPrintf("-d%d", last_reg + 8);
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ uint8_t last_reg = (byte & 0x7);
+ std::string msg = "pop {d8";
+ if (last_reg) {
+ msg += android::base::StringPrintf("-d%d", last_reg + 8);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ log(log_indent_, "Unsupported DX register display");
}
- log(log_indent_, "%s}", msg.c_str());
+
if (log_skip_execution_) {
return true;
}
@@ -489,14 +573,19 @@
}
// 11000110 sssscccc: Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc]
- if (log_) {
- uint8_t start_reg = byte >> 4;
- std::string msg = android::base::StringPrintf("pop {wR%d", start_reg);
- uint8_t end_reg = byte & 0xf;
- if (end_reg) {
- msg += android::base::StringPrintf("-wR%d", start_reg + end_reg);
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ uint8_t start_reg = byte >> 4;
+ std::string msg = android::base::StringPrintf("pop {wR%d", start_reg);
+ uint8_t end_reg = byte & 0xf;
+ if (end_reg) {
+ msg += android::base::StringPrintf("-wR%d", start_reg + end_reg);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ log(log_indent_, "Unsupported wRX register display");
}
- log(log_indent_, "%s}", msg.c_str());
+
if (log_skip_execution_) {
return true;
}
@@ -510,32 +599,40 @@
if (byte == 0) {
// 11000111 00000000: Spare
- if (log_) {
+ if (log_type_ != ARM_LOG_NONE) {
log(log_indent_, "Spare");
}
status_ = ARM_STATUS_SPARE;
return false;
} else if ((byte >> 4) == 0) {
// 11000111 0000iiii: Intel Wireless MMX pop wCGR registers {wCGR0,1,2,3}
- if (log_) {
- bool add_comma = false;
- std::string msg = "pop {";
- for (size_t i = 0; i < 4; i++) {
- if (byte & (1 << i)) {
- if (add_comma) {
- msg += ", ";
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ bool add_comma = false;
+ std::string msg = "pop {";
+ for (size_t i = 0; i < 4; i++) {
+ if (byte & (1 << i)) {
+ if (add_comma) {
+ msg += ", ";
+ }
+ msg += android::base::StringPrintf("wCGR%zu", i);
+ add_comma = true;
}
- msg += android::base::StringPrintf("wCGR%zu", i);
- add_comma = true;
}
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ log(log_indent_, "Unsupported wCGR register display");
}
- log(log_indent_, "%s}", msg.c_str());
+
+ if (log_skip_execution_) {
+ return true;
+ }
}
// Only update the cfa.
cfa_ += __builtin_popcount(byte) * 4;
} else {
// 11000111 xxxxyyyy: Spare (xxxx != 0000)
- if (log_) {
+ if (log_type_ != ARM_LOG_NONE) {
log(log_indent_, "Spare");
}
status_ = ARM_STATUS_SPARE;
@@ -543,13 +640,18 @@
}
} else {
// 11000nnn: Intel Wireless MMX pop wR[10]-wR[10+nnn] (nnn != 6, 7)
- if (log_) {
- std::string msg = "pop {wR10";
- uint8_t nnn = byte & 0x7;
- if (nnn) {
- msg += android::base::StringPrintf("-wR%d", 10 + nnn);
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ std::string msg = "pop {wR10";
+ uint8_t nnn = byte & 0x7;
+ if (nnn) {
+ msg += android::base::StringPrintf("-wR%d", 10 + nnn);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ log(log_indent_, "Unsupported wRX register display");
}
- log(log_indent_, "%s}", msg.c_str());
+
if (log_skip_execution_) {
return true;
}
@@ -570,14 +672,19 @@
return false;
}
- if (log_) {
- uint8_t start_reg = byte >> 4;
- std::string msg = android::base::StringPrintf("pop {d%d", 16 + start_reg);
- uint8_t end_reg = byte & 0xf;
- if (end_reg) {
- msg += android::base::StringPrintf("-d%d", 16 + start_reg + end_reg);
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ uint8_t start_reg = byte >> 4;
+ std::string msg = android::base::StringPrintf("pop {d%d", 16 + start_reg);
+ uint8_t end_reg = byte & 0xf;
+ if (end_reg) {
+ msg += android::base::StringPrintf("-d%d", 16 + start_reg + end_reg);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ log(log_indent_, "Unsupported DX register display");
}
- log(log_indent_, "%s}", msg.c_str());
+
if (log_skip_execution_) {
return true;
}
@@ -590,14 +697,19 @@
return false;
}
- if (log_) {
- uint8_t start_reg = byte >> 4;
- std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
- uint8_t end_reg = byte & 0xf;
- if (end_reg) {
- msg += android::base::StringPrintf("-d%d", start_reg + end_reg);
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ uint8_t start_reg = byte >> 4;
+ std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
+ uint8_t end_reg = byte & 0xf;
+ if (end_reg) {
+ msg += android::base::StringPrintf("-d%d", start_reg + end_reg);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ log(log_indent_, "Unsupported DX register display");
}
- log(log_indent_, "%s}", msg.c_str());
+
if (log_skip_execution_) {
return true;
}
@@ -606,7 +718,7 @@
cfa_ += (byte & 0xf) * 8 + 8;
} else {
// 11001yyy: Spare (yyy != 000, 001)
- if (log_) {
+ if (log_type_ != ARM_LOG_NONE) {
log(log_indent_, "Spare");
}
status_ = ARM_STATUS_SPARE;
@@ -619,13 +731,18 @@
CHECK((byte & ~0x07) == 0xd0);
// 11010nnn: Pop VFP double precision registers D[8]-D[8+nnn] by VPUSH
- if (log_) {
- std::string msg = "pop {d8";
- uint8_t end_reg = byte & 0x7;
- if (end_reg) {
- msg += android::base::StringPrintf("-d%d", 8 + end_reg);
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ std::string msg = "pop {d8";
+ uint8_t end_reg = byte & 0x7;
+ if (end_reg) {
+ msg += android::base::StringPrintf("-d%d", 8 + end_reg);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ log(log_indent_, "Unsupported DX register display");
}
- log(log_indent_, "%s}", msg.c_str());
+
if (log_skip_execution_) {
return true;
}
@@ -646,7 +763,7 @@
return DecodePrefix_11_010(byte);
default:
// 11xxxyyy: Spare (xxx != 000, 001, 010)
- if (log_) {
+ if (log_type_ != ARM_LOG_NONE) {
log(log_indent_, "Spare");
}
status_ = ARM_STATUS_SPARE;
@@ -664,8 +781,15 @@
switch (byte >> 6) {
case 0:
// 00xxxxxx: vsp = vsp + (xxxxxxx << 2) + 4
- if (log_) {
- log(log_indent_, "vsp = vsp + %d", ((byte & 0x3f) << 2) + 4);
+ if (log_type_ != ARM_LOG_NONE) {
+ int32_t cfa_offset = ((byte & 0x3f) << 2) + 4;
+ if (log_type_ == ARM_LOG_FULL) {
+ log(log_indent_, "vsp = vsp + %d", cfa_offset);
+ } else {
+ log_cfa_offset_ += cfa_offset;
+ }
+ AdjustRegisters(cfa_offset);
+
if (log_skip_execution_) {
break;
}
@@ -674,8 +798,15 @@
break;
case 1:
// 01xxxxxx: vsp = vsp - (xxxxxxx << 2) + 4
- if (log_) {
- log(log_indent_, "vsp = vsp - %d", ((byte & 0x3f) << 2) + 4);
+ if (log_type_ != ARM_LOG_NONE) {
+ uint32_t cfa_offset = ((byte & 0x3f) << 2) + 4;
+ if (log_type_ == ARM_LOG_FULL) {
+ log(log_indent_, "vsp = vsp - %d", cfa_offset);
+ } else {
+ log_cfa_offset_ -= cfa_offset;
+ }
+ AdjustRegisters(-cfa_offset);
+
if (log_skip_execution_) {
break;
}
@@ -696,4 +827,36 @@
return status_ == ARM_STATUS_FINISH;
}
+void ArmExidx::LogByReg() {
+ if (log_type_ != ARM_LOG_BY_REG) {
+ return;
+ }
+
+ uint8_t cfa_reg;
+ if (log_regs_.count(LOG_CFA_REG) == 0) {
+ cfa_reg = 13;
+ } else {
+ cfa_reg = log_regs_[LOG_CFA_REG];
+ }
+
+ if (log_cfa_offset_ != 0) {
+ char sign = (log_cfa_offset_ > 0) ? '+' : '-';
+ log(log_indent_, "cfa = r%zu %c %d", cfa_reg, sign, abs(log_cfa_offset_));
+ } else {
+ log(log_indent_, "cfa = r%zu", cfa_reg);
+ }
+
+ for (const auto& entry : log_regs_) {
+ if (entry.first >= LOG_CFA_REG) {
+ break;
+ }
+ if (entry.second == 0) {
+ log(log_indent_, "r%zu = [cfa]", entry.first);
+ } else {
+ char sign = (entry.second > 0) ? '-' : '+';
+ log(log_indent_, "r%zu = [cfa %c %d]", entry.first, sign, abs(entry.second));
+ }
+ }
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/ArmExidx.h b/libunwindstack/ArmExidx.h
index 96756a0..d9fc371 100644
--- a/libunwindstack/ArmExidx.h
+++ b/libunwindstack/ArmExidx.h
@@ -20,6 +20,7 @@
#include <stdint.h>
#include <deque>
+#include <map>
namespace unwindstack {
@@ -44,6 +45,12 @@
ARM_OP_FINISH = 0xb0,
};
+enum ArmLogType : uint8_t {
+ ARM_LOG_NONE,
+ ARM_LOG_FULL,
+ ARM_LOG_BY_REG,
+};
+
class ArmExidx {
public:
ArmExidx(RegsArm* regs, Memory* elf_memory, Memory* process_memory)
@@ -52,6 +59,8 @@
void LogRawData();
+ void LogByReg();
+
bool ExtractEntryData(uint32_t entry_offset);
bool Eval();
@@ -71,12 +80,13 @@
bool pc_set() { return pc_set_; }
void set_pc_set(bool pc_set) { pc_set_ = pc_set; }
- void set_log(bool log) { log_ = log; }
+ void set_log(ArmLogType log_type) { log_type_ = log_type; }
void set_log_skip_execution(bool skip_execution) { log_skip_execution_ = skip_execution; }
void set_log_indent(uint8_t indent) { log_indent_ = indent; }
private:
bool GetByte(uint8_t* byte);
+ void AdjustRegisters(int32_t offset);
bool DecodePrefix_10_00(uint8_t byte);
bool DecodePrefix_10_01(uint8_t byte);
@@ -103,10 +113,12 @@
Memory* elf_memory_;
Memory* process_memory_;
- bool log_ = false;
+ ArmLogType log_type_ = ARM_LOG_NONE;
uint8_t log_indent_ = 0;
bool log_skip_execution_ = false;
bool pc_set_ = false;
+ int32_t log_cfa_offset_ = 0;
+ std::map<uint8_t, int32_t> log_regs_;
};
} // namespace unwindstack
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index 6ecedce..cd9ef61 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -264,8 +264,8 @@
}
template <typename AddressType>
-bool DwarfCfa<AddressType>::Log(uint32_t indent, uint64_t pc, uint64_t load_bias,
- uint64_t start_offset, uint64_t end_offset) {
+bool DwarfCfa<AddressType>::Log(uint32_t indent, uint64_t pc, uint64_t start_offset,
+ uint64_t end_offset) {
memory_->set_cur_offset(start_offset);
uint64_t cfa_offset;
uint64_t cur_pc = fde_->pc_start;
@@ -301,8 +301,8 @@
break;
}
if (cur_pc != old_pc) {
- log(indent, "");
- log(indent, "PC 0x%" PRIx64, cur_pc + load_bias);
+ log(0, "");
+ log(indent, "PC 0x%" PRIx64, cur_pc);
}
old_pc = cur_pc;
}
diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h
index 16c66e2..c5ffb8e 100644
--- a/libunwindstack/DwarfCfa.h
+++ b/libunwindstack/DwarfCfa.h
@@ -71,8 +71,7 @@
bool GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
dwarf_loc_regs_t* loc_regs);
- bool Log(uint32_t indent, uint64_t pc, uint64_t load_bias, uint64_t start_offset,
- uint64_t end_offset);
+ bool Log(uint32_t indent, uint64_t pc, uint64_t start_offset, uint64_t end_offset);
const DwarfErrorData& last_error() { return last_error_; }
DwarfErrorCode LastErrorCode() { return last_error_.code; }
diff --git a/libunwindstack/DwarfEhFrameWithHdr.cpp b/libunwindstack/DwarfEhFrameWithHdr.cpp
index 9a49013..fd6a457 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.cpp
+++ b/libunwindstack/DwarfEhFrameWithHdr.cpp
@@ -22,12 +22,18 @@
#include "Check.h"
#include "DwarfEhFrameWithHdr.h"
+#include "DwarfEncoding.h"
namespace unwindstack {
+static inline bool IsEncodingRelative(uint8_t encoding) {
+ encoding >>= 4;
+ return encoding > 0 && encoding <= DW_EH_PE_funcrel;
+}
+
template <typename AddressType>
-bool DwarfEhFrameWithHdr<AddressType>::Init(uint64_t offset, uint64_t size) {
- uint8_t data[4];
+bool DwarfEhFrameWithHdr<AddressType>::Init(uint64_t offset, uint64_t size, uint64_t load_bias) {
+ load_bias_ = load_bias;
memory_.clear_func_offset();
memory_.clear_text_offset();
@@ -35,6 +41,7 @@
memory_.set_cur_offset(offset);
// Read the first four bytes all at once.
+ uint8_t data[4];
if (!memory_.ReadBytes(data, 4)) {
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
last_error_.address = memory_.cur_offset();
@@ -100,7 +107,7 @@
memory_.set_data_offset(entries_data_offset_);
memory_.set_cur_offset(entries_offset_ + 2 * index * table_entry_size_);
- memory_.set_pc_offset(memory_.cur_offset());
+ memory_.set_pc_offset(0);
uint64_t value;
if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
@@ -109,6 +116,11 @@
fde_info_.erase(index);
return nullptr;
}
+
+ // Relative encodings require adding in the load bias.
+ if (IsEncodingRelative(table_encoding_)) {
+ value += load_bias_;
+ }
info->pc = value;
return info;
}
@@ -174,27 +186,27 @@
memory_.set_data_offset(entries_data_offset_);
memory_.set_cur_offset(cur_entries_offset_);
+ memory_.set_pc_offset(0);
cur_entries_offset_ = 0;
FdeInfo* prev_info = nullptr;
for (size_t current = fde_info_.size();
current < fde_count_ && memory_.cur_offset() < entries_end_; current++) {
- memory_.set_pc_offset(memory_.cur_offset());
- uint64_t value;
- if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value)) {
- last_error_.code = DWARF_ERROR_MEMORY_INVALID;
- last_error_.address = memory_.cur_offset();
- return false;
- }
-
FdeInfo* info = &fde_info_[current];
- if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
+ uint64_t value;
+ if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
+ !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
fde_info_.erase(current);
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
last_error_.address = memory_.cur_offset();
return false;
}
- info->pc = value + 4;
+
+ // Relative encodings require adding in the load bias.
+ if (IsEncodingRelative(table_encoding_)) {
+ value += load_bias_;
+ }
+ info->pc = value;
if (pc < info->pc) {
if (prev_info == nullptr) {
diff --git a/libunwindstack/DwarfEhFrameWithHdr.h b/libunwindstack/DwarfEhFrameWithHdr.h
index 3571166..d16dd10 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.h
+++ b/libunwindstack/DwarfEhFrameWithHdr.h
@@ -38,6 +38,7 @@
using DwarfSectionImpl<AddressType>::entries_offset_;
using DwarfSectionImpl<AddressType>::entries_end_;
using DwarfSectionImpl<AddressType>::last_error_;
+ using DwarfSectionImpl<AddressType>::load_bias_;
struct FdeInfo {
AddressType pc;
@@ -47,7 +48,7 @@
DwarfEhFrameWithHdr(Memory* memory) : DwarfEhFrame<AddressType>(memory) {}
virtual ~DwarfEhFrameWithHdr() = default;
- bool Init(uint64_t offset, uint64_t size) override;
+ bool Init(uint64_t offset, uint64_t size, uint64_t load_bias) override;
bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 65eec65..eb83949 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -420,6 +420,7 @@
last_error_.address = memory_.cur_offset();
return false;
}
+ memory_.set_pc_offset(pc_offset_);
if (!memory_.ReadEncodedValue<AddressType>(encoding, &cie->personality_handler)) {
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
last_error_.address = memory_.cur_offset();
@@ -521,19 +522,19 @@
}
memory_.set_cur_offset(cur_offset);
- if (!memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding & 0xf, &fde->pc_start)) {
- last_error_.code = DWARF_ERROR_MEMORY_INVALID;
- last_error_.address = memory_.cur_offset();
- return false;
- }
+ // The load bias only applies to the start.
+ memory_.set_pc_offset(load_bias_);
+ bool valid = memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding, &fde->pc_start);
fde->pc_start = AdjustPcFromFde(fde->pc_start);
- if (!memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding & 0xf, &fde->pc_end)) {
+ memory_.set_pc_offset(0);
+ if (!valid || !memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding, &fde->pc_end)) {
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
last_error_.address = memory_.cur_offset();
return false;
}
fde->pc_end += fde->pc_start;
+
if (cie->augmentation_string.size() > 0 && cie->augmentation_string[0] == 'z') {
// Augmentation Size
uint64_t aug_length;
@@ -544,6 +545,7 @@
}
uint64_t cur_offset = memory_.cur_offset();
+ memory_.set_pc_offset(pc_offset_);
if (!memory_.ReadEncodedValue<AddressType>(cie->lsda_encoding, &fde->lsda_address)) {
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
last_error_.address = memory_.cur_offset();
@@ -582,17 +584,16 @@
}
template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, uint64_t load_bias,
- const DwarfFde* fde) {
+bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) {
DwarfCfa<AddressType> cfa(&memory_, fde);
// Always print the cie information.
const DwarfCie* cie = fde->cie;
- if (!cfa.Log(indent, pc, load_bias, cie->cfa_instructions_offset, cie->cfa_instructions_end)) {
+ if (!cfa.Log(indent, pc, cie->cfa_instructions_offset, cie->cfa_instructions_end)) {
last_error_ = cfa.last_error();
return false;
}
- if (!cfa.Log(indent, pc, load_bias, fde->cfa_instructions_offset, fde->cfa_instructions_end)) {
+ if (!cfa.Log(indent, pc, fde->cfa_instructions_offset, fde->cfa_instructions_end)) {
last_error_ = cfa.last_error();
return false;
}
@@ -600,15 +601,16 @@
}
template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::Init(uint64_t offset, uint64_t size) {
+bool DwarfSectionImpl<AddressType>::Init(uint64_t offset, uint64_t size, uint64_t load_bias) {
+ load_bias_ = load_bias;
entries_offset_ = offset;
entries_end_ = offset + size;
memory_.clear_func_offset();
memory_.clear_text_offset();
- memory_.set_data_offset(offset);
memory_.set_cur_offset(offset);
- memory_.set_pc_offset(offset);
+ memory_.set_data_offset(offset);
+ pc_offset_ = offset;
return CreateSortedFdeList();
}
@@ -717,6 +719,7 @@
return false;
}
uint64_t value;
+ memory_.set_pc_offset(pc_offset_);
if (!memory_.template ReadEncodedValue<AddressType>(encoding, &value)) {
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
last_error_.address = memory_.cur_offset();
@@ -737,15 +740,13 @@
}
uint64_t start;
- if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &start)) {
- last_error_.code = DWARF_ERROR_MEMORY_INVALID;
- last_error_.address = memory_.cur_offset();
- return false;
- }
+ memory_.set_pc_offset(load_bias_);
+ bool valid = memory_.template ReadEncodedValue<AddressType>(encoding, &start);
start = AdjustPcFromFde(start);
uint64_t length;
- if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &length)) {
+ memory_.set_pc_offset(0);
+ if (!valid || !memory_.template ReadEncodedValue<AddressType>(encoding, &length)) {
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
last_error_.address = memory_.cur_offset();
return false;
@@ -877,7 +878,7 @@
while (first < last) {
size_t current = (first + last) / 2;
const FdeInfo* info = &fdes_[current];
- if (pc >= info->start && pc <= info->end) {
+ if (pc >= info->start && pc < info->end) {
*fde_offset = info->offset;
return true;
}
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 3762107..4723606 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -53,7 +53,7 @@
valid_ = interface_->Init(&load_bias_);
if (valid_) {
- interface_->InitHeaders();
+ interface_->InitHeaders(load_bias_);
if (init_gnu_debugdata) {
InitGnuDebugdata();
} else {
@@ -83,7 +83,7 @@
// is in the uncompressed data.
uint64_t load_bias;
if (gnu->Init(&load_bias)) {
- gnu->InitHeaders();
+ gnu->InitHeaders(load_bias);
interface_->SetGnuDebugdataInterface(gnu);
} else {
// Free all of the memory associated with the gnu_debugdata section.
@@ -103,9 +103,9 @@
bool Elf::GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) {
std::lock_guard<std::mutex> guard(lock_);
- return valid_ && (interface_->GetFunctionName(addr, load_bias_, name, func_offset) ||
- (gnu_debugdata_interface_ && gnu_debugdata_interface_->GetFunctionName(
- addr, load_bias_, name, func_offset)));
+ return valid_ && (interface_->GetFunctionName(addr, name, func_offset) ||
+ (gnu_debugdata_interface_ &&
+ gnu_debugdata_interface_->GetFunctionName(addr, name, func_offset)));
}
bool Elf::GetGlobalVariable(const std::string& name, uint64_t* memory_address) {
@@ -174,7 +174,7 @@
// Lock during the step which can update information in the object.
std::lock_guard<std::mutex> guard(lock_);
- return interface_->Step(adjusted_rel_pc, load_bias_, regs, process_memory, finished);
+ return interface_->Step(adjusted_rel_pc, regs, process_memory, finished);
}
bool Elf::IsValidElf(Memory* memory) {
@@ -220,7 +220,6 @@
if (!valid_ || pc < load_bias_) {
return false;
}
- pc -= load_bias_;
if (interface_->IsValidPc(pc)) {
return true;
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 4c05a1b..954a821 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -124,10 +124,10 @@
}
template <typename AddressType>
-void ElfInterface::InitHeadersWithTemplate() {
+void ElfInterface::InitHeadersWithTemplate(uint64_t load_bias) {
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_)) {
+ if (!eh_frame_->Init(eh_frame_hdr_offset_, eh_frame_hdr_size_, load_bias)) {
eh_frame_.reset(nullptr);
}
}
@@ -136,7 +136,7 @@
// 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_)) {
+ if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_, load_bias)) {
eh_frame_.reset(nullptr);
}
}
@@ -150,7 +150,7 @@
if (debug_frame_offset_ != 0) {
debug_frame_.reset(new DwarfDebugFrame<AddressType>(memory_));
- if (!debug_frame_->Init(debug_frame_offset_, debug_frame_size_)) {
+ if (!debug_frame_->Init(debug_frame_offset_, debug_frame_size_, load_bias)) {
debug_frame_.reset(nullptr);
debug_frame_offset_ = 0;
debug_frame_size_ = static_cast<uint64_t>(-1);
@@ -441,14 +441,14 @@
}
template <typename SymType>
-bool ElfInterface::GetFunctionNameWithTemplate(uint64_t addr, uint64_t load_bias, std::string* name,
+bool ElfInterface::GetFunctionNameWithTemplate(uint64_t addr, std::string* name,
uint64_t* func_offset) {
if (symbols_.empty()) {
return false;
}
for (const auto symbol : symbols_) {
- if (symbol->GetName<SymType>(addr, load_bias, memory_, name, func_offset)) {
+ if (symbol->GetName<SymType>(addr, memory_, name, func_offset)) {
return true;
}
}
@@ -469,34 +469,25 @@
return false;
}
-bool ElfInterface::Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
- bool* finished) {
+bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
last_error_.code = ERROR_NONE;
last_error_.address = 0;
- // Adjust the load bias to get the real relative pc.
- if (pc < load_bias) {
- last_error_.code = ERROR_UNWIND_INFO;
- return false;
- }
- uint64_t adjusted_pc = pc - load_bias;
-
// Try the debug_frame first since it contains the most specific unwind
// information.
DwarfSection* debug_frame = debug_frame_.get();
- if (debug_frame != nullptr && debug_frame->Step(adjusted_pc, regs, process_memory, finished)) {
+ if (debug_frame != nullptr && debug_frame->Step(pc, regs, process_memory, finished)) {
return true;
}
// Try the eh_frame next.
DwarfSection* eh_frame = eh_frame_.get();
- if (eh_frame != nullptr && eh_frame->Step(adjusted_pc, regs, process_memory, finished)) {
+ if (eh_frame != nullptr && eh_frame->Step(pc, regs, process_memory, finished)) {
return true;
}
- // Finally try the gnu_debugdata interface, but always use a zero load bias.
if (gnu_debugdata_interface_ != nullptr &&
- gnu_debugdata_interface_->Step(pc, 0, regs, process_memory, finished)) {
+ gnu_debugdata_interface_->Step(pc, regs, process_memory, finished)) {
return true;
}
@@ -559,8 +550,8 @@
}
// Instantiate all of the needed template functions.
-template void ElfInterface::InitHeadersWithTemplate<uint32_t>();
-template void ElfInterface::InitHeadersWithTemplate<uint64_t>();
+template void ElfInterface::InitHeadersWithTemplate<uint32_t>(uint64_t);
+template void ElfInterface::InitHeadersWithTemplate<uint64_t>(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*);
@@ -574,9 +565,9 @@
template bool ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(std::string*);
template bool ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(std::string*);
-template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, uint64_t, std::string*,
+template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, std::string*,
uint64_t*);
-template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, uint64_t, std::string*,
+template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, std::string*,
uint64_t*);
template bool ElfInterface::GetGlobalVariableWithTemplate<Elf32_Sym>(const std::string&, uint64_t*);
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index f93baeb..9b61599 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -26,6 +26,14 @@
namespace unwindstack {
+bool ElfInterfaceArm::Init(uint64_t* load_bias) {
+ if (!ElfInterface32::Init(load_bias)) {
+ return false;
+ }
+ load_bias_ = *load_bias;
+ return true;
+}
+
bool ElfInterfaceArm::FindEntry(uint32_t pc, uint64_t* entry_offset) {
if (start_offset_ == 0 || total_entries_ == 0) {
last_error_.code = ERROR_UNWIND_INFO;
@@ -96,24 +104,22 @@
return true;
}
-bool ElfInterfaceArm::Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
- bool* finished) {
+bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
// Dwarf unwind information is precise about whether a pc is covered or not,
// but arm unwind information only has ranges of pc. In order to avoid
// incorrectly doing a bad unwind using arm unwind information for a
// different function, always try and unwind with the dwarf information first.
- return ElfInterface32::Step(pc, load_bias, regs, process_memory, finished) ||
- StepExidx(pc, load_bias, regs, process_memory, finished);
+ return ElfInterface32::Step(pc, regs, process_memory, finished) ||
+ StepExidx(pc, regs, process_memory, finished);
}
-bool ElfInterfaceArm::StepExidx(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
- bool* finished) {
+bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
// Adjust the load bias to get the real relative pc.
- if (pc < load_bias) {
+ if (pc < load_bias_) {
last_error_.code = ERROR_UNWIND_INFO;
return false;
}
- pc -= load_bias;
+ pc -= load_bias_;
RegsArm* regs_arm = reinterpret_cast<RegsArm*>(regs);
uint64_t entry_offset;
@@ -167,13 +173,12 @@
return return_value;
}
-bool ElfInterfaceArm::GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
- uint64_t* offset) {
+bool ElfInterfaceArm::GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) {
// For ARM, thumb function symbols have bit 0 set, but the address passed
// in here might not have this bit set and result in a failure to find
// the thumb function names. Adjust the address and offset to account
// for this possible case.
- if (ElfInterface32::GetFunctionName(addr | 1, load_bias, name, offset)) {
+ if (ElfInterface32::GetFunctionName(addr | 1, name, offset)) {
*offset &= ~1;
return true;
}
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index c1597ce..18efb6c 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -64,28 +64,30 @@
iterator begin() { return iterator(this, 0); }
iterator end() { return iterator(this, total_entries_); }
+ bool Init(uint64_t* load_bias) override;
+
bool GetPrel31Addr(uint32_t offset, uint32_t* addr);
bool FindEntry(uint32_t pc, uint64_t* entry_offset);
bool HandleType(uint64_t offset, uint32_t type, uint64_t load_bias) override;
- bool Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
- bool* finished) override;
+ bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) override;
- bool StepExidx(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
- bool* finished);
+ bool StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished);
- bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
- uint64_t* offset) override;
+ bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) override;
uint64_t start_offset() { return start_offset_; }
size_t total_entries() { return total_entries_; }
+ void set_load_bias(uint64_t load_bias) { load_bias_ = load_bias; }
+
protected:
uint64_t start_offset_ = 0;
size_t total_entries_ = 0;
+ uint64_t load_bias_ = 0;
std::unordered_map<size_t, uint32_t> addrs_;
};
diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp
index 25def40..14ebdbb 100644
--- a/libunwindstack/Symbols.cpp
+++ b/libunwindstack/Symbols.cpp
@@ -54,10 +54,7 @@
}
template <typename SymType>
-bool Symbols::GetName(uint64_t addr, uint64_t load_bias, Memory* elf_memory, std::string* name,
- uint64_t* func_offset) {
- addr += load_bias;
-
+bool Symbols::GetName(uint64_t addr, Memory* elf_memory, std::string* name, uint64_t* func_offset) {
if (symbols_.size() != 0) {
const Info* info = GetInfoFromCache(addr);
if (info) {
@@ -81,9 +78,6 @@
if (entry.st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry.st_info) == STT_FUNC) {
// Treat st_value as virtual address.
uint64_t start_offset = entry.st_value;
- if (entry.st_shndx != SHN_ABS) {
- start_offset += load_bias;
- }
uint64_t end_offset = start_offset + entry.st_size;
// Cache the value.
@@ -134,8 +128,8 @@
}
// Instantiate all of the needed template functions.
-template bool Symbols::GetName<Elf32_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
-template bool Symbols::GetName<Elf64_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
+template bool Symbols::GetName<Elf32_Sym>(uint64_t, Memory*, std::string*, uint64_t*);
+template bool Symbols::GetName<Elf64_Sym>(uint64_t, Memory*, std::string*, uint64_t*);
template bool Symbols::GetGlobal<Elf32_Sym>(Memory*, const std::string&, uint64_t*);
template bool Symbols::GetGlobal<Elf64_Sym>(Memory*, const std::string&, uint64_t*);
diff --git a/libunwindstack/Symbols.h b/libunwindstack/Symbols.h
index 7d239c1..7fcd067 100644
--- a/libunwindstack/Symbols.h
+++ b/libunwindstack/Symbols.h
@@ -44,8 +44,7 @@
const Info* GetInfoFromCache(uint64_t addr);
template <typename SymType>
- bool GetName(uint64_t addr, uint64_t load_bias, Memory* elf_memory, std::string* name,
- uint64_t* func_offset);
+ bool GetName(uint64_t addr, Memory* elf_memory, std::string* name, uint64_t* func_offset);
template <typename SymType>
bool GetGlobal(Memory* elf_memory, const std::string& name, uint64_t* memory_address);
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
index 209c54a..847f382 100644
--- a/libunwindstack/include/unwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -78,13 +78,13 @@
DwarfErrorCode LastErrorCode() { return last_error_.code; }
uint64_t LastErrorAddress() { return last_error_.address; }
- virtual bool Init(uint64_t offset, uint64_t size) = 0;
+ virtual bool Init(uint64_t offset, uint64_t size, uint64_t load_bias) = 0;
virtual bool Eval(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*) = 0;
virtual bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) = 0;
- virtual bool Log(uint8_t indent, uint64_t pc, uint64_t load_bias, const DwarfFde* fde) = 0;
+ virtual bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) = 0;
virtual const DwarfFde* GetFdeFromIndex(size_t index) = 0;
@@ -131,7 +131,7 @@
DwarfSectionImpl(Memory* memory) : DwarfSection(memory) {}
virtual ~DwarfSectionImpl() = default;
- bool Init(uint64_t offset, uint64_t size) override;
+ bool Init(uint64_t offset, uint64_t size, uint64_t load_bias) override;
bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
@@ -150,7 +150,7 @@
bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) override;
- bool Log(uint8_t indent, uint64_t pc, uint64_t load_bias, const DwarfFde* fde) override;
+ bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) override;
protected:
bool EvalExpression(const DwarfLocation& loc, Memory* regular_memory, AddressType* value,
@@ -162,6 +162,9 @@
bool CreateSortedFdeList();
+ uint64_t load_bias_ = 0;
+ uint64_t pc_offset_ = 0;
+
std::vector<FdeInfo> fdes_;
uint64_t entries_offset_;
uint64_t entries_end_;
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index 3a221bc..4d25c40 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -54,17 +54,15 @@
virtual bool Init(uint64_t* load_bias) = 0;
- virtual void InitHeaders() = 0;
+ virtual void InitHeaders(uint64_t load_bias) = 0;
virtual bool GetSoname(std::string* name) = 0;
- virtual bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
- uint64_t* offset) = 0;
+ virtual bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) = 0;
virtual bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) = 0;
- virtual bool Step(uint64_t rel_pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
- bool* finished);
+ virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
virtual bool IsValidPc(uint64_t pc);
@@ -100,7 +98,7 @@
protected:
template <typename AddressType>
- void InitHeadersWithTemplate();
+ void InitHeadersWithTemplate(uint64_t load_bias);
template <typename EhdrType, typename PhdrType, typename ShdrType>
bool ReadAllHeaders(uint64_t* load_bias);
@@ -115,8 +113,7 @@
bool GetSonameWithTemplate(std::string* soname);
template <typename SymType>
- bool GetFunctionNameWithTemplate(uint64_t addr, uint64_t load_bias, std::string* name,
- uint64_t* func_offset);
+ bool GetFunctionNameWithTemplate(uint64_t addr, std::string* name, uint64_t* func_offset);
template <typename SymType>
bool GetGlobalVariableWithTemplate(const std::string& name, uint64_t* memory_address);
@@ -169,15 +166,16 @@
return ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(load_bias);
}
- void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint32_t>(); }
+ void InitHeaders(uint64_t load_bias) override {
+ ElfInterface::InitHeadersWithTemplate<uint32_t>(load_bias);
+ }
bool GetSoname(std::string* soname) override {
return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(soname);
}
- bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
- uint64_t* func_offset) override {
- return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, load_bias, name, func_offset);
+ bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
+ return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, name, func_offset);
}
bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) override {
@@ -198,15 +196,16 @@
return ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(load_bias);
}
- void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint64_t>(); }
+ void InitHeaders(uint64_t load_bias) override {
+ ElfInterface::InitHeadersWithTemplate<uint64_t>(load_bias);
+ }
bool GetSoname(std::string* soname) override {
return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(soname);
}
- bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
- uint64_t* func_offset) override {
- return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, load_bias, name, func_offset);
+ bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
+ return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, name, func_offset);
}
bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) override {
diff --git a/libunwindstack/tests/ArmExidxDecodeTest.cpp b/libunwindstack/tests/ArmExidxDecodeTest.cpp
index 8d6d00d..5f3d1ea 100644
--- a/libunwindstack/tests/ArmExidxDecodeTest.cpp
+++ b/libunwindstack/tests/ArmExidxDecodeTest.cpp
@@ -36,8 +36,6 @@
class ArmExidxDecodeTest : public ::testing::TestWithParam<std::string> {
protected:
void Init(Memory* process_memory = nullptr) {
- TearDown();
-
if (process_memory == nullptr) {
process_memory = &process_memory_;
}
@@ -50,8 +48,8 @@
regs_arm_->set_sp(0);
exidx_.reset(new ArmExidx(regs_arm_.get(), &elf_memory_, process_memory));
- if (log_) {
- exidx_->set_log(true);
+ if (log_ != ARM_LOG_NONE) {
+ exidx_->set_log(log_);
exidx_->set_log_indent(0);
exidx_->set_log_skip_execution(false);
}
@@ -60,14 +58,20 @@
}
void SetUp() override {
- if (GetParam() != "no_logging") {
- log_ = false;
+ if (GetParam() == "no_logging") {
+ log_ = ARM_LOG_NONE;
+ } else if (GetParam() == "register_logging") {
+ log_ = ARM_LOG_BY_REG;
} else {
- log_ = true;
+ log_ = ARM_LOG_FULL;
}
- ResetLogs();
elf_memory_.Clear();
process_memory_.Clear();
+ ResetExidx();
+ }
+
+ void ResetExidx() {
+ ResetLogs();
Init();
}
@@ -77,7 +81,7 @@
MemoryFake elf_memory_;
MemoryFake process_memory_;
- bool log_;
+ ArmLogType log_;
};
TEST_P(ArmExidxDecodeTest, vsp_incr) {
@@ -86,38 +90,59 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = vsp + 4\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp + 4\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 + 4\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10004U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->clear();
data_->push_back(0x01);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = vsp + 8\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp + 8\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 + 8\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x1000cU, exidx_->cfa());
+ ASSERT_EQ(0x10008U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->clear();
data_->push_back(0x3f);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = vsp + 256\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp + 256\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 + 256\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x1010cU, exidx_->cfa());
+ ASSERT_EQ(0x10100U, exidx_->cfa());
}
TEST_P(ArmExidxDecodeTest, vsp_decr) {
@@ -126,38 +151,59 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = vsp - 4\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp - 4\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 - 4\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0xfffcU, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->clear();
data_->push_back(0x41);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = vsp - 8\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp - 8\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 - 8\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0xfff4U, exidx_->cfa());
+ ASSERT_EQ(0xfff8U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->clear();
data_->push_back(0x7f);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = vsp - 256\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp - 256\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 - 256\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0xfef4U, exidx_->cfa());
+ ASSERT_EQ(0xff00U, exidx_->cfa());
}
TEST_P(ArmExidxDecodeTest, refuse_unwind) {
@@ -166,10 +212,14 @@
data_->push_back(0x00);
ASSERT_FALSE(exidx_->Decode());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind Refuse to unwind\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Refuse to unwind\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(ARM_STATUS_NO_UNWIND, exidx_->status());
}
@@ -182,29 +232,60 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_TRUE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {r15}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r15}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 4\n"
+ "4 unwind r15 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10004U, exidx_->cfa());
ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
- ResetLogs();
+ ResetExidx();
data_->push_back(0x8f);
data_->push_back(0xff);
for (size_t i = 0; i < 12; i++) {
- process_memory_.SetData32(0x10004 + i * 4, i + 0x20);
+ process_memory_.SetData32(0x10000 + i * 4, i + 0x20);
}
exidx_->set_pc_set(false);
ASSERT_TRUE(exidx_->Decode());
ASSERT_TRUE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15}\n",
- GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15}\n",
+ GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 48\n"
+ "4 unwind r4 = [cfa - 48]\n"
+ "4 unwind r5 = [cfa - 44]\n"
+ "4 unwind r6 = [cfa - 40]\n"
+ "4 unwind r7 = [cfa - 36]\n"
+ "4 unwind r8 = [cfa - 32]\n"
+ "4 unwind r9 = [cfa - 28]\n"
+ "4 unwind r10 = [cfa - 24]\n"
+ "4 unwind r11 = [cfa - 20]\n"
+ "4 unwind r12 = [cfa - 16]\n"
+ "4 unwind r13 = [cfa - 12]\n"
+ "4 unwind r14 = [cfa - 8]\n"
+ "4 unwind r15 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
}
// Popping r13 results in a modified cfa.
ASSERT_EQ(0x29U, exidx_->cfa());
@@ -222,7 +303,7 @@
ASSERT_EQ(0x2aU, (*exidx_->regs())[14]);
ASSERT_EQ(0x2bU, (*exidx_->regs())[15]);
- ResetLogs();
+ ResetExidx();
exidx_->set_cfa(0x10034);
data_->push_back(0x81);
data_->push_back(0x28);
@@ -233,10 +314,22 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {r7, r9, r12}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r7, r9, r12}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 12\n"
+ "4 unwind r7 = [cfa - 12]\n"
+ "4 unwind r9 = [cfa - 8]\n"
+ "4 unwind r12 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10040U, exidx_->cfa());
ASSERT_EQ(0x11U, (*exidx_->regs())[7]);
@@ -255,34 +348,63 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = r0\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = r0\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r0\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(1U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
+ exidx_->set_cfa(0x100);
+ for (size_t i = 0; i < 15; i++) {
+ (*regs_arm_)[i] = i + 1;
+ }
data_->push_back(0x93);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = r3\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = r3\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r3\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(4U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
+ exidx_->set_cfa(0x100);
+ for (size_t i = 0; i < 15; i++) {
+ (*regs_arm_)[i] = i + 1;
+ }
data_->push_back(0x9e);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = r14\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = r14\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r14\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(15U, exidx_->cfa());
}
@@ -292,22 +414,30 @@
data_->push_back(0x9d);
ASSERT_FALSE(exidx_->Decode());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(ARM_STATUS_RESERVED, exidx_->status());
// 10011111: Reserved as prefix for Intel Wireless MMX register to register moves
- ResetLogs();
+ ResetExidx();
data_->push_back(0x9f);
ASSERT_FALSE(exidx_->Decode());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(ARM_STATUS_RESERVED, exidx_->status());
}
@@ -319,53 +449,93 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {r4}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r4}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 4\n"
+ "4 unwind r4 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10004U, exidx_->cfa());
ASSERT_EQ(0x14U, (*exidx_->regs())[4]);
- ResetLogs();
+ ResetExidx();
data_->push_back(0xa3);
- process_memory_.SetData32(0x10004, 0x20);
- process_memory_.SetData32(0x10008, 0x30);
- process_memory_.SetData32(0x1000c, 0x40);
- process_memory_.SetData32(0x10010, 0x50);
+ process_memory_.SetData32(0x10000, 0x20);
+ process_memory_.SetData32(0x10004, 0x30);
+ process_memory_.SetData32(0x10008, 0x40);
+ process_memory_.SetData32(0x1000c, 0x50);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {r4-r7}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r4-r7}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 16\n"
+ "4 unwind r4 = [cfa - 16]\n"
+ "4 unwind r5 = [cfa - 12]\n"
+ "4 unwind r6 = [cfa - 8]\n"
+ "4 unwind r7 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10014U, exidx_->cfa());
+ ASSERT_EQ(0x10010U, exidx_->cfa());
ASSERT_EQ(0x20U, (*exidx_->regs())[4]);
ASSERT_EQ(0x30U, (*exidx_->regs())[5]);
ASSERT_EQ(0x40U, (*exidx_->regs())[6]);
ASSERT_EQ(0x50U, (*exidx_->regs())[7]);
- ResetLogs();
+ ResetExidx();
data_->push_back(0xa7);
- process_memory_.SetData32(0x10014, 0x41);
- process_memory_.SetData32(0x10018, 0x51);
- process_memory_.SetData32(0x1001c, 0x61);
- process_memory_.SetData32(0x10020, 0x71);
- process_memory_.SetData32(0x10024, 0x81);
- process_memory_.SetData32(0x10028, 0x91);
- process_memory_.SetData32(0x1002c, 0xa1);
- process_memory_.SetData32(0x10030, 0xb1);
+ process_memory_.SetData32(0x10000, 0x41);
+ process_memory_.SetData32(0x10004, 0x51);
+ process_memory_.SetData32(0x10008, 0x61);
+ process_memory_.SetData32(0x1000c, 0x71);
+ process_memory_.SetData32(0x10010, 0x81);
+ process_memory_.SetData32(0x10014, 0x91);
+ process_memory_.SetData32(0x10018, 0xa1);
+ process_memory_.SetData32(0x1001c, 0xb1);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {r4-r11}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r4-r11}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 32\n"
+ "4 unwind r4 = [cfa - 32]\n"
+ "4 unwind r5 = [cfa - 28]\n"
+ "4 unwind r6 = [cfa - 24]\n"
+ "4 unwind r7 = [cfa - 20]\n"
+ "4 unwind r8 = [cfa - 16]\n"
+ "4 unwind r9 = [cfa - 12]\n"
+ "4 unwind r10 = [cfa - 8]\n"
+ "4 unwind r11 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10034U, exidx_->cfa());
+ ASSERT_EQ(0x10020U, exidx_->cfa());
ASSERT_EQ(0x41U, (*exidx_->regs())[4]);
ASSERT_EQ(0x51U, (*exidx_->regs())[5]);
ASSERT_EQ(0x61U, (*exidx_->regs())[6]);
@@ -384,57 +554,100 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {r4, r14}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r4, r14}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 8\n"
+ "4 unwind r4 = [cfa - 8]\n"
+ "4 unwind r14 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10008U, exidx_->cfa());
ASSERT_EQ(0x12U, (*exidx_->regs())[4]);
ASSERT_EQ(0x22U, (*exidx_->regs())[14]);
- ResetLogs();
+ ResetExidx();
data_->push_back(0xab);
- process_memory_.SetData32(0x10008, 0x1);
- process_memory_.SetData32(0x1000c, 0x2);
- process_memory_.SetData32(0x10010, 0x3);
- process_memory_.SetData32(0x10014, 0x4);
- process_memory_.SetData32(0x10018, 0x5);
+ process_memory_.SetData32(0x10000, 0x1);
+ process_memory_.SetData32(0x10004, 0x2);
+ process_memory_.SetData32(0x10008, 0x3);
+ process_memory_.SetData32(0x1000c, 0x4);
+ process_memory_.SetData32(0x10010, 0x5);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {r4-r7, r14}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r4-r7, r14}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 20\n"
+ "4 unwind r4 = [cfa - 20]\n"
+ "4 unwind r5 = [cfa - 16]\n"
+ "4 unwind r6 = [cfa - 12]\n"
+ "4 unwind r7 = [cfa - 8]\n"
+ "4 unwind r14 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x1001cU, exidx_->cfa());
+ ASSERT_EQ(0x10014U, exidx_->cfa());
ASSERT_EQ(0x1U, (*exidx_->regs())[4]);
ASSERT_EQ(0x2U, (*exidx_->regs())[5]);
ASSERT_EQ(0x3U, (*exidx_->regs())[6]);
ASSERT_EQ(0x4U, (*exidx_->regs())[7]);
ASSERT_EQ(0x5U, (*exidx_->regs())[14]);
- ResetLogs();
+ ResetExidx();
data_->push_back(0xaf);
- process_memory_.SetData32(0x1001c, 0x1a);
- process_memory_.SetData32(0x10020, 0x2a);
- process_memory_.SetData32(0x10024, 0x3a);
- process_memory_.SetData32(0x10028, 0x4a);
- process_memory_.SetData32(0x1002c, 0x5a);
- process_memory_.SetData32(0x10030, 0x6a);
- process_memory_.SetData32(0x10034, 0x7a);
- process_memory_.SetData32(0x10038, 0x8a);
- process_memory_.SetData32(0x1003c, 0x9a);
+ process_memory_.SetData32(0x10000, 0x1a);
+ process_memory_.SetData32(0x10004, 0x2a);
+ process_memory_.SetData32(0x10008, 0x3a);
+ process_memory_.SetData32(0x1000c, 0x4a);
+ process_memory_.SetData32(0x10010, 0x5a);
+ process_memory_.SetData32(0x10014, 0x6a);
+ process_memory_.SetData32(0x10018, 0x7a);
+ process_memory_.SetData32(0x1001c, 0x8a);
+ process_memory_.SetData32(0x10020, 0x9a);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {r4-r11, r14}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r4-r11, r14}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 36\n"
+ "4 unwind r4 = [cfa - 36]\n"
+ "4 unwind r5 = [cfa - 32]\n"
+ "4 unwind r6 = [cfa - 28]\n"
+ "4 unwind r7 = [cfa - 24]\n"
+ "4 unwind r8 = [cfa - 20]\n"
+ "4 unwind r9 = [cfa - 16]\n"
+ "4 unwind r10 = [cfa - 12]\n"
+ "4 unwind r11 = [cfa - 8]\n"
+ "4 unwind r14 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10040U, exidx_->cfa());
+ ASSERT_EQ(0x10024U, exidx_->cfa());
ASSERT_EQ(0x1aU, (*exidx_->regs())[4]);
ASSERT_EQ(0x2aU, (*exidx_->regs())[5]);
ASSERT_EQ(0x3aU, (*exidx_->regs())[6]);
@@ -451,10 +664,17 @@
data_->push_back(0xb0);
ASSERT_FALSE(exidx_->Decode());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind finish\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind finish\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10000U, exidx_->cfa());
ASSERT_EQ(ARM_STATUS_FINISH, exidx_->status());
@@ -466,10 +686,14 @@
data_->push_back(0x00);
ASSERT_FALSE(exidx_->Decode());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10000U, exidx_->cfa());
ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -477,15 +701,19 @@
// 10110001 xxxxyyyy: Spare (xxxx != 0000)
for (size_t x = 1; x < 16; x++) {
for (size_t y = 0; y < 16; y++) {
- ResetLogs();
+ ResetExidx();
data_->push_back(0xb1);
data_->push_back((x << 4) | y);
ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
- if (log_) {
- ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+ break;
}
ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -494,29 +722,37 @@
// 101101nn: Spare
for (size_t n = 0; n < 4; n++) {
- ResetLogs();
+ ResetExidx();
data_->push_back(0xb4 | n);
ASSERT_FALSE(exidx_->Decode()) << "n = " << n;
ASSERT_EQ("", GetFakeLogBuf()) << "n = " << n;
- if (log_) {
- ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "n = " << n;
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "n = " << n;
+ break;
}
ASSERT_EQ(0x10000U, exidx_->cfa()) << "n = " << n;
ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
}
// 11000111 00000000: Spare
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc7);
data_->push_back(0x00);
ASSERT_FALSE(exidx_->Decode());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10000U, exidx_->cfa());
ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -524,15 +760,19 @@
// 11000111 xxxxyyyy: Spare (xxxx != 0000)
for (size_t x = 1; x < 16; x++) {
for (size_t y = 0; y < 16; y++) {
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc7);
data_->push_back(0x10);
ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
- if (log_) {
- ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+ break;
}
ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -541,14 +781,18 @@
// 11001yyy: Spare (yyy != 000, 001)
for (size_t y = 2; y < 8; y++) {
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc8 | y);
ASSERT_FALSE(exidx_->Decode()) << "y = " << y;
ASSERT_EQ("", GetFakeLogBuf()) << "y = " << y;
- if (log_) {
- ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "y = " << y;
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "y = " << y;
+ break;
}
ASSERT_EQ(0x10000U, exidx_->cfa()) << "y = " << y;
ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -557,14 +801,18 @@
// 11xxxyyy: Spare (xxx != 000, 001, 010)
for (size_t x = 3; x < 8; x++) {
for (size_t y = 0; y < 8; y++) {
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc0 | (x << 3) | y);
ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
- if (log_) {
- ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+ break;
}
ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -580,47 +828,81 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {r0}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r0}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 4\n"
+ "4 unwind r0 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10004U, exidx_->cfa());
ASSERT_EQ(0x45U, (*exidx_->regs())[0]);
- ResetLogs();
+ ResetExidx();
data_->push_back(0xb1);
data_->push_back(0x0a);
- process_memory_.SetData32(0x10004, 0x23);
- process_memory_.SetData32(0x10008, 0x24);
+ process_memory_.SetData32(0x10000, 0x23);
+ process_memory_.SetData32(0x10004, 0x24);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {r1, r3}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r1, r3}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 8\n"
+ "4 unwind r1 = [cfa - 8]\n"
+ "4 unwind r3 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x1000cU, exidx_->cfa());
+ ASSERT_EQ(0x10008U, exidx_->cfa());
ASSERT_EQ(0x23U, (*exidx_->regs())[1]);
ASSERT_EQ(0x24U, (*exidx_->regs())[3]);
- ResetLogs();
+ ResetExidx();
data_->push_back(0xb1);
data_->push_back(0x0f);
- process_memory_.SetData32(0x1000c, 0x65);
- process_memory_.SetData32(0x10010, 0x54);
- process_memory_.SetData32(0x10014, 0x43);
- process_memory_.SetData32(0x10018, 0x32);
+ process_memory_.SetData32(0x10000, 0x65);
+ process_memory_.SetData32(0x10004, 0x54);
+ process_memory_.SetData32(0x10008, 0x43);
+ process_memory_.SetData32(0x1000c, 0x32);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {r0, r1, r2, r3}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r0, r1, r2, r3}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 16\n"
+ "4 unwind r0 = [cfa - 16]\n"
+ "4 unwind r1 = [cfa - 12]\n"
+ "4 unwind r2 = [cfa - 8]\n"
+ "4 unwind r3 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x1001cU, exidx_->cfa());
+ ASSERT_EQ(0x10010U, exidx_->cfa());
ASSERT_EQ(0x65U, (*exidx_->regs())[0]);
ASSERT_EQ(0x54U, (*exidx_->regs())[1]);
ASSERT_EQ(0x43U, (*exidx_->regs())[2]);
@@ -634,28 +916,42 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = vsp + 1024\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp + 1024\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 + 1024\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10400U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xb2);
data_->push_back(0xff);
data_->push_back(0x02);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = vsp + 2048\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp + 2048\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 + 2048\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10c00U, exidx_->cfa());
+ ASSERT_EQ(0x10800U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xb2);
data_->push_back(0xff);
data_->push_back(0x82);
@@ -663,12 +959,19 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = vsp + 3147776\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp + 3147776\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 + 3147776\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x311400U, exidx_->cfa());
+ ASSERT_EQ(0x310800U, exidx_->cfa());
}
TEST_P(ArmExidxDecodeTest, pop_vfp_fstmfdx) {
@@ -678,25 +981,37 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x1000cU, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xb3);
data_->push_back(0x48);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d4-d12}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d4-d12}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10058U, exidx_->cfa());
+ ASSERT_EQ(0x1004cU, exidx_->cfa());
}
TEST_P(ArmExidxDecodeTest, pop_vfp8_fstmfdx) {
@@ -705,36 +1020,54 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x1000cU, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xbb);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d8-d11}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d8-d11}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10030U, exidx_->cfa());
+ ASSERT_EQ(0x10024U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xbf);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10074U, exidx_->cfa());
+ ASSERT_EQ(0x10044U, exidx_->cfa());
}
TEST_P(ArmExidxDecodeTest, pop_mmx_wr10) {
@@ -743,36 +1076,54 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {wR10}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wR10}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10008U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc2);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {wR10-wR12}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wR10-wR12}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10020U, exidx_->cfa());
+ ASSERT_EQ(0x10018U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc5);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {wR10-wR15}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wR10-wR15}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10050U, exidx_->cfa());
+ ASSERT_EQ(0x10030U, exidx_->cfa());
}
TEST_P(ArmExidxDecodeTest, pop_mmx_wr) {
@@ -782,38 +1133,56 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {wR0}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wR0}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10008U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc6);
data_->push_back(0x25);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {wR2-wR7}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wR2-wR7}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10038U, exidx_->cfa());
+ ASSERT_EQ(0x10030U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc6);
data_->push_back(0xff);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {wR15-wR30}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wR15-wR30}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x100b8U, exidx_->cfa());
+ ASSERT_EQ(0x10080U, exidx_->cfa());
}
TEST_P(ArmExidxDecodeTest, pop_mmx_wcgr) {
@@ -823,38 +1192,56 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {wCGR0}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wCGR0}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wCGR register display\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10004U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc7);
data_->push_back(0x0a);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {wCGR1, wCGR3}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wCGR1, wCGR3}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wCGR register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x1000cU, exidx_->cfa());
+ ASSERT_EQ(0x10008U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc7);
data_->push_back(0x0f);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {wCGR0, wCGR1, wCGR2, wCGR3}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wCGR0, wCGR1, wCGR2, wCGR3}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wCGR register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x1001cU, exidx_->cfa());
+ ASSERT_EQ(0x10010U, exidx_->cfa());
}
TEST_P(ArmExidxDecodeTest, pop_vfp16_vpush) {
@@ -864,38 +1251,56 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d16}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d16}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10008U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc8);
data_->push_back(0x14);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d17-d21}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d17-d21}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10030U, exidx_->cfa());
+ ASSERT_EQ(0x10028U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc8);
data_->push_back(0xff);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d31-d46}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d31-d46}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x100b0U, exidx_->cfa());
+ ASSERT_EQ(0x10080U, exidx_->cfa());
}
TEST_P(ArmExidxDecodeTest, pop_vfp_vpush) {
@@ -905,38 +1310,56 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10008U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc9);
data_->push_back(0x23);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d2-d5}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d2-d5}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10028U, exidx_->cfa());
+ ASSERT_EQ(0x10020U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc9);
data_->push_back(0xff);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d15-d30}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d15-d30}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x100a8U, exidx_->cfa());
+ ASSERT_EQ(0x10080U, exidx_->cfa());
}
TEST_P(ArmExidxDecodeTest, pop_vfp8_vpush) {
@@ -945,36 +1368,54 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10008U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xd2);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d8-d10}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d8-d10}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10020U, exidx_->cfa());
+ ASSERT_EQ(0x10018U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xd7);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10060U, exidx_->cfa());
+ ASSERT_EQ(0x10040U, exidx_->cfa());
}
TEST_P(ArmExidxDecodeTest, expect_truncated) {
@@ -1047,32 +1488,147 @@
TEST_P(ArmExidxDecodeTest, eval_multiple_decodes) {
// vsp = vsp + 4
data_->push_back(0x00);
- // vsp = vsp + 8
+ // vsp = vsp + 12
data_->push_back(0x02);
// Finish
data_->push_back(0xb0);
ASSERT_TRUE(exidx_->Eval());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = vsp + 4\n"
- "4 unwind vsp = vsp + 12\n"
- "4 unwind finish\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ(
+ "4 unwind vsp = vsp + 4\n"
+ "4 unwind vsp = vsp + 12\n"
+ "4 unwind finish\n",
+ GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 + 16\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10010U, exidx_->cfa());
ASSERT_FALSE(exidx_->pc_set());
}
+TEST_P(ArmExidxDecodeTest, eval_vsp_add_after_pop) {
+ // Pop {r15}
+ data_->push_back(0x88);
+ data_->push_back(0x00);
+ // vsp = vsp + 12
+ data_->push_back(0x02);
+ // Finish
+ data_->push_back(0xb0);
+ process_memory_.SetData32(0x10000, 0x10);
+
+ ASSERT_TRUE(exidx_->Eval());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ(
+ "4 unwind pop {r15}\n"
+ "4 unwind vsp = vsp + 12\n"
+ "4 unwind finish\n",
+ GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 16\n"
+ "4 unwind r15 = [cfa - 16]\n",
+ GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10010U, exidx_->cfa());
+ ASSERT_TRUE(exidx_->pc_set());
+ ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
+}
+
+TEST_P(ArmExidxDecodeTest, eval_vsp_add_large_after_pop) {
+ // Pop {r15}
+ data_->push_back(0x88);
+ data_->push_back(0x00);
+ // vsp = vsp + 1024
+ data_->push_back(0xb2);
+ data_->push_back(0x7f);
+ // Finish
+ data_->push_back(0xb0);
+ process_memory_.SetData32(0x10000, 0x10);
+
+ ASSERT_TRUE(exidx_->Eval());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ(
+ "4 unwind pop {r15}\n"
+ "4 unwind vsp = vsp + 1024\n"
+ "4 unwind finish\n",
+ GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 1028\n"
+ "4 unwind r15 = [cfa - 1028]\n",
+ GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10404U, exidx_->cfa());
+ ASSERT_TRUE(exidx_->pc_set());
+ ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
+}
+
+TEST_P(ArmExidxDecodeTest, eval_vsp_sub_after_pop) {
+ // Pop {r15}
+ data_->push_back(0x88);
+ data_->push_back(0x00);
+ // vsp = vsp - 4
+ data_->push_back(0x41);
+ // Finish
+ data_->push_back(0xb0);
+ process_memory_.SetData32(0x10000, 0x10);
+
+ ASSERT_TRUE(exidx_->Eval());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ(
+ "4 unwind pop {r15}\n"
+ "4 unwind vsp = vsp - 8\n"
+ "4 unwind finish\n",
+ GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 - 4\n"
+ "4 unwind r15 = [cfa + 4]\n",
+ GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0xfffcU, exidx_->cfa());
+ ASSERT_TRUE(exidx_->pc_set());
+ ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
+}
+
TEST_P(ArmExidxDecodeTest, eval_pc_set) {
// vsp = vsp + 4
data_->push_back(0x00);
- // vsp = vsp + 8
+ // vsp = vsp + 12
data_->push_back(0x02);
// Pop {r15}
data_->push_back(0x88);
data_->push_back(0x00);
- // vsp = vsp + 8
+ // vsp = vsp + 12
data_->push_back(0x02);
// Finish
data_->push_back(0xb0);
@@ -1080,20 +1636,33 @@
process_memory_.SetData32(0x10010, 0x10);
ASSERT_TRUE(exidx_->Eval());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = vsp + 4\n"
- "4 unwind vsp = vsp + 12\n"
- "4 unwind pop {r15}\n"
- "4 unwind vsp = vsp + 12\n"
- "4 unwind finish\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ(
+ "4 unwind vsp = vsp + 4\n"
+ "4 unwind vsp = vsp + 12\n"
+ "4 unwind pop {r15}\n"
+ "4 unwind vsp = vsp + 12\n"
+ "4 unwind finish\n",
+ GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 32\n"
+ "4 unwind r15 = [cfa - 16]\n",
+ GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10020U, exidx_->cfa());
ASSERT_TRUE(exidx_->pc_set());
ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
}
-INSTANTIATE_TEST_CASE_P(, ArmExidxDecodeTest, ::testing::Values("logging", "no_logging"));
+INSTANTIATE_TEST_CASE_P(, ArmExidxDecodeTest,
+ ::testing::Values("logging", "register_logging", "no_logging"));
} // namespace unwindstack
diff --git a/libunwindstack/tests/ArmExidxExtractTest.cpp b/libunwindstack/tests/ArmExidxExtractTest.cpp
index 8d0f0e5..79c799c 100644
--- a/libunwindstack/tests/ArmExidxExtractTest.cpp
+++ b/libunwindstack/tests/ArmExidxExtractTest.cpp
@@ -301,7 +301,7 @@
elf_memory_.SetData32(0x1000, 0x7fff2340);
elf_memory_.SetData32(0x1004, 1);
- exidx_->set_log(true);
+ exidx_->set_log(ARM_LOG_FULL);
exidx_->set_log_indent(0);
exidx_->set_log_skip_execution(false);
@@ -316,7 +316,7 @@
elf_memory_.SetData32(0x4000, 0x7ffa3000);
elf_memory_.SetData32(0x4004, 0x80a8b0b0);
- exidx_->set_log(true);
+ exidx_->set_log(ARM_LOG_FULL);
exidx_->set_log_indent(0);
exidx_->set_log_skip_execution(false);
@@ -330,7 +330,7 @@
elf_memory_.SetData32(0x6234, 0x2);
elf_memory_.SetData32(0x6238, 0x00112233);
- exidx_->set_log(true);
+ exidx_->set_log(ARM_LOG_FULL);
exidx_->set_log_indent(0);
exidx_->set_log_skip_execution(false);
diff --git a/libunwindstack/tests/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp
index b17ca33..bb2e8f0 100644
--- a/libunwindstack/tests/DwarfCfaLogTest.cpp
+++ b/libunwindstack/tests/DwarfCfaLogTest.cpp
@@ -79,7 +79,7 @@
this->memory_.SetMemory(0x2000, std::vector<uint8_t>{i});
ResetLogs();
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2001));
std::string expected = "4 unwind Illegal\n";
expected += android::base::StringPrintf("4 unwind Raw Data: 0x%02x\n", i);
ASSERT_EQ(expected, GetFakeLogPrint());
@@ -90,7 +90,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_nop) {
this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x00});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2001));
std::string expected =
"4 unwind DW_CFA_nop\n"
"4 unwind Raw Data: 0x00\n";
@@ -101,7 +101,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_offset) {
this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x83, 0x04});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2002));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2002));
std::string expected =
"4 unwind DW_CFA_offset register(3) 4\n"
"4 unwind Raw Data: 0x83 0x04\n";
@@ -111,7 +111,7 @@
ResetLogs();
this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x83, 0x84, 0x01});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2100, 0x2103));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2100, 0x2103));
expected =
"4 unwind DW_CFA_offset register(3) 132\n"
"4 unwind Raw Data: 0x83 0x84 0x01\n";
@@ -122,7 +122,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_offset_extended) {
this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x05, 0x03, 0x02});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x500, 0x503));
std::string expected =
"4 unwind DW_CFA_offset_extended register(3) 2\n"
"4 unwind Raw Data: 0x05 0x03 0x02\n";
@@ -132,7 +132,7 @@
ResetLogs();
this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x05, 0x81, 0x01, 0x82, 0x12});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1500, 0x1505));
expected =
"4 unwind DW_CFA_offset_extended register(129) 2306\n"
"4 unwind Raw Data: 0x05 0x81 0x01 0x82 0x12\n";
@@ -143,7 +143,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_offset_extended_sf) {
this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x11, 0x05, 0x10});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x500, 0x503));
std::string expected =
"4 unwind DW_CFA_offset_extended_sf register(5) 16\n"
"4 unwind Raw Data: 0x11 0x05 0x10\n";
@@ -154,7 +154,7 @@
ResetLogs();
this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x11, 0x86, 0x01, 0xff, 0x7f});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1500, 0x1505));
expected =
"4 unwind DW_CFA_offset_extended_sf register(134) -1\n"
"4 unwind Raw Data: 0x11 0x86 0x01 0xff 0x7f\n";
@@ -165,7 +165,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_restore) {
this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0xc2});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2001));
std::string expected =
"4 unwind DW_CFA_restore register(2)\n"
"4 unwind Raw Data: 0xc2\n";
@@ -175,7 +175,7 @@
ResetLogs();
this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x82, 0x04, 0xc2});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x3000, 0x3003));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x3000, 0x3003));
expected =
"4 unwind DW_CFA_offset register(2) 4\n"
"4 unwind Raw Data: 0x82 0x04\n"
@@ -188,7 +188,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_restore_extended) {
this->memory_.SetMemory(0x4000, std::vector<uint8_t>{0x06, 0x08});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4000, 0x4002));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x4000, 0x4002));
std::string expected =
"4 unwind DW_CFA_restore_extended register(8)\n"
"4 unwind Raw Data: 0x06 0x08\n";
@@ -198,7 +198,7 @@
ResetLogs();
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x05, 0x82, 0x02, 0x04, 0x06, 0x82, 0x02});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x5000, 0x5007));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x5000, 0x5007));
expected =
"4 unwind DW_CFA_offset_extended register(258) 4\n"
"4 unwind Raw Data: 0x05 0x82 0x02 0x04\n"
@@ -228,7 +228,7 @@
this->memory_.SetMemory(0x50, buffer, sizeof(buffer));
ResetLogs();
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x50, 0x51 + sizeof(TypeParam)));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x50, 0x51 + sizeof(TypeParam)));
std::string expected = "4 unwind DW_CFA_set_loc " + address_str + "\n";
expected += "4 unwind " + raw_data + "\n";
expected += "4 unwind \n";
@@ -240,7 +240,7 @@
ResetLogs();
this->fde_.pc_start = address + 0x10;
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x50, 0x51 + sizeof(TypeParam)));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x50, 0x51 + sizeof(TypeParam)));
expected = "4 unwind DW_CFA_set_loc " + address_str + "\n";
expected += "4 unwind " + raw_data + "\n";
expected += "4 unwind \n";
@@ -252,7 +252,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc) {
this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x44});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x201));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x201));
std::string expected =
"4 unwind DW_CFA_advance_loc 4\n"
"4 unwind Raw Data: 0x44\n"
@@ -260,22 +260,12 @@
"4 unwind PC 0x2010\n";
ASSERT_EQ(expected, GetFakeLogPrint());
ASSERT_EQ("", GetFakeLogBuf());
-
- ResetLogs();
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x200, 0x201));
- expected =
- "4 unwind DW_CFA_advance_loc 4\n"
- "4 unwind Raw Data: 0x44\n"
- "4 unwind \n"
- "4 unwind PC 0x2110\n";
- ASSERT_EQ(expected, GetFakeLogPrint());
- ASSERT_EQ("", GetFakeLogBuf());
}
TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc1) {
this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x02, 0x04});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x202));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x202));
std::string expected =
"4 unwind DW_CFA_advance_loc1 4\n"
"4 unwind Raw Data: 0x02 0x04\n"
@@ -283,22 +273,12 @@
"4 unwind PC 0x2004\n";
ASSERT_EQ(expected, GetFakeLogPrint());
ASSERT_EQ("", GetFakeLogBuf());
-
- ResetLogs();
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x10, 0x200, 0x202));
- expected =
- "4 unwind DW_CFA_advance_loc1 4\n"
- "4 unwind Raw Data: 0x02 0x04\n"
- "4 unwind \n"
- "4 unwind PC 0x2014\n";
- ASSERT_EQ(expected, GetFakeLogPrint());
- ASSERT_EQ("", GetFakeLogBuf());
}
TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc2) {
this->memory_.SetMemory(0x600, std::vector<uint8_t>{0x03, 0x04, 0x03});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x600, 0x603));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x600, 0x603));
std::string expected =
"4 unwind DW_CFA_advance_loc2 772\n"
"4 unwind Raw Data: 0x03 0x04 0x03\n"
@@ -306,22 +286,12 @@
"4 unwind PC 0x2304\n";
ASSERT_EQ(expected, GetFakeLogPrint());
ASSERT_EQ("", GetFakeLogBuf());
-
- ResetLogs();
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1000, 0x600, 0x603));
- expected =
- "4 unwind DW_CFA_advance_loc2 772\n"
- "4 unwind Raw Data: 0x03 0x04 0x03\n"
- "4 unwind \n"
- "4 unwind PC 0x3304\n";
- ASSERT_EQ(expected, GetFakeLogPrint());
- ASSERT_EQ("", GetFakeLogBuf());
}
TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc4) {
this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x04, 0x04, 0x03, 0x02, 0x01});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x505));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x500, 0x505));
std::string expected =
"4 unwind DW_CFA_advance_loc4 16909060\n"
"4 unwind Raw Data: 0x04 0x04 0x03 0x02 0x01\n"
@@ -329,22 +299,12 @@
"4 unwind PC 0x1022304\n";
ASSERT_EQ(expected, GetFakeLogPrint());
ASSERT_EQ("", GetFakeLogBuf());
-
- ResetLogs();
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x500, 0x505));
- expected =
- "4 unwind DW_CFA_advance_loc4 16909060\n"
- "4 unwind Raw Data: 0x04 0x04 0x03 0x02 0x01\n"
- "4 unwind \n"
- "4 unwind PC 0x1024304\n";
- ASSERT_EQ(expected, GetFakeLogPrint());
- ASSERT_EQ("", GetFakeLogBuf());
}
TYPED_TEST_P(DwarfCfaLogTest, cfa_undefined) {
this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x07, 0x09});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xa02));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0xa00, 0xa02));
std::string expected =
"4 unwind DW_CFA_undefined register(9)\n"
"4 unwind Raw Data: 0x07 0x09\n";
@@ -355,7 +315,7 @@
dwarf_loc_regs_t cie_loc_regs;
this->memory_.SetMemory(0x1a00, std::vector<uint8_t>{0x07, 0x81, 0x01});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1a00, 0x1a03));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1a00, 0x1a03));
expected =
"4 unwind DW_CFA_undefined register(129)\n"
"4 unwind Raw Data: 0x07 0x81 0x01\n";
@@ -366,7 +326,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_same) {
this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x08, 0x7f});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
std::string expected =
"4 unwind DW_CFA_same_value register(127)\n"
"4 unwind Raw Data: 0x08 0x7f\n";
@@ -376,7 +336,7 @@
ResetLogs();
this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x08, 0xff, 0x01});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2100, 0x2103));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2100, 0x2103));
expected =
"4 unwind DW_CFA_same_value register(255)\n"
"4 unwind Raw Data: 0x08 0xff 0x01\n";
@@ -387,7 +347,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_register) {
this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x303));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x300, 0x303));
std::string expected =
"4 unwind DW_CFA_register register(2) register(1)\n"
"4 unwind Raw Data: 0x09 0x02 0x01\n";
@@ -397,7 +357,7 @@
ResetLogs();
this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x09, 0xff, 0x01, 0xff, 0x03});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4300, 0x4305));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x4300, 0x4305));
expected =
"4 unwind DW_CFA_register register(255) register(511)\n"
"4 unwind Raw Data: 0x09 0xff 0x01 0xff 0x03\n";
@@ -408,7 +368,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_state) {
this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x0a});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x301));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x300, 0x301));
std::string expected =
"4 unwind DW_CFA_remember_state\n"
@@ -419,7 +379,7 @@
ResetLogs();
this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x0b});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4300, 0x4301));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x4300, 0x4301));
expected =
"4 unwind DW_CFA_restore_state\n"
@@ -431,7 +391,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_state_cfa_offset_restore) {
this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x0a, 0x0e, 0x40, 0x0b});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x3000, 0x3004));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x3000, 0x3004));
std::string expected =
"4 unwind DW_CFA_remember_state\n"
@@ -447,7 +407,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa) {
this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0c, 0x7f, 0x74});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x103));
std::string expected =
"4 unwind DW_CFA_def_cfa register(127) 116\n"
@@ -458,7 +418,7 @@
ResetLogs();
this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0c, 0xff, 0x02, 0xf4, 0x04});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x205));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x205));
expected =
"4 unwind DW_CFA_def_cfa register(383) 628\n"
@@ -470,7 +430,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_sf) {
this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x12, 0x30, 0x25});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x103));
std::string expected =
"4 unwind DW_CFA_def_cfa_sf register(48) 37\n"
@@ -482,7 +442,7 @@
ResetLogs();
this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x12, 0xa3, 0x01, 0xfa, 0x7f});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x205));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x205));
expected =
"4 unwind DW_CFA_def_cfa_sf register(163) -6\n"
@@ -494,7 +454,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_register) {
this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0d, 0x72});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
std::string expected =
"4 unwind DW_CFA_def_cfa_register register(114)\n"
@@ -505,7 +465,7 @@
ResetLogs();
this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0d, 0xf9, 0x20});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x203));
expected =
"4 unwind DW_CFA_def_cfa_register register(4217)\n"
@@ -517,7 +477,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_offset) {
this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0e, 0x59});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
std::string expected =
"4 unwind DW_CFA_def_cfa_offset 89\n"
@@ -526,7 +486,7 @@
ASSERT_EQ("", GetFakeLogBuf());
ResetLogs();
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
expected =
"4 unwind DW_CFA_def_cfa_offset 89\n"
@@ -537,7 +497,7 @@
ResetLogs();
this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0e, 0xd4, 0x0a});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x203));
expected =
"4 unwind DW_CFA_def_cfa_offset 1364\n"
@@ -549,7 +509,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_offset_sf) {
this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x13, 0x23});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
std::string expected =
"4 unwind DW_CFA_def_cfa_offset_sf 35\n"
@@ -558,7 +518,7 @@
ASSERT_EQ("", GetFakeLogBuf());
ResetLogs();
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
expected =
"4 unwind DW_CFA_def_cfa_offset_sf 35\n"
@@ -570,7 +530,7 @@
ResetLogs();
this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x13, 0xf6, 0x7f});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x203));
expected =
"4 unwind DW_CFA_def_cfa_offset_sf -10\n"
@@ -582,7 +542,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_expression) {
this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0f, 0x04, 0x01, 0x02, 0x04, 0x05});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x106));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x106));
std::string expected =
"4 unwind DW_CFA_def_cfa_expression 4\n"
@@ -614,7 +574,7 @@
}
expected += '\n';
this->memory_.SetMemory(0x200, ops);
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x284));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x284));
expected = "4 unwind DW_CFA_def_cfa_expression 129\n" + expected;
ASSERT_EQ(expected + op_string, GetFakeLogPrint());
@@ -624,7 +584,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_expression) {
this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x10, 0x04, 0x02, 0xc0, 0xc1});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x105));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x105));
std::string expected =
"4 unwind DW_CFA_expression register(4) 2\n"
@@ -652,7 +612,7 @@
expected = "4 unwind DW_CFA_expression register(255) 130\n" + expected + "\n";
this->memory_.SetMemory(0x200, ops);
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x287));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x287));
ASSERT_EQ(expected + op_string, GetFakeLogPrint());
ASSERT_EQ("", GetFakeLogBuf());
@@ -661,7 +621,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_val_offset) {
this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x14, 0x45, 0x54});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x103));
std::string expected =
"4 unwind DW_CFA_val_offset register(69) 84\n"
@@ -672,7 +632,7 @@
ResetLogs();
this->memory_.SetMemory(0x400, std::vector<uint8_t>{0x14, 0xa2, 0x02, 0xb4, 0x05});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x400, 0x405));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x400, 0x405));
expected =
"4 unwind DW_CFA_val_offset register(290) 692\n"
@@ -684,7 +644,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_val_offset_sf) {
this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x15, 0x56, 0x12});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x103));
std::string expected =
"4 unwind DW_CFA_val_offset_sf register(86) 18\n"
@@ -696,7 +656,7 @@
ResetLogs();
this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x15, 0xff, 0x01, 0xc0, 0x7f});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xa05));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0xa00, 0xa05));
expected =
"4 unwind DW_CFA_val_offset_sf register(255) -64\n"
@@ -708,7 +668,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_val_expression) {
this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x16, 0x05, 0x02, 0xb0, 0xb1});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x105));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x105));
std::string expected =
"4 unwind DW_CFA_val_expression register(5) 2\n"
@@ -737,7 +697,7 @@
this->memory_.SetMemory(0xa00, ops);
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xaad));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0xa00, 0xaad));
ASSERT_EQ(expected + op_string, GetFakeLogPrint());
ASSERT_EQ("", GetFakeLogBuf());
@@ -746,7 +706,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_gnu_args_size) {
this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x2e, 0x04});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2002));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2002));
std::string expected =
"4 unwind DW_CFA_GNU_args_size 4\n"
@@ -757,7 +717,7 @@
ResetLogs();
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x2e, 0xa4, 0x80, 0x04});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x5000, 0x5004));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x5000, 0x5004));
expected =
"4 unwind DW_CFA_GNU_args_size 65572\n"
@@ -769,7 +729,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_gnu_negative_offset_extended) {
this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x2f, 0x08, 0x10});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x500, 0x503));
std::string expected =
"4 unwind DW_CFA_GNU_negative_offset_extended register(8) 16\n"
@@ -780,7 +740,7 @@
ResetLogs();
this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x2f, 0x81, 0x02, 0xff, 0x01});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1500, 0x1505));
expected =
"4 unwind DW_CFA_GNU_negative_offset_extended register(257) 255\n"
@@ -792,7 +752,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_register_override) {
this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01, 0x09, 0x02, 0x04});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x306));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x300, 0x306));
std::string expected =
"4 unwind DW_CFA_register register(2) register(1)\n"
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
index c28a41e..3a52044 100644
--- a/libunwindstack/tests/DwarfDebugFrameTest.cpp
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -103,7 +103,7 @@
this->memory_.SetData32(0x5508, 0x4500);
this->memory_.SetData32(0x550c, 0x500);
- ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
ASSERT_EQ(4U, this->debug_frame_->TestGetFdeCount());
typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
@@ -142,7 +142,7 @@
this->memory_.SetData32(0x5108, 0x1500);
this->memory_.SetData32(0x510c, 0x200);
- ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600));
+ ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600, 0));
ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->LastErrorCode());
}
@@ -181,7 +181,7 @@
this->memory_.SetData32(0x5508, 0x4500);
this->memory_.SetData32(0x550c, 0x500);
- ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
ASSERT_EQ(2U, this->debug_frame_->TestGetFdeCount());
}
@@ -226,7 +226,7 @@
this->memory_.SetData64(0x5514, 0x4500);
this->memory_.SetData64(0x551c, 0x500);
- ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
ASSERT_EQ(4U, this->debug_frame_->TestGetFdeCount());
typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
@@ -267,7 +267,7 @@
this->memory_.SetData64(0x5114, 0x1500);
this->memory_.SetData64(0x511c, 0x200);
- ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600));
+ ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600, 0));
ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->LastErrorCode());
}
@@ -312,10 +312,48 @@
this->memory_.SetData64(0x5514, 0x4500);
this->memory_.SetData64(0x551c, 0x500);
- ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
ASSERT_EQ(2U, this->debug_frame_->TestGetFdeCount());
}
+TYPED_TEST_P(DwarfDebugFrameTest, Init_non_zero_load_bias) {
+ // CIE 32 information.
+ this->memory_.SetData32(0x5000, 0xfc);
+ this->memory_.SetData32(0x5004, 0xffffffff);
+ this->memory_.SetData8(0x5008, 1);
+ this->memory_.SetData8(0x5009, 'z');
+ this->memory_.SetData8(0x500a, 'R');
+ this->memory_.SetData8(0x500b, '\0');
+ this->memory_.SetData8(0x500c, 0);
+ this->memory_.SetData8(0x500d, 0);
+ this->memory_.SetData8(0x500e, 0);
+ this->memory_.SetData8(0x500f, 0);
+ this->memory_.SetData8(0x5010, 0x1b);
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x5100, 0xfc);
+ this->memory_.SetData32(0x5104, 0);
+ this->memory_.SetData32(0x5108, 0x1500);
+ this->memory_.SetData32(0x510c, 0x200);
+ this->memory_.SetData8(0x5110, 0);
+ this->memory_.SetData8(0x5111, 0);
+
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200, 0x1000));
+ ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount());
+
+ typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+
+ this->debug_frame_->TestGetFdeInfo(0, &info);
+ EXPECT_EQ(0x5100U, info.offset);
+ EXPECT_EQ(0x2500U, info.start);
+ EXPECT_EQ(0x2700U, info.end);
+
+ const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x2504);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x2500U, fde->pc_start);
+ EXPECT_EQ(0x2700U, fde->pc_end);
+}
+
TYPED_TEST_P(DwarfDebugFrameTest, Init_version1) {
// CIE 32 information.
this->memory_.SetData32(0x5000, 0xfc);
@@ -340,7 +378,7 @@
this->memory_.SetData16(0x5108, 0x1500);
this->memory_.SetData16(0x510a, 0x200);
- ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200));
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200, 0));
ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount());
typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
@@ -383,7 +421,7 @@
this->memory_.SetData16(0x5108, 0x1500);
this->memory_.SetData16(0x510a, 0x200);
- ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200));
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200, 0));
ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount());
typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
@@ -538,8 +576,8 @@
REGISTER_TYPED_TEST_CASE_P(DwarfDebugFrameTest, Init32, Init32_fde_not_following_cie,
Init32_do_not_fail_on_bad_next_entry, Init64,
Init64_do_not_fail_on_bad_next_entry, Init64_fde_not_following_cie,
- Init_version1, Init_version4, GetFdeOffsetFromPc, GetCieFde32,
- GetCieFde64);
+ Init_non_zero_load_bias, Init_version1, Init_version4,
+ GetFdeOffsetFromPc, GetCieFde32, GetCieFde64);
typedef ::testing::Types<uint32_t, uint64_t> DwarfDebugFrameTestTypes;
INSTANTIATE_TYPED_TEST_CASE_P(, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
index a73db65..e8d53e6 100644
--- a/libunwindstack/tests/DwarfEhFrameTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -103,7 +103,7 @@
this->memory_.SetData32(0x5508, 0x4500);
this->memory_.SetData32(0x550c, 0x500);
- ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x600));
+ ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x600, 0));
ASSERT_EQ(4U, this->eh_frame_->TestGetFdeCount());
typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
@@ -142,7 +142,7 @@
this->memory_.SetData32(0x5108, 0x1500);
this->memory_.SetData32(0x510c, 0x200);
- ASSERT_FALSE(this->eh_frame_->Init(0x5000, 0x600));
+ ASSERT_FALSE(this->eh_frame_->Init(0x5000, 0x600, 0));
ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->eh_frame_->LastErrorCode());
}
@@ -187,7 +187,7 @@
this->memory_.SetData64(0x5514, 0x4500);
this->memory_.SetData64(0x551c, 0x500);
- ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x600));
+ ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x600, 0));
ASSERT_EQ(4U, this->eh_frame_->TestGetFdeCount());
typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
@@ -228,10 +228,48 @@
this->memory_.SetData64(0x5114, 0x1500);
this->memory_.SetData64(0x511c, 0x200);
- ASSERT_FALSE(this->eh_frame_->Init(0x5000, 0x600));
+ ASSERT_FALSE(this->eh_frame_->Init(0x5000, 0x600, 0));
ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->eh_frame_->LastErrorCode());
}
+TYPED_TEST_P(DwarfEhFrameTest, Init_non_zero_load_bias) {
+ // CIE 32 information.
+ this->memory_.SetData32(0x5000, 0xfc);
+ this->memory_.SetData32(0x5004, 0);
+ this->memory_.SetData8(0x5008, 1);
+ this->memory_.SetData8(0x5009, 'z');
+ this->memory_.SetData8(0x500a, 'R');
+ this->memory_.SetData8(0x500b, '\0');
+ this->memory_.SetData8(0x500c, 0);
+ this->memory_.SetData8(0x500d, 0);
+ this->memory_.SetData8(0x500e, 0);
+ this->memory_.SetData8(0x500f, 0);
+ this->memory_.SetData8(0x5010, 0x1b);
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x5100, 0xfc);
+ this->memory_.SetData32(0x5104, 0x104);
+ this->memory_.SetData32(0x5108, 0x1500);
+ this->memory_.SetData32(0x510c, 0x200);
+ this->memory_.SetData8(0x5110, 0);
+ this->memory_.SetData8(0x5111, 0);
+
+ ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x200, 0x2000));
+ ASSERT_EQ(1U, this->eh_frame_->TestGetFdeCount());
+
+ typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
+
+ this->eh_frame_->TestGetFdeInfo(0, &info);
+ EXPECT_EQ(0x5100U, info.offset);
+ EXPECT_EQ(0x8608U, info.start);
+ EXPECT_EQ(0x8808U, info.end);
+
+ const DwarfFde* fde = this->eh_frame_->GetFdeFromPc(0x8700);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x8608U, fde->pc_start);
+ EXPECT_EQ(0x8808U, fde->pc_end);
+}
+
TYPED_TEST_P(DwarfEhFrameTest, Init_version1) {
// CIE 32 information.
this->memory_.SetData32(0x5000, 0xfc);
@@ -256,7 +294,7 @@
this->memory_.SetData16(0x5108, 0x1500);
this->memory_.SetData16(0x510a, 0x200);
- ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x200));
+ ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x200, 0));
ASSERT_EQ(1U, this->eh_frame_->TestGetFdeCount());
typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
@@ -299,7 +337,7 @@
this->memory_.SetData16(0x5108, 0x1500);
this->memory_.SetData16(0x510a, 0x200);
- ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x200));
+ ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x200, 0));
ASSERT_EQ(1U, this->eh_frame_->TestGetFdeCount());
typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
@@ -450,8 +488,8 @@
}
REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameTest, Init32, Init32_fde_not_following_cie, Init64,
- Init64_fde_not_following_cie, Init_version1, Init_version4,
- GetFdeOffsetFromPc, GetCieFde32, GetCieFde64);
+ Init64_fde_not_following_cie, Init_non_zero_load_bias, Init_version1,
+ Init_version4, GetFdeOffsetFromPc, GetCieFde32, GetCieFde64);
typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameTestTypes;
INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameTest, DwarfEhFrameTestTypes);
diff --git a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
index 4240419..19c7b98 100644
--- a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
@@ -83,7 +83,7 @@
this->memory_.SetData16(0x1004, 0x500);
this->memory_.SetData32(0x1006, 126);
- ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100));
+ 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());
@@ -97,19 +97,66 @@
// Verify a zero fde count fails to init.
this->memory_.SetData32(0x1006, 0);
- ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
+ ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100, 0));
ASSERT_EQ(DWARF_ERROR_NO_FDES, this->eh_frame_->LastErrorCode());
// Verify an unexpected version will cause a fail.
this->memory_.SetData32(0x1006, 126);
this->memory_.SetData8(0x1000, 0);
- ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
+ ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100, 0));
ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->LastErrorCode());
this->memory_.SetData8(0x1000, 2);
- ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
+ ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100, 0));
ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->LastErrorCode());
}
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, Init_non_zero_load_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_.SetData8(0x1308, 1);
+ this->memory_.SetData8(0x1309, 'z');
+ this->memory_.SetData8(0x130a, 'R');
+ this->memory_.SetData8(0x130b, '\0');
+ this->memory_.SetData8(0x130c, 0);
+ this->memory_.SetData8(0x130d, 0);
+ this->memory_.SetData8(0x130e, 0);
+ this->memory_.SetData8(0x130f, 0);
+ this->memory_.SetData8(0x1310, 0x1b);
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x1400, 0xfc);
+ this->memory_.SetData32(0x1404, 0x104);
+ this->memory_.SetData32(0x1408, 0x10f8);
+ this->memory_.SetData32(0x140c, 0x200);
+ this->memory_.SetData8(0x1410, 0);
+ this->memory_.SetData8(0x1411, 0);
+
+ 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());
+
+ 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, GetFdeInfoFromIndex_expect_cache_fail) {
this->eh_frame_->TestSetTableEntrySize(0x10);
this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
@@ -123,6 +170,7 @@
EXPECT_EQ(0x1000U, this->eh_frame_->LastErrorAddress());
}
+// 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);
@@ -134,8 +182,8 @@
auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
ASSERT_TRUE(info != nullptr);
- EXPECT_EQ(0x1380U, info->pc);
- EXPECT_EQ(0x1540U, info->offset);
+ EXPECT_EQ(0x340U, info->pc);
+ EXPECT_EQ(0x500U, info->offset);
}
TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_read_datarel) {
@@ -430,14 +478,14 @@
ASSERT_EQ(nullptr, this->eh_frame_->GetFdeFromPc(0x800));
}
-REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest, Init, GetFdeInfoFromIndex_expect_cache_fail,
- GetFdeInfoFromIndex_read_pcrel, GetFdeInfoFromIndex_read_datarel,
- GetFdeInfoFromIndex_cached, GetFdeOffsetBinary_verify,
- GetFdeOffsetBinary_index_fail, GetFdeOffsetSequential,
- GetFdeOffsetSequential_last_element, GetFdeOffsetSequential_end_check,
- GetFdeOffsetFromPc_fail_fde_count, GetFdeOffsetFromPc_binary_search,
- GetFdeOffsetFromPc_sequential_search, GetCieFde32, GetCieFde64,
- GetFdeFromPc_fde_not_found);
+REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest, Init, Init_non_zero_load_bias,
+ GetFdeInfoFromIndex_expect_cache_fail, GetFdeInfoFromIndex_read_pcrel,
+ GetFdeInfoFromIndex_read_datarel, GetFdeInfoFromIndex_cached,
+ GetFdeOffsetBinary_verify, GetFdeOffsetBinary_index_fail,
+ GetFdeOffsetSequential, GetFdeOffsetSequential_last_element,
+ GetFdeOffsetSequential_end_check, GetFdeOffsetFromPc_fail_fde_count,
+ GetFdeOffsetFromPc_binary_search, GetFdeOffsetFromPc_sequential_search,
+ GetCieFde32, GetCieFde64, GetFdeFromPc_fde_not_found);
typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameWithHdrTestTypes;
INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameWithHdrTest, DwarfEhFrameWithHdrTestTypes);
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index c340291..414f2f2 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -36,7 +36,7 @@
MockDwarfSectionImpl(Memory* memory) : DwarfSectionImpl<TypeParam>(memory) {}
virtual ~MockDwarfSectionImpl() = default;
- MOCK_METHOD2(Init, bool(uint64_t, uint64_t));
+ MOCK_METHOD3(Init, bool(uint64_t, uint64_t, uint64_t));
MOCK_METHOD2(GetFdeOffsetFromPc, bool(uint64_t, uint64_t*));
@@ -884,7 +884,7 @@
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x00});
this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0xc2});
- ASSERT_TRUE(this->section_->Log(2, 0x1000, 0x1000, &fde));
+ ASSERT_TRUE(this->section_->Log(2, 0x1000, &fde));
ASSERT_EQ(
"4 unwind DW_CFA_nop\n"
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
index 071d2df..2c6c879 100644
--- a/libunwindstack/tests/DwarfSectionTest.cpp
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -30,13 +30,13 @@
MockDwarfSection(Memory* memory) : DwarfSection(memory) {}
virtual ~MockDwarfSection() = default;
- MOCK_METHOD4(Log, bool(uint8_t, uint64_t, uint64_t, const DwarfFde*));
+ MOCK_METHOD3(Log, bool(uint8_t, uint64_t, const DwarfFde*));
MOCK_METHOD5(Eval, bool(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*));
MOCK_METHOD3(GetCfaLocationInfo, bool(uint64_t, const DwarfFde*, dwarf_loc_regs_t*));
- MOCK_METHOD2(Init, bool(uint64_t, uint64_t));
+ MOCK_METHOD3(Init, bool(uint64_t, uint64_t, uint64_t));
MOCK_METHOD2(GetFdeOffsetFromPc, bool(uint64_t, uint64_t*));
diff --git a/libunwindstack/tests/ElfFake.cpp b/libunwindstack/tests/ElfFake.cpp
index 66207db..3d5ddd6 100644
--- a/libunwindstack/tests/ElfFake.cpp
+++ b/libunwindstack/tests/ElfFake.cpp
@@ -32,7 +32,7 @@
std::deque<FunctionData> ElfInterfaceFake::functions_;
std::deque<StepData> ElfInterfaceFake::steps_;
-bool ElfInterfaceFake::GetFunctionName(uint64_t, uint64_t, std::string* name, uint64_t* offset) {
+bool ElfInterfaceFake::GetFunctionName(uint64_t, std::string* name, uint64_t* offset) {
if (functions_.empty()) {
return false;
}
@@ -52,7 +52,7 @@
return true;
}
-bool ElfInterfaceFake::Step(uint64_t, uint64_t, Regs* regs, Memory*, bool* finished) {
+bool ElfInterfaceFake::Step(uint64_t, Regs* regs, Memory*, bool* finished) {
if (steps_.empty()) {
return false;
}
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index e232986..a3bf5ce 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -67,13 +67,13 @@
virtual ~ElfInterfaceFake() = default;
bool Init(uint64_t*) override { return false; }
- void InitHeaders() override {}
+ void InitHeaders(uint64_t) override {}
bool GetSoname(std::string*) override { return false; }
- bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override;
+ bool GetFunctionName(uint64_t, std::string*, uint64_t*) override;
bool GetGlobalVariable(const std::string&, uint64_t*) override;
- bool Step(uint64_t, uint64_t, Regs*, Memory*, bool*) override;
+ bool Step(uint64_t, Regs*, Memory*, bool*) override;
void FakeSetGlobalVariable(const std::string& global, uint64_t offset) {
globals_[global] = offset;
diff --git a/libunwindstack/tests/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp
index 70a52ad..5f1c2ac 100644
--- a/libunwindstack/tests/ElfInterfaceArmTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp
@@ -302,7 +302,7 @@
// FindEntry fails.
bool finished;
- ASSERT_FALSE(interface.StepExidx(0x7000, 0, nullptr, nullptr, &finished));
+ ASSERT_FALSE(interface.StepExidx(0x7000, nullptr, nullptr, &finished));
EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
// ExtractEntry should fail.
@@ -316,18 +316,18 @@
regs[ARM_REG_LR] = 0x20000;
regs.set_sp(regs[ARM_REG_SP]);
regs.set_pc(0x1234);
- ASSERT_FALSE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
+ ASSERT_FALSE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
EXPECT_EQ(ERROR_MEMORY_INVALID, interface.LastErrorCode());
EXPECT_EQ(0x1004U, interface.LastErrorAddress());
// Eval should fail.
memory_.SetData32(0x1004, 0x81000000);
- ASSERT_FALSE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
+ ASSERT_FALSE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
// Everything should pass.
memory_.SetData32(0x1004, 0x80b0b0b0);
- ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
+ ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
ASSERT_FALSE(finished);
ASSERT_EQ(0x1000U, regs.sp());
@@ -336,11 +336,13 @@
ASSERT_EQ(0x20000U, regs[ARM_REG_PC]);
// Load bias is non-zero.
- ASSERT_TRUE(interface.StepExidx(0x8000, 0x1000, ®s, &process_memory_, &finished));
+ interface.set_load_bias(0x1000);
+ ASSERT_TRUE(interface.StepExidx(0x8000, ®s, &process_memory_, &finished));
EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
// Pc too small.
- ASSERT_FALSE(interface.StepExidx(0x8000, 0x9000, ®s, &process_memory_, &finished));
+ interface.set_load_bias(0x9000);
+ ASSERT_FALSE(interface.StepExidx(0x8000, ®s, &process_memory_, &finished));
EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
}
@@ -362,7 +364,7 @@
// Everything should pass.
bool finished;
- ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
+ ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
ASSERT_FALSE(finished);
ASSERT_EQ(0x10004U, regs.sp());
@@ -386,7 +388,7 @@
regs.set_pc(0x1234);
bool finished;
- ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
+ ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
ASSERT_TRUE(finished);
ASSERT_EQ(0x10000U, regs.sp());
@@ -409,7 +411,7 @@
regs.set_pc(0x1234);
bool finished;
- ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
+ ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
ASSERT_TRUE(finished);
ASSERT_EQ(0x10000U, regs.sp());
@@ -436,7 +438,7 @@
regs.set_pc(0x1234);
bool finished;
- ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
+ ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
ASSERT_TRUE(finished);
ASSERT_EQ(0U, regs.pc());
@@ -449,7 +451,7 @@
regs.set_sp(regs[ARM_REG_SP]);
regs.set_pc(0x1234);
- ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
+ ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
ASSERT_TRUE(finished);
ASSERT_EQ(0U, regs.pc());
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index bf97e30..4008e9b 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -647,7 +647,7 @@
memory_.SetData32(0x10004, 0x500);
memory_.SetData32(0x10008, 250);
- elf.InitHeaders();
+ elf.InitHeaders(0);
EXPECT_FALSE(elf.eh_frame() == nullptr);
EXPECT_TRUE(elf.debug_frame() == nullptr);
@@ -680,7 +680,7 @@
memory_.SetData32(0x5108, 0x1500);
memory_.SetData32(0x510c, 0x200);
- elf.InitHeaders();
+ elf.InitHeaders(0);
EXPECT_TRUE(elf.eh_frame() == nullptr);
EXPECT_FALSE(elf.debug_frame() == nullptr);
@@ -703,7 +703,7 @@
elf.FakeSetDebugFrameOffset(0);
elf.FakeSetDebugFrameSize(0);
- elf.InitHeaders();
+ elf.InitHeaders(0);
EXPECT_TRUE(elf.eh_frame() == nullptr);
EXPECT_EQ(0U, elf.eh_frame_offset());
@@ -728,7 +728,7 @@
elf.FakeSetDebugFrameOffset(0x1000);
elf.FakeSetDebugFrameSize(0x100);
- elf.InitHeaders();
+ elf.InitHeaders(0);
EXPECT_TRUE(elf.eh_frame() == nullptr);
EXPECT_TRUE(elf.debug_frame() == nullptr);
@@ -833,10 +833,10 @@
// Look in the first symbol table.
std::string name;
uint64_t name_offset;
- ASSERT_TRUE(elf->GetFunctionName(0x90010, 0, &name, &name_offset));
+ ASSERT_TRUE(elf->GetFunctionName(0x90010, &name, &name_offset));
EXPECT_EQ("function_one", name);
EXPECT_EQ(16U, name_offset);
- ASSERT_TRUE(elf->GetFunctionName(0xd0020, 0, &name, &name_offset));
+ ASSERT_TRUE(elf->GetFunctionName(0xd0020, &name, &name_offset));
EXPECT_EQ("function_two", name);
EXPECT_EQ(32U, name_offset);
}
@@ -1065,7 +1065,7 @@
uint64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
- elf->InitHeaders();
+ elf->InitHeaders(0);
EXPECT_EQ(0U, load_bias);
EXPECT_FALSE(elf->IsValidPc(0));
EXPECT_FALSE(elf->IsValidPc(0x20ff));
@@ -1128,7 +1128,7 @@
uint64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
- elf->InitHeaders();
+ elf->InitHeaders(0);
EXPECT_EQ(0U, load_bias);
EXPECT_FALSE(elf->IsValidPc(0));
EXPECT_FALSE(elf->IsValidPc(0x27ff));
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index aecbf6d..55fe16f 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -297,16 +297,11 @@
elf.FakeSetInterface(interface);
elf.FakeSetValid(true);
- elf.FakeSetLoadBias(0);
MapInfo map_info(0x1000, 0x2000);
ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
- elf.FakeSetLoadBias(0x3000);
- ASSERT_EQ(0x3101U, elf.GetRelPc(0x1101, &map_info));
-
elf.FakeSetValid(false);
- elf.FakeSetLoadBias(0);
ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
}
@@ -328,7 +323,6 @@
}
elf.FakeSetValid(true);
- elf.FakeSetLoadBias(0);
bool finished;
ASSERT_TRUE(elf.Step(0x3000, 0x1000, ®s, &process_memory, &finished));
EXPECT_FALSE(finished);
@@ -342,11 +336,11 @@
virtual ~ElfInterfaceMock() = default;
bool Init(uint64_t*) override { return false; }
- void InitHeaders() override {}
+ void InitHeaders(uint64_t) override {}
bool GetSoname(std::string*) override { return false; }
- bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override { return false; }
+ bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; }
- MOCK_METHOD5(Step, bool(uint64_t, uint64_t, Regs*, Memory*, bool*));
+ MOCK_METHOD4(Step, bool(uint64_t, Regs*, Memory*, bool*));
MOCK_METHOD2(GetGlobalVariable, bool(const std::string&, uint64_t*));
MOCK_METHOD1(IsValidPc, bool(uint64_t));
@@ -358,7 +352,6 @@
TEST_F(ElfTest, step_in_interface) {
ElfFake elf(memory_);
elf.FakeSetValid(true);
- elf.FakeSetLoadBias(0);
RegsArm regs;
@@ -367,30 +360,12 @@
MemoryFake process_memory;
bool finished;
- EXPECT_CALL(*interface, Step(0x1000, 0, ®s, &process_memory, &finished))
+ EXPECT_CALL(*interface, Step(0x1000, ®s, &process_memory, &finished))
.WillOnce(::testing::Return(true));
ASSERT_TRUE(elf.Step(0x1004, 0x1000, ®s, &process_memory, &finished));
}
-TEST_F(ElfTest, step_in_interface_non_zero_load_bias) {
- ElfFake elf(memory_);
- elf.FakeSetValid(true);
- elf.FakeSetLoadBias(0x4000);
-
- RegsArm regs;
-
- ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
- elf.FakeSetInterface(interface);
- MemoryFake process_memory;
-
- bool finished;
- EXPECT_CALL(*interface, Step(0x7300, 0x4000, ®s, &process_memory, &finished))
- .WillOnce(::testing::Return(true));
-
- ASSERT_TRUE(elf.Step(0x7304, 0x7300, ®s, &process_memory, &finished));
-}
-
TEST_F(ElfTest, get_global_invalid_elf) {
ElfFake elf(memory_);
elf.FakeSetValid(false);
@@ -403,7 +378,6 @@
TEST_F(ElfTest, get_global_valid_not_in_interface) {
ElfFake elf(memory_);
elf.FakeSetValid(true);
- elf.FakeSetLoadBias(0);
ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
elf.FakeSetInterface(interface);
@@ -431,10 +405,26 @@
ASSERT_FALSE(elf.GetGlobalVariable(global, &offset));
}
+TEST_F(ElfTest, get_global_valid_dynamic_zero_non_zero_load_bias) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetLoadBias(0x100);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+
+ uint64_t offset;
+ std::string global("something");
+ EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
+ .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true)));
+
+ ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
+ EXPECT_EQ(0x200U, offset);
+}
+
TEST_F(ElfTest, get_global_valid_dynamic_zero) {
ElfFake elf(memory_);
elf.FakeSetValid(true);
- elf.FakeSetLoadBias(0);
ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
elf.FakeSetInterface(interface);
@@ -456,7 +446,6 @@
TEST_F(ElfTest, get_global_valid_in_gnu_debugdata_dynamic_zero) {
ElfFake elf(memory_);
elf.FakeSetValid(true);
- elf.FakeSetLoadBias(0);
ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
elf.FakeSetInterface(interface);
@@ -470,27 +459,9 @@
EXPECT_EQ(0x300U, offset);
}
-TEST_F(ElfTest, get_global_valid_dynamic_zero_non_zero_load_bias) {
- ElfFake elf(memory_);
- elf.FakeSetValid(true);
- elf.FakeSetLoadBias(0x100);
-
- ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
- elf.FakeSetInterface(interface);
-
- uint64_t offset;
- std::string global("something");
- EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
- .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true)));
-
- ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
- EXPECT_EQ(0x200U, offset);
-}
-
TEST_F(ElfTest, get_global_valid_dynamic_adjust_negative) {
ElfFake elf(memory_);
elf.FakeSetValid(true);
- elf.FakeSetLoadBias(0);
ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
interface->MockSetDynamicOffset(0x400);
@@ -510,7 +481,6 @@
TEST_F(ElfTest, get_global_valid_dynamic_adjust_positive) {
ElfFake elf(memory_);
elf.FakeSetValid(true);
- elf.FakeSetLoadBias(0);
ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
interface->MockSetDynamicOffset(0x1000);
@@ -530,7 +500,6 @@
TEST_F(ElfTest, is_valid_pc_elf_invalid) {
ElfFake elf(memory_);
elf.FakeSetValid(false);
- elf.FakeSetLoadBias(0);
EXPECT_FALSE(elf.IsValidPc(0x100));
EXPECT_FALSE(elf.IsValidPc(0x200));
@@ -539,7 +508,6 @@
TEST_F(ElfTest, is_valid_pc_interface) {
ElfFake elf(memory_);
elf.FakeSetValid(true);
- elf.FakeSetLoadBias(0);
ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
elf.FakeSetInterface(interface);
@@ -549,25 +517,9 @@
EXPECT_TRUE(elf.IsValidPc(0x1500));
}
-TEST_F(ElfTest, is_valid_pc_non_zero_load_bias) {
- ElfFake elf(memory_);
- elf.FakeSetValid(true);
- elf.FakeSetLoadBias(0x1000);
-
- ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
- elf.FakeSetInterface(interface);
-
- EXPECT_CALL(*interface, IsValidPc(0x500)).WillOnce(::testing::Return(true));
-
- EXPECT_FALSE(elf.IsValidPc(0x100));
- EXPECT_FALSE(elf.IsValidPc(0x200));
- EXPECT_TRUE(elf.IsValidPc(0x1500));
-}
-
TEST_F(ElfTest, is_valid_pc_from_gnu_debugdata) {
ElfFake elf(memory_);
elf.FakeSetValid(true);
- elf.FakeSetLoadBias(0);
ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
elf.FakeSetInterface(interface);
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
index 45a7b58..b40a253 100644
--- a/libunwindstack/tests/SymbolsTest.cpp
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -70,18 +70,18 @@
std::string name;
uint64_t func_offset;
- ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
ASSERT_EQ("fake_function", name);
ASSERT_EQ(0U, func_offset);
name.clear();
- ASSERT_TRUE(symbols.GetName<TypeParam>(0x500f, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x500f, &this->memory_, &name, &func_offset));
ASSERT_EQ("fake_function", name);
ASSERT_EQ(0xfU, func_offset);
// Check one before and one after the function.
- ASSERT_FALSE(symbols.GetName<TypeParam>(0x4fff, 0, &this->memory_, &name, &func_offset));
- ASSERT_FALSE(symbols.GetName<TypeParam>(0x5010, 0, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x4fff, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x5010, &this->memory_, &name, &func_offset));
}
TYPED_TEST_P(SymbolsTest, no_symbol) {
@@ -98,7 +98,7 @@
// First verify that we can get the name.
std::string name;
uint64_t func_offset;
- ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
ASSERT_EQ("fake_function", name);
ASSERT_EQ(0U, func_offset);
@@ -107,7 +107,7 @@
this->memory_.SetMemory(offset, &sym, sizeof(sym));
// Clear the cache to force the symbol data to be re-read.
symbols.ClearCache();
- ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
// Set the function back, and set the shndx to UNDEF.
sym.st_info = STT_FUNC;
@@ -115,7 +115,7 @@
this->memory_.SetMemory(offset, &sym, sizeof(sym));
// Clear the cache to force the symbol data to be re-read.
symbols.ClearCache();
- ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
}
TYPED_TEST_P(SymbolsTest, multiple_entries) {
@@ -144,34 +144,34 @@
std::string name;
uint64_t func_offset;
- ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, &this->memory_, &name, &func_offset));
ASSERT_EQ("function_two", name);
ASSERT_EQ(1U, func_offset);
name.clear();
- ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, &this->memory_, &name, &func_offset));
ASSERT_EQ("function_one", name);
ASSERT_EQ(4U, func_offset);
name.clear();
- ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, &this->memory_, &name, &func_offset));
ASSERT_EQ("function_three", name);
ASSERT_EQ(1U, func_offset);
// Reget some of the others to verify getting one function name doesn't
// affect any of the next calls.
name.clear();
- ASSERT_TRUE(symbols.GetName<TypeParam>(0x5008, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5008, &this->memory_, &name, &func_offset));
ASSERT_EQ("function_one", name);
ASSERT_EQ(8U, func_offset);
name.clear();
- ASSERT_TRUE(symbols.GetName<TypeParam>(0x3008, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x3008, &this->memory_, &name, &func_offset));
ASSERT_EQ("function_two", name);
ASSERT_EQ(4U, func_offset);
name.clear();
- ASSERT_TRUE(symbols.GetName<TypeParam>(0xa01a, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0xa01a, &this->memory_, &name, &func_offset));
ASSERT_EQ("function_three", name);
ASSERT_EQ(0xaU, func_offset);
}
@@ -203,47 +203,21 @@
std::string name;
uint64_t func_offset;
- ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, &this->memory_, &name, &func_offset));
ASSERT_EQ("function_two", name);
ASSERT_EQ(1U, func_offset);
name.clear();
- ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, &this->memory_, &name, &func_offset));
ASSERT_EQ("function_one", name);
ASSERT_EQ(4U, func_offset);
name.clear();
- ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, &this->memory_, &name, &func_offset));
ASSERT_EQ("function_three", name);
ASSERT_EQ(1U, func_offset);
}
-TYPED_TEST_P(SymbolsTest, load_bias) {
- Symbols symbols(0x1000, sizeof(TypeParam), sizeof(TypeParam), 0x2000, 0x100);
-
- TypeParam sym;
- this->InitSym(&sym, 0x5000, 0x10, 0x40);
- uint64_t offset = 0x1000;
- this->memory_.SetMemory(offset, &sym, sizeof(sym));
-
- std::string fake_name("fake_function");
- this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
-
- // Set a non-zero load_bias that should be a valid function offset.
- std::string name;
- uint64_t func_offset;
- ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0x1000, &this->memory_, &name, &func_offset));
- ASSERT_EQ("fake_function", name);
- ASSERT_EQ(4U, func_offset);
-
- // Set a flag that should cause the load_bias to be ignored.
- sym.st_shndx = SHN_ABS;
- this->memory_.SetMemory(offset, &sym, sizeof(sym));
- // Clear the cache to force the symbol data to be re-read.
- symbols.ClearCache();
- ASSERT_FALSE(symbols.GetName<TypeParam>(0x5004, 0x1000, &this->memory_, &name, &func_offset));
-}
-
TYPED_TEST_P(SymbolsTest, symtab_value_out_of_bounds) {
Symbols symbols_end_at_100(0x1000, sizeof(TypeParam) * 2, sizeof(TypeParam), 0x2000, 0x100);
Symbols symbols_end_at_200(0x1000, sizeof(TypeParam) * 2, sizeof(TypeParam), 0x2000, 0x200);
@@ -265,18 +239,16 @@
std::string name;
uint64_t func_offset;
// Verify that we can get the function name properly for both entries.
- ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
ASSERT_EQ("fake_function", name);
ASSERT_EQ(0U, func_offset);
- ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x3000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x3000, &this->memory_, &name, &func_offset));
ASSERT_EQ("function", name);
ASSERT_EQ(0U, func_offset);
// Now use the symbol table that ends at 0x100.
- ASSERT_FALSE(
- symbols_end_at_100.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
- ASSERT_FALSE(
- symbols_end_at_100.GetName<TypeParam>(0x3000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols_end_at_100.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols_end_at_100.GetName<TypeParam>(0x3000, &this->memory_, &name, &func_offset));
}
// Verify the entire func table is cached.
@@ -302,9 +274,9 @@
// Do call that should cache all of the entries (except the string data).
std::string name;
uint64_t func_offset;
- ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, &this->memory_, &name, &func_offset));
this->memory_.Clear();
- ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, &this->memory_, &name, &func_offset));
// Clear the memory and only put the symbol data string data in memory.
this->memory_.Clear();
@@ -317,15 +289,15 @@
fake_name = "third_entry";
this->memory_.SetMemory(0xa300, fake_name.c_str(), fake_name.size() + 1);
- ASSERT_TRUE(symbols.GetName<TypeParam>(0x5001, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5001, &this->memory_, &name, &func_offset));
ASSERT_EQ("first_entry", name);
ASSERT_EQ(1U, func_offset);
- ASSERT_TRUE(symbols.GetName<TypeParam>(0x2002, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x2002, &this->memory_, &name, &func_offset));
ASSERT_EQ("second_entry", name);
ASSERT_EQ(2U, func_offset);
- ASSERT_TRUE(symbols.GetName<TypeParam>(0x1003, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x1003, &this->memory_, &name, &func_offset));
ASSERT_EQ("third_entry", name);
ASSERT_EQ(3U, func_offset);
}
@@ -381,17 +353,17 @@
EXPECT_FALSE(symbols.GetGlobal<TypeParam>(&this->memory_, "function_1", &offset));
std::string name;
- EXPECT_TRUE(symbols.GetName<TypeParam>(0x10002, 0, &this->memory_, &name, &offset));
+ EXPECT_TRUE(symbols.GetName<TypeParam>(0x10002, &this->memory_, &name, &offset));
EXPECT_EQ("function_0", name);
EXPECT_EQ(2U, offset);
- EXPECT_TRUE(symbols.GetName<TypeParam>(0x12004, 0, &this->memory_, &name, &offset));
+ EXPECT_TRUE(symbols.GetName<TypeParam>(0x12004, &this->memory_, &name, &offset));
EXPECT_EQ("function_1", name);
EXPECT_EQ(4U, offset);
}
REGISTER_TYPED_TEST_CASE_P(SymbolsTest, function_bounds_check, no_symbol, multiple_entries,
- multiple_entries_nonstandard_size, load_bias, symtab_value_out_of_bounds,
+ multiple_entries_nonstandard_size, symtab_value_out_of_bounds,
symtab_read_cached, get_global);
typedef ::testing::Types<Elf32_Sym, Elf64_Sym> SymbolsTestTypes;
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 285fc9e..a65c077 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -1200,4 +1200,43 @@
EXPECT_EQ(0xffcc1558U, unwinder.frames()[18].sp);
}
+// Test using a non-zero load bias library that has the fde entries
+// encoded as 0xb, which is not set as pc relative.
+TEST_F(UnwindOfflineTest, debug_frame_load_bias_arm) {
+ ASSERT_NO_FATAL_FAILURE(Init("debug_frame_load_bias_arm/", ARCH_ARM));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(8U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 0005138c libc.so (__ioctl+8)\n"
+ " #01 pc 0002140f libc.so (ioctl+30)\n"
+ " #02 pc 00039535 libbinder.so (_ZN7android14IPCThreadState14talkWithDriverEb+204)\n"
+ " #03 pc 00039633 libbinder.so (_ZN7android14IPCThreadState20getAndExecuteCommandEv+10)\n"
+ " #04 pc 00039b57 libbinder.so (_ZN7android14IPCThreadState14joinThreadPoolEb+38)\n"
+ " #05 pc 00000c21 mediaserver (main+104)\n"
+ " #06 pc 00084b89 libc.so (__libc_init+48)\n"
+ " #07 pc 00000b77 mediaserver (_start_main+38)\n",
+ frame_info);
+
+ EXPECT_EQ(0xf0be238cU, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xffd4a638U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0xf0bb240fU, unwinder.frames()[1].pc);
+ EXPECT_EQ(0xffd4a638U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0xf1a75535U, unwinder.frames()[2].pc);
+ EXPECT_EQ(0xffd4a650U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0xf1a75633U, unwinder.frames()[3].pc);
+ EXPECT_EQ(0xffd4a6b0U, unwinder.frames()[3].sp);
+ EXPECT_EQ(0xf1a75b57U, unwinder.frames()[4].pc);
+ EXPECT_EQ(0xffd4a6d0U, unwinder.frames()[4].sp);
+ EXPECT_EQ(0x8d1cc21U, unwinder.frames()[5].pc);
+ EXPECT_EQ(0xffd4a6e8U, unwinder.frames()[5].sp);
+ EXPECT_EQ(0xf0c15b89U, unwinder.frames()[6].pc);
+ EXPECT_EQ(0xffd4a700U, unwinder.frames()[6].sp);
+ EXPECT_EQ(0x8d1cb77U, unwinder.frames()[7].pc);
+ EXPECT_EQ(0xffd4a718U, unwinder.frames()[7].sp);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libbinder.so b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libbinder.so
new file mode 100644
index 0000000..4b7bf44
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libbinder.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libc.so b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libc.so
new file mode 100644
index 0000000..013858e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/maps.txt b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/maps.txt
new file mode 100644
index 0000000..10f1325
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/maps.txt
@@ -0,0 +1,3 @@
+8d1c000-8d1f000 r-xp 0 00:00 0 mediaserver
+f0b91000-f0c2c000 r-xp 0 00:00 0 libc.so
+f1a41000-f1a97000 r-xp 0 00:00 0 libbinder.so
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/mediaserver b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/mediaserver
new file mode 100644
index 0000000..9e4a83f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/mediaserver
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/regs.txt b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/regs.txt
new file mode 100644
index 0000000..f147247
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/regs.txt
@@ -0,0 +1,16 @@
+r0: 3
+r1: c0306201
+r2: ffd4a658
+r3: 0
+r4: f0c36d8c
+r5: ffd4a658
+r6: f0168000
+r7: 36
+r8: ffd4a678
+r9: f016802c
+r10: ffd4a660
+r11: 0
+ip: 0
+sp: ffd4a638
+lr: f0bb2413
+pc: f0be238c
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/stack.data b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/stack.data
new file mode 100644
index 0000000..847c819
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/stack.data
Binary files differ
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index 5a8edfd..266a6db 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -37,7 +37,7 @@
namespace unwindstack {
-void DumpArm(ElfInterfaceArm* interface) {
+void DumpArm(Elf* elf, ElfInterfaceArm* interface) {
if (interface == nullptr) {
printf("No ARM Unwind Information.\n\n");
return;
@@ -48,12 +48,11 @@
uint64_t load_bias = entry.second.table_offset;
printf(" PC Range 0x%" PRIx64 " - 0x%" PRIx64 "\n", entry.second.offset + load_bias,
entry.second.table_size + load_bias);
- for (auto addr : *interface) {
+ for (auto pc : *interface) {
std::string name;
- printf(" PC 0x%" PRIx64, addr + load_bias);
+ printf(" PC 0x%" PRIx64, pc + load_bias);
uint64_t func_offset;
- uint64_t pc = addr + load_bias;
- if (interface->GetFunctionName(pc, load_bias, &name, &func_offset) && !name.empty()) {
+ if (elf->GetFunctionName(pc + load_bias, &name, &func_offset) && !name.empty()) {
printf(" <%s>", name.c_str());
}
printf("\n");
@@ -63,7 +62,7 @@
continue;
}
ArmExidx arm(nullptr, interface->memory(), nullptr);
- arm.set_log(true);
+ arm.set_log(ARM_LOG_FULL);
arm.set_log_skip_execution(true);
arm.set_log_indent(2);
if (!arm.ExtractEntryData(entry)) {
@@ -82,21 +81,21 @@
printf("\n");
}
-void DumpDwarfSection(ElfInterface* interface, DwarfSection* section, uint64_t load_bias) {
+void DumpDwarfSection(Elf* elf, DwarfSection* section, uint64_t) {
for (const DwarfFde* fde : *section) {
// Sometimes there are entries that have empty length, skip those since
// they don't contain any interesting information.
if (fde == nullptr || fde->pc_start == fde->pc_end) {
continue;
}
- printf("\n PC 0x%" PRIx64, fde->pc_start + load_bias);
+ printf("\n PC 0x%" PRIx64 "-0x%" PRIx64, fde->pc_start, fde->pc_end);
std::string name;
uint64_t func_offset;
- if (interface->GetFunctionName(fde->pc_start, load_bias, &name, &func_offset) && !name.empty()) {
+ if (elf->GetFunctionName(fde->pc_start, &name, &func_offset) && !name.empty()) {
printf(" <%s>", name.c_str());
}
printf("\n");
- if (!section->Log(2, UINT64_MAX, load_bias, fde)) {
+ if (!section->Log(2, UINT64_MAX, fde)) {
printf("Failed to process cfa information for entry at 0x%" PRIx64 "\n", fde->pc_start);
}
}
@@ -126,13 +125,13 @@
ElfInterface* interface = elf.interface();
if (elf.machine_type() == EM_ARM) {
- DumpArm(reinterpret_cast<ElfInterfaceArm*>(interface));
+ DumpArm(&elf, reinterpret_cast<ElfInterfaceArm*>(interface));
printf("\n");
}
if (interface->eh_frame() != nullptr) {
printf("eh_frame information:\n");
- DumpDwarfSection(interface, interface->eh_frame(), elf.GetLoadBias());
+ DumpDwarfSection(&elf, interface->eh_frame(), elf.GetLoadBias());
printf("\n");
} else {
printf("\nno eh_frame information\n");
@@ -140,7 +139,7 @@
if (interface->debug_frame() != nullptr) {
printf("\ndebug_frame information:\n");
- DumpDwarfSection(interface, interface->debug_frame(), elf.GetLoadBias());
+ DumpDwarfSection(&elf, interface->debug_frame(), elf.GetLoadBias());
printf("\n");
} else {
printf("\nno debug_frame information\n");
@@ -151,12 +150,12 @@
if (gnu_debugdata_interface != nullptr) {
if (gnu_debugdata_interface->eh_frame() != nullptr) {
printf("\ngnu_debugdata (eh_frame):\n");
- DumpDwarfSection(gnu_debugdata_interface, gnu_debugdata_interface->eh_frame(), 0);
+ DumpDwarfSection(&elf, gnu_debugdata_interface->eh_frame(), 0);
printf("\n");
}
if (gnu_debugdata_interface->debug_frame() != nullptr) {
printf("\ngnu_debugdata (debug_frame):\n");
- DumpDwarfSection(gnu_debugdata_interface, gnu_debugdata_interface->debug_frame(), 0);
+ DumpDwarfSection(&elf, gnu_debugdata_interface->debug_frame(), 0);
printf("\n");
}
} else {
diff --git a/libunwindstack/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp
index 47a4f91..0f01566 100644
--- a/libunwindstack/tools/unwind_reg_info.cpp
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -34,7 +34,9 @@
#include <unwindstack/ElfInterface.h>
#include <unwindstack/Log.h>
+#include "ArmExidx.h"
#include "DwarfOp.h"
+#include "ElfInterfaceArm.h"
namespace unwindstack {
@@ -136,6 +138,32 @@
}
}
+void PrintArmRegInformation(ElfInterfaceArm* interface, uint64_t pc) {
+ printf("\nArm exidx:\n");
+ uint64_t entry_offset;
+ if (!interface->FindEntry(pc, &entry_offset)) {
+ return;
+ }
+
+ ArmExidx arm(nullptr, interface->memory(), nullptr);
+
+ log_to_stdout(true);
+ arm.set_log(ARM_LOG_BY_REG);
+ arm.set_log_skip_execution(true);
+ arm.set_log_indent(1);
+ if (!arm.ExtractEntryData(entry_offset)) {
+ if (arm.status() != ARM_STATUS_NO_UNWIND) {
+ printf(" Error trying to extract data.\n");
+ }
+ return;
+ }
+ if (arm.data()->size() != 0 && arm.Eval()) {
+ arm.LogByReg();
+ } else {
+ printf(" Error tring to evaluate exidx data.\n");
+ }
+}
+
int GetInfo(const char* file, uint64_t pc) {
MemoryFileAtOffset* memory = new MemoryFileAtOffset;
if (!memory->Init(file, 0)) {
@@ -162,12 +190,22 @@
printf("Soname: %s\n\n", soname.c_str());
}
- printf("PC 0x%" PRIx64 ":\n", pc);
+ printf("PC 0x%" PRIx64, pc);
+ std::string function_name;
+ uint64_t function_offset;
+ if (elf.GetFunctionName(pc, &function_name, &function_offset)) {
+ printf(" (%s)", function_name.c_str());
+ }
+ printf(":\n");
+
+ if (elf.machine_type() == EM_ARM) {
+ PrintArmRegInformation(reinterpret_cast<ElfInterfaceArm*>(interface), pc - load_bias);
+ }
DwarfSection* section = interface->eh_frame();
if (section != nullptr) {
printf("\neh_frame:\n");
- PrintRegInformation(section, memory, pc - load_bias, elf.class_type());
+ PrintRegInformation(section, memory, pc, elf.class_type());
} else {
printf("\nno eh_frame information\n");
}
@@ -175,7 +213,7 @@
section = interface->debug_frame();
if (section != nullptr) {
printf("\ndebug_frame:\n");
- PrintRegInformation(section, memory, pc - load_bias, elf.class_type());
+ PrintRegInformation(section, memory, pc, elf.class_type());
printf("\n");
} else {
printf("\nno debug_frame information\n");
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
index 086dffe..f8e3e92 100644
--- a/libunwindstack/tools/unwind_symbols.cpp
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -95,7 +95,6 @@
}
std::string name;
- uint64_t load_bias = elf.GetLoadBias();
if (argc == 3) {
std::string cur_name;
uint64_t func_offset;
@@ -113,8 +112,8 @@
// This is a crude way to get the symbols in order.
for (const auto& entry : elf.interface()->pt_loads()) {
- uint64_t start = entry.second.offset + load_bias;
- uint64_t end = entry.second.table_size + load_bias;
+ uint64_t start = entry.second.offset;
+ uint64_t end = entry.second.table_size;
for (uint64_t addr = start; addr < end; addr += 4) {
std::string cur_name;
uint64_t func_offset;
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 9395ef8..bbfa9d8 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -47,6 +47,7 @@
cc_defaults {
name: "libutils_defaults",
vendor_available: true,
+ recovery_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -90,6 +91,10 @@
},
},
+ recovery: {
+ exclude_shared_libs: ["libvndksupport"],
+ },
+
host: {
cflags: ["-DLIBUTILS_NATIVE=1"],
diff --git a/libutils/misc.cpp b/libutils/misc.cpp
index da28dfa..f074341 100644
--- a/libutils/misc.cpp
+++ b/libutils/misc.cpp
@@ -23,7 +23,7 @@
#include <utils/Log.h>
#include <utils/Vector.h>
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
#include <dlfcn.h>
#include <vndksupport/linker.h>
#endif
@@ -70,7 +70,7 @@
void add_sysprop_change_callback(sysprop_change_callback, int) {}
#endif
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
void (*get_report_sysprop_change_func())() {
void (*func)() = nullptr;
void* handle = android_load_sphal_library("libutils.so", RTLD_NOW);
@@ -85,7 +85,7 @@
void report_sysprop_change() {
do_report_sysprop_change();
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
// libutils.so is double loaded; from the default namespace and from the
// 'sphal' namespace. Redirect the sysprop change event to the other instance
// of libutils.so loaded in the 'sphal' namespace so that listeners attached
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 6c06618..2606aa9 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -58,6 +58,7 @@
name: "libziparchive",
host_supported: true,
vendor_available: true,
+ recovery_available: true,
vndk: {
enabled: true,
},
diff --git a/logd/logd.rc b/logd/logd.rc
index bd303b7..c740ecf 100644
--- a/logd/logd.rc
+++ b/logd/logd.rc
@@ -6,6 +6,7 @@
file /dev/kmsg w
user logd
group logd system package_info readproc
+ capabilities SYSLOG AUDIT_CONTROL SETGID
writepid /dev/cpuset/system-background/tasks
service logd-reinit /system/bin/logd --reinit
diff --git a/logd/main.cpp b/logd/main.cpp
index 606aa63..b697d44 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -87,37 +87,27 @@
//
static int drop_privs(bool klogd, bool auditd) {
- // Tricky, if ro.build.type is "eng" then this is true because of the
- // side effect that ro.debuggable == 1 as well, else it is false.
- bool eng =
- __android_logger_property_get_bool("ro.build.type", BOOL_DEFAULT_FALSE);
-
- struct sched_param param;
- memset(¶m, 0, sizeof(param));
+ sched_param param = {};
if (set_sched_policy(0, SP_BACKGROUND) < 0) {
android::prdebug("failed to set background scheduling policy");
- if (!eng) return -1;
+ return -1;
}
if (sched_setscheduler((pid_t)0, SCHED_BATCH, ¶m) < 0) {
android::prdebug("failed to set batch scheduler");
- if (!eng) return -1;
+ return -1;
}
if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
android::prdebug("failed to set background cgroup");
- if (!eng) return -1;
- }
-
- if (!eng && (prctl(PR_SET_DUMPABLE, 0) < 0)) {
- android::prdebug("failed to clear PR_SET_DUMPABLE");
return -1;
}
- if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
- android::prdebug("failed to set PR_SET_KEEPCAPS");
- if (!eng) return -1;
+ if (__android_logger_property_get_bool("ro.debuggable", BOOL_DEFAULT_FALSE) &&
+ prctl(PR_SET_DUMPABLE, 0) == -1) {
+ android::prdebug("failed to clear PR_SET_DUMPABLE");
+ return -1;
}
std::unique_ptr<struct _cap_struct, int (*)(void*)> caps(cap_init(),
@@ -138,24 +128,24 @@
android::prdebug(
"failed to set CAP_SETGID, CAP_SYSLOG or CAP_AUDIT_CONTROL (%d)",
errno);
- if (!eng) return -1;
+ return -1;
}
gid_t groups[] = { AID_READPROC };
if (setgroups(arraysize(groups), groups) == -1) {
android::prdebug("failed to set AID_READPROC groups");
- if (!eng) return -1;
+ return -1;
}
if (setgid(AID_LOGD) != 0) {
android::prdebug("failed to set AID_LOGD gid");
- if (!eng) return -1;
+ return -1;
}
if (setuid(AID_LOGD) != 0) {
android::prdebug("failed to set AID_LOGD uid");
- if (!eng) return -1;
+ return -1;
}
if (cap_set_flag(caps.get(), CAP_PERMITTED, 1, cap_value, CAP_CLEAR) < 0) {
@@ -166,7 +156,7 @@
}
if (cap_set_proc(caps.get()) < 0) {
android::prdebug("failed to clear CAP_SETGID (%d)", errno);
- if (!eng) return -1;
+ return -1;
}
return 0;
@@ -473,7 +463,7 @@
bool auditd =
__android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);
if (drop_privs(klogd, auditd) != 0) {
- return -1;
+ return EXIT_FAILURE;
}
// Serves the purpose of managing the last logs times read on a
@@ -501,7 +491,7 @@
LogReader* reader = new LogReader(logBuf);
if (reader->startListener()) {
- exit(1);
+ return EXIT_FAILURE;
}
// LogListener listens on /dev/socket/logdw for client
@@ -511,7 +501,7 @@
LogListener* swl = new LogListener(logBuf, reader);
// Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
if (swl->startListener(600)) {
- exit(1);
+ return EXIT_FAILURE;
}
// Command listener listens on /dev/socket/logd for incoming logd
@@ -519,7 +509,7 @@
CommandListener* cl = new CommandListener(logBuf, reader, swl);
if (cl->startListener()) {
- exit(1);
+ return EXIT_FAILURE;
}
// LogAudit listens on NETLINK_AUDIT socket for selinux
@@ -554,5 +544,5 @@
TEMP_FAILURE_RETRY(pause());
- exit(0);
+ return EXIT_SUCCESS;
}
diff --git a/mkbootimg/OWNERS b/mkbootimg/OWNERS
index 39448cf..3a00860 100644
--- a/mkbootimg/OWNERS
+++ b/mkbootimg/OWNERS
@@ -1 +1,2 @@
+hridya@google.com
tbao@google.com
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 3c9e5f3..70b6a3e 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -306,6 +306,16 @@
_enforce_vndk_lite_at_runtime :=
#######################################
+# ld.config.txt for recovery
+include $(CLEAR_VARS)
+LOCAL_MODULE := ld.config.recovery.txt
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := etc/ld.config.recovery.txt
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/etc
+LOCAL_MODULE_STEM := ld.config.txt
+include $(BUILD_PREBUILT)
+
+#######################################
# llndk.libraries.txt
include $(CLEAR_VARS)
LOCAL_MODULE := llndk.libraries.txt
diff --git a/rootdir/etc/ld.config.recovery.txt b/rootdir/etc/ld.config.recovery.txt
new file mode 100644
index 0000000..5d6c01a
--- /dev/null
+++ b/rootdir/etc/ld.config.recovery.txt
@@ -0,0 +1,9 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Bionic loader config file for recovery mode
+#
+
+dir.recovery = /system/bin
+
+[recovery]
+namespace.default.search.paths = /system/${LIB}
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 2e42b70..6438e3d 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -12,10 +12,13 @@
"mkshrc_vendor",
"reboot",
"sh",
+ "sh.recovery",
"sh_vendor",
"toolbox",
+ "toolbox.recovery",
"toolbox_vendor",
"toybox",
+ "toybox.recovery",
"toybox_vendor",
],
}
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index 678b853..b15be1f 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -18,11 +18,9 @@
(a) didn't stand out given all the other systems-level changes and (b)
in Marshmallow we changed direction and started the move to toybox.
-Not everything is provided by toybox, though. We currently still use
-the BSD dd and grep (because the toybox versions are still unfinished),
-and for the bzip2 command-line tools we use the ones that are part of
-the bzip2 distribution. The awk added in Android P is Brian Kernighan's
-"one true" awk.
+Not everything is provided by toybox, though. For the bzip2 command-line tools
+we use the ones that are part of the bzip2 distribution. The awk added in
+Android P is Brian Kernighan's "one true" awk.
The lists below show what tools were provided and where they came from in
each release starting with Gingerbread. This doesn't tell the full story,
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index 077f542..f12c810 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -46,6 +46,7 @@
cc_binary {
name: "toolbox",
defaults: ["toolbox_binary_defaults"],
+ recovery_available: true,
}
cc_binary {