[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

2. Global data

An application's variables can be divided into three classes according to where they are stored. Automatic variables local to functions are stored on the stack, and have lifetimes corresponding to the duration of the function invocation with which they are associated. Some data are explicitly allocated on the heap via malloc(), MemPtrNew(), or MemHandleNew(), and live until they are explicitly freed. The remainder--global and static variables--constitute an application's global data, and have lifetimes corresponding to the complete runtime of the application.

Shared libraries can also have global data: GLibs automatically have globals just as applications do, while system libraries don't by default, but could set up their own globals manually, if desired.

Currently, both C++ virtual tables and the support data for multiple code resources are stored as global data. This means that these features can only be used when global data is available.

2.1 Accessing global data  How to access global data
2.2 Initialising A4  Initialising a non-standard global pointer
2.3 The extralogue attribute and entry points  Using extralogue attributes

2.1 Accessing global data

How global data should be accessed depends on what you are building:

By default, or with `-mno-own-gp', compiled code will access global data via (a negative offset from) A5. Shared libraries and other special purpose programs should be compiled with `-mown-gp', and will use (a positive offset from) A4 (see see section 1.1 Palm OS-specific GCC options).

2.2 Initialising A4

In the following discussion, we use the term "executable" to refer to either an application or a shared library of either type.

There are two ways a function can be called: either directly, or when its address is taken and a call is subsequently made through that pointer. An executable can construct a pointer to a function within itself, or to a function in a GLib it uses (this will really be a pointer to the relevant stub), but not to a SysLib or operating system API function (since you can't construct a pointer to a systrap).

In simple cases, access to globals isn't a major problem:

However, the situation gets more complex when an executable constructs a pointer to a function and then gives it to someone else, perhaps by passing it as an argument or by returning it to its caller. "Someone else" may be either another executable or the operating system (so the pointer is a "callback function").

The rules here are:

In previous versions of prc-tools, applications also accessed their globals via the A4 register, and their function pointers were therefore subject to the same restrictions as shared library function pointers. The appropriate steps to initialise the global pointer are much simpler for an application than a shared library, and were available via the macros CALLBACK_PROLOGUE and CALLBACK_EPILOGUE. This is no longer necessary. However, if you really want to use `-mown-gp' when building an application, you can use APP_ENTRYPOINT as a more convenient equivalent of the CALLBACK_* macros.

2.3 The extralogue attribute and entry points

An extralogue attribute allows you to add arbitrary code to a function's prologue and epilogue. Typically, it is used in conjunction with an owngp attribute to initialise a separate globals pointer for the duration of the function. The ENTRYPOINT macro is defined in `EntryPoints.h' for precisely this purpose:

  __attribute__ ((__owngp__, __extralogue__ EXTRAS))

It takes between zero and eight parameters, each of which is a string with the same syntax as an asm template string (see section `Assembler Instructions with C Expression Operands' in Using and Porting GCC):

extralogue (prologue, epilogue, single-epilogue, void-epilogue,
            far-prologue, far-epilogue, far-single-epilogue, far-void-epilogue)

This looks daunting, but often most of the parameters are unneeded. A replacement will be chosen, as described below, for each argument you leave empty (i.e., ""). If all arguments beyond a certain point would be empty, you can omit them entirely. Generally you only need to specify one or two parameters because:

Thus, in practice, extralogue is often only used with one argument. For example, an ENTRYPOINT equivalent to the CALLBACK_* macros looks like this:

ENTRYPOINT (("move.l %%a5,%%a4; sub.l #edata,%%a4"))

Note that ENTRYPOINT, being a macro, requires two sets of parentheses to emulate varargs.

See `EntryPoints.h' for more examples of the use of this attribute.


The function prologue first emits code to save the current A4 value on the stack. It then emits the first one of the following which is non-empty and whose condition, if any, is satisfied:

far-prologue if function has a section attribute

The string selected can use %0 to refer to the address of the function's first parameter. %0 will be a Mode 5 EA of the form offset(fp) or offset(sp).

Your prologue must not leave any extra data on the stack! If your prologue and epilogue need to communicate with each other and their needs aren't satisfied by A4, your function should declare some local variables for storage space, and your templates may access them at appropriate offsets from FP.

However, if you use `-fomit-frame-pointer', this won't work. In this case, you would need to fake your extra epilogue with asm, thus letting the compiler figure out the correct offsets from SP. As with the old CALLBACK_EPILOGUE, you would need to ensure that execution couldn't escape from your function without going through your `epilogue'.


The function epilogue emits the first one of the following which is non-empty and whose condition, if any, is satisfied:

far-void-epilogue if function has a section attribute and needs no return register
far-single-epilogue if function has a section attribute and needs at most a single return register
far-epilogue if function has a section attribute
void-epilogue if function needs no return register
single-epilogue if function needs at most a single return register

The string selected can use %0 to refer to the function's return value, if any. If the function requires several registers to return its value (e.g., long longs are returned in D0 and D1), then %0 will be a register list suitable for a movem instruction. If the function returns its value in a single register, then %0 denotes that register, and is either %%d0 or %%a0 depending on whether the return type is a pointer. Otherwise the function is either void or it returns a large struct; in this case, %0 will always be %%a0.

The epilogue then emits code to restore the old value of A4 from the stack.

The `far-' variants

You need to supply a `far-' variant when the normal template won't work properly when included in a function in a non-default section. (This is not a question of optimization, as is the case for the epilogue variants, because the `far-' variants generally won't work for a single code resource executable. Usually such variants are needed because they need to use the __text__ symbols, which would lead to link errors in a single code resource executable.)

Two extra operands are available to `far-' templates: %a1 is the function's section's `__text__section' symbol, and %2 is the address of the main text section's base pointer global, i.e., it is __text__(%%pic), where %%pic is either A4 or A5. (This shows dramatically why `far-' variants are unlikely to be used with ENTRYPOINT: it's not much use accessing the text section's base pointer via A4 when that register hasn't been set up yet!)

There are other possible uses of extralogue for which the `far-' variants are necessary. For example, you could arrange for a profiling function to be called at the start of a function like this:

  __attribute__ ((extralogue ("bsr.w __count", "", "", "",
                              "move.l %2,%%a0; jsr __count(%%a0)")))

If you couldn't tell the compiler to use a cross-section jump when needed, you would need to supply a separate version of the CALL_PROFILER macro for use with functions with section attributes. This would lead to mysterious errors: the program would crash if the programmer used the wrong version.

Alternatively, you could just call the profiling function like this:

int f() {

So this isn't a good example, but hopefully it serves to demonstrate the issues. Perhaps someone will find a good use for all this flexibility!

[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]