1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // Implements the crazy linker C-based API exposed by <crazy_linker.h> 6 7 #include <crazy_linker.h> 8 9 #include <string.h> 10 11 #include "crazy_linker_error.h" 12 #include "crazy_linker_ashmem.h" 13 #include "crazy_linker_globals.h" 14 #include "crazy_linker_proc_maps.h" 15 #include "crazy_linker_search_path_list.h" 16 #include "crazy_linker_shared_library.h" 17 #include "crazy_linker_thread.h" 18 #include "crazy_linker_util.h" 19 #include "crazy_linker_library_view.h" 20 #include "crazy_linker_system.h" 21 22 using crazy::Globals; 23 using crazy::Error; 24 using crazy::SearchPathList; 25 using crazy::ScopedGlobalLock; 26 using crazy::LibraryView; 27 28 // 29 // crazy_context_t 30 // 31 32 struct crazy_context_t { 33 public: 34 crazy_context_t() 35 : load_address(0), 36 file_offset(0), 37 error(), 38 search_paths(), 39 java_vm(NULL), 40 minimum_jni_version(0), 41 callback_poster(NULL), 42 callback_poster_opaque(NULL) { 43 ResetSearchPaths(); 44 } 45 46 void ResetSearchPaths(); 47 48 size_t load_address; 49 size_t file_offset; 50 Error error; 51 SearchPathList search_paths; 52 void* java_vm; 53 int minimum_jni_version; 54 crazy_callback_poster_t callback_poster; 55 void* callback_poster_opaque; 56 }; 57 58 void crazy_context_t::ResetSearchPaths() { 59 search_paths.ResetFromEnv("LD_LIBRARY_PATH"); 60 } 61 62 // 63 // API functions 64 // 65 66 extern "C" { 67 68 crazy_context_t* crazy_context_create(void) { return new crazy_context_t(); } 69 70 const char* crazy_context_get_error(crazy_context_t* context) { 71 const char* error = context->error.c_str(); 72 return (error[0] != '\0') ? error : NULL; 73 } 74 75 // Clear error in a given context. 76 void crazy_context_clear_error(crazy_context_t* context) { 77 context->error = ""; 78 } 79 80 void crazy_context_set_load_address(crazy_context_t* context, 81 size_t load_address) { 82 context->load_address = load_address; 83 } 84 85 size_t crazy_context_get_load_address(crazy_context_t* context) { 86 return context->load_address; 87 } 88 89 void crazy_context_set_file_offset(crazy_context_t* context, 90 size_t file_offset) { 91 context->file_offset = file_offset; 92 } 93 94 size_t crazy_context_get_file_offset(crazy_context_t* context) { 95 return context->file_offset; 96 } 97 98 crazy_status_t crazy_context_add_search_path(crazy_context_t* context, 99 const char* file_path) { 100 context->search_paths.AddPaths(file_path); 101 return CRAZY_STATUS_SUCCESS; 102 } 103 104 crazy_status_t crazy_context_add_search_path_for_address( 105 crazy_context_t* context, 106 void* address) { 107 uintptr_t load_address; 108 char path[512]; 109 char* p; 110 111 if (crazy::FindElfBinaryForAddress( 112 address, &load_address, path, sizeof(path)) && 113 (p = strrchr(path, '/')) != NULL && p[1]) { 114 *p = '\0'; 115 return crazy_context_add_search_path(context, path); 116 } 117 118 context->error.Format("Could not find ELF binary at address @%p", address); 119 return CRAZY_STATUS_FAILURE; 120 } 121 122 void crazy_context_reset_search_paths(crazy_context_t* context) { 123 context->ResetSearchPaths(); 124 } 125 126 void crazy_context_set_java_vm(crazy_context_t* context, 127 void* java_vm, 128 int minimum_jni_version) { 129 context->java_vm = java_vm; 130 context->minimum_jni_version = minimum_jni_version; 131 } 132 133 void crazy_context_get_java_vm(crazy_context_t* context, 134 void** java_vm, 135 int* minimum_jni_version) { 136 *java_vm = context->java_vm; 137 *minimum_jni_version = context->minimum_jni_version; 138 } 139 140 void crazy_context_set_callback_poster(crazy_context_t* context, 141 crazy_callback_poster_t poster, 142 void* poster_opaque) { 143 context->callback_poster = poster; 144 context->callback_poster_opaque = poster_opaque; 145 } 146 147 void crazy_context_get_callback_poster(crazy_context_t* context, 148 crazy_callback_poster_t* poster, 149 void** poster_opaque) { 150 *poster = context->callback_poster; 151 *poster_opaque = context->callback_poster_opaque; 152 } 153 154 void crazy_callback_run(crazy_callback_t* callback) { 155 (*callback->handler)(callback->opaque); 156 } 157 158 void crazy_context_destroy(crazy_context_t* context) { delete context; } 159 160 // Scoped delayed execution, removes RDebug callbacks on scope exit. No-op 161 // if callback is NULL. 162 class ScopedDelayedCallbackPoster { 163 public: 164 ScopedDelayedCallbackPoster(crazy_context_t* context) { 165 if (context && context->callback_poster) { 166 crazy::Globals::GetRDebug()->SetDelayedCallbackPoster(&PostFromContext, 167 context); 168 set_delayed_callback_poster_ = true; 169 } else { 170 set_delayed_callback_poster_ = false; 171 } 172 } 173 174 ~ScopedDelayedCallbackPoster() { 175 if (set_delayed_callback_poster_) 176 crazy::Globals::GetRDebug()->SetDelayedCallbackPoster(NULL, NULL); 177 } 178 179 private: 180 // Wrap callback hander and opaque into a call to a crazy_context_poster_t. 181 static bool PostFromContext(void* crazy_context, 182 crazy_callback_handler_t handler, 183 void* opaque) { 184 crazy_context_t* context = static_cast<crazy_context_t*>(crazy_context); 185 crazy_callback_t callback; 186 callback.handler = handler; 187 callback.opaque = opaque; 188 return context->callback_poster(&callback, 189 context->callback_poster_opaque); 190 } 191 192 // True if the context offered a callback_poster, otherwise false. 193 bool set_delayed_callback_poster_; 194 }; 195 196 crazy_status_t crazy_library_open(crazy_library_t** library, 197 const char* lib_name, 198 crazy_context_t* context) { 199 ScopedDelayedCallbackPoster poster(context); 200 ScopedGlobalLock lock; 201 202 LibraryView* wrap = 203 crazy::Globals::GetLibraries()->LoadLibrary(lib_name, 204 RTLD_NOW, 205 context->load_address, 206 context->file_offset, 207 &context->search_paths, 208 &context->error); 209 210 if (!wrap) 211 return CRAZY_STATUS_FAILURE; 212 213 if (context->java_vm != NULL && wrap->IsCrazy()) { 214 crazy::SharedLibrary* lib = wrap->GetCrazy(); 215 if (!lib->SetJavaVM( 216 context->java_vm, context->minimum_jni_version, &context->error)) { 217 crazy::Globals::GetLibraries()->UnloadLibrary(wrap); 218 return CRAZY_STATUS_FAILURE; 219 } 220 } 221 222 *library = reinterpret_cast<crazy_library_t*>(wrap); 223 return CRAZY_STATUS_SUCCESS; 224 } 225 226 crazy_status_t crazy_library_get_info(crazy_library_t* library, 227 crazy_context_t* context, 228 crazy_library_info_t* info) { 229 if (!library) { 230 context->error = "Invalid library file handle"; 231 return CRAZY_STATUS_FAILURE; 232 } 233 234 LibraryView* wrap = reinterpret_cast<LibraryView*>(library); 235 if (!wrap->GetInfo(&info->load_address, 236 &info->load_size, 237 &info->relro_start, 238 &info->relro_size, 239 &context->error)) { 240 return CRAZY_STATUS_FAILURE; 241 } 242 243 return CRAZY_STATUS_SUCCESS; 244 } 245 246 crazy_status_t crazy_system_can_share_relro(void) { 247 crazy::AshmemRegion region; 248 if (!region.Allocate(PAGE_SIZE, NULL) || 249 !region.SetProtectionFlags(PROT_READ) || 250 !crazy::AshmemRegion::CheckFileDescriptorIsReadOnly(region.fd())) 251 return CRAZY_STATUS_FAILURE; 252 253 return CRAZY_STATUS_SUCCESS; 254 } 255 256 crazy_status_t crazy_library_create_shared_relro(crazy_library_t* library, 257 crazy_context_t* context, 258 size_t load_address, 259 size_t* relro_start, 260 size_t* relro_size, 261 int* relro_fd) { 262 LibraryView* wrap = reinterpret_cast<LibraryView*>(library); 263 264 if (!library || !wrap->IsCrazy()) { 265 context->error = "Invalid library file handle"; 266 return CRAZY_STATUS_FAILURE; 267 } 268 269 crazy::SharedLibrary* lib = wrap->GetCrazy(); 270 if (!lib->CreateSharedRelro( 271 load_address, relro_start, relro_size, relro_fd, &context->error)) 272 return CRAZY_STATUS_FAILURE; 273 274 return CRAZY_STATUS_SUCCESS; 275 } 276 277 crazy_status_t crazy_library_use_shared_relro(crazy_library_t* library, 278 crazy_context_t* context, 279 size_t relro_start, 280 size_t relro_size, 281 int relro_fd) { 282 LibraryView* wrap = reinterpret_cast<LibraryView*>(library); 283 284 if (!library || !wrap->IsCrazy()) { 285 context->error = "Invalid library file handle"; 286 return CRAZY_STATUS_FAILURE; 287 } 288 289 crazy::SharedLibrary* lib = wrap->GetCrazy(); 290 if (!lib->UseSharedRelro(relro_start, relro_size, relro_fd, &context->error)) 291 return CRAZY_STATUS_FAILURE; 292 293 return CRAZY_STATUS_SUCCESS; 294 } 295 296 crazy_status_t crazy_library_find_by_name(const char* library_name, 297 crazy_library_t** library) { 298 { 299 ScopedGlobalLock lock; 300 LibraryView* wrap = 301 Globals::GetLibraries()->FindLibraryByName(library_name); 302 if (!wrap) 303 return CRAZY_STATUS_FAILURE; 304 305 wrap->AddRef(); 306 *library = reinterpret_cast<crazy_library_t*>(wrap); 307 } 308 return CRAZY_STATUS_SUCCESS; 309 } 310 311 crazy_status_t crazy_library_find_symbol(crazy_library_t* library, 312 const char* symbol_name, 313 void** symbol_address) { 314 LibraryView* wrap = reinterpret_cast<LibraryView*>(library); 315 316 // TODO(digit): Handle NULL symbols properly. 317 *symbol_address = wrap->LookupSymbol(symbol_name); 318 return (*symbol_address == NULL) ? CRAZY_STATUS_FAILURE 319 : CRAZY_STATUS_SUCCESS; 320 } 321 322 crazy_status_t crazy_linker_find_symbol(const char* symbol_name, 323 void** symbol_address) { 324 // TODO(digit): Implement this. 325 return CRAZY_STATUS_FAILURE; 326 } 327 328 crazy_status_t crazy_library_find_from_address(void* address, 329 crazy_library_t** library) { 330 { 331 ScopedGlobalLock lock; 332 LibraryView* wrap = Globals::GetLibraries()->FindLibraryForAddress(address); 333 if (!wrap) 334 return CRAZY_STATUS_FAILURE; 335 336 wrap->AddRef(); 337 338 *library = reinterpret_cast<crazy_library_t*>(wrap); 339 return CRAZY_STATUS_SUCCESS; 340 } 341 } 342 343 void crazy_library_close(crazy_library_t* library) { 344 crazy_library_close_with_context(library, NULL); 345 } 346 347 void crazy_library_close_with_context(crazy_library_t* library, 348 crazy_context_t* context) { 349 if (library) { 350 ScopedDelayedCallbackPoster poster(context); 351 ScopedGlobalLock lock; 352 LibraryView* wrap = reinterpret_cast<LibraryView*>(library); 353 354 Globals::GetLibraries()->UnloadLibrary(wrap); 355 } 356 } 357 358 } // extern "C" 359