headers/bsd: Add sys/queue.h.
[haiku.git] / src / system / kernel / image.cpp
blob31fe3088fc6eee2382f92e656e561fc96bbde247
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 */
6 /*! User Runtime Loader support in the kernel */
9 #include <KernelExport.h>
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>
22 #include <stdlib.h>
23 #include <string.h>
26 //#define TRACE_IMAGE
27 #ifdef TRACE_IMAGE
28 # define TRACE(x) dprintf x
29 #else
30 # define TRACE(x) ;
31 #endif
33 #define ADD_DEBUGGER_COMMANDS
36 namespace {
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 {
54 public:
55 ImageNotificationService()
56 : DefaultNotificationService("images")
60 void Notify(uint32 eventCode, struct image* image)
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.basic_info.id);
67 event.AddPointer("imageStruct", image);
69 DefaultNotificationService::Notify(event, eventCode);
73 } // namespace
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.
84 static image_id
85 register_image(Team *team, extended_image_info *info, size_t size, bool locked)
87 image_id id = atomic_add(&sNextImageID, 1);
88 struct image *image;
90 image = (struct image*)malloc(sizeof(struct image));
91 if (image == NULL)
92 return B_NO_MEMORY;
94 memcpy(&image->info, info, sizeof(extended_image_info));
95 image->team = team->id;
97 if (!locked)
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);
106 else
107 list_add_item(&team->image_list, image);
108 sImageTable->Insert(image);
110 // notify listeners
111 sNotificationService.Notify(IMAGE_ADDED, image);
113 if (!locked)
114 mutex_unlock(&sImageMutex);
116 TRACE(("register_image(team = %p, image id = %ld, image = %p\n", team, id, image));
117 return id;
121 /*! Registers an image with the specified team.
123 image_id
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.
132 status_t
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);
143 status = B_OK;
146 mutex_unlock(&sImageMutex);
148 if (status == B_OK) {
149 // notify the debugger
150 user_debug_image_deleted(&image->info.basic_info);
152 // notify listeners
153 sNotificationService.Notify(IMAGE_REMOVED, image);
155 free(image);
158 return status;
162 status_t
163 copy_images(team_id fromTeamId, Team *toTeam)
165 // get the team
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,
175 image)) != NULL) {
176 image_id id = register_image(toTeam, &image->info, sizeof(image->info),
177 true);
178 if (id < 0)
179 return id;
182 return B_OK;
186 /*! Counts the registered images from the specified team.
187 Interrupts must be enabled.
189 int32
190 count_images(Team *team)
192 struct image *image = NULL;
193 int32 count = 0;
195 MutexLocker locker(sImageMutex);
197 while ((image = (struct image*)list_get_next_item(&team->image_list, image))
198 != NULL) {
199 count++;
202 return count;
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()).
209 status_t
210 remove_images(Team *team)
212 struct image *image;
214 ASSERT(team != NULL);
216 mutex_lock(&sImageMutex);
218 while ((image = (struct image*)list_remove_head_item(&team->image_list))
219 != NULL) {
220 sImageTable->Remove(image);
221 free(image);
224 mutex_unlock(&sImageMutex);
226 return B_OK;
230 status_t
231 _get_image_info(image_id id, image_info *info, size_t size)
233 if (size > sizeof(image_info))
234 return B_BAD_VALUE;
236 status_t status = B_ENTRY_NOT_FOUND;
238 mutex_lock(&sImageMutex);
240 struct image *image = sImageTable->Lookup(id);
241 if (image != NULL) {
242 memcpy(info, &image->info.basic_info, size);
243 status = B_OK;
246 mutex_unlock(&sImageMutex);
248 return status;
252 status_t
253 _get_next_image_info(team_id teamID, int32 *cookie, image_info *info,
254 size_t size)
256 if (size > sizeof(image_info))
257 return B_BAD_VALUE;
259 // get the team
260 Team* team = Team::Get(teamID);
261 if (team == NULL)
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;
269 int32 count = 0;
271 while ((image = (struct image*)list_get_next_item(&team->image_list,
272 image)) != NULL) {
273 if (count == *cookie) {
274 memcpy(info, &image->info.basic_info, size);
275 (*cookie)++;
276 return B_OK;
278 count++;
281 return B_ENTRY_NOT_FOUND;
285 #ifdef ADD_DEBUGGER_COMMANDS
286 static int
287 dump_images_list(int argc, char **argv)
289 struct image *image = NULL;
290 Team *team;
292 if (argc > 1) {
293 team_id id = strtol(argv[1], NULL, 0);
294 team = team_get_team_struct_locked(id);
295 if (team == NULL) {
296 kprintf("No team with ID %" B_PRId32 " found\n", id);
297 return 1;
299 } else
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))
307 != NULL) {
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,
312 info->name);
315 return 0;
317 #endif
320 struct image*
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))
329 break;
332 return image;
336 struct image*
337 image_iterate_through_team_images(team_id teamID,
338 image_iterator_callback callback, void* cookie)
340 // get the team
341 Team* team = Team::Get(teamID);
342 if (team == NULL)
343 return NULL;
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,
352 image)) != NULL) {
353 if (callback(image, cookie))
354 break;
357 return image;
361 status_t
362 image_debug_lookup_user_symbol_address(Team *team, addr_t address,
363 addr_t *_baseAddress, const char **_symbolName, const char **_imageName,
364 bool *_exactMatch)
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))
372 != NULL) {
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))
379 continue;
381 // found image
382 *_symbolName = NULL;
383 *_imageName = info->name;
384 *_baseAddress = (addr_t)info->text;
385 *_exactMatch = false;
387 return B_OK;
390 return B_ENTRY_NOT_FOUND;
394 status_t
395 image_init(void)
397 sImageTable = new(std::nothrow) ImageTable;
398 if (sImageTable == NULL) {
399 panic("image_init(): Failed to allocate image table!");
400 return B_NO_MEMORY;
403 status_t error = sImageTable->Init();
404 if (error != B_OK) {
405 panic("image_init(): Failed to init image table: %s", strerror(error));
406 return 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");
415 #endif
417 return B_OK;
421 static void
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
437 teamLocker.Unlock();
439 thread_prepare_suspend();
441 // wake up the waiting thread
442 thread_continue(loadingInfo->thread);
444 // suspend ourselves, if desired
445 if (suspend)
446 thread_suspend(true);
451 // #pragma mark -
452 // Functions exported for the user space
455 status_t
456 _user_unregister_image(image_id id)
458 return unregister_image(thread_get_current_thread()->team, id);
462 image_id
463 _user_register_image(extended_image_info *userInfo, size_t size)
465 extended_image_info info;
467 if (size != sizeof(info))
468 return B_BAD_VALUE;
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);
478 void
479 _user_image_relocated(image_id id)
481 image_info info;
482 status_t error;
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;
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);
502 void
503 _user_loading_app_failed(status_t error)
505 if (error >= B_OK)
506 error = B_ERROR;
508 notify_loading_app(error, false);
510 _user_exit_team(error);
514 status_t
515 _user_get_image_info(image_id id, image_info *userInfo, size_t size)
517 image_info info;
518 status_t status;
520 if (size > sizeof(image_info))
521 return B_BAD_VALUE;
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;
531 return status;
535 status_t
536 _user_get_next_image_info(team_id team, int32 *_cookie, image_info *userInfo,
537 size_t size)
539 image_info info;
540 status_t status;
542 if (size > sizeof(image_info))
543 return B_BAD_VALUE;
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;
553 return status;