Merge "bootstat: Three more boot reasons."
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 950a551..f584021 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -65,6 +65,7 @@
#include "property_service.h"
#include "reboot.h"
#include "rlimit_parser.h"
+#include "selinux.h"
#include "service.h"
#include "subcontext.h"
#include "util.h"
@@ -641,8 +642,26 @@
return Success();
}
+static int MakeSymlink(const std::string& target, const std::string& linkpath) {
+ std::string secontext;
+ // Passing 0 for mode should work.
+ if (SelabelLookupFileContext(linkpath, 0, &secontext) && !secontext.empty()) {
+ setfscreatecon(secontext.c_str());
+ }
+
+ int rc = symlink(target.c_str(), linkpath.c_str());
+
+ if (!secontext.empty()) {
+ int save_errno = errno;
+ setfscreatecon(nullptr);
+ errno = save_errno;
+ }
+
+ return rc;
+}
+
static Result<Success> do_symlink(const BuiltinArguments& args) {
- if (symlink(args[1].c_str(), args[2].c_str()) < 0) {
+ if (MakeSymlink(args[1], args[2]) < 0) {
// The symlink builtin is often used to create symlinks for older devices to be backwards
// compatible with new paths, therefore we skip reporting this error.
if (errno == EEXIST && android::base::GetMinimumLogSeverity() > android::base::DEBUG) {
diff --git a/init/util.cpp b/init/util.cpp
index a19a6f3..d80cb1e 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -178,9 +178,26 @@
return content;
}
+static int OpenFile(const std::string& path, int flags, mode_t mode) {
+ std::string secontext;
+ if (SelabelLookupFileContext(path, mode, &secontext) && !secontext.empty()) {
+ setfscreatecon(secontext.c_str());
+ }
+
+ int rc = open(path.c_str(), flags, mode);
+
+ if (!secontext.empty()) {
+ int save_errno = errno;
+ setfscreatecon(nullptr);
+ errno = save_errno;
+ }
+
+ return rc;
+}
+
Result<Success> WriteFile(const std::string& path, const std::string& content) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(
- open(path.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
+ OpenFile(path, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
if (fd == -1) {
return ErrnoError() << "open() failed";
}
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 40364fe..75aa427 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -130,6 +130,7 @@
"tests/RegsStepIfSignalHandlerTest.cpp",
"tests/RegsTest.cpp",
"tests/SymbolsTest.cpp",
+ "tests/UnwindOfflineTest.cpp",
"tests/UnwindTest.cpp",
"tests/UnwinderTest.cpp",
],
@@ -153,6 +154,8 @@
data: [
"tests/files/elf32.xz",
"tests/files/elf64.xz",
+ "tests/files/offline/straddle_arm32/*",
+ "tests/files/offline/straddle_arm64/*",
],
}
diff --git a/libunwindstack/DwarfEhFrame.h b/libunwindstack/DwarfEhFrame.h
index 561d23a..2589c89 100644
--- a/libunwindstack/DwarfEhFrame.h
+++ b/libunwindstack/DwarfEhFrame.h
@@ -40,7 +40,7 @@
uint64_t AdjustPcFromFde(uint64_t pc) override {
// The eh_frame uses relative pcs.
- return pc + this->memory_.cur_offset();
+ return pc + this->memory_.cur_offset() - 4;
}
};
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 0c65895..48e33ee 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -104,8 +104,8 @@
}
// The relative pc is always relative to the start of the map from which it comes.
-bool Elf::Step(uint64_t rel_pc, uint64_t elf_offset, Regs* regs, Memory* process_memory,
- bool* finished) {
+bool Elf::Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs,
+ Memory* process_memory, bool* finished) {
if (!valid_) {
return false;
}
@@ -117,16 +117,16 @@
}
// Adjust the load bias to get the real relative pc.
- if (rel_pc < load_bias_) {
+ if (adjusted_rel_pc < load_bias_) {
return false;
}
- rel_pc -= load_bias_;
+ adjusted_rel_pc -= load_bias_;
// Lock during the step which can update information in the object.
std::lock_guard<std::mutex> guard(lock_);
- return interface_->Step(rel_pc, regs, process_memory, finished) ||
+ return interface_->Step(adjusted_rel_pc, regs, process_memory, finished) ||
(gnu_debugdata_interface_ &&
- gnu_debugdata_interface_->Step(rel_pc, regs, process_memory, finished));
+ gnu_debugdata_interface_->Step(adjusted_rel_pc, regs, process_memory, finished));
}
bool Elf::IsValidElf(Memory* memory) {
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index 5012ffd..56370c1 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -64,13 +64,13 @@
// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so
char* str;
const char* old_str = line;
- uint64_t start = strtoul(old_str, &str, 16);
+ uint64_t start = strtoull(old_str, &str, 16);
if (old_str == str || *str++ != '-') {
return nullptr;
}
old_str = str;
- uint64_t end = strtoul(old_str, &str, 16);
+ uint64_t end = strtoull(old_str, &str, 16);
if (old_str == str || !std::isspace(*str++)) {
return nullptr;
}
@@ -112,14 +112,14 @@
}
old_str = str;
- uint64_t offset = strtoul(old_str, &str, 16);
+ uint64_t offset = strtoull(old_str, &str, 16);
if (old_str == str || !std::isspace(*str)) {
return nullptr;
}
// Ignore the 00:00 values.
old_str = str;
- (void)strtoul(old_str, &str, 16);
+ (void)strtoull(old_str, &str, 16);
if (old_str == str || *str++ != ':') {
return nullptr;
}
@@ -129,14 +129,14 @@
// Skip the inode.
old_str = str;
- (void)strtoul(str, &str, 16);
+ (void)strtoull(str, &str, 16);
if (old_str == str || !std::isspace(*str++)) {
return nullptr;
}
// Skip decimal digit.
old_str = str;
- (void)strtoul(old_str, &str, 10);
+ (void)strtoull(old_str, &str, 10);
if (old_str == str || (!std::isspace(*str) && *str != '\0')) {
return nullptr;
}
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 2e46a11..3092a62 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -32,27 +32,20 @@
namespace unwindstack {
-void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, bool adjust_pc) {
+void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc) {
size_t frame_num = frames_.size();
frames_.resize(frame_num + 1);
FrameData* frame = &frames_.at(frame_num);
frame->num = frame_num;
- frame->pc = regs_->pc();
frame->sp = regs_->sp();
- frame->rel_pc = rel_pc;
+ frame->rel_pc = adjusted_rel_pc;
if (map_info == nullptr) {
+ frame->pc = regs_->pc();
return;
}
- if (adjust_pc) {
- // Don't adjust the first frame pc.
- frame->rel_pc = regs_->GetAdjustedPc(rel_pc, elf);
-
- // Adjust the original pc.
- frame->pc -= rel_pc - frame->rel_pc;
- }
-
+ frame->pc = map_info->start + adjusted_rel_pc;
frame->map_name = map_info->name;
frame->map_offset = map_info->offset;
frame->map_start = map_info->start;
@@ -92,21 +85,29 @@
MapInfo* map_info = maps_->Find(regs_->pc());
uint64_t rel_pc;
+ uint64_t adjusted_rel_pc;
Elf* elf;
if (map_info == nullptr) {
rel_pc = regs_->pc();
+ adjusted_rel_pc = rel_pc;
} else {
if (ShouldStop(map_suffixes_to_ignore, map_info->name)) {
break;
}
elf = map_info->GetElf(process_memory_, true);
rel_pc = elf->GetRelPc(regs_->pc(), map_info);
+ if (adjust_pc) {
+ adjusted_rel_pc = regs_->GetAdjustedPc(rel_pc, elf);
+ } else {
+ adjusted_rel_pc = rel_pc;
+ }
}
if (map_info == nullptr || initial_map_names_to_skip == nullptr ||
std::find(initial_map_names_to_skip->begin(), initial_map_names_to_skip->end(),
basename(map_info->name.c_str())) == initial_map_names_to_skip->end()) {
- FillInFrame(map_info, elf, rel_pc, adjust_pc);
+ FillInFrame(map_info, elf, adjusted_rel_pc);
+
// Once a frame is added, stop skipping frames.
initial_map_names_to_skip = nullptr;
}
@@ -133,7 +134,8 @@
in_device_map = true;
} else {
bool finished;
- stepped = elf->Step(rel_pc, map_info->elf_offset, regs_, process_memory_.get(), &finished);
+ stepped = elf->Step(rel_pc, adjusted_rel_pc, map_info->elf_offset, regs_,
+ process_memory_.get(), &finished);
if (stepped && finished) {
break;
}
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index 71d7aca..da2ddc0 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -51,8 +51,8 @@
uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
- bool Step(uint64_t rel_pc, uint64_t elf_offset, Regs* regs, Memory* process_memory,
- bool* finished);
+ bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs,
+ Memory* process_memory, bool* finished);
ElfInterface* CreateInterfaceFromMemory(Memory* memory);
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index 37a76b2..b64d460 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -70,7 +70,7 @@
static std::string FormatFrame(const FrameData& frame, bool bits32);
private:
- void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, bool adjust_pc);
+ void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc);
size_t max_frames_;
Maps* maps_;
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
index 53ee719..3a629f8 100644
--- a/libunwindstack/tests/DwarfEhFrameTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -109,23 +109,23 @@
this->eh_frame_->TestGetFdeInfo(0, &info);
EXPECT_EQ(0x5100U, info.offset);
- EXPECT_EQ(0x660cU, info.start);
- EXPECT_EQ(0x680cU, info.end);
+ EXPECT_EQ(0x6608U, info.start);
+ EXPECT_EQ(0x6808U, info.end);
this->eh_frame_->TestGetFdeInfo(1, &info);
EXPECT_EQ(0x5200U, info.offset);
- EXPECT_EQ(0x770cU, info.start);
- EXPECT_EQ(0x7a0cU, info.end);
+ EXPECT_EQ(0x7708U, info.start);
+ EXPECT_EQ(0x7a08U, info.end);
this->eh_frame_->TestGetFdeInfo(2, &info);
EXPECT_EQ(0x5400U, info.offset);
- EXPECT_EQ(0x890cU, info.start);
- EXPECT_EQ(0x8d0cU, info.end);
+ EXPECT_EQ(0x8908U, info.start);
+ EXPECT_EQ(0x8d08U, info.end);
this->eh_frame_->TestGetFdeInfo(3, &info);
EXPECT_EQ(0x5500U, info.offset);
- EXPECT_EQ(0x9a0cU, info.start);
- EXPECT_EQ(0x9f0cU, info.end);
+ EXPECT_EQ(0x9a08U, info.start);
+ EXPECT_EQ(0x9f08U, info.end);
}
TYPED_TEST_P(DwarfEhFrameTest, Init32_fde_not_following_cie) {
@@ -193,23 +193,23 @@
this->eh_frame_->TestGetFdeInfo(0, &info);
EXPECT_EQ(0x5100U, info.offset);
- EXPECT_EQ(0x661cU, info.start);
- EXPECT_EQ(0x681cU, info.end);
+ EXPECT_EQ(0x6618U, info.start);
+ EXPECT_EQ(0x6818U, info.end);
this->eh_frame_->TestGetFdeInfo(1, &info);
EXPECT_EQ(0x5200U, info.offset);
- EXPECT_EQ(0x771cU, info.start);
- EXPECT_EQ(0x7a1cU, info.end);
+ EXPECT_EQ(0x7718U, info.start);
+ EXPECT_EQ(0x7a18U, info.end);
this->eh_frame_->TestGetFdeInfo(2, &info);
EXPECT_EQ(0x5400U, info.offset);
- EXPECT_EQ(0x891cU, info.start);
- EXPECT_EQ(0x8d1cU, info.end);
+ EXPECT_EQ(0x8918U, info.start);
+ EXPECT_EQ(0x8d18U, info.end);
this->eh_frame_->TestGetFdeInfo(3, &info);
EXPECT_EQ(0x5500U, info.offset);
- EXPECT_EQ(0x9a1cU, info.start);
- EXPECT_EQ(0x9f1cU, info.end);
+ EXPECT_EQ(0x9a18U, info.start);
+ EXPECT_EQ(0x9f18U, info.end);
}
TYPED_TEST_P(DwarfEhFrameTest, Init64_fde_not_following_cie) {
@@ -261,8 +261,8 @@
typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
this->eh_frame_->TestGetFdeInfo(0, &info);
EXPECT_EQ(0x5100U, info.offset);
- EXPECT_EQ(0x660aU, info.start);
- EXPECT_EQ(0x680aU, info.end);
+ EXPECT_EQ(0x6606U, info.start);
+ EXPECT_EQ(0x6806U, info.end);
}
TYPED_TEST_P(DwarfEhFrameTest, Init_version4) {
@@ -304,8 +304,8 @@
typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
this->eh_frame_->TestGetFdeInfo(0, &info);
EXPECT_EQ(0x5100U, info.offset);
- EXPECT_EQ(0x660aU, info.start);
- EXPECT_EQ(0x680aU, info.end);
+ EXPECT_EQ(0x6606U, info.start);
+ EXPECT_EQ(0x6806U, info.end);
}
TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc) {
@@ -384,8 +384,8 @@
ASSERT_TRUE(fde != nullptr);
EXPECT_EQ(0x14010U, fde->cfa_instructions_offset);
EXPECT_EQ(0x14024U, fde->cfa_instructions_end);
- EXPECT_EQ(0x1d00cU, fde->pc_start);
- EXPECT_EQ(0x1d10cU, fde->pc_end);
+ EXPECT_EQ(0x1d008U, fde->pc_start);
+ EXPECT_EQ(0x1d108U, fde->pc_end);
EXPECT_EQ(0xf000U, fde->cie_offset);
EXPECT_EQ(0U, fde->lsda_address);
@@ -428,8 +428,8 @@
ASSERT_TRUE(fde != nullptr);
EXPECT_EQ(0x8024U, fde->cfa_instructions_offset);
EXPECT_EQ(0x820cU, fde->cfa_instructions_end);
- EXPECT_EQ(0xd01cU, fde->pc_start);
- EXPECT_EQ(0xd31cU, fde->pc_end);
+ EXPECT_EQ(0xd018U, fde->pc_start);
+ EXPECT_EQ(0xd318U, fde->pc_end);
EXPECT_EQ(0x6000U, fde->cie_offset);
EXPECT_EQ(0U, fde->lsda_address);
diff --git a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
index 1028ab9..64b325b 100644
--- a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
@@ -345,8 +345,8 @@
ASSERT_TRUE(fde != nullptr);
EXPECT_EQ(0x14010U, fde->cfa_instructions_offset);
EXPECT_EQ(0x14024U, fde->cfa_instructions_end);
- EXPECT_EQ(0x1d00cU, fde->pc_start);
- EXPECT_EQ(0x1d10cU, fde->pc_end);
+ EXPECT_EQ(0x1d008U, fde->pc_start);
+ EXPECT_EQ(0x1d108U, fde->pc_end);
EXPECT_EQ(0xf000U, fde->cie_offset);
EXPECT_EQ(0U, fde->lsda_address);
@@ -387,8 +387,8 @@
ASSERT_TRUE(fde != nullptr);
EXPECT_EQ(0x8024U, fde->cfa_instructions_offset);
EXPECT_EQ(0x820cU, fde->cfa_instructions_end);
- EXPECT_EQ(0xd01cU, fde->pc_start);
- EXPECT_EQ(0xd31cU, fde->pc_end);
+ EXPECT_EQ(0xd018U, fde->pc_start);
+ EXPECT_EQ(0xd318U, fde->pc_end);
EXPECT_EQ(0x6000U, fde->cie_offset);
EXPECT_EQ(0U, fde->lsda_address);
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 098f5f4..afd113d 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -132,7 +132,7 @@
ASSERT_FALSE(elf.GetFunctionName(0, &name, &func_offset));
bool finished;
- ASSERT_FALSE(elf.Step(0, 0, nullptr, nullptr, &finished));
+ ASSERT_FALSE(elf.Step(0, 0, 0, nullptr, nullptr, &finished));
}
TEST_F(ElfTest, elf32_invalid_machine) {
@@ -306,7 +306,7 @@
elf.FakeSetValid(true);
elf.FakeSetLoadBias(0);
bool finished;
- ASSERT_TRUE(elf.Step(0x1000, 0x2000, ®s, &process_memory, &finished));
+ ASSERT_TRUE(elf.Step(0x1000, 0x1000, 0x2000, ®s, &process_memory, &finished));
EXPECT_FALSE(finished);
EXPECT_EQ(15U, regs.pc());
EXPECT_EQ(13U, regs.sp());
@@ -339,7 +339,7 @@
EXPECT_CALL(*interface, Step(0x1000, ®s, &process_memory, &finished))
.WillOnce(::testing::Return(true));
- ASSERT_TRUE(elf.Step(0x1000, 0x2000, ®s, &process_memory, &finished));
+ ASSERT_TRUE(elf.Step(0x1004, 0x1000, 0x2000, ®s, &process_memory, &finished));
}
TEST_F(ElfTest, step_in_interface_non_zero_load_bias) {
@@ -355,12 +355,12 @@
// Invalid relative pc given load_bias.
bool finished;
- ASSERT_FALSE(elf.Step(0x1000, 0x2000, ®s, &process_memory, &finished));
+ ASSERT_FALSE(elf.Step(0x1004, 0x1000, 0x2000, ®s, &process_memory, &finished));
EXPECT_CALL(*interface, Step(0x3300, ®s, &process_memory, &finished))
.WillOnce(::testing::Return(true));
- ASSERT_TRUE(elf.Step(0x7300, 0x2000, ®s, &process_memory, &finished));
+ ASSERT_TRUE(elf.Step(0x7304, 0x7300, 0x2000, ®s, &process_memory, &finished));
}
} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTestUtils.cpp b/libunwindstack/tests/ElfTestUtils.cpp
index 069386b..69163ac 100644
--- a/libunwindstack/tests/ElfTestUtils.cpp
+++ b/libunwindstack/tests/ElfTestUtils.cpp
@@ -47,7 +47,7 @@
ehdr->e_ehsize = sizeof(Ehdr);
}
-static std::string GetTestFileDirectory() {
+std::string TestGetFileDirectory() {
std::string exec(testing::internal::GetArgvs()[0]);
auto const value = exec.find_last_of('/');
if (value == std::string::npos) {
@@ -102,7 +102,7 @@
offset = symtab_offset + 0x100;
if (init_gnu_debugdata) {
// Read in the compressed elf data and copy it in.
- name = GetTestFileDirectory();
+ name = TestGetFileDirectory();
if (elf_class == ELFCLASS32) {
name += "elf32.xz";
} else {
diff --git a/libunwindstack/tests/ElfTestUtils.h b/libunwindstack/tests/ElfTestUtils.h
index 6ef00e1..62cd59a 100644
--- a/libunwindstack/tests/ElfTestUtils.h
+++ b/libunwindstack/tests/ElfTestUtils.h
@@ -18,6 +18,7 @@
#define _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
#include <functional>
+#include <string>
namespace unwindstack {
@@ -30,6 +31,8 @@
void TestInitGnuDebugdata(uint32_t elf_class, uint32_t machine_type, bool init_gnu_debudata,
TestCopyFuncType copy_func);
+std::string TestGetFileDirectory();
+
} // namespace unwindstack
#endif // _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
new file mode 100644
index 0000000..d24abe4
--- /dev/null
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <string>
+#include <vector>
+
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
+
+#include "Machine.h"
+
+#include "ElfTestUtils.h"
+
+namespace unwindstack {
+
+static std::string DumpFrames(Unwinder& unwinder) {
+ std::string str;
+ for (size_t i = 0; i < unwinder.NumFrames(); i++) {
+ str += unwinder.FormatFrame(i) + "\n";
+ }
+ return str;
+}
+
+TEST(UnwindOfflineTest, pc_straddle_arm32) {
+ std::string dir(TestGetFileDirectory() + "offline/straddle_arm32/");
+
+ MemoryOffline* memory = new MemoryOffline;
+ ASSERT_TRUE(memory->Init((dir + "stack.data").c_str(), 0));
+
+ FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
+ ASSERT_TRUE(fp != nullptr);
+ RegsArm regs;
+ uint64_t reg_value;
+ ASSERT_EQ(1, fscanf(fp, "pc: %" SCNx64 "\n", ®_value));
+ regs[ARM_REG_PC] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "sp: %" SCNx64 "\n", ®_value));
+ regs[ARM_REG_SP] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "lr: %" SCNx64 "\n", ®_value));
+ regs[ARM_REG_LR] = reg_value;
+ regs.SetFromRaw();
+ fclose(fp);
+
+ fp = fopen((dir + "maps.txt").c_str(), "r");
+ ASSERT_TRUE(fp != nullptr);
+ // The file is guaranteed to be less than 4096 bytes.
+ std::vector<char> buffer(4096);
+ ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
+ fclose(fp);
+
+ BufferMaps maps(buffer.data());
+ ASSERT_TRUE(maps.Parse());
+
+ ASSERT_EQ(static_cast<uint32_t>(EM_ARM), regs.MachineType());
+
+ std::shared_ptr<Memory> process_memory(memory);
+
+ char* cwd = getcwd(nullptr, 0);
+ ASSERT_EQ(0, chdir(dir.c_str()));
+ Unwinder unwinder(128, &maps, ®s, process_memory);
+ unwinder.Unwind();
+ ASSERT_EQ(0, chdir(cwd));
+ free(cwd);
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(4U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 0001a9f8 libc.so (abort+63)\n"
+ " #01 pc 00006a1b libbase.so (_ZN7android4base14DefaultAborterEPKc+6)\n"
+ " #02 pc 00007441 libbase.so (_ZN7android4base10LogMessageD2Ev+748)\n"
+ " #03 pc 00015149 /does/not/exist/libhidlbase.so\n",
+ frame_info);
+}
+
+TEST(UnwindOfflineTest, pc_straddle_arm64) {
+ std::string dir(TestGetFileDirectory() + "offline/straddle_arm64/");
+
+ MemoryOffline* memory = new MemoryOffline;
+ ASSERT_TRUE(memory->Init((dir + "stack.data").c_str(), 0));
+
+ FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
+ ASSERT_TRUE(fp != nullptr);
+ RegsArm64 regs;
+ uint64_t reg_value;
+ ASSERT_EQ(1, fscanf(fp, "pc: %" SCNx64 "\n", ®_value));
+ regs[ARM64_REG_PC] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "sp: %" SCNx64 "\n", ®_value));
+ regs[ARM64_REG_SP] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "lr: %" SCNx64 "\n", ®_value));
+ regs[ARM64_REG_LR] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "x29: %" SCNx64 "\n", ®_value));
+ regs[ARM64_REG_R29] = reg_value;
+ regs.SetFromRaw();
+ fclose(fp);
+
+ fp = fopen((dir + "maps.txt").c_str(), "r");
+ ASSERT_TRUE(fp != nullptr);
+ // The file is guaranteed to be less than 4096 bytes.
+ std::vector<char> buffer(4096);
+ ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
+ fclose(fp);
+
+ BufferMaps maps(buffer.data());
+ ASSERT_TRUE(maps.Parse());
+
+ ASSERT_EQ(static_cast<uint32_t>(EM_AARCH64), regs.MachineType());
+
+ std::shared_ptr<Memory> process_memory(memory);
+
+ char* cwd = getcwd(nullptr, 0);
+ ASSERT_EQ(0, chdir(dir.c_str()));
+ Unwinder unwinder(128, &maps, ®s, process_memory);
+ unwinder.Unwind();
+ ASSERT_EQ(0, chdir(cwd));
+ free(cwd);
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(6U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 0000000000429fd8 libunwindstack_test (SignalInnerFunction+24)\n"
+ " #01 pc 000000000042a078 libunwindstack_test (SignalMiddleFunction+8)\n"
+ " #02 pc 000000000042a08c libunwindstack_test (SignalOuterFunction+8)\n"
+ " #03 pc 000000000042d8fc libunwindstack_test "
+ "(_ZN11unwindstackL19RemoteThroughSignalEij+20)\n"
+ " #04 pc 000000000042d8d8 libunwindstack_test "
+ "(_ZN11unwindstack37UnwindTest_remote_through_signal_Test8TestBodyEv+32)\n"
+ " #05 pc 0000000000455d70 libunwindstack_test (_ZN7testing4Test3RunEv+392)\n",
+ frame_info);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/files/offline/straddle_arm32/libbase.so b/libunwindstack/tests/files/offline/straddle_arm32/libbase.so
new file mode 100644
index 0000000..d1f16ee
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm32/libbase.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm32/libc.so b/libunwindstack/tests/files/offline/straddle_arm32/libc.so
new file mode 100644
index 0000000..4dc19ca
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm32/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm32/maps.txt b/libunwindstack/tests/files/offline/straddle_arm32/maps.txt
new file mode 100644
index 0000000..8c26479
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm32/maps.txt
@@ -0,0 +1,4 @@
+f2d9a000-f2da7fff r-xp 00000000 00:00 0 libbase.so
+f3002000-f3005fff rw-p 00000000 00:00 0 [stack:25941]
+f31d0000-f326bfff r-xp 00000000 00:00 0 libc.so
+f3352000-f336bfff r-xp 00000000 00:00 0 /does/not/exist/libhidlbase.so
diff --git a/libunwindstack/tests/files/offline/straddle_arm32/regs.txt b/libunwindstack/tests/files/offline/straddle_arm32/regs.txt
new file mode 100644
index 0000000..3baedf3
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm32/regs.txt
@@ -0,0 +1,3 @@
+pc: f31ea9f8
+sp: e9c866f8
+lr: f31f179f
diff --git a/libunwindstack/tests/files/offline/straddle_arm32/stack.data b/libunwindstack/tests/files/offline/straddle_arm32/stack.data
new file mode 100644
index 0000000..83aeb4a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm32/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm64/libunwindstack_test b/libunwindstack/tests/files/offline/straddle_arm64/libunwindstack_test
new file mode 100644
index 0000000..092fc3a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm64/libunwindstack_test
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm64/maps.txt b/libunwindstack/tests/files/offline/straddle_arm64/maps.txt
new file mode 100644
index 0000000..bdf29b5
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm64/maps.txt
@@ -0,0 +1,2 @@
+00000064d05ab000-00000064d0a6cfff r-xp 00000000 00:00 0 libunwindstack_test
+0000007fe0d64000-0000007fe0d84fff rw-p 00000000 00:00 0 [stack]
diff --git a/libunwindstack/tests/files/offline/straddle_arm64/regs.txt b/libunwindstack/tests/files/offline/straddle_arm64/regs.txt
new file mode 100644
index 0000000..ff8a936
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm64/regs.txt
@@ -0,0 +1,4 @@
+pc: 00000064d09d4fd8
+sp: 0000007fe0d84040
+lr: 00000064d09d507c
+x29: 0000007fe0d84070
diff --git a/libunwindstack/tests/files/offline/straddle_arm64/stack.data b/libunwindstack/tests/files/offline/straddle_arm64/stack.data
new file mode 100644
index 0000000..824d0e2
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm64/stack.data
Binary files differ