diff --git a/init/README.md b/init/README.md index 5c2352b9f..90994279e 100644 --- a/init/README.md +++ b/init/README.md @@ -187,6 +187,10 @@ runs the service. seclabel or computed based on the service executable file security context. For native executables see libcutils android\_get\_control\_socket(). +`enter_namespace ` +> Enters the namespace of type _type_ located at _path_. Only network namespaces are supported with + _type_ set to "net". Note that only one namespace of a given _type_ may be entered. + `file ` > Open a file path and pass its fd to the launched process. _type_ must be "r", "w" or "rw". For native executables see libcutils diff --git a/init/property_service.cpp b/init/property_service.cpp index c3100a5f1..4172ba754 100644 --- a/init/property_service.cpp +++ b/init/property_service.cpp @@ -95,6 +95,11 @@ uint32_t (*property_set)(const std::string& name, const std::string& value) = In void CreateSerializedPropertyInfo(); +struct PropertyAuditData { + const ucred* cr; + const char* name; +}; + void property_init() { mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH); CreateSerializedPropertyInfo(); @@ -111,7 +116,7 @@ static bool CheckMacPerms(const std::string& name, const char* target_context, return false; } - property_audit_data audit_data; + PropertyAuditData audit_data; audit_data.name = name.c_str(); audit_data.cr = &cr; @@ -388,6 +393,35 @@ class SocketConnection { DISALLOW_IMPLICIT_CONSTRUCTORS(SocketConnection); }; +bool CheckControlPropertyPerms(const std::string& name, const std::string& value, + const std::string& source_context, const ucred& cr) { + // We check the legacy method first but these properties are dontaudit, so we only log an audit + // if the newer method fails as well. We only do this with the legacy ctl. properties. + if (name == "ctl.start" || name == "ctl.stop" || name == "ctl.restart") { + // The legacy permissions model is that ctl. properties have their name ctl. and + // their value is the name of the service to apply that action to. Permissions for these + // actions are based on the service, so we must create a fake name of ctl. to + // check permissions. + auto control_string_legacy = "ctl." + value; + const char* target_context_legacy = nullptr; + const char* type_legacy = nullptr; + property_info_area->GetPropertyInfo(control_string_legacy.c_str(), &target_context_legacy, + &type_legacy); + + if (CheckMacPerms(control_string_legacy, target_context_legacy, source_context.c_str(), cr)) { + return true; + } + } + + auto control_string_full = name + "$" + value; + const char* target_context_full = nullptr; + const char* type_full = nullptr; + property_info_area->GetPropertyInfo(control_string_full.c_str(), &target_context_full, + &type_full); + + return CheckMacPerms(control_string_full, target_context_full, source_context.c_str(), cr); +} + // This returns one of the enum of PROP_SUCCESS or PROP_ERROR*. uint32_t HandlePropertySet(const std::string& name, const std::string& value, const std::string& source_context, const ucred& cr, std::string* error) { @@ -397,15 +431,9 @@ uint32_t HandlePropertySet(const std::string& name, const std::string& value, } if (StartsWith(name, "ctl.")) { - // ctl. properties have their name ctl. and their value is the name of the service - // to apply that action to. Permissions for these actions are based on the service, so we - // must create a fake name of ctl. to check permissions. - auto control_string = "ctl." + value; - const char* target_context = nullptr; - const char* type = nullptr; - property_info_area->GetPropertyInfo(control_string.c_str(), &target_context, &type); - if (!CheckMacPerms(control_string, target_context, source_context.c_str(), cr)) { - *error = StringPrintf("Unable to '%s' service %s", name.c_str() + 4, value.c_str()); + if (!CheckControlPropertyPerms(name, value, source_context, cr)) { + *error = StringPrintf("Invalid permissions to perform '%s' on '%s'", name.c_str() + 4, + value.c_str()); return PROP_ERROR_HANDLE_CONTROL_MESSAGE; } @@ -737,7 +765,7 @@ void load_system_props() { } static int SelinuxAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) { - property_audit_data* d = reinterpret_cast(data); + auto* d = reinterpret_cast(data); if (!d || !d->name || !d->cr) { LOG(ERROR) << "AuditCallback invoked with null data arguments!"; diff --git a/init/property_service.h b/init/property_service.h index 29eaaa901..897ac1519 100644 --- a/init/property_service.h +++ b/init/property_service.h @@ -24,11 +24,6 @@ namespace android { namespace init { -struct property_audit_data { - const ucred* cr; - const char* name; -}; - extern uint32_t (*property_set)(const std::string& name, const std::string& value); uint32_t HandlePropertySet(const std::string& name, const std::string& value, diff --git a/init/service.cpp b/init/service.cpp index 09d8dae11..37d3a8807 100644 --- a/init/service.cpp +++ b/init/service.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -59,13 +60,13 @@ using android::base::Join; using android::base::ParseInt; using android::base::StartsWith; using android::base::StringPrintf; +using android::base::unique_fd; using android::base::WriteStringToFile; namespace android { namespace init { -static Result ComputeContextFromExecutable(std::string& service_name, - const std::string& service_path) { +static Result ComputeContextFromExecutable(const std::string& service_path) { std::string computed_context; char* raw_con = nullptr; @@ -101,36 +102,49 @@ static Result ComputeContextFromExecutable(std::string& service_nam return computed_context; } -static void SetUpPidNamespace(const std::string& service_name) { +Result Service::SetUpMountNamespace() const { constexpr unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID; - // It's OK to LOG(FATAL) in this function since it's running in the first - // child process. - // Recursively remount / as slave like zygote does so unmounting and mounting /proc // doesn't interfere with the parent namespace's /proc mount. This will also // prevent any other mounts/unmounts initiated by the service from interfering // with the parent namespace but will still allow mount events from the parent // namespace to propagate to the child. if (mount("rootfs", "/", nullptr, (MS_SLAVE | MS_REC), nullptr) == -1) { - PLOG(FATAL) << "couldn't remount(/) recursively as slave for " << service_name; - } - // umount() then mount() /proc. - // Note that it is not sufficient to mount with MS_REMOUNT. - if (umount("/proc") == -1) { - PLOG(FATAL) << "couldn't umount(/proc) for " << service_name; - } - if (mount("", "/proc", "proc", kSafeFlags, "") == -1) { - PLOG(FATAL) << "couldn't mount(/proc) for " << service_name; + return ErrnoError() << "Could not remount(/) recursively as slave"; } - if (prctl(PR_SET_NAME, service_name.c_str()) == -1) { - PLOG(FATAL) << "couldn't set name for " << service_name; + // umount() then mount() /proc and/or /sys + // Note that it is not sufficient to mount with MS_REMOUNT. + if (namespace_flags_ & CLONE_NEWPID) { + if (umount("/proc") == -1) { + return ErrnoError() << "Could not umount(/proc)"; + } + if (mount("", "/proc", "proc", kSafeFlags, "") == -1) { + return ErrnoError() << "Could not mount(/proc)"; + } + } + bool remount_sys = std::any_of(namespaces_to_enter_.begin(), namespaces_to_enter_.end(), + [](const auto& entry) { return entry.first == CLONE_NEWNET; }); + if (remount_sys) { + if (umount2("/sys", MNT_DETACH) == -1) { + return ErrnoError() << "Could not umount(/sys)"; + } + if (mount("", "/sys", "sys", kSafeFlags, "") == -1) { + return ErrnoError() << "Could not mount(/sys)"; + } + } + return Success(); +} + +Result Service::SetUpPidNamespace() const { + if (prctl(PR_SET_NAME, name_.c_str()) == -1) { + return ErrnoError() << "Could not set name"; } pid_t child_pid = fork(); if (child_pid == -1) { - PLOG(FATAL) << "couldn't fork init inside the PID namespace for " << service_name; + return ErrnoError() << "Could not fork init inside the PID namespace"; } if (child_pid > 0) { @@ -153,6 +167,20 @@ static void SetUpPidNamespace(const std::string& service_name) { } _exit(WEXITSTATUS(init_exitstatus)); } + return Success(); +} + +Result Service::EnterNamespaces() const { + for (const auto& [nstype, path] : namespaces_to_enter_) { + auto fd = unique_fd{open(path.c_str(), O_RDONLY | O_CLOEXEC)}; + if (!fd) { + return ErrnoError() << "Could not open namespace at " << path; + } + if (setns(fd, nstype) == -1) { + return ErrnoError() << "Could not setns() namespace at " << path; + } + } + return Success(); } static bool ExpandArgsAndExecv(const std::vector& args) { @@ -418,6 +446,20 @@ Result Service::ParseDisabled(const std::vector& args) { return Success(); } +Result Service::ParseEnterNamespace(const std::vector& args) { + if (args[1] != "net") { + return Error() << "Init only supports entering network namespaces"; + } + if (!namespaces_to_enter_.empty()) { + return Error() << "Only one network namespace may be entered"; + } + // Network namespaces require that /sys is remounted, otherwise the old adapters will still be + // present. Therefore, they also require mount namespaces. + namespace_flags_ |= CLONE_NEWNS; + namespaces_to_enter_.emplace_back(CLONE_NEWNET, args[2]); + return Success(); +} + Result Service::ParseGroup(const std::vector& args) { auto gid = DecodeUid(args[1]); if (!gid) { @@ -682,6 +724,8 @@ const Service::OptionParserMap::Map& Service::OptionParserMap::map() const { {"console", {0, 1, &Service::ParseConsole}}, {"critical", {0, 0, &Service::ParseCritical}}, {"disabled", {0, 0, &Service::ParseDisabled}}, + {"enter_namespace", + {2, 2, &Service::ParseEnterNamespace}}, {"group", {1, NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}}, {"interface", {2, 2, &Service::ParseInterface}}, {"ioprio", {2, 2, &Service::ParseIoprio}}, @@ -783,7 +827,7 @@ Result Service::Start() { if (!seclabel_.empty()) { scon = seclabel_; } else { - auto result = ComputeContextFromExecutable(name_, args_[0]); + auto result = ComputeContextFromExecutable(args_[0]); if (!result) { return result.error(); } @@ -802,10 +846,24 @@ Result Service::Start() { if (pid == 0) { umask(077); + if (auto result = EnterNamespaces(); !result) { + LOG(FATAL) << "Service '" << name_ << "' could not enter namespaces: " << result.error(); + } + + if (namespace_flags_ & CLONE_NEWNS) { + if (auto result = SetUpMountNamespace(); !result) { + LOG(FATAL) << "Service '" << name_ + << "' could not set up mount namespace: " << result.error(); + } + } + if (namespace_flags_ & CLONE_NEWPID) { // This will fork again to run an init process inside the PID // namespace. - SetUpPidNamespace(name_); + if (auto result = SetUpPidNamespace(); !result) { + LOG(FATAL) << "Service '" << name_ + << "' could not set up PID namespace: " << result.error(); + } } for (const auto& [key, value] : environment_vars_) { diff --git a/init/service.h b/init/service.h index bcf194386..87c9ac804 100644 --- a/init/service.h +++ b/init/service.h @@ -124,6 +124,9 @@ class Service { using OptionParser = Result (Service::*)(const std::vector& args); class OptionParserMap; + Result SetUpMountNamespace() const; + Result SetUpPidNamespace() const; + Result EnterNamespaces() const; void NotifyStateChange(const std::string& new_state) const; void StopOrReset(int how); void ZapStdio() const; @@ -136,6 +139,7 @@ class Service { Result ParseConsole(const std::vector& args); Result ParseCritical(const std::vector& args); Result ParseDisabled(const std::vector& args); + Result ParseEnterNamespace(const std::vector& args); Result ParseGroup(const std::vector& args); Result ParsePriority(const std::vector& args); Result ParseInterface(const std::vector& args); @@ -179,6 +183,8 @@ class Service { std::vector supp_gids_; CapSet capabilities_; unsigned namespace_flags_; + // Pair of namespace type, path to namespace. + std::vector> namespaces_to_enter_; std::string seclabel_; diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index 7b8111c5d..adbacdbcd 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -261,6 +261,7 @@ cc_test { "tests/files/offline/jit_debug_arm/*", "tests/files/offline/jit_debug_x86/*", "tests/files/offline/gnu_debugdata_arm/*", + "tests/files/offline/offset_arm/*", "tests/files/offline/straddle_arm/*", "tests/files/offline/straddle_arm64/*", ], diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp index 02f8a9ae0..3762107d6 100644 --- a/libunwindstack/Elf.cpp +++ b/libunwindstack/Elf.cpp @@ -160,14 +160,14 @@ uint64_t Elf::GetLastErrorAddress() { } // The relative pc is always relative to the start of the map from which it comes. -bool Elf::Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs, - Memory* process_memory, bool* finished) { +bool Elf::Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, Regs* regs, Memory* process_memory, + bool* finished) { if (!valid_) { return false; } // The relative pc expectd by StepIfSignalHandler is relative to the start of the elf. - if (regs->StepIfSignalHandler(rel_pc + elf_offset, this, process_memory)) { + if (regs->StepIfSignalHandler(rel_pc, this, process_memory)) { *finished = false; return true; } diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp index 94edb1cc8..98ab30ff3 100644 --- a/libunwindstack/Unwinder.cpp +++ b/libunwindstack/Unwinder.cpp @@ -210,8 +210,7 @@ void Unwinder::Unwind(const std::vector* initial_map_names_to_skip, in_device_map = true; } else { bool finished; - stepped = elf->Step(rel_pc, step_pc, map_info->elf_offset, regs_, process_memory_.get(), - &finished); + stepped = elf->Step(rel_pc, step_pc, regs_, process_memory_.get(), &finished); elf->GetLastError(&last_error_); if (stepped && finished) { break; diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h index 385973e9f..f4cdbdaf4 100644 --- a/libunwindstack/include/unwindstack/Elf.h +++ b/libunwindstack/include/unwindstack/Elf.h @@ -65,8 +65,8 @@ class Elf { uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info); - bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs, - Memory* process_memory, bool* finished); + bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, Regs* regs, Memory* process_memory, + bool* finished); ElfInterface* CreateInterfaceFromMemory(Memory* memory); diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp index f9028c4a5..aecbf6dcb 100644 --- a/libunwindstack/tests/ElfTest.cpp +++ b/libunwindstack/tests/ElfTest.cpp @@ -133,7 +133,7 @@ TEST_F(ElfTest, elf_invalid) { ASSERT_FALSE(elf.GetFunctionName(0, &name, &func_offset)); bool finished; - ASSERT_FALSE(elf.Step(0, 0, 0, nullptr, nullptr, &finished)); + ASSERT_FALSE(elf.Step(0, 0, nullptr, nullptr, &finished)); } TEST_F(ElfTest, elf32_invalid_machine) { @@ -330,7 +330,7 @@ TEST_F(ElfTest, step_in_signal_map) { elf.FakeSetValid(true); elf.FakeSetLoadBias(0); bool finished; - ASSERT_TRUE(elf.Step(0x1000, 0x1000, 0x2000, ®s, &process_memory, &finished)); + ASSERT_TRUE(elf.Step(0x3000, 0x1000, ®s, &process_memory, &finished)); EXPECT_FALSE(finished); EXPECT_EQ(15U, regs.pc()); EXPECT_EQ(13U, regs.sp()); @@ -370,7 +370,7 @@ TEST_F(ElfTest, step_in_interface) { EXPECT_CALL(*interface, Step(0x1000, 0, ®s, &process_memory, &finished)) .WillOnce(::testing::Return(true)); - ASSERT_TRUE(elf.Step(0x1004, 0x1000, 0x2000, ®s, &process_memory, &finished)); + ASSERT_TRUE(elf.Step(0x1004, 0x1000, ®s, &process_memory, &finished)); } TEST_F(ElfTest, step_in_interface_non_zero_load_bias) { @@ -388,7 +388,7 @@ TEST_F(ElfTest, step_in_interface_non_zero_load_bias) { EXPECT_CALL(*interface, Step(0x7300, 0x4000, ®s, &process_memory, &finished)) .WillOnce(::testing::Return(true)); - ASSERT_TRUE(elf.Step(0x7304, 0x7300, 0x2000, ®s, &process_memory, &finished)); + ASSERT_TRUE(elf.Step(0x7304, 0x7300, ®s, &process_memory, &finished)); } TEST_F(ElfTest, get_global_invalid_elf) { diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp index 8cb640f54..a187dc379 100644 --- a/libunwindstack/tests/UnwindOfflineTest.cpp +++ b/libunwindstack/tests/UnwindOfflineTest.cpp @@ -45,6 +45,12 @@ namespace unwindstack { +static void AddMemory(std::string file_name, MemoryOfflineParts* parts) { + MemoryOffline* memory = new MemoryOffline; + ASSERT_TRUE(memory->Init(file_name.c_str(), 0)); + parts->Add(memory); +} + class UnwindOfflineTest : public ::testing::Test { protected: void TearDown() override { @@ -63,9 +69,24 @@ class UnwindOfflineTest : public ::testing::Test { maps_.reset(new BufferMaps(data.c_str())); ASSERT_TRUE(maps_->Parse()); - std::unique_ptr stack_memory(new MemoryOffline); - ASSERT_TRUE(stack_memory->Init((dir_ + "stack.data").c_str(), 0)); - process_memory_.reset(stack_memory.release()); + std::string stack_name(dir_ + "stack.data"); + struct stat st; + if (stat(stack_name.c_str(), &st) == 0 && S_ISREG(st.st_mode)) { + std::unique_ptr stack_memory(new MemoryOffline); + ASSERT_TRUE(stack_memory->Init((dir_ + "stack.data").c_str(), 0)); + process_memory_.reset(stack_memory.release()); + } else { + std::unique_ptr stack_memory(new MemoryOfflineParts); + for (size_t i = 0;; i++) { + stack_name = dir_ + "stack" + std::to_string(i) + ".data"; + if (stat(stack_name.c_str(), &st) == -1 || !S_ISREG(st.st_mode)) { + ASSERT_TRUE(i != 0) << "No stack data files found."; + break; + } + AddMemory(stack_name, stack_memory.get()); + } + process_memory_.reset(stack_memory.release()); + } switch (arch) { case ARCH_ARM: { @@ -179,7 +200,7 @@ static std::string DumpFrames(Unwinder& unwinder) { } TEST_F(UnwindOfflineTest, pc_straddle_arm) { - Init("straddle_arm/", ARCH_ARM); + ASSERT_NO_FATAL_FAILURE(Init("straddle_arm/", ARCH_ARM)); Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_); unwinder.Unwind(); @@ -203,7 +224,7 @@ TEST_F(UnwindOfflineTest, pc_straddle_arm) { } TEST_F(UnwindOfflineTest, pc_in_gnu_debugdata_arm) { - Init("gnu_debugdata_arm/", ARCH_ARM); + ASSERT_NO_FATAL_FAILURE(Init("gnu_debugdata_arm/", ARCH_ARM)); Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_); unwinder.Unwind(); @@ -223,7 +244,7 @@ TEST_F(UnwindOfflineTest, pc_in_gnu_debugdata_arm) { } TEST_F(UnwindOfflineTest, pc_straddle_arm64) { - Init("straddle_arm64/", ARCH_ARM64); + ASSERT_NO_FATAL_FAILURE(Init("straddle_arm64/", ARCH_ARM64)); Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_); unwinder.Unwind(); @@ -254,14 +275,8 @@ TEST_F(UnwindOfflineTest, pc_straddle_arm64) { EXPECT_EQ(0x7fe0d84110U, unwinder.frames()[5].sp); } -static void AddMemory(std::string file_name, MemoryOfflineParts* parts) { - MemoryOffline* memory = new MemoryOffline; - ASSERT_TRUE(memory->Init(file_name.c_str(), 0)); - parts->Add(memory); -} - TEST_F(UnwindOfflineTest, jit_debug_x86) { - Init("jit_debug_x86/", ARCH_X86); + ASSERT_NO_FATAL_FAILURE(Init("jit_debug_x86/", ARCH_X86)); MemoryOfflineParts* memory = new MemoryOfflineParts; AddMemory(dir_ + "descriptor.data", memory); @@ -554,7 +569,7 @@ TEST_F(UnwindOfflineTest, jit_debug_x86) { } TEST_F(UnwindOfflineTest, jit_debug_arm) { - Init("jit_debug_arm/", ARCH_ARM); + ASSERT_NO_FATAL_FAILURE(Init("jit_debug_arm/", ARCH_ARM)); MemoryOfflineParts* memory = new MemoryOfflineParts; AddMemory(dir_ + "descriptor.data", memory); @@ -872,7 +887,7 @@ TEST_F(UnwindOfflineTest, jit_debug_arm) { // fallback to iterating over the cies/fdes and ignore the eh_frame_hdr. // No .gnu_debugdata section in the elf file, so no symbols. TEST_F(UnwindOfflineTest, bad_eh_frame_hdr_arm64) { - Init("bad_eh_frame_hdr_arm64/", ARCH_ARM64); + ASSERT_NO_FATAL_FAILURE(Init("bad_eh_frame_hdr_arm64/", ARCH_ARM64)); Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_); unwinder.Unwind(); @@ -901,7 +916,7 @@ TEST_F(UnwindOfflineTest, bad_eh_frame_hdr_arm64) { // The elf has bad eh_frame unwind information for the pcs. If eh_frame // is used first, the unwind will not match the expected output. TEST_F(UnwindOfflineTest, debug_frame_first_x86) { - Init("debug_frame_first_x86/", ARCH_X86); + ASSERT_NO_FATAL_FAILURE(Init("debug_frame_first_x86/", ARCH_X86)); Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_); unwinder.Unwind(); @@ -929,7 +944,7 @@ TEST_F(UnwindOfflineTest, debug_frame_first_x86) { // Make sure that a pc that is at the beginning of an fde unwinds correctly. TEST_F(UnwindOfflineTest, eh_frame_hdr_begin_x86_64) { - Init("eh_frame_hdr_begin_x86_64/", ARCH_X86_64); + ASSERT_NO_FATAL_FAILURE(Init("eh_frame_hdr_begin_x86_64/", ARCH_X86_64)); Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_); unwinder.Unwind(); @@ -956,7 +971,7 @@ TEST_F(UnwindOfflineTest, eh_frame_hdr_begin_x86_64) { } TEST_F(UnwindOfflineTest, art_quick_osr_stub_arm) { - Init("art_quick_osr_stub_arm/", ARCH_ARM); + ASSERT_NO_FATAL_FAILURE(Init("art_quick_osr_stub_arm/", ARCH_ARM)); MemoryOfflineParts* memory = new MemoryOfflineParts; AddMemory(dir_ + "descriptor.data", memory); @@ -1070,4 +1085,78 @@ TEST_F(UnwindOfflineTest, art_quick_osr_stub_arm) { EXPECT_EQ(0xcd4ff960U, unwinder.frames()[24].sp); } +TEST_F(UnwindOfflineTest, offset_arm) { + ASSERT_NO_FATAL_FAILURE(Init("offset_arm/", ARCH_ARM)); + + Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_); + unwinder.Unwind(); + + std::string frame_info(DumpFrames(unwinder)); + ASSERT_EQ(19U, unwinder.NumFrames()) << "Unwind:\n" << frame_info; + EXPECT_EQ( + " #00 pc 0032bfa0 (offset 0x42000) libunwindstack_test (SignalInnerFunction+40)\n" + " #01 pc 0032bfeb (offset 0x42000) libunwindstack_test (SignalMiddleFunction+2)\n" + " #02 pc 0032bff3 (offset 0x42000) libunwindstack_test (SignalOuterFunction+2)\n" + " #03 pc 0032fed3 (offset 0x42000) libunwindstack_test " + "(_ZN11unwindstackL19SignalCallerHandlerEiP7siginfoPv+26)\n" + " #04 pc 00026528 (offset 0x25000) libc.so\n" + " #05 pc 00000000 \n" + " #06 pc 0032c2d9 (offset 0x42000) libunwindstack_test (InnerFunction+736)\n" + " #07 pc 0032cc4f (offset 0x42000) libunwindstack_test (MiddleFunction+42)\n" + " #08 pc 0032cc81 (offset 0x42000) libunwindstack_test (OuterFunction+42)\n" + " #09 pc 0032e547 (offset 0x42000) libunwindstack_test " + "(_ZN11unwindstackL19RemoteThroughSignalEij+270)\n" + " #10 pc 0032ed99 (offset 0x42000) libunwindstack_test " + "(_ZN11unwindstack55UnwindTest_remote_through_signal_with_invalid_func_Test8TestBodyEv+16)\n" + " #11 pc 00354453 (offset 0x42000) libunwindstack_test (_ZN7testing4Test3RunEv+154)\n" + " #12 pc 00354de7 (offset 0x42000) libunwindstack_test (_ZN7testing8TestInfo3RunEv+194)\n" + " #13 pc 00355105 (offset 0x42000) libunwindstack_test (_ZN7testing8TestCase3RunEv+180)\n" + " #14 pc 0035a215 (offset 0x42000) libunwindstack_test " + "(_ZN7testing8internal12UnitTestImpl11RunAllTestsEv+664)\n" + " #15 pc 00359f4f (offset 0x42000) libunwindstack_test (_ZN7testing8UnitTest3RunEv+110)\n" + " #16 pc 0034d3db (offset 0x42000) libunwindstack_test (main+38)\n" + " #17 pc 00092c0d (offset 0x25000) libc.so (__libc_init+48)\n" + " #18 pc 0004202f (offset 0x42000) libunwindstack_test (_start_main+38)\n", + frame_info); + + EXPECT_EQ(0x2e55fa0U, unwinder.frames()[0].pc); + EXPECT_EQ(0xf43d2cccU, unwinder.frames()[0].sp); + EXPECT_EQ(0x2e55febU, unwinder.frames()[1].pc); + EXPECT_EQ(0xf43d2ce0U, unwinder.frames()[1].sp); + EXPECT_EQ(0x2e55ff3U, unwinder.frames()[2].pc); + EXPECT_EQ(0xf43d2ce8U, unwinder.frames()[2].sp); + EXPECT_EQ(0x2e59ed3U, unwinder.frames()[3].pc); + EXPECT_EQ(0xf43d2cf0U, unwinder.frames()[3].sp); + EXPECT_EQ(0xf4136528U, unwinder.frames()[4].pc); + EXPECT_EQ(0xf43d2d10U, unwinder.frames()[4].sp); + EXPECT_EQ(0U, unwinder.frames()[5].pc); + EXPECT_EQ(0xffcc0ee0U, unwinder.frames()[5].sp); + EXPECT_EQ(0x2e562d9U, unwinder.frames()[6].pc); + EXPECT_EQ(0xffcc0ee0U, unwinder.frames()[6].sp); + EXPECT_EQ(0x2e56c4fU, unwinder.frames()[7].pc); + EXPECT_EQ(0xffcc1060U, unwinder.frames()[7].sp); + EXPECT_EQ(0x2e56c81U, unwinder.frames()[8].pc); + EXPECT_EQ(0xffcc1078U, unwinder.frames()[8].sp); + EXPECT_EQ(0x2e58547U, unwinder.frames()[9].pc); + EXPECT_EQ(0xffcc1090U, unwinder.frames()[9].sp); + EXPECT_EQ(0x2e58d99U, unwinder.frames()[10].pc); + EXPECT_EQ(0xffcc1438U, unwinder.frames()[10].sp); + EXPECT_EQ(0x2e7e453U, unwinder.frames()[11].pc); + EXPECT_EQ(0xffcc1448U, unwinder.frames()[11].sp); + EXPECT_EQ(0x2e7ede7U, unwinder.frames()[12].pc); + EXPECT_EQ(0xffcc1458U, unwinder.frames()[12].sp); + EXPECT_EQ(0x2e7f105U, unwinder.frames()[13].pc); + EXPECT_EQ(0xffcc1490U, unwinder.frames()[13].sp); + EXPECT_EQ(0x2e84215U, unwinder.frames()[14].pc); + EXPECT_EQ(0xffcc14c0U, unwinder.frames()[14].sp); + EXPECT_EQ(0x2e83f4fU, unwinder.frames()[15].pc); + EXPECT_EQ(0xffcc1510U, unwinder.frames()[15].sp); + EXPECT_EQ(0x2e773dbU, unwinder.frames()[16].pc); + EXPECT_EQ(0xffcc1528U, unwinder.frames()[16].sp); + EXPECT_EQ(0xf41a2c0dU, unwinder.frames()[17].pc); + EXPECT_EQ(0xffcc1540U, unwinder.frames()[17].sp); + EXPECT_EQ(0x2b6c02fU, unwinder.frames()[18].pc); + EXPECT_EQ(0xffcc1558U, unwinder.frames()[18].sp); +} + } // namespace unwindstack diff --git a/libunwindstack/tests/files/offline/offset_arm/libc.so b/libunwindstack/tests/files/offline/offset_arm/libc.so new file mode 100644 index 000000000..9f5c8ca44 Binary files /dev/null and b/libunwindstack/tests/files/offline/offset_arm/libc.so differ diff --git a/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test b/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test new file mode 100644 index 000000000..7a30bfa44 Binary files /dev/null and b/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test differ diff --git a/libunwindstack/tests/files/offline/offset_arm/maps.txt b/libunwindstack/tests/files/offline/offset_arm/maps.txt new file mode 100644 index 000000000..62244645e --- /dev/null +++ b/libunwindstack/tests/files/offline/offset_arm/maps.txt @@ -0,0 +1,2 @@ +2b6c000-2e92000 r-xp 42000 00:00 0 libunwindstack_test +f4135000-f41a9000 r-xp 25000 00:00 0 libc.so diff --git a/libunwindstack/tests/files/offline/offset_arm/regs.txt b/libunwindstack/tests/files/offline/offset_arm/regs.txt new file mode 100644 index 000000000..1f4ac8f7b --- /dev/null +++ b/libunwindstack/tests/files/offline/offset_arm/regs.txt @@ -0,0 +1,16 @@ +r0: 5 +r1: 5 +r2: 4 +r3: 1 +r4: 73804b6b +r5: f3c9c000 +r6: 2ea09ac +r7: 10624dd3 +r8: f41b5d8c +r9: f3c9c000 +r10: 6f17 +r11: f3c94048 +ip: 2ea0807 +sp: f43d2ccc +lr: 2e55fef +pc: 2e55fa0 diff --git a/libunwindstack/tests/files/offline/offset_arm/stack0.data b/libunwindstack/tests/files/offline/offset_arm/stack0.data new file mode 100644 index 000000000..23a987415 Binary files /dev/null and b/libunwindstack/tests/files/offline/offset_arm/stack0.data differ diff --git a/libunwindstack/tests/files/offline/offset_arm/stack1.data b/libunwindstack/tests/files/offline/offset_arm/stack1.data new file mode 100644 index 000000000..49bdd1e26 Binary files /dev/null and b/libunwindstack/tests/files/offline/offset_arm/stack1.data differ diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp index 74868d43d..589731d0e 100644 --- a/libunwindstack/tools/unwind_for_offline.cpp +++ b/libunwindstack/tools/unwind_for_offline.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -78,30 +79,44 @@ bool SaveRegs(unwindstack::Regs* regs) { return true; } -bool SaveStack(pid_t pid, uint64_t sp_start, uint64_t sp_end) { - std::unique_ptr fp(fopen("stack.data", "w+"), &fclose); - if (fp == nullptr) { - printf("Failed to create stack.data.\n"); - return false; - } +bool SaveStack(pid_t pid, const std::vector>& stacks) { + for (size_t i = 0; i < stacks.size(); i++) { + std::string file_name; + if (stacks.size() != 1) { + file_name = "stack" + std::to_string(i) + ".data"; + } else { + file_name = "stack.data"; + } - size_t bytes = fwrite(&sp_start, 1, sizeof(sp_start), fp.get()); - if (bytes != sizeof(sp_start)) { - perror("Failed to write all data."); - return false; - } + // Do this first, so if it fails, we don't create the file. + uint64_t sp_start = stacks[i].first; + uint64_t sp_end = stacks[i].second; + std::vector buffer(sp_end - sp_start); + auto process_memory = unwindstack::Memory::CreateProcessMemory(pid); + if (!process_memory->Read(sp_start, buffer.data(), buffer.size())) { + printf("Unable to read stack data.\n"); + return false; + } - std::vector buffer(sp_end - sp_start); - auto process_memory = unwindstack::Memory::CreateProcessMemory(pid); - if (!process_memory->Read(sp_start, buffer.data(), buffer.size())) { - printf("Unable to read stack data.\n"); - return false; - } + printf("Saving the stack 0x%" PRIx64 "-0x%" PRIx64 "\n", sp_start, sp_end); - bytes = fwrite(buffer.data(), 1, buffer.size(), fp.get()); - if (bytes != buffer.size()) { - printf("Failed to write all stack data: stack size %zu, written %zu\n", buffer.size(), bytes); - return 1; + std::unique_ptr fp(fopen(file_name.c_str(), "w+"), &fclose); + if (fp == nullptr) { + printf("Failed to create stack.data.\n"); + return false; + } + + size_t bytes = fwrite(&sp_start, 1, sizeof(sp_start), fp.get()); + if (bytes != sizeof(sp_start)) { + perror("Failed to write all data."); + return false; + } + + bytes = fwrite(buffer.data(), 1, buffer.size(), fp.get()); + if (bytes != buffer.size()) { + printf("Failed to write all stack data: stack size %zu, written %zu\n", buffer.size(), bytes); + return false; + } } return true; @@ -110,17 +125,11 @@ bool SaveStack(pid_t pid, uint64_t sp_start, uint64_t sp_end) { bool CreateElfFromMemory(std::shared_ptr& memory, map_info_t* info) { std::string cur_name; if (info->name.empty()) { - cur_name = android::base::StringPrintf("anonymous:%" PRIx64, info->start); + cur_name = android::base::StringPrintf("anonymous_%" PRIx64, info->start); } else { - cur_name = basename(info->name.c_str()); - cur_name = android::base::StringPrintf("%s:%" PRIx64, basename(info->name.c_str()), info->start); + cur_name = android::base::StringPrintf("%s_%" PRIx64, basename(info->name.c_str()), info->start); } - std::unique_ptr output(fopen(cur_name.c_str(), "w+"), &fclose); - if (output == nullptr) { - printf("Cannot create %s\n", cur_name.c_str()); - return false; - } std::vector buffer(info->end - info->start); // If this is a mapped in file, it might not be possible to read the entire // map, so read all that is readable. @@ -129,6 +138,13 @@ bool CreateElfFromMemory(std::shared_ptr& memory, map_info_ printf("Cannot read data from address %" PRIx64 " length %zu\n", info->start, buffer.size()); return false; } + + std::unique_ptr output(fopen(cur_name.c_str(), "w+"), &fclose); + if (output == nullptr) { + printf("Cannot create %s\n", cur_name.c_str()); + return false; + } + size_t bytes_written = fwrite(buffer.data(), 1, bytes, output.get()); if (bytes_written != bytes) { printf("Failed to write all data to file: bytes read %zu, written %zu\n", bytes, bytes_written); @@ -198,9 +214,21 @@ int SaveData(pid_t pid) { unwinder.Unwind(); std::unordered_map maps_by_start; - uint64_t last_sp; + std::vector> stacks; + uint64_t sp_map_start = 0; + unwindstack::MapInfo* map_info = maps.Find(sp); + if (map_info != nullptr) { + stacks.emplace_back(std::make_pair(sp, map_info->end)); + sp_map_start = map_info->start; + } + for (auto frame : unwinder.frames()) { - last_sp = frame.sp; + map_info = maps.Find(frame.sp); + if (map_info != nullptr && sp_map_start != map_info->start) { + stacks.emplace_back(std::make_pair(frame.sp, map_info->end)); + sp_map_start = map_info->start; + } + if (maps_by_start.count(frame.map_start) == 0) { auto info = &maps_by_start[frame.map_start]; info->start = frame.map_start; @@ -211,7 +239,12 @@ int SaveData(pid_t pid) { // Try to create the elf from memory, this will handle cases where // the data only exists in memory such as vdso data on x86. if (!CreateElfFromMemory(process_memory, info)) { - return 1; + printf("Ignoring map "); + if (!info->name.empty()) { + printf("%s\n", info->name.c_str()); + } else { + printf("anonymous:%" PRIx64 "\n", info->start); + } } } } @@ -221,7 +254,7 @@ int SaveData(pid_t pid) { printf("%s\n", unwinder.FormatFrame(i).c_str()); } - if (!SaveStack(pid, sp, last_sp)) { + if (!SaveStack(pid, stacks)) { return 1; } diff --git a/rootdir/init.rc b/rootdir/init.rc index 4cc66933f..b9464e7fd 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -429,6 +429,7 @@ on post-fs-data mkdir /data/misc/radio 0770 system radio mkdir /data/misc/sms 0770 system radio mkdir /data/misc/carrierid 0770 system radio + mkdir /data/misc/apns 0770 system radio mkdir /data/misc/zoneinfo 0775 system system mkdir /data/misc/network_watchlist 0774 system system mkdir /data/misc/textclassifier 0771 system system