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