here's an example i found in ww.cs.clemson.edu/~mark/231/19
Clemson University -- CPSC 231 -- Fall 2001 19
Non-stack variables
global variable - a variable declared/defined in one source file that is
available to routines in other source files
external variable - a variable needed by but not defined in the current
source file
static variable - a local variable that must retain its value between
procedure calls
variable type "global" "static" "automatic" dynamic
-------- -------- ----------- ------------------
scope/visibility global local local global or local
lifetime program program fn. call from alloc to free
Program sections
reflect different logical parts of program
text - program code
data - initialized variables
bss (block started by symbol) - uninitialized variables
stack - automatically allocated variables (local variables) and other stack
frame entries
heap - dynamically allocated variables
| . . . |
+-------+ -. int x = 1; /* x placed in data */
| text | \ int y; /* y in placed bss */
|-------| | program's int main(){
| data | | layout in int z; /* z alloc. on stack */
|-------| | memory ...
| bss | | y = 2; /* code put in text */
|-------| | ...
| heap | | return 0; /* retval in reg %o0 */
|---|---| | }
| . V . | |
| . . . | | a dynamically allocated variable will
| . ^ . | | placed in the heap
|---|---| |
| stack | / a local variable declared with the
+-------+ -' keyword "static" in C will be placed
| . . . | in data if initialized, else in bss
different protection -- text is read-only; data, bss, the heap, and the stack
are read-write; a special rodata section is read-only
the assembler maintains a separate location counter for the first three
program sections above (the stack and heap do not have to be declared
in the assembly program)
pseudo-ops can be used to switch back and forth among the sections in
the source code, but the assembler will gather like kinds of regions
into separate memory sections
.section ".text" - assembler switches to the text section location counter
(the assembler starts in text section by default)
.section ".data" - assembler switches to the data section location counter
.word - followed by a list of initialized values
.byte - followed by a list of initialized values (an ASCII character
code is recognized by a single character placed in double quotes)
.ascii - character string in double quotes
.asciz - null-terminated character string, useful for strings when using
routines from the C library
.section ".bss" - assembler switches to the bss section location counter
.skip - advances location counter a given number of bytes
.section ".rodata" - read-only initialized data section
.align - variables of word size must be aligned (.align 4)
.global - allows symbol to be referenced beyond current source file (also,
you need to declare a symbol global for gdb to be able to use it)
example
int x = 1;
int y;
int main(){
int z;
printf( "variable x from data section has value %d\n", x );
y = 2;
printf( "variable y from bss section has value %d\n", y );
z = 3;
printf( "local variable z has value %d\n", z );
return 0;
}
.global main
main:
save %sp,-104,%sp
/* load value from initialized non-stack variable "x" */
set x,%o0
ld [%o0],%o1
set fmt1,%o0
call printf
nop
/* place value into uninitialized non-stack variable "y" */
mov 2,%o1
set y,%o0
st %o1,[%o0]
/* load value from non-stack variable "y" */
set y,%o0
ld [%o0],%o1
set fmt2,%o0
call printf
nop
/* place value into local variable "z" on stack */
/* note that the address of "z" is not z but %fp-4 instead */
mov 3,%o0
st %o0,[%fp-4]
/* load value from local variable "z" on stack */
/* note that the address of "z" is not z but %fp-4 instead */
ld [%fp-4],%o1
set fmt3,%o0
call printf
nop
/* place return value in %i0 here, will be in %o0 for caller */
clr %i0
ret
restore
.section ".data"
x: .word 1
.section ".bss"
y: .skip 4
.section ".rodata"
fmt1: .asciz "variable x from data section has value %d\n"
fmt2: .asciz "variable y from bss section has value %d\n"
fmt3: .asciz "local variable z has value %d\n"
in C++ programs, the library initializers (i.e., startup code) should run
first, then application constructors, then the application program, then
application destructors, and finally the library finalizers (exit code).
so, a C++ compiler may generate four additional program sections:
.init - followed by pointers to library initializers
.ctor - followed by pointers to application constructors
.dtor - followed by pointers to application destructors
.fini - followed by pointers to library finalizers
C++ allows overloaded function names, so the compiler and/or linker need to
use mangled names, e.g.,
for fn(int x), the function name becomes fn__i
for fn(float x), the function name becomes fn__f
additionally, templates in C++ can cause problems with missing routines or
superfluous routines. some C++ compilers require a trial linking step to
cause the linker to generate error messages indicating the missing routines,
and then those versions of the template are generated.