1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #define LOG_TAG "Corkscrew" 18 //#define LOG_NDEBUG 0 19 20 #include <corkscrew/map_info.h> 21 22 #include <ctype.h> 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <limits.h> 27 #include <pthread.h> 28 #include <unistd.h> 29 #include <cutils/log.h> 30 #include <sys/time.h> 31 32 // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so\n 33 // 012345678901234567890123456789012345678901234567890123456789 34 // 0 1 2 3 4 5 35 static map_info_t* parse_maps_line(const char* line) 36 { 37 unsigned long int start; 38 unsigned long int end; 39 char permissions[5]; 40 int name_pos; 41 if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*d%n", &start, &end, 42 permissions, &name_pos) != 3) { 43 return NULL; 44 } 45 46 while (isspace(line[name_pos])) { 47 name_pos += 1; 48 } 49 const char* name = line + name_pos; 50 size_t name_len = strlen(name); 51 if (name_len && name[name_len - 1] == '\n') { 52 name_len -= 1; 53 } 54 55 map_info_t* mi = calloc(1, sizeof(map_info_t) + name_len + 1); 56 if (mi) { 57 mi->start = start; 58 mi->end = end; 59 mi->is_readable = strlen(permissions) == 4 && permissions[0] == 'r'; 60 mi->is_writable = strlen(permissions) == 4 && permissions[1] == 'w'; 61 mi->is_executable = strlen(permissions) == 4 && permissions[2] == 'x'; 62 mi->data = NULL; 63 memcpy(mi->name, name, name_len); 64 mi->name[name_len] = '\0'; 65 ALOGV("Parsed map: start=0x%08x, end=0x%08x, " 66 "is_readable=%d, is_writable=%d, is_executable=%d, name=%s", 67 mi->start, mi->end, 68 mi->is_readable, mi->is_writable, mi->is_executable, mi->name); 69 } 70 return mi; 71 } 72 73 map_info_t* load_map_info_list(pid_t tid) { 74 char path[PATH_MAX]; 75 char line[1024]; 76 FILE* fp; 77 map_info_t* milist = NULL; 78 79 snprintf(path, PATH_MAX, "/proc/%d/maps", tid); 80 fp = fopen(path, "r"); 81 if (fp) { 82 while(fgets(line, sizeof(line), fp)) { 83 map_info_t* mi = parse_maps_line(line); 84 if (mi) { 85 mi->next = milist; 86 milist = mi; 87 } 88 } 89 fclose(fp); 90 } 91 return milist; 92 } 93 94 void free_map_info_list(map_info_t* milist) { 95 while (milist) { 96 map_info_t* next = milist->next; 97 free(milist); 98 milist = next; 99 } 100 } 101 102 const map_info_t* find_map_info(const map_info_t* milist, uintptr_t addr) { 103 const map_info_t* mi = milist; 104 while (mi && !(addr >= mi->start && addr < mi->end)) { 105 mi = mi->next; 106 } 107 return mi; 108 } 109 110 bool is_readable_map(const map_info_t* milist, uintptr_t addr) { 111 const map_info_t* mi = find_map_info(milist, addr); 112 return mi && mi->is_readable; 113 } 114 115 bool is_writable_map(const map_info_t* milist, uintptr_t addr) { 116 const map_info_t* mi = find_map_info(milist, addr); 117 return mi && mi->is_writable; 118 } 119 120 bool is_executable_map(const map_info_t* milist, uintptr_t addr) { 121 const map_info_t* mi = find_map_info(milist, addr); 122 return mi && mi->is_executable; 123 } 124 125 static pthread_mutex_t g_my_map_info_list_mutex = PTHREAD_MUTEX_INITIALIZER; 126 static map_info_t* g_my_map_info_list = NULL; 127 128 static const int64_t MAX_CACHE_AGE = 5 * 1000 * 1000000LL; 129 130 typedef struct { 131 uint32_t refs; 132 int64_t timestamp; 133 } my_map_info_data_t; 134 135 static int64_t now() { 136 struct timespec t; 137 t.tv_sec = t.tv_nsec = 0; 138 clock_gettime(CLOCK_MONOTONIC, &t); 139 return t.tv_sec * 1000000000LL + t.tv_nsec; 140 } 141 142 static void dec_ref(map_info_t* milist, my_map_info_data_t* data) { 143 if (!--data->refs) { 144 ALOGV("Freed my_map_info_list %p.", milist); 145 free(data); 146 free_map_info_list(milist); 147 } 148 } 149 150 map_info_t* acquire_my_map_info_list() { 151 pthread_mutex_lock(&g_my_map_info_list_mutex); 152 153 int64_t time = now(); 154 if (g_my_map_info_list) { 155 my_map_info_data_t* data = (my_map_info_data_t*)g_my_map_info_list->data; 156 int64_t age = time - data->timestamp; 157 if (age >= MAX_CACHE_AGE) { 158 ALOGV("Invalidated my_map_info_list %p, age=%lld.", g_my_map_info_list, age); 159 dec_ref(g_my_map_info_list, data); 160 g_my_map_info_list = NULL; 161 } else { 162 ALOGV("Reusing my_map_info_list %p, age=%lld.", g_my_map_info_list, age); 163 } 164 } 165 166 if (!g_my_map_info_list) { 167 my_map_info_data_t* data = (my_map_info_data_t*)malloc(sizeof(my_map_info_data_t)); 168 g_my_map_info_list = load_map_info_list(getpid()); 169 if (g_my_map_info_list) { 170 ALOGV("Loaded my_map_info_list %p.", g_my_map_info_list); 171 g_my_map_info_list->data = data; 172 data->refs = 1; 173 data->timestamp = time; 174 } else { 175 free(data); 176 } 177 } 178 179 map_info_t* milist = g_my_map_info_list; 180 if (milist) { 181 my_map_info_data_t* data = (my_map_info_data_t*)g_my_map_info_list->data; 182 data->refs += 1; 183 } 184 185 pthread_mutex_unlock(&g_my_map_info_list_mutex); 186 return milist; 187 } 188 189 void release_my_map_info_list(map_info_t* milist) { 190 if (milist) { 191 pthread_mutex_lock(&g_my_map_info_list_mutex); 192 193 my_map_info_data_t* data = (my_map_info_data_t*)milist->data; 194 dec_ref(milist, data); 195 196 pthread_mutex_unlock(&g_my_map_info_list_mutex); 197 } 198 } 199