Declared in: be/kernel/image.h
Library: libroot.so
An image is compiled code. There are three types of images:
![]() |
An app image is an application. Every application has a single app image.
| |
![]() |
A library image is a dynamically linked library (a "shared library"). Most applications link against the system libraries (libroot.so, libbe.so, and so on) that Be provides.
| |
![]() |
An add-on image is an image that you load into your application as it's running. Symbols from the add-on image are linked and references are resolved when the image is loaded. An add-on image provides a sort of "heightened dynamic linking" beyond that of a DLL. |
The following sections explain how to load and run an app image, how to create a shared library, and how to create and load an add-on image.
Loading an app image is like running a "sub-program." The image that you load is launched in much the same way as had you double-clicked it in the Browser, or launched it from the command line. It runs in its own team--it doesn't share the address space of the application from which it was launched--and, generally, leads its own life.
Any application can be loaded as an app image; you don't need to issue special compile instructions or otherwise manipulate the binary. The one requirement of an app image is that it must have a main() function; hardly a restrictive request.
To load an app image, you call the load_image() function, the protocol for which is:
thread_id load_image(int32 argc,
const char **argv, const char **env) |
The function's first two arguments identify the app image (file) that you want to launch--we'll return to this in a moment. Having located the file, the function creates a new team, spawns a main thread in that team, and then returns the thread_id of that thread to you. The thread that's returned is the executable's main thread. It won't be running: To make it run you pass the thread_id to resume_thread() or wait_for_thread() (as explained in the major section "Threads and Teams").
The argc/argv argument pair is copied and forwarded to the new thread's main() function:
The following example demonstrates a typical use of load_image(). First, we include the appropriate files and declare the necessary variables:
#include <image.h> /* load_executable() */ #include <OS.h> /* wait_for_thread() */ #include <stdlib.h> /* malloc() */ /* Declare the environ array. */ extern char **environ; char **arg_v; /* choose a name that doesn't collide with argv */ int32 arg_c; /* same here vis a vis argc */ thread_id exec_thread; int32 return_value;
Install, in the arg_v array, the "command line" arguments. Let's pretend we're launching a program found in /boot/home/apps/adder that takes two integers, adds them together, and returns the result as main()'s exit code. Thus, there are three arguments: The name of the program, and the values of the two addends converted to strings. Since there are three arguments, we allocate arg_v to hold four pointers (to accommodate the final NULL). Then we allocate and copy the arguments.
arg_c = 3; arg_v = (char **)malloc(sizeof(char *) * (arg_c + 1)); arg_v[0] = strdup("/boot/home/apps/adder"); arg_v[1] = strdup("5"); arg_v[2] = strdup("3"); arg_v[3] = NULL;
Now that everything is properly set up, we call load_image(). After the function returns, it's safe to free the allocated arg_v array:
exec_thread = load_image(arg_c, arg_v, environ); free(arg_v);
At this point, exec_thread is suspended (the natural state of a newly-spawned thread). In order to retrieve its return value, we use wait_for_thread() to tell the thread to run:
wait_for_thread(exec_thread, &return_value);
After wait_for_thread() returns, the value of return_value should be 8 (i.e. 5 + 3).
The primary documentation for creating a shared library is provided by MetroWerks in their CodeWarrior manual. Beyond the information that you find there, you should be aware of the following amendments and caveats.
![]() |
You mustn't export your library's symbols through the -export all compiler flag. Instead, you should either use -export pragma or -@export filename (which is the same as -f filename). See the MetroWerks manual for details on how to use these flags. |
![]() |
The loader looks for libraries by following the LIBRARY_PATH environment variable. |
The default library path looks like this:
$ echo $LIBRARY_PATH %A/lib:/boot/home/config/lib:/boot/beos/system/lib
where "%A" means the directory that contains the app that the user is lauching.
An add-on image is indistinguishable from a shared library image. Creating an add-on is exactly like creating a shared library, a topic that we breezed through immediately above. The one difference is where the loader looks for add-ons:
![]() |
The loader follows the trail given by the ADDON_PATH environment variable. |
The default ADDON_PATH looks like this:
$ echo $ADDON_PATH %A/add-ons:/boot/home/config/add-ons:/boot/beos/system/add-ons
To load an add-on into your application, you call the load_add_on() function. The function takes a pathname (absolute or relative to the current working directory) to the add-on file, and returns an image_id number that uniquely identifies the image across the entire system.
For example, let's say you've created an add-on image that's stored in the file /boot/home/add-ons/adder. The code that loads the add-on would look like this:
/* For brevity, we won't check errors. */ image_id addon_image; /* Load the add-on. */ addon_image = load_add_on("/boot/home/add-ons/adder");
Unlike loading an executable, loading an add-on doesn't create a separate team, nor does it spawn another thread. The whole point of loading an add-on is to bring the image into your application's address space so you can call the functions and fiddle with the variables that the add-on defines.
After you've loaded an add-on into your application, you'll want to examine the symbols (variables and functions) that it has brought with it. To get information about a symbol, you call the get_image_symbol() function:
status_t get_image_symbol(image_id image,
char *symbol_name, int32 symbol_type, void **location) |
The function's first three arguments identify the symbol that you want to get:
The function returns, by reference in its final argument, a pointer to the symbol's address. For example, let's say the adder add-on code looks like this:
int32 a1 = 0; int32 a2 = 0; int32 adder(void) { return (a1 + a2); }
To examine the variables (addend1 and addend2), you would call get_image_symbol() thus:
int32 *var_a1, *var_a2; get_image_symbol(addon_image, "a1", B_SYMBOL_TYPE_DATA, &var_a1); get_image_symbol(addon_image, "a2", B_SYMBOL_TYPE_DATA, &var_a2);
To get the symbol for the adder() function is a bit more complicated. The compiler mangles a function's name to encode the data types of the function's arguments. The encoding scheme is explained in the next section; to continue with the example, we'll simply accept that the adder() function's symbol is
adder__Fv
And so...
int32 (*func_add)(); get_image_symbol(addon_image, "adder__Fv", B_SYMBOL_TYPE_TEXT, &func_add);
Now that we've retrieved all the symbols, we can set the values of the two addends and call the function:
*var_a1 = 5; *var_a2 = 3; int32 return_value = (*func_add)();
The compiler encodes function symbols according to this format:
function__<Nclass>F<arg1><arg2><arg3>.... |
function is thename of the function; some C++ names are special:
The <Nclass> symbol is used only if the function is a member of a class; N is the length (in characters) of the class name and class is the name itself.
The optional <argN> symbols encode the argument types:
Code | Type |
---|---|
i | int |
s | short |
l | long |
f | float |
d | double |
c | char |
v | void |
In addition, if the argument is declared as unsigned, the type code character is preceded by "U". If it's a pointer, the type code (and, potentially, the "U") is preceded by "P"; a pointer to a pointer is preceded by "PP". For example, a function that's declared as
void Func(int32, unsigned char **, float *, double);
would have the following symbol name:
Func__FlUPPcPfd
Note that typedef's are translated to their natural types. So, for example, this:
void dump_thread(thread_id, bool);
becomes
dump_thread__FlUc
There's actually an easier, if less elegant, way to find a function's compiler-mangled name:
![]() |
Use pefdump. |
pefdump the add-on that you want to use, and then copy the function name from the "export symbol table:" section. As an example, let's pefdump the Device Kit library, libdevice.so:
$ cd /system/lib $ pefdump libdevice.so /* A LOT of output; and then, at the end... */ export symbol table: class name value TVECT Close__4BA2DFv 10002260 TVECT Close__9BJoystickFv 10002220 TVECT __dt__11BSerialPortFv 10002180 TVECT IsDSR__11BSerialPortFv 100020c0 TVECT __ct__4BA2DFv 10002280 TVECT __dt__4BA2DFv 10002278 TVECT Close__12BDigitalPortFv 100021d8 TVECT ParityMode__11BSerialPortFv 10002110 ...
status_t get_image_info(image_id image, image_info *info) status_t get_next_image_info(team_id team, int32 *cookie, image_info *info) struct {} image_info
These functions copy, into the info argument, the image_info structure for a particular image. The get_image_info() function gets the information for the image identified by image.
The get_next_image_info() function lets you step through the list of a team's images through iterated calls. The team argument identifies the team you want to look at; a team value of 0 means the team of the calling thread. The cookie argument is a placemark; you set it to 0 on your first call, and let the function do the rest. The function returns B_BAD_VALUE when there are no more images to visit:
/* Get the image_info for every image in this team. */ image_info info; int32 cookie = 0; while (get_next_image_info(0, &cookie, &info) == B_OK) ...
The image_info structure is defined as:
typedef struct { image_id id; image_type type; int32 sequence; int32 init_order; B_PFV init_routine; B_PFV term_routine; dev_t device; ino_t node; char name[MAXPATHLEN]; void *text; void *data; int32 text_size; int32 data_size; } image_info
The fields are:
The self-explanatory image_type constants are:
RETURN CODES
status_t get_image_symbol(image_id image, char *symbol_name, int32 symbol_type, void **location) status_t get_nth_image_symbol(image_id image, int32 n, char *name, int32 *name_length, int32 *symbol_type, void **location)
get_image_symbol() returns, in location, a pointer to the address of the symbol that's identified by the image, symbol_name, and symbol_type arguments. An example demonstrating the use of this function is given in "Symbols."
get_nth_image_symbol() returns information about the n'th symbol in the given image. The information is returned in the arguments:
![]() |
Keep in mind that name_length is reset each time you call get_nth_image_symbol(). If you're calling the function iteratively (to retrieve all the symbols in an image), you need to reset the name_length value between calls. |
To retrieve image_id numbers on which these functions can act, use the get_next_image_info() function. Such numbers are also returned directly when you load an add-on image through the load_add_on() function.
RETURN CODES
image_id load_add_on(const char *pathname) status_t unload_add_on(image_id image)
load_add_on() loads an add-on image, identified by pathname, into your application's address space.
An example that demonstrates the use of load_add_on() is given in "Loading an Add-on Image."
You can load the same add-on image twice; each time you load the add-on a new, unique image_id is created and returned.
unload_add_on() removes the add-on image identified by the argument. The image's symbols are removed, and the memory that they represent is freed. If the argument doesn't identify a valid image, the function returns B_ERROR. Otherwise, it returns B_NO_ERROR.
RETURN CODES
thread_id load_image(int argc, const char **argv, const char **env)
Loads an app image into the system (it doesn't load the image into the caller's address space), creates a separate team for the new application, and spawns and returns the ID of the team's main thread. The image is identified by the pathname given in argv[0].
The arguments are passed to the image's main() function (they show up there as the function's similarly named arguments):
extern char **environ; load_image(..., environ);
The argv and envp arrays are copied into the new thread's address space. If you allocated either of these arrays, it's safe to free them immediately after load_image() returns.
The thread that's returned by load_image() is in a suspended state. To start the thread running, you pass the thread_id to resume_thread() or wait_for_thread().
An example that demonstrates the use of load_image() is given in "Loading an App Image."
RETURN CODES
The Be Book, in lovely HTML, for the BeOS Preview Release.
Copyright © 1997 Be, Inc. All rights reserved.
Be is a registered trademark; BeOS, BeBox, BeWare, GeekPort, the Be logo, and the BeOS logo are trademarks of Be, Inc.
Last modified July 10, 1997.