xref: /haiku/src/system/kernel/image.cpp (revision 7457ccb4b2f4786525d3b7bda42598487d57ab7d)
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 namespace {
37 
38 struct ImageTableDefinition {
39 	typedef image_id		KeyType;
40 	typedef struct image	ValueType;
41 
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; }
48 };
49 
50 typedef BOpenHashTable<ImageTableDefinition> ImageTable;
51 
52 
53 class ImageNotificationService : public DefaultNotificationService {
54 public:
55 	ImageNotificationService()
56 		: DefaultNotificationService("images")
57 	{
58 	}
59 
60 	void Notify(uint32 eventCode, struct image* image)
61 	{
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);
68 
69 		DefaultNotificationService::Notify(event, eventCode);
70 	}
71 };
72 
73 } // namespace
74 
75 
76 static image_id sNextImageID = 1;
77 static mutex sImageMutex = MUTEX_INITIALIZER("image");
78 static ImageTable* sImageTable;
79 static ImageNotificationService sNotificationService;
80 
81 
82 /*!	Registers an image with the specified team.
83 */
84 static image_id
85 register_image(Team *team, extended_image_info *info, size_t size, bool locked)
86 {
87 	image_id id = atomic_add(&sNextImageID, 1);
88 	struct image *image;
89 
90 	image = (struct image*)malloc(sizeof(struct image));
91 	if (image == NULL)
92 		return B_NO_MEMORY;
93 
94 	memcpy(&image->info, info, sizeof(extended_image_info));
95 	image->team = team->id;
96 
97 	if (!locked)
98 		mutex_lock(&sImageMutex);
99 
100 	image->info.basic_info.id = id;
101 
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);
109 
110 	// notify listeners
111 	sNotificationService.Notify(IMAGE_ADDED, image);
112 
113 	if (!locked)
114 		mutex_unlock(&sImageMutex);
115 
116 	TRACE(("register_image(team = %p, image id = %ld, image = %p\n", team, id, image));
117 	return id;
118 }
119 
120 
121 /*!	Registers an image with the specified team.
122 */
123 image_id
124 register_image(Team *team, extended_image_info *info, size_t size)
125 {
126 	return register_image(team, info, size, false);
127 }
128 
129 
130 /*!	Unregisters an image from the specified team.
131 */
132 status_t
133 unregister_image(Team *team, image_id id)
134 {
135 	status_t status = B_ENTRY_NOT_FOUND;
136 
137 	mutex_lock(&sImageMutex);
138 
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;
144 	}
145 
146 	mutex_unlock(&sImageMutex);
147 
148 	if (status == B_OK) {
149 		// notify the debugger
150 		user_debug_image_deleted(&image->info.basic_info);
151 
152 		// notify listeners
153 		sNotificationService.Notify(IMAGE_REMOVED, image);
154 
155 		free(image);
156 	}
157 
158 	return status;
159 }
160 
161 
162 status_t
163 copy_images(team_id fromTeamId, Team *toTeam)
164 {
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);
170 
171 	MutexLocker locker(sImageMutex);
172 
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;
180 	}
181 
182 	return B_OK;
183 }
184 
185 
186 /*!	Counts the registered images from the specified team.
187 	Interrupts must be enabled.
188 */
189 int32
190 count_images(Team *team)
191 {
192 	struct image *image = NULL;
193 	int32 count = 0;
194 
195 	MutexLocker locker(sImageMutex);
196 
197 	while ((image = (struct image*)list_get_next_item(&team->image_list, image))
198 			!= NULL) {
199 		count++;
200 	}
201 
202 	return count;
203 }
204 
205 
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()).
208 */
209 status_t
210 remove_images(Team *team)
211 {
212 	struct image *image;
213 
214 	ASSERT(team != NULL);
215 
216 	mutex_lock(&sImageMutex);
217 
218 	while ((image = (struct image*)list_remove_head_item(&team->image_list))
219 			!= NULL) {
220 		sImageTable->Remove(image);
221 		free(image);
222 	}
223 
224 	mutex_unlock(&sImageMutex);
225 
226 	return B_OK;
227 }
228 
229 
230 status_t
231 _get_image_info(image_id id, image_info *info, size_t size)
232 {
233 	if (size > sizeof(image_info))
234 		return B_BAD_VALUE;
235 
236 	status_t status = B_ENTRY_NOT_FOUND;
237 
238 	mutex_lock(&sImageMutex);
239 
240 	struct image *image = sImageTable->Lookup(id);
241 	if (image != NULL) {
242 		memcpy(info, &image->info.basic_info, size);
243 		status = B_OK;
244 	}
245 
246 	mutex_unlock(&sImageMutex);
247 
248 	return status;
249 }
250 
251 
252 status_t
253 _get_next_image_info(team_id teamID, int32 *cookie, image_info *info,
254 	size_t size)
255 {
256 	if (size > sizeof(image_info))
257 		return B_BAD_VALUE;
258 
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);
264 
265 	// iterate through the team's images
266 	MutexLocker imageLocker(sImageMutex);
267 
268 	struct image* image = NULL;
269 	int32 count = 0;
270 
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;
277 		}
278 		count++;
279 	}
280 
281 	return B_ENTRY_NOT_FOUND;
282 }
283 
284 
285 #ifdef ADD_DEBUGGER_COMMANDS
286 static int
287 dump_images_list(int argc, char **argv)
288 {
289 	struct image *image = NULL;
290 	Team *team;
291 
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;
298 		}
299 	} else
300 		team = thread_get_current_thread()->team;
301 
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");
305 
306 	while ((image = (struct image*)list_get_next_item(&team->image_list, image))
307 			!= NULL) {
308 		image_info *info = &image->info.basic_info;
309 
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);
313 	}
314 
315 	return 0;
316 }
317 #endif
318 
319 
320 struct image*
321 image_iterate_through_images(image_iterator_callback callback, void* cookie)
322 {
323 	MutexLocker locker(sImageMutex);
324 
325 	ImageTable::Iterator it = sImageTable->GetIterator();
326 	struct image* image = NULL;
327 	while ((image = it.Next()) != NULL) {
328 		if (callback(image, cookie))
329 			break;
330 	}
331 
332 	return image;
333 }
334 
335 
336 struct image*
337 image_iterate_through_team_images(team_id teamID,
338 	image_iterator_callback callback, void* cookie)
339 {
340 	// get the team
341 	Team* team = Team::Get(teamID);
342 	if (team == NULL)
343 		return NULL;
344 	BReference<Team> teamReference(team, true);
345 
346 	// iterate through the team's images
347 	MutexLocker imageLocker(sImageMutex);
348 
349 	struct image* image = NULL;
350 
351 	while ((image = (struct image*)list_get_next_item(&team->image_list,
352 			image)) != NULL) {
353 		if (callback(image, cookie))
354 			break;
355 	}
356 
357 	return image;
358 }
359 
360 
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)
365 {
366 	// TODO: Work together with ELF reader and runtime_loader. For regular user
367 	// images we have the symbol and string table addresses.
368 
369 	struct image *image = NULL;
370 
371 	while ((image = (struct image*)list_get_next_item(&team->image_list, image))
372 			!= NULL) {
373 		image_info *info = &image->info.basic_info;
374 
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;
380 
381 		// found image
382 		*_symbolName = NULL;
383 		*_imageName = info->name;
384 		*_baseAddress = (addr_t)info->text;
385 		*_exactMatch = false;
386 
387 		return B_OK;
388 	}
389 
390 	return B_ENTRY_NOT_FOUND;
391 }
392 
393 
394 status_t
395 image_init(void)
396 {
397 	sImageTable = new(std::nothrow) ImageTable;
398 	if (sImageTable == NULL) {
399 		panic("image_init(): Failed to allocate image table!");
400 		return B_NO_MEMORY;
401 	}
402 
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;
407 	}
408 
409 	new(&sNotificationService) ImageNotificationService();
410 
411 	sNotificationService.Register();
412 
413 #ifdef ADD_DEBUGGER_COMMANDS
414 	add_debugger_command("team_images", &dump_images_list, "Dump all registered images from the current team");
415 #endif
416 
417 	return B_OK;
418 }
419 
420 
421 static void
422 notify_loading_app(status_t result, bool suspend)
423 {
424 	Team* team = thread_get_current_thread()->team;
425 
426 	TeamLocker teamLocker(team);
427 
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;
432 
433 		loadingInfo->result = result;
434 		loadingInfo->done = true;
435 
436 		// we're done with the team stuff, get the scheduler lock instead
437 		teamLocker.Unlock();
438 
439 		thread_prepare_suspend();
440 
441 		// wake up the waiting thread
442 		thread_continue(loadingInfo->thread);
443 
444 		// suspend ourselves, if desired
445 		if (suspend)
446 			thread_suspend(true);
447 	}
448 }
449 
450 
451 //	#pragma mark -
452 //	Functions exported for the user space
453 
454 
455 status_t
456 _user_unregister_image(image_id id)
457 {
458 	return unregister_image(thread_get_current_thread()->team, id);
459 }
460 
461 
462 image_id
463 _user_register_image(extended_image_info *userInfo, size_t size)
464 {
465 	extended_image_info info;
466 
467 	if (size != sizeof(info))
468 		return B_BAD_VALUE;
469 
470 	if (!IS_USER_ADDRESS(userInfo)
471 		|| user_memcpy(&info, userInfo, size) < B_OK)
472 		return B_BAD_ADDRESS;
473 
474 	return register_image(thread_get_current_thread()->team, &info, size);
475 }
476 
477 
478 void
479 _user_image_relocated(image_id id)
480 {
481 	image_info info;
482 	status_t error;
483 
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;
490 	}
491 
492 	// notify the debugger
493 	user_debug_image_created(&info);
494 
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);
499 }
500 
501 
502 void
503 _user_loading_app_failed(status_t error)
504 {
505 	if (error >= B_OK)
506 		error = B_ERROR;
507 
508 	notify_loading_app(error, false);
509 
510 	_user_exit_team(error);
511 }
512 
513 
514 status_t
515 _user_get_image_info(image_id id, image_info *userInfo, size_t size)
516 {
517 	image_info info;
518 	status_t status;
519 
520 	if (size > sizeof(image_info))
521 		return B_BAD_VALUE;
522 
523 	if (!IS_USER_ADDRESS(userInfo))
524 		return B_BAD_ADDRESS;
525 
526 	status = _get_image_info(id, &info, sizeof(image_info));
527 
528 	if (user_memcpy(userInfo, &info, size) < B_OK)
529 		return B_BAD_ADDRESS;
530 
531 	return status;
532 }
533 
534 
535 status_t
536 _user_get_next_image_info(team_id team, int32 *_cookie, image_info *userInfo,
537 	size_t size)
538 {
539 	image_info info;
540 	status_t status;
541 	int32 cookie;
542 
543 	if (size > sizeof(image_info))
544 		return B_BAD_VALUE;
545 
546 	if (!IS_USER_ADDRESS(userInfo) || !IS_USER_ADDRESS(_cookie)
547 		|| user_memcpy(&cookie, _cookie, sizeof(int32)) < B_OK) {
548 		return B_BAD_ADDRESS;
549 	}
550 
551 	status = _get_next_image_info(team, &cookie, &info, sizeof(image_info));
552 
553 	if (user_memcpy(userInfo, &info, size) < B_OK
554 		|| user_memcpy(_cookie, &cookie, sizeof(int32)) < B_OK) {
555 		return B_BAD_ADDRESS;
556 	}
557 
558 	return status;
559 }
560 
561