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 #if defined(__APPLE__) 33 34 // Mac OS vmmap(1) output: 35 // __TEXT 0009f000-000a1000 [ 8K 8K] r-x/rwx SM=COW /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n 36 // 012345678901234567890123456789012345678901234567890123456789 37 // 0 1 2 3 4 5 38 static map_info_t* parse_vmmap_line(const char* line) { 39 unsigned long int start; 40 unsigned long int end; 41 char permissions[4]; 42 int name_pos; 43 if (sscanf(line, "%*21c %lx-%lx [%*13c] %3c/%*3c SM=%*3c %n", 44 &start, &end, permissions, &name_pos) != 3) { 45 return NULL; 46 } 47 48 const char* name = line + name_pos; 49 size_t name_len = strlen(name); 50 51 map_info_t* mi = calloc(1, sizeof(map_info_t) + name_len); 52 if (mi != NULL) { 53 mi->start = start; 54 mi->end = end; 55 mi->is_readable = permissions[0] == 'r'; 56 mi->is_writable = permissions[1] == 'w'; 57 mi->is_executable = permissions[2] == 'x'; 58 mi->data = NULL; 59 memcpy(mi->name, name, name_len); 60 mi->name[name_len - 1] = '\0'; 61 ALOGV("Parsed map: start=0x%08x, end=0x%08x, " 62 "is_readable=%d, is_writable=%d is_executable=%d, name=%s", 63 mi->start, mi->end, 64 mi->is_readable, mi->is_writable, mi->is_executable, mi->name); 65 } 66 return mi; 67 } 68 69 map_info_t* load_map_info_list(pid_t pid) { 70 char cmd[1024]; 71 snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid); 72 FILE* fp = popen(cmd, "r"); 73 if (fp == NULL) { 74 return NULL; 75 } 76 77 char line[1024]; 78 map_info_t* milist = NULL; 79 while (fgets(line, sizeof(line), fp) != NULL) { 80 map_info_t* mi = parse_vmmap_line(line); 81 if (mi != NULL) { 82 mi->next = milist; 83 milist = mi; 84 } 85 } 86 pclose(fp); 87 return milist; 88 } 89 90 #else 91 92 // Linux /proc/<pid>/maps lines: 93 // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so\n 94 // 012345678901234567890123456789012345678901234567890123456789 95 // 0 1 2 3 4 5 96 static map_info_t* parse_maps_line(const char* line) 97 { 98 unsigned long int start; 99 unsigned long int end; 100 char permissions[5]; 101 int name_pos; 102 if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*d%n", &start, &end, 103 permissions, &name_pos) != 3) { 104 return NULL; 105 } 106 107 while (isspace(line[name_pos])) { 108 name_pos += 1; 109 } 110 const char* name = line + name_pos; 111 size_t name_len = strlen(name); 112 if (name_len && name[name_len - 1] == '\n') { 113 name_len -= 1; 114 } 115 116 map_info_t* mi = calloc(1, sizeof(map_info_t) + name_len + 1); 117 if (mi) { 118 mi->start = start; 119 mi->end = end; 120 mi->is_readable = strlen(permissions) == 4 && permissions[0] == 'r'; 121 mi->is_writable = strlen(permissions) == 4 && permissions[1] == 'w'; 122 mi->is_executable = strlen(permissions) == 4 && permissions[2] == 'x'; 123 mi->data = NULL; 124 memcpy(mi->name, name, name_len); 125 mi->name[name_len] = '\0'; 126 ALOGV("Parsed map: start=0x%08x, end=0x%08x, " 127 "is_readable=%d, is_writable=%d, is_executable=%d, name=%s", 128 mi->start, mi->end, 129 mi->is_readable, mi->is_writable, mi->is_executable, mi->name); 130 } 131 return mi; 132 } 133 134 map_info_t* load_map_info_list(pid_t tid) { 135 char path[PATH_MAX]; 136 char line[1024]; 137 FILE* fp; 138 map_info_t* milist = NULL; 139 140 snprintf(path, PATH_MAX, "/proc/%d/maps", tid); 141 fp = fopen(path, "r"); 142 if (fp) { 143 while(fgets(line, sizeof(line), fp)) { 144 map_info_t* mi = parse_maps_line(line); 145 if (mi) { 146 mi->next = milist; 147 milist = mi; 148 } 149 } 150 fclose(fp); 151 } 152 return milist; 153 } 154 155 #endif 156 157 void free_map_info_list(map_info_t* milist) { 158 while (milist) { 159 map_info_t* next = milist->next; 160 free(milist); 161 milist = next; 162 } 163 } 164 165 const map_info_t* find_map_info(const map_info_t* milist, uintptr_t addr) { 166 const map_info_t* mi = milist; 167 while (mi && !(addr >= mi->start && addr < mi->end)) { 168 mi = mi->next; 169 } 170 return mi; 171 } 172 173 bool is_readable_map(const map_info_t* milist, uintptr_t addr) { 174 const map_info_t* mi = find_map_info(milist, addr); 175 return mi && mi->is_readable; 176 } 177 178 bool is_writable_map(const map_info_t* milist, uintptr_t addr) { 179 const map_info_t* mi = find_map_info(milist, addr); 180 return mi && mi->is_writable; 181 } 182 183 bool is_executable_map(const map_info_t* milist, uintptr_t addr) { 184 const map_info_t* mi = find_map_info(milist, addr); 185 return mi && mi->is_executable; 186 } 187 188 static pthread_mutex_t g_my_map_info_list_mutex = PTHREAD_MUTEX_INITIALIZER; 189 static map_info_t* g_my_map_info_list = NULL; 190 191 static const int64_t MAX_CACHE_AGE = 5 * 1000 * 1000000LL; 192 193 typedef struct { 194 uint32_t refs; 195 int64_t timestamp; 196 } my_map_info_data_t; 197 198 static int64_t now_ns() { 199 #if defined(HAVE_POSIX_CLOCKS) 200 struct timespec t; 201 t.tv_sec = t.tv_nsec = 0; 202 clock_gettime(CLOCK_MONOTONIC, &t); 203 return t.tv_sec * 1000000000LL + t.tv_nsec; 204 #else 205 struct timeval t; 206 gettimeofday(&t, NULL); 207 return t.tv_sec * 1000000000LL + t.tv_usec * 1000LL; 208 #endif 209 } 210 211 static void dec_ref(map_info_t* milist, my_map_info_data_t* data) { 212 if (!--data->refs) { 213 ALOGV("Freed my_map_info_list %p.", milist); 214 free(data); 215 free_map_info_list(milist); 216 } 217 } 218 219 map_info_t* acquire_my_map_info_list() { 220 pthread_mutex_lock(&g_my_map_info_list_mutex); 221 222 int64_t time = now_ns(); 223 if (g_my_map_info_list != NULL) { 224 my_map_info_data_t* data = (my_map_info_data_t*)g_my_map_info_list->data; 225 int64_t age = time - data->timestamp; 226 if (age >= MAX_CACHE_AGE) { 227 ALOGV("Invalidated my_map_info_list %p, age=%lld.", g_my_map_info_list, age); 228 dec_ref(g_my_map_info_list, data); 229 g_my_map_info_list = NULL; 230 } else { 231 ALOGV("Reusing my_map_info_list %p, age=%lld.", g_my_map_info_list, age); 232 } 233 } 234 235 if (g_my_map_info_list == NULL) { 236 my_map_info_data_t* data = (my_map_info_data_t*)malloc(sizeof(my_map_info_data_t)); 237 g_my_map_info_list = load_map_info_list(getpid()); 238 if (g_my_map_info_list != NULL) { 239 ALOGV("Loaded my_map_info_list %p.", g_my_map_info_list); 240 g_my_map_info_list->data = data; 241 data->refs = 1; 242 data->timestamp = time; 243 } else { 244 free(data); 245 } 246 } 247 248 map_info_t* milist = g_my_map_info_list; 249 if (milist) { 250 my_map_info_data_t* data = (my_map_info_data_t*)g_my_map_info_list->data; 251 data->refs += 1; 252 } 253 254 pthread_mutex_unlock(&g_my_map_info_list_mutex); 255 return milist; 256 } 257 258 void release_my_map_info_list(map_info_t* milist) { 259 if (milist) { 260 pthread_mutex_lock(&g_my_map_info_list_mutex); 261 262 my_map_info_data_t* data = (my_map_info_data_t*)milist->data; 263 dec_ref(milist, data); 264 265 pthread_mutex_unlock(&g_my_map_info_list_mutex); 266 } 267 } 268 269 void flush_my_map_info_list() { 270 pthread_mutex_lock(&g_my_map_info_list_mutex); 271 272 if (g_my_map_info_list != NULL) { 273 my_map_info_data_t* data = (my_map_info_data_t*) g_my_map_info_list->data; 274 dec_ref(g_my_map_info_list, data); 275 g_my_map_info_list = NULL; 276 } 277 278 pthread_mutex_unlock(&g_my_map_info_list_mutex); 279 } 280