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