[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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 pointsUsing extralogue attributes
How global data should be accessed depends on what you are building:
Open
vector if it wants any,
and then can access it via A4.
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).
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.
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:
#define ENTRYPOINT(EXTRAS) \ __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:
extralogue
used to arrange access to global variables had better not
require such special treatment!
epilogue
is capable of handling all epilogues. The
`single-' and `void-' variants allow you to supply more efficient
code in most cases, but are not truly necessary.
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
|
prologue |
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 |
epilogue |
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.
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:
#define CALL_PROFILER \ __attribute__ ((extralogue ("bsr.w __count", "", "", "", "move.l %2,%%a0; jsr __count(%%a0)"))) int f() CALL_PROFILER; |
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() { __count(); ... } |
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] | [ ? ] |