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