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; 214 215 ASSERT(team != NULL); 216 217 mutex_lock(&sImageMutex); 218 219 while ((image = (struct image*)list_remove_head_item(&team->image_list)) 220 != NULL) { 221 sImageTable->Remove(image); 222 free(image); 223 } 224 225 mutex_unlock(&sImageMutex); 226 227 return B_OK; 228 } 229 230 231 status_t 232 _get_image_info(image_id id, image_info *info, size_t size) 233 { 234 if (size > sizeof(image_info)) 235 return B_BAD_VALUE; 236 237 status_t status = B_ENTRY_NOT_FOUND; 238 239 mutex_lock(&sImageMutex); 240 241 struct image *image = sImageTable->Lookup(id); 242 if (image != NULL) { 243 memcpy(info, &image->info.basic_info, size); 244 status = B_OK; 245 } 246 247 mutex_unlock(&sImageMutex); 248 249 return status; 250 } 251 252 253 status_t 254 _get_next_image_info(team_id teamID, int32 *cookie, image_info *info, 255 size_t size) 256 { 257 if (size > sizeof(image_info)) 258 return B_BAD_VALUE; 259 260 // get the team 261 Team* team = Team::Get(teamID); 262 if (team == NULL) 263 return B_BAD_TEAM_ID; 264 BReference<Team> teamReference(team, true); 265 266 // iterate through the team's images 267 MutexLocker imageLocker(sImageMutex); 268 269 struct image* image = NULL; 270 int32 count = 0; 271 272 while ((image = (struct image*)list_get_next_item(&team->image_list, 273 image)) != NULL) { 274 if (count == *cookie) { 275 memcpy(info, &image->info.basic_info, size); 276 (*cookie)++; 277 return B_OK; 278 } 279 count++; 280 } 281 282 return B_ENTRY_NOT_FOUND; 283 } 284 285 286 #ifdef ADD_DEBUGGER_COMMANDS 287 static int 288 dump_images_list(int argc, char **argv) 289 { 290 struct image *image = NULL; 291 Team *team; 292 293 if (argc > 1) { 294 team_id id = strtol(argv[1], NULL, 0); 295 team = team_get_team_struct_locked(id); 296 if (team == NULL) { 297 kprintf("No team with ID %" B_PRId32 " found\n", id); 298 return 1; 299 } 300 } else 301 team = thread_get_current_thread()->team; 302 303 kprintf("Registered images of team %" B_PRId32 "\n", team->id); 304 kprintf(" ID %-*s size %-*s size name\n", 305 B_PRINTF_POINTER_WIDTH, "text", B_PRINTF_POINTER_WIDTH, "data"); 306 307 while ((image = (struct image*)list_get_next_item(&team->image_list, image)) 308 != NULL) { 309 image_info *info = &image->info.basic_info; 310 311 kprintf("%6" B_PRId32 " %p %-7" B_PRId32 " %p %-7" B_PRId32 " %s\n", 312 info->id, info->text, info->text_size, info->data, info->data_size, 313 info->name); 314 } 315 316 return 0; 317 } 318 #endif 319 320 321 struct image* 322 image_iterate_through_images(image_iterator_callback callback, void* cookie) 323 { 324 MutexLocker locker(sImageMutex); 325 326 ImageTable::Iterator it = sImageTable->GetIterator(); 327 struct image* image = NULL; 328 while ((image = it.Next()) != NULL) { 329 if (callback(image, cookie)) 330 break; 331 } 332 333 return image; 334 } 335 336 337 struct image* 338 image_iterate_through_team_images(team_id teamID, 339 image_iterator_callback callback, void* cookie) 340 { 341 // get the team 342 Team* team = Team::Get(teamID); 343 if (team == NULL) 344 return NULL; 345 BReference<Team> teamReference(team, true); 346 347 // iterate through the team's images 348 MutexLocker imageLocker(sImageMutex); 349 350 struct image* image = NULL; 351 352 while ((image = (struct image*)list_get_next_item(&team->image_list, 353 image)) != NULL) { 354 if (callback(image, cookie)) 355 break; 356 } 357 358 return image; 359 } 360 361 362 status_t 363 image_debug_lookup_user_symbol_address(Team *team, addr_t address, 364 addr_t *_baseAddress, const char **_symbolName, const char **_imageName, 365 bool *_exactMatch) 366 { 367 // TODO: Work together with ELF reader and runtime_loader. For regular user 368 // images we have the symbol and string table addresses. 369 370 struct image *image = NULL; 371 372 while ((image = (struct image*)list_get_next_item(&team->image_list, image)) 373 != NULL) { 374 image_info *info = &image->info.basic_info; 375 376 if ((address < (addr_t)info->text 377 || address >= (addr_t)info->text + info->text_size) 378 && (address < (addr_t)info->data 379 || address >= (addr_t)info->data + info->data_size)) 380 continue; 381 382 // found image 383 *_symbolName = NULL; 384 *_imageName = info->name; 385 *_baseAddress = (addr_t)info->text; 386 *_exactMatch = false; 387 388 return B_OK; 389 } 390 391 return B_ENTRY_NOT_FOUND; 392 } 393 394 395 status_t 396 image_init(void) 397 { 398 sImageTable = new(std::nothrow) ImageTable; 399 if (sImageTable == NULL) { 400 panic("image_init(): Failed to allocate image table!"); 401 return B_NO_MEMORY; 402 } 403 404 status_t error = sImageTable->Init(); 405 if (error != B_OK) { 406 panic("image_init(): Failed to init image table: %s", strerror(error)); 407 return error; 408 } 409 410 new(&sNotificationService) ImageNotificationService(); 411 412 sNotificationService.Register(); 413 414 #ifdef ADD_DEBUGGER_COMMANDS 415 add_debugger_command("team_images", &dump_images_list, "Dump all registered images from the current team"); 416 #endif 417 418 return B_OK; 419 } 420 421 422 static void 423 notify_loading_app(status_t result, bool suspend) 424 { 425 Team* team = thread_get_current_thread()->team; 426 427 TeamLocker teamLocker(team); 428 429 if (team->loading_info) { 430 // there's indeed someone waiting 431 struct team_loading_info* loadingInfo = team->loading_info; 432 team->loading_info = NULL; 433 434 loadingInfo->result = result; 435 436 // we're done with the team stuff, get the scheduler lock instead 437 teamLocker.Unlock(); 438 439 thread_prepare_suspend(); 440 441 // wake up the waiting thread 442 loadingInfo->condition.NotifyAll(); 443 444 // suspend ourselves, if desired 445 if (suspend) 446 thread_suspend(true); 447 } 448 } 449 450 451 // #pragma mark - 452 // Functions exported for the user space 453 454 455 status_t 456 _user_unregister_image(image_id id) 457 { 458 return unregister_image(thread_get_current_thread()->team, id); 459 } 460 461 462 image_id 463 _user_register_image(extended_image_info *userInfo, size_t size) 464 { 465 extended_image_info info; 466 467 if (size != sizeof(info)) 468 return B_BAD_VALUE; 469 470 if (!IS_USER_ADDRESS(userInfo) 471 || user_memcpy(&info, userInfo, size) < B_OK) 472 return B_BAD_ADDRESS; 473 474 return register_image(thread_get_current_thread()->team, &info, size); 475 } 476 477 478 void 479 _user_image_relocated(image_id id) 480 { 481 image_info info; 482 status_t error; 483 484 // get an image info 485 error = _get_image_info(id, &info, sizeof(image_info)); 486 if (error != B_OK) { 487 dprintf("_user_image_relocated(%" B_PRId32 "): Failed to get image " 488 "info: %" B_PRIx32 "\n", id, error); 489 return; 490 } 491 492 // notify the debugger 493 user_debug_image_created(&info); 494 495 // If the image is the app image, loading is done. We need to notify the 496 // thread who initiated the process and is now waiting for us to be done. 497 if (info.type == B_APP_IMAGE) 498 notify_loading_app(B_OK, true); 499 } 500 501 502 void 503 _user_loading_app_failed(status_t error) 504 { 505 if (error >= B_OK) 506 error = B_ERROR; 507 508 notify_loading_app(error, false); 509 510 _user_exit_team(error); 511 } 512 513 514 status_t 515 _user_get_image_info(image_id id, image_info *userInfo, size_t size) 516 { 517 image_info info; 518 status_t status; 519 520 if (size > sizeof(image_info)) 521 return B_BAD_VALUE; 522 523 if (!IS_USER_ADDRESS(userInfo)) 524 return B_BAD_ADDRESS; 525 526 status = _get_image_info(id, &info, sizeof(image_info)); 527 528 if (user_memcpy(userInfo, &info, size) < B_OK) 529 return B_BAD_ADDRESS; 530 531 return status; 532 } 533 534 535 status_t 536 _user_get_next_image_info(team_id team, int32 *_cookie, image_info *userInfo, 537 size_t size) 538 { 539 image_info info; 540 status_t status; 541 int32 cookie; 542 543 if (size > sizeof(image_info)) 544 return B_BAD_VALUE; 545 546 if (!IS_USER_ADDRESS(userInfo) || !IS_USER_ADDRESS(_cookie) 547 || user_memcpy(&cookie, _cookie, sizeof(int32)) < B_OK) { 548 return B_BAD_ADDRESS; 549 } 550 551 status = _get_next_image_info(team, &cookie, &info, sizeof(image_info)); 552 553 if (user_memcpy(userInfo, &info, size) < B_OK 554 || user_memcpy(_cookie, &cookie, sizeof(int32)) < B_OK) { 555 return B_BAD_ADDRESS; 556 } 557 558 return status; 559 } 560 561