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_executable = strlen(permissions) == 4 && permissions[2] == 'x'; 61 mi->data = NULL; 62 memcpy(mi->name, name, name_len); 63 mi->name[name_len] = '\0'; 64 ALOGV("Parsed map: start=0x%08x, end=0x%08x, " 65 "is_readable=%d, is_executable=%d, name=%s", 66 mi->start, mi->end, mi->is_readable, mi->is_executable, mi->name); 67 } 68 return mi; 69 } 70 71 map_info_t* load_map_info_list(pid_t tid) { 72 char path[PATH_MAX]; 73 char line[1024]; 74 FILE* fp; 75 map_info_t* milist = NULL; 76 77 snprintf(path, PATH_MAX, "/proc/%d/maps", tid); 78 fp = fopen(path, "r"); 79 if (fp) { 80 while(fgets(line, sizeof(line), fp)) { 81 map_info_t* mi = parse_maps_line(line); 82 if (mi) { 83 mi->next = milist; 84 milist = mi; 85 } 86 } 87 fclose(fp); 88 } 89 return milist; 90 } 91 92 void free_map_info_list(map_info_t* milist) { 93 while (milist) { 94 map_info_t* next = milist->next; 95 free(milist); 96 milist = next; 97 } 98 } 99 100 const map_info_t* find_map_info(const map_info_t* milist, uintptr_t addr) { 101 const map_info_t* mi = milist; 102 while (mi && !(addr >= mi->start && addr < mi->end)) { 103 mi = mi->next; 104 } 105 return mi; 106 } 107 108 bool is_readable_map(const map_info_t* milist, uintptr_t addr) { 109 const map_info_t* mi = find_map_info(milist, addr); 110 return mi && mi->is_readable; 111 } 112 113 bool is_executable_map(const map_info_t* milist, uintptr_t addr) { 114 const map_info_t* mi = find_map_info(milist, addr); 115 return mi && mi->is_executable; 116 } 117 118 static pthread_mutex_t g_my_map_info_list_mutex = PTHREAD_MUTEX_INITIALIZER; 119 static map_info_t* g_my_map_info_list = NULL; 120 121 static const int64_t MAX_CACHE_AGE = 5 * 1000 * 1000000LL; 122 123 typedef struct { 124 uint32_t refs; 125 int64_t timestamp; 126 } my_map_info_data_t; 127 128 static int64_t now() { 129 struct timespec t; 130 t.tv_sec = t.tv_nsec = 0; 131 clock_gettime(CLOCK_MONOTONIC, &t); 132 return t.tv_sec * 1000000000LL + t.tv_nsec; 133 } 134 135 static void dec_ref(map_info_t* milist, my_map_info_data_t* data) { 136 if (!--data->refs) { 137 ALOGV("Freed my_map_info_list %p.", milist); 138 free(data); 139 free_map_info_list(milist); 140 } 141 } 142 143 map_info_t* acquire_my_map_info_list() { 144 pthread_mutex_lock(&g_my_map_info_list_mutex); 145 146 int64_t time = now(); 147 if (g_my_map_info_list) { 148 my_map_info_data_t* data = (my_map_info_data_t*)g_my_map_info_list->data; 149 int64_t age = time - data->timestamp; 150 if (age >= MAX_CACHE_AGE) { 151 ALOGV("Invalidated my_map_info_list %p, age=%lld.", g_my_map_info_list, age); 152 dec_ref(g_my_map_info_list, data); 153 g_my_map_info_list = NULL; 154 } else { 155 ALOGV("Reusing my_map_info_list %p, age=%lld.", g_my_map_info_list, age); 156 } 157 } 158 159 if (!g_my_map_info_list) { 160 my_map_info_data_t* data = (my_map_info_data_t*)malloc(sizeof(my_map_info_data_t)); 161 g_my_map_info_list = load_map_info_list(getpid()); 162 if (g_my_map_info_list) { 163 ALOGV("Loaded my_map_info_list %p.", g_my_map_info_list); 164 g_my_map_info_list->data = data; 165 data->refs = 1; 166 data->timestamp = time; 167 } else { 168 free(data); 169 } 170 } 171 172 map_info_t* milist = g_my_map_info_list; 173 if (milist) { 174 my_map_info_data_t* data = (my_map_info_data_t*)g_my_map_info_list->data; 175 data->refs += 1; 176 } 177 178 pthread_mutex_unlock(&g_my_map_info_list_mutex); 179 return milist; 180 } 181 182 void release_my_map_info_list(map_info_t* milist) { 183 if (milist) { 184 pthread_mutex_lock(&g_my_map_info_list_mutex); 185 186 my_map_info_data_t* data = (my_map_info_data_t*)milist->data; 187 dec_ref(milist, data); 188 189 pthread_mutex_unlock(&g_my_map_info_list_mutex); 190 } 191 } 192