1 /* 2 * Copyright 2003-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 /*! User Runtime Loader support in the kernel */ 7 8 9 #include <KernelExport.h> 10 11 #include <kernel.h> 12 #include <kimage.h> 13 #include <kscheduler.h> 14 #include <lock.h> 15 #include <Notifications.h> 16 #include <team.h> 17 #include <thread.h> 18 #include <thread_types.h> 19 #include <user_debugger.h> 20 #include <util/AutoLock.h> 21 22 #include <stdlib.h> 23 #include <string.h> 24 25 26 //#define TRACE_IMAGE 27 #ifdef TRACE_IMAGE 28 # define TRACE(x) dprintf x 29 #else 30 # define TRACE(x) ; 31 #endif 32 33 #define ADD_DEBUGGER_COMMANDS 34 35 36 struct ImageTableDefinition { 37 typedef image_id KeyType; 38 typedef struct image ValueType; 39 40 size_t HashKey(image_id key) const { return key; } 41 size_t Hash(struct image* value) const { return value->info.id; } 42 bool Compare(image_id key, struct image* value) const 43 { return value->info.id == key; } 44 struct image*& GetLink(struct image* value) const 45 { return value->hash_link; } 46 }; 47 48 typedef BOpenHashTable<ImageTableDefinition> ImageTable; 49 50 51 class ImageNotificationService : public DefaultNotificationService { 52 public: 53 ImageNotificationService() 54 : DefaultNotificationService("images") 55 { 56 } 57 58 void Notify(uint32 eventCode, struct image* image) 59 { 60 char eventBuffer[128]; 61 KMessage event; 62 event.SetTo(eventBuffer, sizeof(eventBuffer), IMAGE_MONITOR); 63 event.AddInt32("event", eventCode); 64 event.AddInt32("image", image->info.id); 65 event.AddPointer("imageStruct", image); 66 67 DefaultNotificationService::Notify(event, eventCode); 68 } 69 }; 70 71 72 static image_id sNextImageID = 1; 73 static mutex sImageMutex = MUTEX_INITIALIZER("image"); 74 static ImageTable* sImageTable; 75 static ImageNotificationService sNotificationService; 76 77 78 /*! Registers an image with the specified team. 79 */ 80 image_id 81 register_image(Team *team, image_info *_info, size_t size) 82 { 83 image_id id = atomic_add(&sNextImageID, 1); 84 struct image *image; 85 86 image = (struct image*)malloc(sizeof(struct image)); 87 if (image == NULL) 88 return B_NO_MEMORY; 89 90 memcpy(&image->info, _info, sizeof(image_info)); 91 image->team = team->id; 92 93 mutex_lock(&sImageMutex); 94 95 image->info.id = id; 96 97 // Add the app image to the head of the list. Some code relies on it being 98 // the first image to be returned by get_next_image_info(). 99 if (image->info.type == B_APP_IMAGE) 100 list_add_link_to_head(&team->image_list, image); 101 else 102 list_add_item(&team->image_list, image); 103 sImageTable->Insert(image); 104 105 // notify listeners 106 sNotificationService.Notify(IMAGE_ADDED, image); 107 108 mutex_unlock(&sImageMutex); 109 110 TRACE(("register_image(team = %p, image id = %ld, image = %p\n", team, id, image)); 111 return id; 112 } 113 114 115 /*! Unregisters an image from the specified team. 116 */ 117 status_t 118 unregister_image(Team *team, image_id id) 119 { 120 status_t status = B_ENTRY_NOT_FOUND; 121 122 mutex_lock(&sImageMutex); 123 124 struct image *image = sImageTable->Lookup(id); 125 if (image != NULL && image->team == team->id) { 126 list_remove_link(image); 127 sImageTable->Remove(image); 128 status = B_OK; 129 } 130 131 mutex_unlock(&sImageMutex); 132 133 if (status == B_OK) { 134 // notify the debugger 135 user_debug_image_deleted(&image->info); 136 137 // notify listeners 138 sNotificationService.Notify(IMAGE_REMOVED, image); 139 140 free(image); 141 } 142 143 return status; 144 } 145 146 147 /*! Counts the registered images from the specified team. 148 Interrupts must be enabled. 149 */ 150 int32 151 count_images(Team *team) 152 { 153 struct image *image = NULL; 154 int32 count = 0; 155 156 MutexLocker locker(sImageMutex); 157 158 while ((image = (struct image*)list_get_next_item(&team->image_list, image)) 159 != NULL) { 160 count++; 161 } 162 163 return count; 164 } 165 166 167 /*! Removes all images from the specified team. Must only be called 168 with a team that has already been removed from the list (in thread_exit()). 169 */ 170 status_t 171 remove_images(Team *team) 172 { 173 struct image *image; 174 175 ASSERT(team != NULL); 176 177 mutex_lock(&sImageMutex); 178 179 while ((image = (struct image*)list_remove_head_item(&team->image_list)) 180 != NULL) { 181 sImageTable->Remove(image); 182 free(image); 183 } 184 185 mutex_unlock(&sImageMutex); 186 187 return B_OK; 188 } 189 190 191 status_t 192 _get_image_info(image_id id, image_info *info, size_t size) 193 { 194 if (size > sizeof(image_info)) 195 return B_BAD_VALUE; 196 197 status_t status = B_ENTRY_NOT_FOUND; 198 199 mutex_lock(&sImageMutex); 200 201 struct image *image = sImageTable->Lookup(id); 202 if (image != NULL) { 203 memcpy(info, &image->info, size); 204 status = B_OK; 205 } 206 207 mutex_unlock(&sImageMutex); 208 209 return status; 210 } 211 212 213 status_t 214 _get_next_image_info(team_id teamID, int32 *cookie, image_info *info, 215 size_t size) 216 { 217 if (size > sizeof(image_info)) 218 return B_BAD_VALUE; 219 220 // get the team 221 Team* team = Team::Get(teamID); 222 if (team == NULL) 223 return B_BAD_TEAM_ID; 224 BReference<Team> teamReference(team, true); 225 226 // iterate through the team's images 227 MutexLocker imageLocker(sImageMutex); 228 229 struct image* image = NULL; 230 int32 count = 0; 231 232 while ((image = (struct image*)list_get_next_item(&team->image_list, 233 image)) != NULL) { 234 if (count == *cookie) { 235 memcpy(info, &image->info, size); 236 (*cookie)++; 237 return B_OK; 238 } 239 count++; 240 } 241 242 return B_ENTRY_NOT_FOUND; 243 } 244 245 246 #ifdef ADD_DEBUGGER_COMMANDS 247 static int 248 dump_images_list(int argc, char **argv) 249 { 250 struct image *image = NULL; 251 Team *team; 252 253 if (argc > 1) { 254 team_id id = strtol(argv[1], NULL, 0); 255 team = team_get_team_struct_locked(id); 256 if (team == NULL) { 257 kprintf("No team with ID %" B_PRId32 " found\n", id); 258 return 1; 259 } 260 } else 261 team = thread_get_current_thread()->team; 262 263 kprintf("Registered images of team %" B_PRId32 "\n", team->id); 264 kprintf(" ID %-*s size %-*s size name\n", 265 B_PRINTF_POINTER_WIDTH, "text", B_PRINTF_POINTER_WIDTH, "data"); 266 267 while ((image = (struct image*)list_get_next_item(&team->image_list, image)) 268 != NULL) { 269 image_info *info = &image->info; 270 271 kprintf("%6" B_PRId32 " %p %-7" B_PRId32 " %p %-7" B_PRId32 " %s\n", 272 info->id, info->text, info->text_size, info->data, info->data_size, 273 info->name); 274 } 275 276 return 0; 277 } 278 #endif 279 280 281 struct image* 282 image_iterate_through_images(image_iterator_callback callback, void* cookie) 283 { 284 MutexLocker locker(sImageMutex); 285 286 ImageTable::Iterator it = sImageTable->GetIterator(); 287 struct image* image = NULL; 288 while ((image = it.Next()) != NULL) { 289 if (callback(image, cookie)) 290 break; 291 } 292 293 return image; 294 } 295 296 297 status_t 298 image_debug_lookup_user_symbol_address(Team *team, addr_t address, 299 addr_t *_baseAddress, const char **_symbolName, const char **_imageName, 300 bool *_exactMatch) 301 { 302 // TODO: work together with ELF reader and runtime_loader 303 304 struct image *image = NULL; 305 306 while ((image = (struct image*)list_get_next_item(&team->image_list, image)) 307 != NULL) { 308 image_info *info = &image->info; 309 310 if ((address < (addr_t)info->text 311 || address >= (addr_t)info->text + info->text_size) 312 && (address < (addr_t)info->data 313 || address >= (addr_t)info->data + info->data_size)) 314 continue; 315 316 // found image 317 *_symbolName = NULL; 318 *_imageName = info->name; 319 *_baseAddress = (addr_t)info->text; 320 *_exactMatch = false; 321 322 return B_OK; 323 } 324 325 return B_ENTRY_NOT_FOUND; 326 } 327 328 329 status_t 330 image_init(void) 331 { 332 sImageTable = new(std::nothrow) ImageTable; 333 if (sImageTable == NULL) { 334 panic("image_init(): Failed to allocate image table!"); 335 return B_NO_MEMORY; 336 } 337 338 status_t error = sImageTable->Init(); 339 if (error != B_OK) { 340 panic("image_init(): Failed to init image table: %s", strerror(error)); 341 return error; 342 } 343 344 new(&sNotificationService) ImageNotificationService(); 345 346 sNotificationService.Register(); 347 348 #ifdef ADD_DEBUGGER_COMMANDS 349 add_debugger_command("team_images", &dump_images_list, "Dump all registered images from the current team"); 350 #endif 351 352 return B_OK; 353 } 354 355 356 static void 357 notify_loading_app(status_t result, bool suspend) 358 { 359 Team* team = thread_get_current_thread()->team; 360 361 TeamLocker teamLocker(team); 362 363 if (team->loading_info) { 364 // there's indeed someone waiting 365 struct team_loading_info* loadingInfo = team->loading_info; 366 team->loading_info = NULL; 367 368 loadingInfo->result = result; 369 loadingInfo->done = true; 370 371 // we're done with the team stuff, get the scheduler lock instead 372 teamLocker.Unlock(); 373 InterruptsSpinLocker schedulerLocker(gSchedulerLock); 374 375 // wake up the waiting thread 376 if (loadingInfo->thread->state == B_THREAD_SUSPENDED) 377 scheduler_enqueue_in_run_queue(loadingInfo->thread); 378 379 // suspend ourselves, if desired 380 if (suspend) { 381 thread_get_current_thread()->next_state = B_THREAD_SUSPENDED; 382 scheduler_reschedule(); 383 } 384 } 385 } 386 387 388 // #pragma mark - 389 // Functions exported for the user space 390 391 392 status_t 393 _user_unregister_image(image_id id) 394 { 395 return unregister_image(thread_get_current_thread()->team, id); 396 } 397 398 399 image_id 400 _user_register_image(image_info *userInfo, size_t size) 401 { 402 image_info info; 403 404 if (size != sizeof(image_info)) 405 return B_BAD_VALUE; 406 407 if (!IS_USER_ADDRESS(userInfo) 408 || user_memcpy(&info, userInfo, size) < B_OK) 409 return B_BAD_ADDRESS; 410 411 return register_image(thread_get_current_thread()->team, &info, size); 412 } 413 414 415 void 416 _user_image_relocated(image_id id) 417 { 418 image_info info; 419 status_t error; 420 421 // get an image info 422 error = _get_image_info(id, &info, sizeof(image_info)); 423 if (error != B_OK) { 424 dprintf("_user_image_relocated(%" B_PRId32 "): Failed to get image " 425 "info: %" B_PRIx32 "\n", id, error); 426 return; 427 } 428 429 // notify the debugger 430 user_debug_image_created(&info); 431 432 // If the image is the app image, loading is done. We need to notify the 433 // thread who initiated the process and is now waiting for us to be done. 434 if (info.type == B_APP_IMAGE) 435 notify_loading_app(B_OK, true); 436 } 437 438 439 void 440 _user_loading_app_failed(status_t error) 441 { 442 if (error >= B_OK) 443 error = B_ERROR; 444 445 notify_loading_app(error, false); 446 447 _user_exit_team(error); 448 } 449 450 451 status_t 452 _user_get_image_info(image_id id, image_info *userInfo, size_t size) 453 { 454 image_info info; 455 status_t status; 456 457 if (size > sizeof(image_info)) 458 return B_BAD_VALUE; 459 460 if (!IS_USER_ADDRESS(userInfo)) 461 return B_BAD_ADDRESS; 462 463 status = _get_image_info(id, &info, sizeof(image_info)); 464 465 if (user_memcpy(userInfo, &info, size) < B_OK) 466 return B_BAD_ADDRESS; 467 468 return status; 469 } 470 471 472 status_t 473 _user_get_next_image_info(team_id team, int32 *_cookie, image_info *userInfo, 474 size_t size) 475 { 476 image_info info; 477 status_t status; 478 479 if (size > sizeof(image_info)) 480 return B_BAD_VALUE; 481 482 if (!IS_USER_ADDRESS(userInfo) || !IS_USER_ADDRESS(_cookie)) 483 return B_BAD_ADDRESS; 484 485 status = _get_next_image_info(team, _cookie, &info, sizeof(image_info)); 486 487 if (user_memcpy(userInfo, &info, size) < B_OK) 488 return B_BAD_ADDRESS; 489 490 return status; 491 } 492 493