The Kernel Kit: Images

Declared in: <kernel/image.h>


Overview

An image is compiled code; put another way, an image is what the compiler produces. There are three types of images:

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

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_executable() function, the protocol for which is:

thread_id load_executable(BFile *file,
int argc,
const char **argv,
const char **env)

The function takes, as its first argument, a BFile object that represents the image file. 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").

In addition to the BFile argument, load_executable() takes an argc/argv argument pair (which are copied and forwarded to the new thread's main() function), as well as a pointer to an array of environment variables (strings):

The following example demonstrates a typical use of load_executable(). 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() */
    
   /* Here's how you declare the environment variable array. */
   extern char **environ;
   
   BFile exec_file;
   record_ref exec_ref;
   char **arg_v; /* choose a name that doesn't collide with argv */
   long arg_c; /* same here vis a vis arg_c */
   thread_id exec_thread;
   long return_value;

Next, we set our BFile's ref so the object refers to the executable file, which we're calling adder. For this example, we use get_ref_for_path() to set the ref's value (see the Storage Kit chapter for more information on these manipulations):

   get_ref_for_path("/hd/my_apps/adder", &exec_ref);
   exec_file.SetRef(exec_ref);

Install, in the arg_v array, the "command line" arguments that we're sending to adder. Let's pretend the adder program 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 ("adder"), 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 *) * (agc + 1));
   
   arg_v[0] = strdup("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_executable(). After the function returns, it's safe to free the allocated arg_v array:

   exec_thread=load_executable(&exec_file, 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).


Creating a Shared Library

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.


Creating and Using an Add-on Image

An add-on image is indistinguishable from a shared library image. Creating an add-on is, therefore, exactly like creating a shared library, a topic that we breezed through immediately above. The one exception to the rules given above is in where the add-on must live: You can keep your add-ons anywhere in the file system. When you load an add-on (through the load_add_on() function), you have to refer to the add-on file directly through the use of a BFile--the system doesn't search for the file for you.

Loading an Add-on Image

To load an add-on into your application, you call the load_add_on() function. The function takes a pointer to a BFile object that refers 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 /hd/addons/adder (the add-on will perform the same adding operation that was demonstrated in the load_executable() example). The code that loads the add-on would look like this:

   /* For brevity, we won't check errors.  */
   BFile addon_file;
   record_ref addon_ref;
   image_id addon_image;
   
   /* Establish the file's ref.  */
   get_ref_for_path("/hd/addons/adder", &addon_ref);
   addon_file.SetRef(addon_ref);
   
   /* Load the add-on. */
   addon_image = load_add_on(&addon_file);

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.

Symbols

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:

long get_image_symbol(image_id image,
char *symbol_name,
long 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:

   long addend1 = 0;
   long addend2 = 0;
   
   long adder(void)
   {
      return (addend1 + addend2);
   }

To examine the variables (addend1 and addend2 ), you would call get_image_symbol() thus:

   long *var_a1, *var_a2; 
   
   /* addon_image is the image_id that was returned by the
    * load_add_on() call in the previous example. 
    */
   get_image_symbol(addon_image, "addend1", 1, &var_a1);
   get_image_symbol(addon_image, "addend2", 1, &var_a2);

To get the symbol for the adder() function is a bit more complicated. The compiler renames a function's symbol 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...

   long (*func_add)();
   get_image_symbol(addon_image, "adder__Fv", 2, &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;
   long return_value = (*func_add)();

Function Symbol Encoding

The compiler encodes function symbols according to this format:

functionName__F<arg1Type><arg2Type ><arg3Type>....

where the argument type codes are

Code Type
i int
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(long, 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


Functions


get_image_info(), get_nth_image_info()

      long get_image_info(image_id image, image_info *info)
      long get_nth_image_info(team_id team, 
         long n, 
         image_info *info)

These functions return information about a particular image. The first version identifies the image by its first argument; the second version locates the n'th image that's loaded into team. The information is returned in the info argument. The image_info structure is defined as:

      typedef struct {
             long  volume;
             long  directory; 
             char  name[B_FILE_NAME_LENGTH]; 
            image_id id;
             void  *text;     
             long  text_size;    
             void  *data;     
             long  data_size;   
             image_type type;    
         } image_info

The volume and directory fields are, practically speaking, private. The other fields are:

  • name. The name of the file whence sprang the image.

  • id. The image's image_id number.

  • text and text_size. The address and the size (in bytes) of the image's text segment.

  • data and data_size. The address and size of the image's data segment.

  • type. A constant that tells whether this is an app, library, or add-on image.
  • The self-explanatory image_type constants are:

  • B_APP_IMAGE

  • B_LIBRARY_IMAGE

  • B_ADD_ON_IMAGE
  • The functions return B_BAD_IMAGE_ID or B_BAD_INDEX ,if the designated image doesn't exist. Otherwise, they return B_NO_ERROR.


    get_image_symbol(), get_nth_image_symbol()

          long get_image_symbol(image_id image,
             char *symbol_name,
             long symbol_type,
             void **location)
          long get_nth_image_symbol(image_id image, 
             long n, 
             char *name,
             int *name_length,
             int *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:

    To retrieve image_id numbers on which these functions can act, use the get_nth_image_info() function. Such numbers are also returned directly when you load an add-on image through the load_add_on() function.

    The functions return B_BAD_IMAGE_ID or B_BAD_INDEX ,if the designated image doesn't exist. Otherwise, they return B_NO_ERROR.


    load_add_on(), unload_add_on()

          image_id load_add_on(BFile *file)
          long unload_add_on(image_id image)

    load_add_on() loads an add-on image, identified by file, into your application's address space. The function returns an image_id (a positive integer) that represents the loaded image. 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. If the requested file couldn't be loaded as an add-on (for whatever reason), the function returns B_ERROR.

    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.


    load_executable()

          thread_id load_executable(BFile *file,
             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 file argument; file must have its ref set before this function is called. It's of no consequence whether the object is open or closed when you call this function.

    The other arguments are passed to the image's main() function (they show up there as the function's similarly named arguments):

          extern char **environ;
          
          load_executable(..., 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_executable() returns.

    The thread that's returned by load_executable() 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_executable() is given in Loading an App Image .

    If the function returns B_ERROR upon failure.




    The Be Book, HTML Edition, for Developer Release 8 of the Be Operating System.

    Copyright © 1996 Be, Inc. All rights reserved.

    Be, the Be logo, BeBox, BeOS, BeWare, and GeekPort are trademarks of Be, Inc.

    Last modified September 6, 1996.