haiku/src/system/kernel/image.cpp
Stephan Aßmus 5147963dcd headers/private/kernel/util/OpenHashTable.h, Hugo's version, is a bit nicer than
Tracker's OpenHashTable.h which it should eventually replace. We've renamed the
class to BOpenHashTable and changed the interface slightly so that HashTableLink
became superfluous.
Adapted all the code that used it. Since the OpenHashTables no longer clash,
this should fix the GCC4 build.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31791 a95241bf-73f2-0310-859d-f6bbb57e9c96
2009-07-27 00:39:12 +00:00

513 lines
10 KiB
C++

/*
* Copyright 2003-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*/
/*! User Runtime Loader support in the kernel */
#include <KernelExport.h>
#include <kernel.h>
#include <kimage.h>
#include <kscheduler.h>
#include <lock.h>
#include <Notifications.h>
#include <team.h>
#include <thread.h>
#include <thread_types.h>
#include <user_debugger.h>
#include <util/AutoLock.h>
#include <stdlib.h>
#include <string.h>
//#define TRACE_IMAGE
#ifdef TRACE_IMAGE
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
#define ADD_DEBUGGER_COMMANDS
struct ImageTableDefinition {
typedef image_id KeyType;
typedef struct image ValueType;
size_t HashKey(image_id key) const { return key; }
size_t Hash(struct image* value) const { return value->info.id; }
bool Compare(image_id key, struct image* value) const
{ return value->info.id == key; }
struct image*& GetLink(struct image* value) const
{ return value->hash_link; }
};
typedef BOpenHashTable<ImageTableDefinition> ImageTable;
class ImageNotificationService : public DefaultNotificationService {
public:
ImageNotificationService()
: DefaultNotificationService("images")
{
}
void Notify(uint32 eventCode, struct image* image)
{
char eventBuffer[128];
KMessage event;
event.SetTo(eventBuffer, sizeof(eventBuffer), IMAGE_MONITOR);
event.AddInt32("event", eventCode);
event.AddInt32("image", image->info.id);
event.AddPointer("imageStruct", image);
DefaultNotificationService::Notify(event, eventCode);
}
};
static image_id sNextImageID = 1;
static mutex sImageMutex = MUTEX_INITIALIZER("image");
static ImageTable* sImageTable;
static ImageNotificationService sNotificationService;
/*! Registers an image with the specified team.
*/
image_id
register_image(struct team *team, image_info *_info, size_t size)
{
image_id id = atomic_add(&sNextImageID, 1);
struct image *image;
image = (struct image*)malloc(sizeof(struct image));
if (image == NULL)
return B_NO_MEMORY;
memcpy(&image->info, _info, sizeof(image_info));
image->team = team->id;
mutex_lock(&sImageMutex);
image->info.id = id;
// Add the app image to the head of the list. Some code relies on it being
// the first image to be returned by get_next_image_info().
if (image->info.type == B_APP_IMAGE)
list_add_link_to_head(&team->image_list, image);
else
list_add_item(&team->image_list, image);
sImageTable->Insert(image);
// notify listeners
sNotificationService.Notify(IMAGE_ADDED, image);
mutex_unlock(&sImageMutex);
TRACE(("register_image(team = %p, image id = %ld, image = %p\n", team, id, image));
return id;
}
/*! Unregisters an image from the specified team.
*/
status_t
unregister_image(struct team *team, image_id id)
{
status_t status = B_ENTRY_NOT_FOUND;
mutex_lock(&sImageMutex);
struct image *image = sImageTable->Lookup(id);
if (image != NULL && image->team == team->id) {
list_remove_link(image);
sImageTable->Remove(image);
status = B_OK;
}
mutex_unlock(&sImageMutex);
if (status == B_OK) {
// notify the debugger
user_debug_image_deleted(&image->info);
// notify listeners
sNotificationService.Notify(IMAGE_REMOVED, image);
free(image);
}
return status;
}
/*! Counts the registered images from the specified team.
The team lock must be held when you call this function.
*/
int32
count_images(struct team *team)
{
struct image *image = NULL;
int32 count = 0;
while ((image = (struct image*)list_get_next_item(&team->image_list, image))
!= NULL) {
count++;
}
return count;
}
/*! Removes all images from the specified team. Must only be called
with a team that has already been removed from the list (in thread_exit()).
*/
status_t
remove_images(struct team *team)
{
struct image *image;
ASSERT(team != NULL);
mutex_lock(&sImageMutex);
while ((image = (struct image*)list_remove_head_item(&team->image_list))
!= NULL) {
sImageTable->Remove(image);
free(image);
}
mutex_unlock(&sImageMutex);
return B_OK;
}
status_t
_get_image_info(image_id id, image_info *info, size_t size)
{
if (size > sizeof(image_info))
return B_BAD_VALUE;
status_t status = B_ENTRY_NOT_FOUND;
mutex_lock(&sImageMutex);
struct image *image = sImageTable->Lookup(id);
if (image != NULL) {
memcpy(info, &image->info, size);
status = B_OK;
}
mutex_unlock(&sImageMutex);
return status;
}
status_t
_get_next_image_info(team_id teamID, int32 *cookie, image_info *info,
size_t size)
{
if (size > sizeof(image_info))
return B_BAD_VALUE;
status_t status = B_ENTRY_NOT_FOUND;
struct team *team;
cpu_status state;
mutex_lock(&sImageMutex);
state = disable_interrupts();
GRAB_TEAM_LOCK();
if (teamID == B_CURRENT_TEAM)
team = thread_get_current_thread()->team;
else if (teamID == B_SYSTEM_TEAM)
team = team_get_kernel_team();
else
team = team_get_team_struct_locked(teamID);
if (team) {
struct image *image = NULL;
int32 count = 0;
while ((image = (struct image*)list_get_next_item(&team->image_list,
image)) != NULL) {
if (count == *cookie) {
memcpy(info, &image->info, size);
status = B_OK;
(*cookie)++;
break;
}
count++;
}
} else
status = B_BAD_TEAM_ID;
RELEASE_TEAM_LOCK();
restore_interrupts(state);
mutex_unlock(&sImageMutex);
return status;
}
#ifdef ADD_DEBUGGER_COMMANDS
static int
dump_images_list(int argc, char **argv)
{
struct image *image = NULL;
struct team *team;
if (argc > 1) {
team_id id = strtol(argv[1], NULL, 0);
team = team_get_team_struct_locked(id);
if (team == NULL) {
kprintf("No team with ID %ld found\n", id);
return 1;
}
} else
team = thread_get_current_thread()->team;
kprintf("Registered images of team %ld\n", team->id);
kprintf(" ID text size data size name\n");
while ((image = (struct image*)list_get_next_item(&team->image_list, image))
!= NULL) {
image_info *info = &image->info;
kprintf("%6ld %p %-7ld %p %-7ld %s\n", info->id, info->text, info->text_size,
info->data, info->data_size, info->name);
}
return 0;
}
#endif
struct image*
image_iterate_through_images(image_iterator_callback callback, void* cookie)
{
MutexLocker locker(sImageMutex);
ImageTable::Iterator it = sImageTable->GetIterator();
struct image* image = NULL;
while ((image = it.Next()) != NULL) {
if (callback(image, cookie))
break;
}
return image;
}
status_t
image_debug_lookup_user_symbol_address(struct team *team, addr_t address,
addr_t *_baseAddress, const char **_symbolName, const char **_imageName,
bool *_exactMatch)
{
// TODO: work together with ELF reader and runtime_loader
struct image *image = NULL;
while ((image = (struct image*)list_get_next_item(&team->image_list, image))
!= NULL) {
image_info *info = &image->info;
if ((address < (addr_t)info->text
|| address >= (addr_t)info->text + info->text_size)
&& (address < (addr_t)info->data
|| address >= (addr_t)info->data + info->data_size))
continue;
// found image
*_symbolName = NULL;
*_imageName = info->name;
*_baseAddress = (addr_t)info->text;
*_exactMatch = false;
return B_OK;
}
return B_ENTRY_NOT_FOUND;
}
status_t
image_init(void)
{
sImageTable = new(std::nothrow) ImageTable;
if (sImageTable == NULL) {
panic("image_init(): Failed to allocate image table!");
return B_NO_MEMORY;
}
status_t error = sImageTable->Init();
if (error != B_OK) {
panic("image_init(): Failed to init image table: %s", strerror(error));
return error;
}
new(&sNotificationService) ImageNotificationService();
#ifdef ADD_DEBUGGER_COMMANDS
add_debugger_command("team_images", &dump_images_list, "Dump all registered images from the current team");
#endif
return B_OK;
}
static void
notify_loading_app(status_t result, bool suspend)
{
cpu_status state;
struct team *team;
state = disable_interrupts();
GRAB_TEAM_LOCK();
team = thread_get_current_thread()->team;
if (team->loading_info) {
// there's indeed someone waiting
struct team_loading_info *loadingInfo = team->loading_info;
team->loading_info = NULL;
loadingInfo->result = result;
loadingInfo->done = true;
// we're done with the team stuff, get the thread lock instead
RELEASE_TEAM_LOCK();
GRAB_THREAD_LOCK();
// wake up the waiting thread
if (loadingInfo->thread->state == B_THREAD_SUSPENDED)
scheduler_enqueue_in_run_queue(loadingInfo->thread);
// suspend ourselves, if desired
if (suspend) {
thread_get_current_thread()->next_state = B_THREAD_SUSPENDED;
scheduler_reschedule();
}
RELEASE_THREAD_LOCK();
} else {
// no-one is waiting
RELEASE_TEAM_LOCK();
}
restore_interrupts(state);
}
// #pragma mark -
// Functions exported for the user space
status_t
_user_unregister_image(image_id id)
{
return unregister_image(thread_get_current_thread()->team, id);
}
image_id
_user_register_image(image_info *userInfo, size_t size)
{
image_info info;
if (size != sizeof(image_info))
return B_BAD_VALUE;
if (!IS_USER_ADDRESS(userInfo)
|| user_memcpy(&info, userInfo, size) < B_OK)
return B_BAD_ADDRESS;
return register_image(thread_get_current_thread()->team, &info, size);
}
void
_user_image_relocated(image_id id)
{
image_info info;
status_t error;
// get an image info
error = _get_image_info(id, &info, sizeof(image_info));
if (error != B_OK) {
dprintf("_user_image_relocated(%ld): Failed to get image info: %lx\n",
id, error);
return;
}
// notify the debugger
user_debug_image_created(&info);
// If the image is the app image, loading is done. We need to notify the
// thread who initiated the process and is now waiting for us to be done.
if (info.type == B_APP_IMAGE)
notify_loading_app(B_OK, true);
}
void
_user_loading_app_failed(status_t error)
{
if (error >= B_OK)
error = B_ERROR;
notify_loading_app(error, false);
_user_exit_team(error);
}
status_t
_user_get_image_info(image_id id, image_info *userInfo, size_t size)
{
image_info info;
status_t status;
if (size > sizeof(image_info))
return B_BAD_VALUE;
if (!IS_USER_ADDRESS(userInfo))
return B_BAD_ADDRESS;
status = _get_image_info(id, &info, sizeof(image_info));
if (user_memcpy(userInfo, &info, size) < B_OK)
return B_BAD_ADDRESS;
return status;
}
status_t
_user_get_next_image_info(team_id team, int32 *_cookie, image_info *userInfo,
size_t size)
{
image_info info;
status_t status;
if (size > sizeof(image_info))
return B_BAD_VALUE;
if (!IS_USER_ADDRESS(userInfo) || !IS_USER_ADDRESS(_cookie))
return B_BAD_ADDRESS;
status = _get_next_image_info(team, _cookie, &info, sizeof(image_info));
if (user_memcpy(userInfo, &info, size) < B_OK)
return B_BAD_ADDRESS;
return status;
}