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