/* * Copyright (C) 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "Maps.h" MapInfo* Maps::Find(uint64_t pc) { if (maps_.empty()) { return nullptr; } size_t first = 0; size_t last = maps_.size(); while (first < last) { size_t index = (first + last) / 2; MapInfo* cur = &maps_[index]; if (pc >= cur->start && pc < cur->end) { return cur; } else if (pc < cur->start) { last = index; } else { first = index + 1; } } return nullptr; } bool Maps::ParseLine(const char* line, MapInfo* map_info) { char permissions[5]; int name_pos; // Linux /proc//maps lines: // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %4s %" SCNx64 " %*x:%*x %*d %n", &map_info->start, &map_info->end, permissions, &map_info->offset, &name_pos) != 4) { return false; } map_info->flags = PROT_NONE; if (permissions[0] == 'r') { map_info->flags |= PROT_READ; } if (permissions[1] == 'w') { map_info->flags |= PROT_WRITE; } if (permissions[2] == 'x') { map_info->flags |= PROT_EXEC; } if (line[name_pos] != '\0') { map_info->name = &line[name_pos]; size_t length = map_info->name.length() - 1; if (map_info->name[length] == '\n') { map_info->name.erase(length); } // Mark a device map in /dev/and not in /dev/ashmem/ specially. if (map_info->name.substr(0, 5) == "/dev/" && map_info->name.substr(5, 7) != "ashmem/") { map_info->flags |= MAPS_FLAGS_DEVICE_MAP; } } return true; } bool Maps::Parse() { std::unique_ptr fp(fopen(GetMapsFile().c_str(), "re"), fclose); if (!fp) { return false; } bool valid = true; char* line = nullptr; size_t line_len; while (getline(&line, &line_len, fp.get()) > 0) { MapInfo map_info; if (!ParseLine(line, &map_info)) { valid = false; break; } maps_.push_back(map_info); } free(line); return valid; } Maps::~Maps() { for (auto& map : maps_) { delete map.elf; map.elf = nullptr; } } bool BufferMaps::Parse() { const char* start_of_line = buffer_; do { std::string line; const char* end_of_line = strchr(start_of_line, '\n'); if (end_of_line == nullptr) { line = start_of_line; } else { end_of_line++; line = std::string(start_of_line, end_of_line - start_of_line); } MapInfo map_info; if (!ParseLine(line.c_str(), &map_info)) { return false; } maps_.push_back(map_info); start_of_line = end_of_line; } while (start_of_line != nullptr && *start_of_line != '\0'); return true; } const std::string RemoteMaps::GetMapsFile() const { return "/proc/" + std::to_string(pid_) + "/maps"; } bool OfflineMaps::Parse() { // Format of maps information: // StartOffset // EndOffset // offset // flags // MapNameLength // MapName android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file_.c_str(), O_RDONLY))); if (fd == -1) { return false; } std::vector name; while (true) { MapInfo map_info; ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.start, sizeof(map_info.start))); if (bytes == 0) { break; } if (bytes == -1 || bytes != sizeof(map_info.start)) { return false; } bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.end, sizeof(map_info.end))); if (bytes == -1 || bytes != sizeof(map_info.end)) { return false; } bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.offset, sizeof(map_info.offset))); if (bytes == -1 || bytes != sizeof(map_info.offset)) { return false; } bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.flags, sizeof(map_info.flags))); if (bytes == -1 || bytes != sizeof(map_info.flags)) { return false; } uint16_t len; bytes = TEMP_FAILURE_RETRY(read(fd, &len, sizeof(len))); if (bytes == -1 || bytes != sizeof(len)) { return false; } if (len > 0) { name.resize(len); bytes = TEMP_FAILURE_RETRY(read(fd, name.data(), len)); if (bytes == -1 || bytes != len) { return false; } map_info.name = std::string(name.data(), len); } maps_.push_back(map_info); } return true; }