2 * Copyright 2003-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 /*! User Runtime Loader support in the kernel */
9 #include <KernelExport.h>
13 #include <kscheduler.h>
15 #include <Notifications.h>
18 #include <thread_types.h>
19 #include <user_debugger.h>
20 #include <util/AutoLock.h>
28 # define TRACE(x) dprintf x
33 #define ADD_DEBUGGER_COMMANDS
38 struct ImageTableDefinition
{
39 typedef image_id KeyType
;
40 typedef struct image ValueType
;
42 size_t HashKey(image_id key
) const { return key
; }
43 size_t Hash(struct image
* value
) const { return value
->info
.basic_info
.id
; }
44 bool Compare(image_id key
, struct image
* value
) const
45 { return value
->info
.basic_info
.id
== key
; }
46 struct image
*& GetLink(struct image
* value
) const
47 { return value
->hash_link
; }
50 typedef BOpenHashTable
<ImageTableDefinition
> ImageTable
;
53 class ImageNotificationService
: public DefaultNotificationService
{
55 ImageNotificationService()
56 : DefaultNotificationService("images")
60 void Notify(uint32 eventCode
, struct image
* image
)
62 char eventBuffer
[128];
64 event
.SetTo(eventBuffer
, sizeof(eventBuffer
), IMAGE_MONITOR
);
65 event
.AddInt32("event", eventCode
);
66 event
.AddInt32("image", image
->info
.basic_info
.id
);
67 event
.AddPointer("imageStruct", image
);
69 DefaultNotificationService::Notify(event
, eventCode
);
76 static image_id sNextImageID
= 1;
77 static mutex sImageMutex
= MUTEX_INITIALIZER("image");
78 static ImageTable
* sImageTable
;
79 static ImageNotificationService sNotificationService
;
82 /*! Registers an image with the specified team.
85 register_image(Team
*team
, extended_image_info
*info
, size_t size
, bool locked
)
87 image_id id
= atomic_add(&sNextImageID
, 1);
90 image
= (struct image
*)malloc(sizeof(struct image
));
94 memcpy(&image
->info
, info
, sizeof(extended_image_info
));
95 image
->team
= team
->id
;
98 mutex_lock(&sImageMutex
);
100 image
->info
.basic_info
.id
= id
;
102 // Add the app image to the head of the list. Some code relies on it being
103 // the first image to be returned by get_next_image_info().
104 if (image
->info
.basic_info
.type
== B_APP_IMAGE
)
105 list_add_link_to_head(&team
->image_list
, image
);
107 list_add_item(&team
->image_list
, image
);
108 sImageTable
->Insert(image
);
111 sNotificationService
.Notify(IMAGE_ADDED
, image
);
114 mutex_unlock(&sImageMutex
);
116 TRACE(("register_image(team = %p, image id = %ld, image = %p\n", team
, id
, image
));
121 /*! Registers an image with the specified team.
124 register_image(Team
*team
, extended_image_info
*info
, size_t size
)
126 return register_image(team
, info
, size
, false);
130 /*! Unregisters an image from the specified team.
133 unregister_image(Team
*team
, image_id id
)
135 status_t status
= B_ENTRY_NOT_FOUND
;
137 mutex_lock(&sImageMutex
);
139 struct image
*image
= sImageTable
->Lookup(id
);
140 if (image
!= NULL
&& image
->team
== team
->id
) {
141 list_remove_link(image
);
142 sImageTable
->Remove(image
);
146 mutex_unlock(&sImageMutex
);
148 if (status
== B_OK
) {
149 // notify the debugger
150 user_debug_image_deleted(&image
->info
.basic_info
);
153 sNotificationService
.Notify(IMAGE_REMOVED
, image
);
163 copy_images(team_id fromTeamId
, Team
*toTeam
)
166 Team
* fromTeam
= Team::Get(fromTeamId
);
167 if (fromTeam
== NULL
)
168 return B_BAD_TEAM_ID
;
169 BReference
<Team
> teamReference(fromTeam
, true);
171 MutexLocker
locker(sImageMutex
);
173 struct image
*image
= NULL
;
174 while ((image
= (struct image
*)list_get_next_item(&fromTeam
->image_list
,
176 image_id id
= register_image(toTeam
, &image
->info
, sizeof(image
->info
),
186 /*! Counts the registered images from the specified team.
187 Interrupts must be enabled.
190 count_images(Team
*team
)
192 struct image
*image
= NULL
;
195 MutexLocker
locker(sImageMutex
);
197 while ((image
= (struct image
*)list_get_next_item(&team
->image_list
, image
))
206 /*! Removes all images from the specified team. Must only be called
207 with a team that has already been removed from the list (in thread_exit()).
210 remove_images(Team
*team
)
214 ASSERT(team
!= NULL
);
216 mutex_lock(&sImageMutex
);
218 while ((image
= (struct image
*)list_remove_head_item(&team
->image_list
))
220 sImageTable
->Remove(image
);
224 mutex_unlock(&sImageMutex
);
231 _get_image_info(image_id id
, image_info
*info
, size_t size
)
233 if (size
> sizeof(image_info
))
236 status_t status
= B_ENTRY_NOT_FOUND
;
238 mutex_lock(&sImageMutex
);
240 struct image
*image
= sImageTable
->Lookup(id
);
242 memcpy(info
, &image
->info
.basic_info
, size
);
246 mutex_unlock(&sImageMutex
);
253 _get_next_image_info(team_id teamID
, int32
*cookie
, image_info
*info
,
256 if (size
> sizeof(image_info
))
260 Team
* team
= Team::Get(teamID
);
262 return B_BAD_TEAM_ID
;
263 BReference
<Team
> teamReference(team
, true);
265 // iterate through the team's images
266 MutexLocker
imageLocker(sImageMutex
);
268 struct image
* image
= NULL
;
271 while ((image
= (struct image
*)list_get_next_item(&team
->image_list
,
273 if (count
== *cookie
) {
274 memcpy(info
, &image
->info
.basic_info
, size
);
281 return B_ENTRY_NOT_FOUND
;
285 #ifdef ADD_DEBUGGER_COMMANDS
287 dump_images_list(int argc
, char **argv
)
289 struct image
*image
= NULL
;
293 team_id id
= strtol(argv
[1], NULL
, 0);
294 team
= team_get_team_struct_locked(id
);
296 kprintf("No team with ID %" B_PRId32
" found\n", id
);
300 team
= thread_get_current_thread()->team
;
302 kprintf("Registered images of team %" B_PRId32
"\n", team
->id
);
303 kprintf(" ID %-*s size %-*s size name\n",
304 B_PRINTF_POINTER_WIDTH
, "text", B_PRINTF_POINTER_WIDTH
, "data");
306 while ((image
= (struct image
*)list_get_next_item(&team
->image_list
, image
))
308 image_info
*info
= &image
->info
.basic_info
;
310 kprintf("%6" B_PRId32
" %p %-7" B_PRId32
" %p %-7" B_PRId32
" %s\n",
311 info
->id
, info
->text
, info
->text_size
, info
->data
, info
->data_size
,
321 image_iterate_through_images(image_iterator_callback callback
, void* cookie
)
323 MutexLocker
locker(sImageMutex
);
325 ImageTable::Iterator it
= sImageTable
->GetIterator();
326 struct image
* image
= NULL
;
327 while ((image
= it
.Next()) != NULL
) {
328 if (callback(image
, cookie
))
337 image_iterate_through_team_images(team_id teamID
,
338 image_iterator_callback callback
, void* cookie
)
341 Team
* team
= Team::Get(teamID
);
344 BReference
<Team
> teamReference(team
, true);
346 // iterate through the team's images
347 MutexLocker
imageLocker(sImageMutex
);
349 struct image
* image
= NULL
;
351 while ((image
= (struct image
*)list_get_next_item(&team
->image_list
,
353 if (callback(image
, cookie
))
362 image_debug_lookup_user_symbol_address(Team
*team
, addr_t address
,
363 addr_t
*_baseAddress
, const char **_symbolName
, const char **_imageName
,
366 // TODO: Work together with ELF reader and runtime_loader. For regular user
367 // images we have the symbol and string table addresses.
369 struct image
*image
= NULL
;
371 while ((image
= (struct image
*)list_get_next_item(&team
->image_list
, image
))
373 image_info
*info
= &image
->info
.basic_info
;
375 if ((address
< (addr_t
)info
->text
376 || address
>= (addr_t
)info
->text
+ info
->text_size
)
377 && (address
< (addr_t
)info
->data
378 || address
>= (addr_t
)info
->data
+ info
->data_size
))
383 *_imageName
= info
->name
;
384 *_baseAddress
= (addr_t
)info
->text
;
385 *_exactMatch
= false;
390 return B_ENTRY_NOT_FOUND
;
397 sImageTable
= new(std::nothrow
) ImageTable
;
398 if (sImageTable
== NULL
) {
399 panic("image_init(): Failed to allocate image table!");
403 status_t error
= sImageTable
->Init();
405 panic("image_init(): Failed to init image table: %s", strerror(error
));
409 new(&sNotificationService
) ImageNotificationService();
411 sNotificationService
.Register();
413 #ifdef ADD_DEBUGGER_COMMANDS
414 add_debugger_command("team_images", &dump_images_list
, "Dump all registered images from the current team");
422 notify_loading_app(status_t result
, bool suspend
)
424 Team
* team
= thread_get_current_thread()->team
;
426 TeamLocker
teamLocker(team
);
428 if (team
->loading_info
) {
429 // there's indeed someone waiting
430 struct team_loading_info
* loadingInfo
= team
->loading_info
;
431 team
->loading_info
= NULL
;
433 loadingInfo
->result
= result
;
434 loadingInfo
->done
= true;
436 // we're done with the team stuff, get the scheduler lock instead
439 thread_prepare_suspend();
441 // wake up the waiting thread
442 thread_continue(loadingInfo
->thread
);
444 // suspend ourselves, if desired
446 thread_suspend(true);
452 // Functions exported for the user space
456 _user_unregister_image(image_id id
)
458 return unregister_image(thread_get_current_thread()->team
, id
);
463 _user_register_image(extended_image_info
*userInfo
, size_t size
)
465 extended_image_info info
;
467 if (size
!= sizeof(info
))
470 if (!IS_USER_ADDRESS(userInfo
)
471 || user_memcpy(&info
, userInfo
, size
) < B_OK
)
472 return B_BAD_ADDRESS
;
474 return register_image(thread_get_current_thread()->team
, &info
, size
);
479 _user_image_relocated(image_id id
)
485 error
= _get_image_info(id
, &info
, sizeof(image_info
));
487 dprintf("_user_image_relocated(%" B_PRId32
"): Failed to get image "
488 "info: %" B_PRIx32
"\n", id
, error
);
492 // notify the debugger
493 user_debug_image_created(&info
);
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);
503 _user_loading_app_failed(status_t error
)
508 notify_loading_app(error
, false);
510 _user_exit_team(error
);
515 _user_get_image_info(image_id id
, image_info
*userInfo
, size_t size
)
520 if (size
> sizeof(image_info
))
523 if (!IS_USER_ADDRESS(userInfo
))
524 return B_BAD_ADDRESS
;
526 status
= _get_image_info(id
, &info
, sizeof(image_info
));
528 if (user_memcpy(userInfo
, &info
, size
) < B_OK
)
529 return B_BAD_ADDRESS
;
536 _user_get_next_image_info(team_id team
, int32
*_cookie
, image_info
*userInfo
,
542 if (size
> sizeof(image_info
))
545 if (!IS_USER_ADDRESS(userInfo
) || !IS_USER_ADDRESS(_cookie
))
546 return B_BAD_ADDRESS
;
548 status
= _get_next_image_info(team
, _cookie
, &info
, sizeof(image_info
));
550 if (user_memcpy(userInfo
, &info
, size
) < B_OK
)
551 return B_BAD_ADDRESS
;