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