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 <string.h> 25 #include <limits.h> 26 #include <pthread.h> 27 #include <unistd.h> 28 #include <cutils/log.h> 29 #include <sys/time.h> 30 31 // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so\n 32 // 012345678901234567890123456789012345678901234567890123456789 33 // 0 1 2 3 4 5 34 static map_info_t* parse_maps_line(const char* line) 35 { 36 unsigned long int start; 37 unsigned long int end; 38 char permissions[5]; 39 int name_pos; 40 if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*d%n", &start, &end, 41 permissions, &name_pos) != 3) { 42 return NULL; 43 } 44 45 while (isspace(line[name_pos])) { 46 name_pos += 1; 47 } 48 const char* name = line + name_pos; 49 size_t name_len = strlen(name); 50 if (name_len && name[name_len - 1] == '\n') { 51 name_len -= 1; 52 } 53 54 map_info_t* mi = calloc(1, sizeof(map_info_t) + name_len + 1); 55 if (mi) { 56 mi->start = start; 57 mi->end = end; 58 mi->is_readable = strlen(permissions) == 4 && permissions[0] == 'r'; 59 mi->is_executable = strlen(permissions) == 4 && permissions[2] == 'x'; 60 mi->data = NULL; 61 memcpy(mi->name, name, name_len); 62 mi->name[name_len] = '\0'; 63 ALOGV("Parsed map: start=0x%08x, end=0x%08x, " 64 "is_readable=%d, is_executable=%d, name=%s", 65 mi->start, mi->end, mi->is_readable, mi->is_executable, mi->name); 66 } 67 return mi; 68 } 69 70 map_info_t* load_map_info_list(pid_t tid) { 71 char path[PATH_MAX]; 72 char line[1024]; 73 FILE* fp; 74 map_info_t* milist = NULL; 75 76 snprintf(path, PATH_MAX, "/proc/%d/maps", tid); 77 fp = fopen(path, "r"); 78 if (fp) { 79 while(fgets(line, sizeof(line), fp)) { 80 map_info_t* mi = parse_maps_line(line); 81 if (mi) { 82 mi->next = milist; 83 milist = mi; 84 } 85 } 86 fclose(fp); 87 } 88 return milist; 89 } 90 91 void free_map_info_list(map_info_t* milist) { 92 while (milist) { 93 map_info_t* next = milist->next; 94 free(milist); 95 milist = next; 96 } 97 } 98 99 const map_info_t* find_map_info(const map_info_t* milist, uintptr_t addr) { 100 const map_info_t* mi = milist; 101 while (mi && !(addr >= mi->start && addr < mi->end)) { 102 mi = mi->next; 103 } 104 return mi; 105 } 106 107 bool is_readable_map(const map_info_t* milist, uintptr_t addr) { 108 const map_info_t* mi = find_map_info(milist, addr); 109 return mi && mi->is_readable; 110 } 111 112 bool is_executable_map(const map_info_t* milist, uintptr_t addr) { 113 const map_info_t* mi = find_map_info(milist, addr); 114 return mi && mi->is_executable; 115 } 116 117 static pthread_mutex_t g_my_map_info_list_mutex = PTHREAD_MUTEX_INITIALIZER; 118 static map_info_t* g_my_map_info_list = NULL; 119 120 static const int64_t MAX_CACHE_AGE = 5 * 1000 * 1000000LL; 121 122 typedef struct { 123 uint32_t refs; 124 int64_t timestamp; 125 } my_map_info_data_t; 126 127 static int64_t now() { 128 struct timespec t; 129 t.tv_sec = t.tv_nsec = 0; 130 clock_gettime(CLOCK_MONOTONIC, &t); 131 return t.tv_sec * 1000000000LL + t.tv_nsec; 132 } 133 134 static void dec_ref(map_info_t* milist, my_map_info_data_t* data) { 135 if (!--data->refs) { 136 ALOGV("Freed my_map_info_list %p.", milist); 137 free(data); 138 free_map_info_list(milist); 139 } 140 } 141 142 map_info_t* acquire_my_map_info_list() { 143 pthread_mutex_lock(&g_my_map_info_list_mutex); 144 145 int64_t time = now(); 146 if (g_my_map_info_list) { 147 my_map_info_data_t* data = (my_map_info_data_t*)g_my_map_info_list->data; 148 int64_t age = time - data->timestamp; 149 if (age >= MAX_CACHE_AGE) { 150 ALOGV("Invalidated my_map_info_list %p, age=%lld.", g_my_map_info_list, age); 151 dec_ref(g_my_map_info_list, data); 152 g_my_map_info_list = NULL; 153 } else { 154 ALOGV("Reusing my_map_info_list %p, age=%lld.", g_my_map_info_list, age); 155 } 156 } 157 158 if (!g_my_map_info_list) { 159 my_map_info_data_t* data = (my_map_info_data_t*)malloc(sizeof(my_map_info_data_t)); 160 g_my_map_info_list = load_map_info_list(getpid()); 161 if (g_my_map_info_list) { 162 ALOGV("Loaded my_map_info_list %p.", g_my_map_info_list); 163 g_my_map_info_list->data = data; 164 data->refs = 1; 165 data->timestamp = time; 166 } else { 167 free(data); 168 } 169 } 170 171 map_info_t* milist = g_my_map_info_list; 172 if (milist) { 173 my_map_info_data_t* data = (my_map_info_data_t*)g_my_map_info_list->data; 174 data->refs += 1; 175 } 176 177 pthread_mutex_unlock(&g_my_map_info_list_mutex); 178 return milist; 179 } 180 181 void release_my_map_info_list(map_info_t* milist) { 182 if (milist) { 183 pthread_mutex_lock(&g_my_map_info_list_mutex); 184 185 my_map_info_data_t* data = (my_map_info_data_t*)milist->data; 186 dec_ref(milist, data); 187 188 pthread_mutex_unlock(&g_my_map_info_list_mutex); 189 } 190 } 191