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