mirror of https://github.com/diegocr/libnix.git
1392 lines
50 KiB
Plaintext
1392 lines
50 KiB
Plaintext
|
\input texinfo
|
|||
|
@setfilename libnix.info
|
|||
|
@settitle libnix - a static library for GCC on the amiga
|
|||
|
@setchapternewpage odd
|
|||
|
|
|||
|
@ifinfo
|
|||
|
This is libnix.texi the documentation of the libnix library.
|
|||
|
Since this document is part of the libnix package it stands
|
|||
|
under the same copyright as the library itself, i.e. it's
|
|||
|
public domain.
|
|||
|
@end ifinfo
|
|||
|
@titlepage
|
|||
|
@title libnix
|
|||
|
@subtitle a static library for GCC on the amiga
|
|||
|
@author Matthias Fleischer & Gunther Nikl
|
|||
|
@end titlepage
|
|||
|
|
|||
|
@node Top,Description,(dir),(dir)
|
|||
|
@ifinfo
|
|||
|
This is the documentation of libnix. The following is a list
|
|||
|
of chapters. You need not read all of them - but reading
|
|||
|
the chapter Features is recommended.
|
|||
|
@end ifinfo
|
|||
|
|
|||
|
@menu
|
|||
|
* Description:: What is libnix?
|
|||
|
* Authors:: Who did it?
|
|||
|
* Disclaimer:: Copyright and other legal stuff.
|
|||
|
* Naming:: Naming conventions.
|
|||
|
* Usage:: How to use it ;-)
|
|||
|
* Features:: Details of the implementation (and more).
|
|||
|
* Special startups:: How to write shared libraries and devices with gcc.
|
|||
|
* Set elements:: A nice feature of gnu ld.
|
|||
|
* geta4:: Some words on code and data models
|
|||
|
* Library bases:: And how they work.
|
|||
|
* Code size:: How to write small programs with gcc
|
|||
|
* FAQs:: Frequently asked questions and answers.
|
|||
|
@end menu
|
|||
|
@node Description,Authors,Top,Top
|
|||
|
@chapter What is libnix?
|
|||
|
|
|||
|
libnix is a static (i.e. link) library for usage on amiga computers
|
|||
|
together with gcc 2.3.3 or above. It is very amigalike and contains
|
|||
|
a lot of features you probably don't want to miss:
|
|||
|
|
|||
|
@itemize @bullet
|
|||
|
@item
|
|||
|
auto-open-library feature
|
|||
|
@item
|
|||
|
SAS compatible handling of WB startup message
|
|||
|
@item
|
|||
|
auto-detach startup code
|
|||
|
@item
|
|||
|
is very short
|
|||
|
@item
|
|||
|
does not require a shared library
|
|||
|
@item
|
|||
|
auto stack-extend
|
|||
|
@item
|
|||
|
and much more
|
|||
|
@end itemize
|
|||
|
|
|||
|
So if you want to write amiga specific programs or if you only need ANSI support
|
|||
|
instead of unix compatibility - and if you don't want to redistribute ixemul.library -
|
|||
|
this can be your choice.
|
|||
|
|
|||
|
But be aware - libnix requires Amiga OS 2.0 or higher :(.
|
|||
|
|
|||
|
@node Authors,Disclaimer,Description,Top
|
|||
|
@chapter Authors
|
|||
|
|
|||
|
If you want to change anything in the library please contact one of us first -
|
|||
|
we know how to do it best since we wrote it. If you want to report
|
|||
|
bugs please contact us, too. If we don't know about them we cannot fix them.
|
|||
|
|
|||
|
Matthias Fleischer
|
|||
|
@*fleischr@@izfm.uni-stuttgart.de
|
|||
|
|
|||
|
Gunther Nikl
|
|||
|
@*gnikl@@informatik.uni-rostock.de
|
|||
|
|
|||
|
Special thanks go to Gerhard M<>ller for writing gerlib, Philippe Brand for
|
|||
|
finding a name for this beast and Kriton Kyrimis who contributed some
|
|||
|
of the non-ANSI functions and a lot of ideas.
|
|||
|
|
|||
|
@node Disclaimer,Naming,Authors,Top
|
|||
|
@chapter Disclaimer
|
|||
|
|
|||
|
This package is public domain. That means that you can copy, use and modify it
|
|||
|
without any problems and that you can get it for free. If you actually
|
|||
|
paid for getting it this is completely your fault - I didn't see a cent
|
|||
|
of that money. It also means that I cannot be made responsible for any damage
|
|||
|
resulting out of the use of it - you simply shouldn't trust anything you didn't
|
|||
|
pay for :-).
|
|||
|
|
|||
|
@node Naming,Usage,Disclaimer,Top
|
|||
|
@chapter Naming conventions
|
|||
|
|
|||
|
This library is not only for the end user but also for the library
|
|||
|
programmer (if you want to write your own startup, etc).
|
|||
|
|
|||
|
If you want to write code for it you should be aware of the normal
|
|||
|
naming conventions for ANSI libraries:
|
|||
|
|
|||
|
@itemize @bullet
|
|||
|
@item
|
|||
|
Names with no underscore @samp{foo} are ANSI or POSIX compliant - there is absolutely
|
|||
|
no risk in using them. If you use only these you can write portable
|
|||
|
programs.
|
|||
|
@item
|
|||
|
Names with a single underscore @samp{_foo} are ANSI extensions for the
|
|||
|
end user. Usually they are very common on certain systems but not used on
|
|||
|
others.
|
|||
|
@item
|
|||
|
Names with two underscores @samp{__foo} are for the library programmers only.
|
|||
|
If they are not documented you cannot rely on them. And even if they are
|
|||
|
you should use them only for writing library code.
|
|||
|
@end itemize
|
|||
|
|
|||
|
There is only one exception of these conventions (@samp{__chkabort()})
|
|||
|
and this is for compatibility reasons.
|
|||
|
|
|||
|
@node Usage,Features,Naming,Top
|
|||
|
@chapter Usage
|
|||
|
|
|||
|
The usage of this library is like any other link library. The only
|
|||
|
important thing is the right linkage order:
|
|||
|
|
|||
|
@enumerate
|
|||
|
@item
|
|||
|
The startup code has to be used first :-)
|
|||
|
@item
|
|||
|
The stubs-library has to be used last since it contains the library base pointers.
|
|||
|
@item
|
|||
|
The commandline parser should be used after your code but before most
|
|||
|
other things or you will run into problems.
|
|||
|
@end enumerate
|
|||
|
|
|||
|
Normally this is handled by the specs file of gcc.
|
|||
|
|
|||
|
@code{gcc (-fbaserel) (-resident) -noixemul YOUR_OBJECTS (-lnix_main) (-lm)}
|
|||
|
|
|||
|
If you use @samp{-lnix_main} you get a different commandline parser.
|
|||
|
@samp{-lm} uses the math library.
|
|||
|
|
|||
|
Be aware that the formatted I/O-functions need the math library
|
|||
|
to work correctly for floating point numbers. Without the math
|
|||
|
library you get only floating point support for simple operators
|
|||
|
like @samp{+}, @samp{*}, casts and that like.
|
|||
|
|
|||
|
If you don't use the assembler inline functions of gcc you will have
|
|||
|
to use @samp{libamiga.a} if you want to use any Amiga OS function.
|
|||
|
gcc comes with a free version of libamiga.a which is a subset
|
|||
|
of the original one. You can build it yourself if you unpack the sources,
|
|||
|
but be prepared that it may take some time.
|
|||
|
|
|||
|
For compiling a4-relative programs you should choose the
|
|||
|
@samp{-fbaserel} option. You get resident (pure) programs if
|
|||
|
you set the @samp{-resident} option. Anything else necessary for these options
|
|||
|
is handled by the specs-file (choosing the right startups and libraries).
|
|||
|
|
|||
|
If you (for some reason) don't trust you specs-file you can call everything
|
|||
|
by hand:
|
|||
|
|
|||
|
@code{gcc -nostdlib ncrt0.o YOUR_OBJECTS libnixmain.a (libm.a) libnix.a
|
|||
|
libstubs.a}
|
|||
|
|
|||
|
But that's not the recommended way. Therefore I don't explain this in
|
|||
|
detail here - use the @samp{-v} option of gcc for more details.
|
|||
|
|
|||
|
@node Features,Special startups,Usage,Top
|
|||
|
@chapter Features - what you get
|
|||
|
|
|||
|
The following list contains the elements of this package in
|
|||
|
the right linkage order. This means if you follow the list from
|
|||
|
top to bottom and take one file of each menu entry you will get
|
|||
|
a working configuration.
|
|||
|
|
|||
|
@menu
|
|||
|
* Startup codes:: Does all work necessary for startup.
|
|||
|
Your objects Need I say anything about that?
|
|||
|
* Commandline parser:: Calculates argc and argv.
|
|||
|
libm.a The math library (optional).
|
|||
|
* libstack.a:: Stack extension code (optional).
|
|||
|
* libnix.a:: The library itself.
|
|||
|
* libamiga.a:: If you have one.
|
|||
|
* detach.o:: Auto detaching
|
|||
|
* libstubs.a:: The library bases.
|
|||
|
@end menu
|
|||
|
|
|||
|
@node Startup codes,Commandline parser,Features,Features
|
|||
|
@chapter Startup codes
|
|||
|
|
|||
|
There is a lot of work to do before your @samp{main} function
|
|||
|
can be called - open shared libraries, open stdin, stdout, etc.
|
|||
|
Depending on the compiler options and the ANSI functions you use.
|
|||
|
This work is done by the startup code.
|
|||
|
|
|||
|
@menu
|
|||
|
* Startup interface::
|
|||
|
* Startup usage::
|
|||
|
@end menu
|
|||
|
|
|||
|
To get a short startup all the necessary modules are optional -
|
|||
|
they get only linked in if you use them. There are 2 exceptions
|
|||
|
from this (since the linker cannot check for it):
|
|||
|
|
|||
|
@itemize @bullet
|
|||
|
@item
|
|||
|
The commandline parser. It can be deactivated by declaring
|
|||
|
|
|||
|
@code{__nocommandline}
|
|||
|
|
|||
|
somewhere in your program (the type doesn't matter).
|
|||
|
@item
|
|||
|
The shared-library-opening module. You should not disable it unless
|
|||
|
you know what you are doing since most library functions depend on it.
|
|||
|
@end itemize
|
|||
|
|
|||
|
The startup codes itself are written in assembly to be as short as
|
|||
|
possible.
|
|||
|
|
|||
|
Here is a little program to get the point:
|
|||
|
|
|||
|
@example
|
|||
|
#include <inline/exec.h>
|
|||
|
#include <dos/dos.h>
|
|||
|
#include <inline/dos.h>
|
|||
|
#include <workbench/workbench.h>
|
|||
|
|
|||
|
int __nocommandline=1; /* Disable commandline parsing */
|
|||
|
int __initlibraries=0; /* Disable auto-library-opening */
|
|||
|
|
|||
|
struct DosLibrary *DOSBase=NULL;
|
|||
|
|
|||
|
extern struct WBStartup *_WBenchMsg;
|
|||
|
|
|||
|
int main(void)
|
|||
|
@{ if(_WBenchMsg==NULL)
|
|||
|
@{ if((DOSBase=(struct DosLibrary *)OpenLibrary("dos.library",37))!=NULL)
|
|||
|
@{ Write(Output(),"Hello world\n",12);
|
|||
|
CloseLibrary((struct Library *)DOSBase); @} @}
|
|||
|
return 0;
|
|||
|
@}
|
|||
|
@end example
|
|||
|
|
|||
|
compiled and linked with
|
|||
|
|
|||
|
@code{gcc -noixemul -s -O2 -fbaserel helloworld.c}
|
|||
|
|
|||
|
gives an executable of 492 bytes. And this with the normal @samp{main}
|
|||
|
function!
|
|||
|
|
|||
|
So you never need to try to write a program without a startup code.
|
|||
|
|
|||
|
@node Startup interface,Startup usage,Startup codes,Startup codes
|
|||
|
@chapter Startup code interface
|
|||
|
|
|||
|
The startup codes do the following:
|
|||
|
|
|||
|
@itemize @bullet
|
|||
|
@item
|
|||
|
They catch the workbench startup message and place it into the
|
|||
|
variable
|
|||
|
|
|||
|
@code{extern struct WBStartup *_WBenchMsg}
|
|||
|
|
|||
|
you can simply look into this place (and test for a @samp{NULL} pointer) to
|
|||
|
check if your program was started from WB. If this is a @samp{NULL} pointer
|
|||
|
|
|||
|
@code{extern char *__commandline}
|
|||
|
|
|||
|
contains the (@samp{\n} terminated) parameters of the commandline.
|
|||
|
@item
|
|||
|
They call all functions in the
|
|||
|
|
|||
|
@code{long __INIT_LIST__[];}
|
|||
|
|
|||
|
with ascending priority.
|
|||
|
@item
|
|||
|
They call the function
|
|||
|
|
|||
|
@code{int main(int argc,char *argv[])}
|
|||
|
|
|||
|
You can exit by simply falling through the end of @samp{main} or by calling
|
|||
|
|
|||
|
@code{__volatile void exit(int returncode)}
|
|||
|
|
|||
|
which does the cleanup:
|
|||
|
@item
|
|||
|
It calls all functions in the
|
|||
|
|
|||
|
@code{long __EXIT_LIST__[];}
|
|||
|
|
|||
|
with descending priority.
|
|||
|
@item
|
|||
|
It replys the WB startup message if necessary, resets the stackpointer
|
|||
|
and returns to the shell.
|
|||
|
@end itemize
|
|||
|
|
|||
|
@samp{__INIT_LIST__} and @samp{__EXIT_LIST__} are two set elements
|
|||
|
which are a speciality of the gnu ld. Since everything that needs
|
|||
|
initialization works over these two lists the bare startups are
|
|||
|
very short. In fact they are even shorter then some low-level-startups
|
|||
|
|
|||
|
You can easily add your
|
|||
|
own functions to the startup procedure by using the macros
|
|||
|
in the file @samp{headers/stabs.h} - but keep in mind that this
|
|||
|
is non-portable. Priority values <=0 are reserved for library implementors.
|
|||
|
|
|||
|
@node Startup usage,,Startup interface,Startup codes
|
|||
|
@chapter Startup code usage
|
|||
|
|
|||
|
There are currently 3 startup codes in this package (maybe there will be more
|
|||
|
in the future). Depending on the code and data model you use and some other
|
|||
|
things you should choose one of them:
|
|||
|
|
|||
|
@table @samp
|
|||
|
@item ncrt0.o
|
|||
|
This is the normal (i.e. large code, large data model) startup.
|
|||
|
It contains a @samp{geta4()} entry point to enable you to use one source
|
|||
|
for two code models. There is no other need for this function.
|
|||
|
@item nbcrt0.o
|
|||
|
This one is for compiling small data model (a4 relative) programs.
|
|||
|
There is a @samp{geta4()} entry that places the right information into a4.
|
|||
|
Use this startup code if you compiled with @samp{-fbaserel}.
|
|||
|
@item nrcrt0.o
|
|||
|
This startup code allocates a new data area every time you call it.
|
|||
|
Even if you don't call it at all the data are is there once.
|
|||
|
This gives you multientrant and reentrant code.
|
|||
|
Therefore this startup code is for compiling resident (pure) programs.
|
|||
|
Resident programs are always small data model if you let the compiler
|
|||
|
do the work.
|
|||
|
|
|||
|
There is no @samp{geta4()} entry - I just don't know how this could be done.
|
|||
|
(If you start your code 10 times and want to access global data
|
|||
|
out of a hook you cannot tell which one of the 10 data areas to use
|
|||
|
because you want to access the data from a different task!)
|
|||
|
@end table
|
|||
|
|
|||
|
@node Commandline parser,libstack.a,Startup codes,Features
|
|||
|
@chapter Commandline parser
|
|||
|
|
|||
|
There are currently 2 commandline parser modules in the libnix package.
|
|||
|
You can easily write your own by looking into the examples
|
|||
|
|
|||
|
@table @code
|
|||
|
@item libnixmain.a
|
|||
|
This is the normal one, i.e. it does all the work necessary for
|
|||
|
ANSI compatibility and gives you the normal @samp{main} calling
|
|||
|
convention. You can shut down the commandline parsing (if you want
|
|||
|
to use the amiga OS commandline parser) by declaring
|
|||
|
|
|||
|
@code{__nocommandline}
|
|||
|
|
|||
|
somewhere in your code (the type of it actually doesn't matter). This
|
|||
|
spares some bytes and is compatible to every other compiler.
|
|||
|
|
|||
|
And you can declare your own WB shell window by declaring a
|
|||
|
|
|||
|
@code{char __stdiowin[]}
|
|||
|
|
|||
|
variable somewhere in your code (but only if you parse the commandline -
|
|||
|
without the commandline parser you get no window at all!).
|
|||
|
|
|||
|
@item libnix_main.a
|
|||
|
This is a special version of a commandline parser - it doesn't call
|
|||
|
the normal @samp{main} but
|
|||
|
|
|||
|
@code{int main(char *commandline)}
|
|||
|
|
|||
|
@samp{commandline} is the complete commandline - including the quoted
|
|||
|
filename of your program (it's only quoted, not escaped - this is
|
|||
|
for compatibility reasons :-( ). You might think the name of the game
|
|||
|
should be @samp{_main} and not @samp{main} - and you are completely
|
|||
|
right. You can use @samp{_main} for @samp{main} and @samp{_exit} for
|
|||
|
@samp{exit} - there are symbol redirections for these and the linker
|
|||
|
does the work.
|
|||
|
|
|||
|
This commandline parser is useful for compatibility.
|
|||
|
You can use it as a second example or for recompiling PD programs that
|
|||
|
use the single argument. You cannot use it for compiling ANSI code.
|
|||
|
@end table
|
|||
|
|
|||
|
@node libstack.a,libnix.a,Commandline parser,Features
|
|||
|
@chapter special stack handling facilities
|
|||
|
|
|||
|
The current Amiga OS (V3.1) has a very limited stack handling compared
|
|||
|
to most other OSs: Every process has it's own fixed sized stack - and that's
|
|||
|
all about it. The usual default for this stack is 4k, but that's not enough
|
|||
|
for more complicated purposes (like for example compilers). Setting a higher
|
|||
|
default is no real solution because it costs a lot of (widely unused) memory
|
|||
|
and may be overrun, too :-(.
|
|||
|
|
|||
|
But fortunately you can get stack extension with a little help of the compiler ;-).
|
|||
|
Starting with V0.9 of libnix and V2.7.0 of gcc you get a fully featured stack
|
|||
|
extension facility. The old stack swap method is still provided (not only for
|
|||
|
compatibility but also because it's simpler) but please don't try to mix it with
|
|||
|
the newer check/extend methods.
|
|||
|
@menu
|
|||
|
* swapstack:: Old method.
|
|||
|
* stackextend implementation:: How stack extend works.
|
|||
|
* stackextend usage:: Usage. @strong{read}
|
|||
|
* Advanced:: Fine tuning.
|
|||
|
* Costs:: Some damned lies (Benchmarks ;-) ).
|
|||
|
@end menu
|
|||
|
@node swapstack,stackextend implementation,libstack.a,libstack.a
|
|||
|
@chapter Minimum stack setting
|
|||
|
|
|||
|
Most large tools need more stack than the default 4096 bytes. If your tool is
|
|||
|
one of them you can either rely on the user being able to raise the current stack
|
|||
|
or you can let libnix raise the stack for you. At startup this module checks if the
|
|||
|
current stack is large enough for your needs and switches to a new one if not.
|
|||
|
All you have to do is to provide a variable
|
|||
|
|
|||
|
@samp{unsigned long __stack=@{required stacksize@};}
|
|||
|
|
|||
|
somewhere in your code and to link with the appropriate swapstack.o module.
|
|||
|
@node stackextend implementation,stackextend usage,swapstack,libstack.a
|
|||
|
@chapter Implementation of stack checking and extension
|
|||
|
|
|||
|
The basic principle of stack checking is that the compiler emits special code
|
|||
|
to check if the stack is large enough whenever there's need for a bigger chunk
|
|||
|
of stackspace, i.e. at function entry when local arrays are allocated, at
|
|||
|
the start of blocks with local variable sized arrays and when calling @samp{alloca()}.
|
|||
|
If the needed stackchunk is bigger than the left stackspace the program ends.
|
|||
|
|
|||
|
Since this special code costs memory and CPU time smaller stackneeds (e.g. when
|
|||
|
calling library functions) are handled by not really checking against the hard border
|
|||
|
of the stackframe but against one that leaves a certain amount of stackspace left
|
|||
|
(@xref{Advanced}.). If you like to call functions with a lot of arguments
|
|||
|
(more than 256 bytes) you should raise this value.
|
|||
|
|
|||
|
Stack extension builds on the same basic principle but allocates a new stackframe
|
|||
|
whenever necessary. If this happens at the entry of a function with arguments
|
|||
|
they have to be copied to the new stackframe so that the function may use them.
|
|||
|
Since C allows for a variable number of arguments the compiler doesn't always know
|
|||
|
how many arguments there are. Therefore only a fixed number of bytes is copied.
|
|||
|
If your functions may have lots of arguments (again more than 256 bytes) you
|
|||
|
should raise this number.
|
|||
|
|
|||
|
Since allocation and freeing of memory through OS functions costs a lot of
|
|||
|
time (while a stack tends to be very dynamic) libnix caches once used stackframes
|
|||
|
and utilizes them again if necessary. The memory needed for this doesn't accumulate
|
|||
|
or such but just sticks to a maximum value raised once. This may look like a memory leak
|
|||
|
(while in fact it isn't). Be prepared for it.
|
|||
|
@node stackextend usage,Advanced,stackextend implementation,libstack.a
|
|||
|
@chapter Using stack checking or extension
|
|||
|
To utilize the stack checking or extension feature you need at least V2.7.0 of
|
|||
|
gcc. With this compiler you get 2 new amiga specific options that emit special
|
|||
|
code whenever necessary:
|
|||
|
|
|||
|
@itemize @bullet
|
|||
|
@item
|
|||
|
@samp{-mstackcheck} Emits code that checks if there is enough stack left.
|
|||
|
The program exits if not.
|
|||
|
@item
|
|||
|
@samp{-mstackextend} Tries to extend the stack before exiting (this may
|
|||
|
happen due to low or fragmented memory).
|
|||
|
@end itemize
|
|||
|
|
|||
|
Always use those switches together with @samp{-lstack} to link with the stack
|
|||
|
extension code or you will get a lot of undefined references ;-).
|
|||
|
You can mix functions compiled with or without stack checking and extention
|
|||
|
without problems.
|
|||
|
|
|||
|
@strong{Caution:}
|
|||
|
|
|||
|
Do not use stack checking and/or extension switches when
|
|||
|
compiling hook or interrupt code. Both run in alien contexts with a different
|
|||
|
stack and all stack magic must fail. Also don't try to do some other stack magic
|
|||
|
on your own if you want to use stack extension.
|
|||
|
|
|||
|
Also note that a program compiled with stack extension/checking may @samp{exit()} at
|
|||
|
@emph{any} function entry or when using alloca or variable sized arrays. Either prepare
|
|||
|
your cleanup function accordingly (use @samp{atexit()}) or don't use this feature.
|
|||
|
|
|||
|
If you like to write or call functions with more than 256 bytes of arguments
|
|||
|
(64 ints, longs or pointers) you should adjust the behaviour of the
|
|||
|
stack extension code (@xref{Advanced}.).
|
|||
|
|
|||
|
@node Advanced,Costs,stackextend usage,libstack.a
|
|||
|
@chapter Stack extension fine tuning
|
|||
|
|
|||
|
To adjust the behaviour of the stack extension code to your personal needs
|
|||
|
you may set some of the following variables (or functions)
|
|||
|
|
|||
|
@samp{unsigned long __stk_minframe} (default: 32768)
|
|||
|
|
|||
|
Minimum amount of memory to allocate for a new stackframe. Setting a higher
|
|||
|
value speeds the code up but costs more memory if it is unused.
|
|||
|
|
|||
|
@samp{unsigned long __stk_safezone} (default: 2048)
|
|||
|
|
|||
|
Size of the safety zone. Set this to a higher value
|
|||
|
if you want to @emph{call} functions with lots of arguments.
|
|||
|
|
|||
|
@samp{unsigned long __stk_argbytes} (default: 256)
|
|||
|
|
|||
|
Number of bytes copied as arguments. Set this to a higher
|
|||
|
value if @emph{your} functions may have lots of arguments.
|
|||
|
|
|||
|
@samp{void _CXOVF(void)}
|
|||
|
|
|||
|
Is a user replaceable stack overflow handler. The default one just pops
|
|||
|
up a requester, then exits. This function is not allowed to return.
|
|||
|
|
|||
|
@node Costs,,Advanced,libstack.a
|
|||
|
@chapter Overhead of stack extension
|
|||
|
|
|||
|
The additional code needed for stack extension (or checking) costs memory
|
|||
|
and CPU power. Here are some numbers to give you a very rough idea for it.
|
|||
|
(Times are in 1/60s, sizes in bytes):
|
|||
|
|
|||
|
@example
|
|||
|
Test normal checking extending extending
|
|||
|
(big stack) (big stack) (big stack) (small stack)
|
|||
|
|
|||
|
Simple recursive
|
|||
|
function runtime 152 221 225 226
|
|||
|
(function calling
|
|||
|
overhead)
|
|||
|
|
|||
|
Variable sized 52 136 398 468
|
|||
|
array runtime
|
|||
|
|
|||
|
alloca runtime 31 118 118 118
|
|||
|
|
|||
|
Own code size 1040 1160 1140
|
|||
|
|
|||
|
Library code size 0 184 788
|
|||
|
@end example
|
|||
|
|
|||
|
@node libnix.a,libamiga.a,libstack.a,Features
|
|||
|
@chapter Some ANSI (mis)features
|
|||
|
|
|||
|
I suppose you are familiar with C and especially ANSI C - if not
|
|||
|
you should read a good book about it
|
|||
|
@footnote{I recommend this one:
|
|||
|
|
|||
|
Brian W. Kernighan, Dennis M.Ritchie:
|
|||
|
@*The C Programming Language (Second Edition)
|
|||
|
@*Prentice Hall, Englewood Cliffs, 1988}.
|
|||
|
This chapter only contains some special features of the
|
|||
|
implementation - you should know these if you want to
|
|||
|
use this library.
|
|||
|
|
|||
|
@menu
|
|||
|
* Locale::
|
|||
|
* Formatted I/O::
|
|||
|
* atof strtod::
|
|||
|
* Memory management::
|
|||
|
* Standard I/O::
|
|||
|
* Signal handling::
|
|||
|
* setjmp longjmp::
|
|||
|
* ctype::
|
|||
|
* clock::
|
|||
|
* Multibyte character functions::
|
|||
|
@end menu
|
|||
|
|
|||
|
@node Locale,Formatted I/O,libnix.a,libnix.a
|
|||
|
@chapter Locale
|
|||
|
|
|||
|
One feature of a complete ANSI compatible library is locale support. The
|
|||
|
ANSI standard only knows of two locales:
|
|||
|
|
|||
|
@itemize @bullet
|
|||
|
@item
|
|||
|
"C" locale (normal C behaviour).
|
|||
|
@item
|
|||
|
Default locale.
|
|||
|
@end itemize
|
|||
|
|
|||
|
Every other locale depends very heavily on the implementation.
|
|||
|
|
|||
|
To do locale support on the amiga I decided to use locale.library (what else).
|
|||
|
This means that you normally have only these two locales - to have more than that
|
|||
|
you must make some extra preferences files with the locale preferences editor
|
|||
|
and give the path of these to the setlocale()-call. If you do not have locale.library
|
|||
|
you will get only "C" locale. This is the default then :-(.
|
|||
|
|
|||
|
Another important point is that the ANSI standard requires the default locale to be
|
|||
|
loaded at program startup. i.e. if you use german locale (for example) you
|
|||
|
will just get it - printf and scanf will not work as expected but use the
|
|||
|
decimal comma @samp{,} instead of the decimal point @samp{.} for their floating
|
|||
|
point numbers, ctype functions will behave differently, too.
|
|||
|
|
|||
|
This can be very annoying if you don't want to use ANSI locale but rather
|
|||
|
locale.library (which is not portable but IMHO much better) or if
|
|||
|
you don't need locale support. And even dangerous if you don't
|
|||
|
test your program under different locales.
|
|||
|
|
|||
|
To get around this problem I decided to do some nasty thing:
|
|||
|
To get locale support you have to make up a reference to setlocale. You can do
|
|||
|
this by just calling
|
|||
|
|
|||
|
setlocale(LC_ALL,"C");
|
|||
|
|
|||
|
immediately after program startup. (And get "C" locale then after program start which
|
|||
|
is a much better choice). Or by just using setlocale anywhere in your program -
|
|||
|
you will get default locale at program startup then.
|
|||
|
|
|||
|
@node Formatted I/O,atof strtod,Locale,libnix.a
|
|||
|
@chapter Formatted I/O
|
|||
|
|
|||
|
The formatted I/O specifications are all there (remember: this library tries to
|
|||
|
be ANSI compliant). But there are two things you should know about them:
|
|||
|
|
|||
|
@itemize @bullet
|
|||
|
@item
|
|||
|
The formatted I/O is affected by the setlocale() call - this is no bug,
|
|||
|
just an ANSI feature.
|
|||
|
@item
|
|||
|
Half of the code of a full blown printf handles floating point numbers -
|
|||
|
but not everybody needs them. So there are two functions for both
|
|||
|
@samp{vfprintf} and @samp{vfscanf} - one in @samp{libnix.a} not including
|
|||
|
floating point support and one in @samp{libm.a} including floating
|
|||
|
point support.
|
|||
|
|
|||
|
So if you want to use one of the formatted I/O specifiers for floats
|
|||
|
you should link with the math library @samp{-lm}.
|
|||
|
@end itemize
|
|||
|
|
|||
|
@node atof strtod,Memory management,Formatted I/O,libnix.a
|
|||
|
@chapter atof strtod
|
|||
|
|
|||
|
The two functions @samp{atof} and @samp{strtod} require a working
|
|||
|
@samp{%f} specifier in @samp{vfscanf} - therefore they
|
|||
|
require the formatted I/O functions in the math library.
|
|||
|
Since @samp{libm.a} is linked before @samp{libnix.a} these
|
|||
|
two functions have been gone into the math library.
|
|||
|
|
|||
|
@node Memory management,Standard I/O,atof strtod,libnix.a
|
|||
|
@chapter Memory management
|
|||
|
|
|||
|
Most of the memory management of this library runs through malloc().
|
|||
|
Only the commandline parser uses AllocVec() - so you can use it without
|
|||
|
having the malloc function somewere in your program.
|
|||
|
|
|||
|
The memory management uses a local (to this task) memory pool to reduce
|
|||
|
memory fragmentation. It uses the system functions to do so (not the
|
|||
|
new pooled memory functions but just the older Allocate(), Deallocate() pair
|
|||
|
which are the <3.0 fallback for libamiga.a's pooled memory functions, too)
|
|||
|
so there should be no problems with it - these functions are tested very good.
|
|||
|
|
|||
|
The default blocksize for memory allocations is 16384 bytes - equivalent
|
|||
|
to 4 MMU pages. Bigger allocations are blown up to a multiple of 4096 bytes.
|
|||
|
So don't be alarmed if your program uses more memory then expected.
|
|||
|
|
|||
|
If you don't like this value (if you use bigger portions frequently or only
|
|||
|
use very little memory) you can replace it by declaring
|
|||
|
|
|||
|
@code{unsigned long _MSTEP}
|
|||
|
|
|||
|
You should use a multiple of MMU pages. If you don't use a full MMU page
|
|||
|
you gain nothing - malloc rounds up anyway.
|
|||
|
|
|||
|
@node Standard I/O,Signal handling,Memory management,libnix.a
|
|||
|
@chapter Standard I/O - where stdin, stdout, stderr come from
|
|||
|
|
|||
|
2 of the 3 standard I/O streams are no real problem:
|
|||
|
|
|||
|
@itemize @bullet
|
|||
|
@item
|
|||
|
@samp{stdin} is set to the value the @samp{Input()} function of @samp{dos.library}
|
|||
|
serves,
|
|||
|
@item
|
|||
|
@samp{stdout} is set to the @samp{Output()} value.
|
|||
|
@end itemize
|
|||
|
|
|||
|
Both streams are managed by the OS and the library need not take much
|
|||
|
care about them. But @samp{stderr} is a different thing since
|
|||
|
there is no @samp{Errput()} ;-) function. So @samp{stderr} is handled
|
|||
|
as follows:
|
|||
|
|
|||
|
@enumerate
|
|||
|
@item
|
|||
|
If @samp{process->pr_CES} is set, this value is taken. There are not
|
|||
|
much shells that set this value so most of the time this leads to NULL.
|
|||
|
@item
|
|||
|
If this didn't work and your program was started from CLI the library
|
|||
|
opens @samp{Open("*",MODE_NEWFILE)}.
|
|||
|
This opens the last interactive terminal attached to stdout, i.e.
|
|||
|
if you use the normal Amiga shell and redirect your output to a file you
|
|||
|
get the terminal, if you redirect your output to @samp{NIL:} you get @samp{NIL:}.
|
|||
|
@item
|
|||
|
If this didn't work too (you never know) or your program was started from WB
|
|||
|
you simply get the same stream as in @samp{stdout}.
|
|||
|
@end enumerate
|
|||
|
|
|||
|
@node Signal handling,setjmp longjmp,Standard I/O,libnix.a
|
|||
|
@chapter Signal handling
|
|||
|
|
|||
|
There is only support for the two signals SIGABRT and SIGINT. The library
|
|||
|
knows of some other signals but cannot generate them. The support for
|
|||
|
SIGABRT is simple - but SIGINT is a completely different thing:
|
|||
|
|
|||
|
You cannot use exec signal handlers since they are called at any time -
|
|||
|
even in the middle of a library call. And if your library just blocked
|
|||
|
a private semaphore and you jump out of the library code you will get a nice
|
|||
|
deadlock :-(. (And for people who don't know: signal handlers are bogus
|
|||
|
upto OS 2.0 (even there you need a good setpatch)).
|
|||
|
|
|||
|
So SIGINT (CTRL-C) is just polled at the start of most I/O-functions by
|
|||
|
calling the function
|
|||
|
|
|||
|
@code{void __chkabort(void)}
|
|||
|
|
|||
|
Other signals are even more difficult to implement:
|
|||
|
|
|||
|
@itemize @bullet
|
|||
|
@item
|
|||
|
SIGSEGV simply doesn't exist - and if it does it's due
|
|||
|
to a VM system and should not be generated.
|
|||
|
@item
|
|||
|
SIGFPE is not generated by the math libraries - so it would
|
|||
|
be a bad thing to generate it by the mathematical coprocessor.
|
|||
|
@item
|
|||
|
SIGILL should never happen - your program must be faulty if you get one.
|
|||
|
Most of the time this happens if you try to run a 68020+ compiled program
|
|||
|
on a plain 68000.
|
|||
|
@item
|
|||
|
SIGTERM couldn't be disabled - even if it was there ;-).
|
|||
|
@end itemize
|
|||
|
|
|||
|
You can disable CTRL-C handling by replacing @samp{__chkabort} with a do-nothing
|
|||
|
stub function - but there is a better way. Just call
|
|||
|
|
|||
|
@code{signal(SIGINT,SIG_IGN)}
|
|||
|
|
|||
|
Replacing __chkabort is used very often by amiga-programs and if
|
|||
|
your application does not need CTRL-C handling at all and is
|
|||
|
amiga specific you can use this. The second method is the
|
|||
|
ANSI standard method and works on all types of machines.
|
|||
|
|
|||
|
@node setjmp longjmp,ctype,Signal handling,libnix.a
|
|||
|
@chapter setjmp, longjmp
|
|||
|
|
|||
|
This library is compatible to the header files that come with gcc -
|
|||
|
and the jmp_buf in there is not large enough for the FPU registers.
|
|||
|
So they are not restored! The ANSI standard doesn't even require to
|
|||
|
restore any of the other local variables (they are restored :-) ),
|
|||
|
so this is NO incompatibility to the ANSI standard.
|
|||
|
|
|||
|
@node ctype,clock,setjmp longjmp,libnix.a
|
|||
|
@chapter ctype.h functions
|
|||
|
|
|||
|
If you look into ctype.h you will see that the functions in there are
|
|||
|
just macros - and that they are duplicate in the library as functions.
|
|||
|
This is NOT a mistake. The ANSI standard requires such macros to be
|
|||
|
duplicate as functions.
|
|||
|
|
|||
|
And remember: These functions are affected by the setlocale() call.
|
|||
|
|
|||
|
@node clock,Multibyte character functions,ctype,libnix.a
|
|||
|
@chapter The clock function
|
|||
|
|
|||
|
The clock() function's work is to measure processor time for the specific
|
|||
|
task - but there is no information like this in the amiga OS :-(.
|
|||
|
So it just measures the time from program start on - and is compatible with
|
|||
|
this behaviour to all single tasking OSs around.
|
|||
|
|
|||
|
@node Multibyte character functions,,clock,libnix.a
|
|||
|
@chapter Multibyte character functions
|
|||
|
|
|||
|
The multibyte character functions are all there - but since the
|
|||
|
Amiga OS uses no other character set than ECMA Latin I they simulate
|
|||
|
just "C" locale. This means they do nothing useful.
|
|||
|
|
|||
|
@node libstubs.a,detach.o,libamiga.a,Features
|
|||
|
@chapter libstubs - automatic library opening
|
|||
|
|
|||
|
The Amiga OS shared libraries are a nice thing. All the tasks can
|
|||
|
use them in parallel, they eat up memory only if you use them and
|
|||
|
they are simple to use - and all this works even without a
|
|||
|
memory management unit (MMU).
|
|||
|
|
|||
|
Another nice feature is the fact that you can open them under
|
|||
|
program control, i.e. you can take some action if they do not
|
|||
|
exist - warn the user, disable some features, etc. This nice
|
|||
|
feature becomes a misfeature if you only need a certain list
|
|||
|
of functions that are there all the time - exec, dos, intuition -
|
|||
|
you still have to open the shared libraries.
|
|||
|
|
|||
|
So most Amiga compilers have a feature called automatic library
|
|||
|
opening feature. This means that all libraries you reference
|
|||
|
(by calling one of the functions) but don't open yourself get
|
|||
|
opened for you by the compiler.
|
|||
|
|
|||
|
@menu
|
|||
|
* Auto-library-opening usage:: How to use it.
|
|||
|
* Auto-library-opening interface:: How it works.
|
|||
|
@end menu
|
|||
|
|
|||
|
@node Auto-library-opening usage,Auto-library-opening interface,libstubs.a,libstubs.a
|
|||
|
@chapter Usage
|
|||
|
|
|||
|
To use this feature you have to do nothing (therefore it's called
|
|||
|
automatic). But you can control the library version if you wish
|
|||
|
by declaring
|
|||
|
|
|||
|
@code{long __oslibversion;}
|
|||
|
|
|||
|
somewhere in your program. But don't set this lower than 37 - most functions
|
|||
|
of libnix (including the commandline parsers) need 37 or more.
|
|||
|
|
|||
|
@node Auto-library-opening interface,,Auto-library-opening usage,libstubs.a
|
|||
|
@chapter Interface
|
|||
|
|
|||
|
Implementing such a feature is no hard work if you know how - this
|
|||
|
implementation uses a (not so good known) feature of the gnu linker
|
|||
|
called set elements:
|
|||
|
|
|||
|
@enumerate
|
|||
|
@item
|
|||
|
You write a library entry for every library base and link this library as the
|
|||
|
last one. This means that the linker uses this library for every library base
|
|||
|
pointer that is not defined but referenced somewhere.
|
|||
|
@item
|
|||
|
You tell the linker to collect these library bases together into a set element.
|
|||
|
@item
|
|||
|
You write a function that opens all libraries in the set element at
|
|||
|
program start and cleans them up later.
|
|||
|
@end enumerate
|
|||
|
|
|||
|
Some details:
|
|||
|
|
|||
|
There are two object files in the library for every library base pointer.
|
|||
|
The first one is a
|
|||
|
|
|||
|
@example
|
|||
|
struct lib
|
|||
|
@{ struct Library *base;
|
|||
|
char *name; @};
|
|||
|
@end example
|
|||
|
|
|||
|
containing the library base pointer (a @samp{NULL} pointer at program start)
|
|||
|
and a pointer to the name of the library. This name
|
|||
|
|
|||
|
@samp{extern char name[]}
|
|||
|
|
|||
|
is the second object. All these structs are collected together into one
|
|||
|
single set element called
|
|||
|
|
|||
|
@samp{extern struct lib *__LIB_LIST__[]}
|
|||
|
|
|||
|
To open and close the shared libraries there are two functions in
|
|||
|
libstubs:
|
|||
|
|
|||
|
@code{void __initlibraries(void)}
|
|||
|
|
|||
|
and
|
|||
|
|
|||
|
@code{void __exitlibraries(void)}
|
|||
|
|
|||
|
Since it is still possible to open the shared libraries by hand I had
|
|||
|
to take care about the library base pointers for libnix itself -
|
|||
|
they are used in the commandline parsers - even before anybody could
|
|||
|
open them. There exists a (library private) duplicate library base
|
|||
|
pointer for each of these. They have normal names with
|
|||
|
two underscores in front.
|
|||
|
|
|||
|
So don't be alarmed if some system monitor tells you that your program
|
|||
|
opened dos.library twice - this is normal behaviour, most libraries
|
|||
|
do this.
|
|||
|
|
|||
|
Opening libraries by hand works exactly the same way as on any other compiler:
|
|||
|
@itemize @bullet
|
|||
|
@item
|
|||
|
You declare the library base variable somewhere globally:
|
|||
|
|
|||
|
@code{struct DosLibrary *DOSBase=NULL;}
|
|||
|
|
|||
|
The initialization @samp{=NULL} is necessary! Uninitialized variables
|
|||
|
get overwritten by initialized ones in other object files - and
|
|||
|
the library base pointers in @samp{libstubs.a} are initialized with @samp{NULL}.
|
|||
|
This is a feature of the GNU ld and I cannot do much about it :(.
|
|||
|
@item
|
|||
|
You open the library before using it:
|
|||
|
|
|||
|
@code{DOSBase=(struct DosLibrary *)OpenLibrary("dos.library",37);}
|
|||
|
|
|||
|
and do some action if it fails :).
|
|||
|
@end itemize
|
|||
|
|
|||
|
@node detach.o,,libstubs.a,Features
|
|||
|
@chapter Detaching from the current CLI
|
|||
|
|
|||
|
Some people like multitasking that much that they tend to start everything
|
|||
|
in the background. Some tools are able to do this automatically - and with
|
|||
|
libnix you can write such tools, too. @footnote{ Please be aware that you
|
|||
|
lose the ability to synchronize your tool with the calling CLI - this can be
|
|||
|
very nasty if one needs to. Use this feature very sparse: Most of the time
|
|||
|
it's better to just rely on the user being able to type @samp{Run >NIL: <NIL:}. }
|
|||
|
|
|||
|
To be able to detach from the current CLI the detach module has to know
|
|||
|
how much stack your program needs, how to call the new process, etc.
|
|||
|
Therefore you will have to provide some global variables that contain this
|
|||
|
information. If you don't provide them you will get default values
|
|||
|
(don't blame me for them - it's your own fault :-@} ).
|
|||
|
|
|||
|
Here is an example set of variables:
|
|||
|
|
|||
|
@example
|
|||
|
char *__procname="My nifty tool";
|
|||
|
long __priority=-1; /* We don't eat that much processor time */
|
|||
|
unsigned long stack=50000; /* but need a large stack */
|
|||
|
@end example
|
|||
|
|
|||
|
@node Special startups,Set elements,Features,Top
|
|||
|
@chapter Special startups
|
|||
|
|
|||
|
As a serious Amiga programmer you may sooner or later want to write your
|
|||
|
own shared library or device. This can be a very difficult task if you never
|
|||
|
did it before. To make your life easier we did some of the work for you
|
|||
|
if you decide to use one of our startups. Be aware that writing a shared library
|
|||
|
is a task for the experienced programmer @footnote{ Most ANSI library functions
|
|||
|
don't work out of a shared library. libnix makes no difference here - even simple
|
|||
|
operations like multiplying two integers can fail - don't take anything for granted.}.
|
|||
|
|
|||
|
@menu
|
|||
|
* Calling convention:: How to interface to the system.
|
|||
|
* libinit.o:: Shared library startup.
|
|||
|
* libinitr.o:: Shared library with a new data segment for each caller.
|
|||
|
* devinit.o:: Device startup.
|
|||
|
@end menu
|
|||
|
|
|||
|
@node Calling convention,libinit.o,Special startups,Special startups
|
|||
|
@chapter Calling convention
|
|||
|
|
|||
|
On the amiga almost all shared libraries are called with the library base
|
|||
|
in a6 and other parameters in other registers. The result is placed in d0
|
|||
|
usually.
|
|||
|
|
|||
|
If you want to write your own shared library you should stick to this model
|
|||
|
to make it easier for others to interface with it - but unfortunately gcc
|
|||
|
doesn't support registerized parameters. The solution to this problem is to
|
|||
|
write an assembler wrapper for each function you need. Since all those
|
|||
|
wrappers look the same it's easy to simplify their notation by using
|
|||
|
preprocessor macros. You can find appropriate macros in the stabs.h file of
|
|||
|
the libnix sources. Usage:
|
|||
|
|
|||
|
@example
|
|||
|
/* Define some function that has 1 argument in d0 */
|
|||
|
ADDTABL_1(__UserFunc,d0);
|
|||
|
@end example
|
|||
|
|
|||
|
As a bonus these macros add your function to your library's jump vector.
|
|||
|
All you have to do is to care for the right linkage order. And don't forget
|
|||
|
to add a @samp{ADDTABL_END();} at the end of the vector.
|
|||
|
|
|||
|
Attention: Other programmers may decide to patch into your library's jump
|
|||
|
vector - therefore it's a good idea to call even own functions over this
|
|||
|
vector. To achieve this you have to provide some inline functions
|
|||
|
(like those in the gnu:os-include/inline directory) or glue code and you have
|
|||
|
to privatize the function's name by adding some '__'s or similar. You will
|
|||
|
also have to set up your OWN library base.
|
|||
|
@node libinit.o,libinitr.o,Calling convention,Special startups
|
|||
|
@chapter Shared library startup
|
|||
|
|
|||
|
This startup gives you one data segment for all possible callers.
|
|||
|
You will have to use semaphores to share special data between them.
|
|||
|
|
|||
|
To write a shared library you will have to provide some global variables
|
|||
|
|
|||
|
@example
|
|||
|
const BYTE LibName[]="simple.library";
|
|||
|
const BYTE LibIdString[]="version 1.0";
|
|||
|
const UWORD LibVersion=1;
|
|||
|
const UWORD LibRevision=0;
|
|||
|
@end example
|
|||
|
|
|||
|
as well as some special functions
|
|||
|
@footnote{It'll be possible to add an Open() and Close() function, too. But this
|
|||
|
would be incompatible to libinitr.o and wouldn't give any advantages over this
|
|||
|
method.}
|
|||
|
|
|||
|
@example
|
|||
|
int __UserLibInit(struct Library *myLib);
|
|||
|
void __UserLibCleanUp();
|
|||
|
@end example
|
|||
|
|
|||
|
Please look into the examples directory for more details.
|
|||
|
|
|||
|
@node libinitr.o,devinit.o,libinit.o,Special startups
|
|||
|
@chapter Shared library with different data segments
|
|||
|
|
|||
|
If you don't like the hassle with semaphores you can use this startup.
|
|||
|
It provides a new data segment for each task that opens your library.
|
|||
|
There are two disadvantages over libinit.o:
|
|||
|
|
|||
|
@itemize @bullet
|
|||
|
@item
|
|||
|
This method needs more memory.
|
|||
|
@item
|
|||
|
You cannot interact between your tasks.
|
|||
|
@end itemize
|
|||
|
|
|||
|
The usage stays the same.
|
|||
|
|
|||
|
@node devinit.o,,libinitr.o,Special startups
|
|||
|
@chapter Device startup
|
|||
|
|
|||
|
The device startup uses different names over the library startup
|
|||
|
(though a device is always a shared library on the Amiga).
|
|||
|
|
|||
|
@example
|
|||
|
const BYTE DevName[]="simple.device";
|
|||
|
const BYTE DevIdString[]="version 1.0";
|
|||
|
const UWORD DevVersion=1;
|
|||
|
const UWORD DevRevision=0;
|
|||
|
int __UserDevInit(struct Device *myDev);
|
|||
|
void __UserDevCleanUp();
|
|||
|
int __UserDevOpen(struct IORequest *iorq,ULONG unit,ULONG flags);
|
|||
|
void __UserDevClose(struct IORequest *iorq);
|
|||
|
(And some begin and abort function as well)
|
|||
|
@end example
|
|||
|
|
|||
|
@node Set elements,geta4,Special startups,Top
|
|||
|
@chapter Set elements - a nice feature of the gnu ld
|
|||
|
|
|||
|
Set elements are used very often by this library. Since most people don't
|
|||
|
know them they are explained here a second time.
|
|||
|
|
|||
|
You can tell the linker to build up an array of pointers to every
|
|||
|
global symbol in your program (functions or variables)
|
|||
|
even if your symbols are scattered among some object files.
|
|||
|
These arrays are called set elements.
|
|||
|
|
|||
|
You can take 4 Library base pointers
|
|||
|
|
|||
|
@samp{DOSBase}, @samp{IntuitionBase}, @samp{GfxBase}, @samp{IconBase}
|
|||
|
|
|||
|
tell the linker to put them together into a set element
|
|||
|
called @samp{librarybases} by placing some assembler lines like
|
|||
|
|
|||
|
@code{asm(".stabs \"_librarybases\",24,0,0,_DOSBase")}
|
|||
|
|
|||
|
into your code (22 for text, 24 for data, 26 for bss - and don't
|
|||
|
forget the single underscore) and get an array of pointers like this:
|
|||
|
|
|||
|
@example
|
|||
|
void *librarybases[]=
|
|||
|
@{ (void *)4,&DOSBase,&IntuitionBase,&GfxBase,&Iconbase,NULL @};
|
|||
|
@end example
|
|||
|
|
|||
|
The first element contains the number of symbols. The last element
|
|||
|
contains a NULL pointer. And remember: This are pointers to the
|
|||
|
pointer variables.
|
|||
|
|
|||
|
This is the basis of global constructors and destructors in C++
|
|||
|
and is very useful on the amiga to implement an auto-library-opening
|
|||
|
feature :-). Set elements are used in this library for collecting
|
|||
|
together library bases, initialization routines and cleanup routines.
|
|||
|
|
|||
|
@node geta4,Library bases,Set elements,Top
|
|||
|
@chapter geta4 and other things - some words on code and data models
|
|||
|
|
|||
|
A program consists of two portions - code and data
|
|||
|
(with the exception of self modifying code - you cannot get this
|
|||
|
out of GCC and it is a bad thing to do - so forget about it).
|
|||
|
|
|||
|
A program usually accesses them in a unique style of addressing
|
|||
|
modes for the machine instructions - called a code (or data) model.
|
|||
|
|
|||
|
On the amiga OS there exist two addressing styles for both of
|
|||
|
them - code and data. With full 32 bit addresses - giving you access
|
|||
|
to 4 GB of address space. And with reduced 16 bit addresses - giving
|
|||
|
you access to only 32k of code and 64k of data. These styles are
|
|||
|
called large (or normal) and small code and data models.
|
|||
|
Usually small code comes together with small data - but that's not
|
|||
|
necessary.
|
|||
|
|
|||
|
(Don't mix code and data models with the memory models of MS-DOS
|
|||
|
machines: The memory in small data model is still flat 4 GB which
|
|||
|
means by using pointers you can still address the whole memory.
|
|||
|
Only the number of variables is limited.)
|
|||
|
|
|||
|
You may think that these limitations are a large disadvantage -
|
|||
|
where are the benefits?
|
|||
|
|
|||
|
The benefits are simple: Code size and performance.
|
|||
|
|
|||
|
Every time you access a 16 bit address instead of 32 bit you spare
|
|||
|
2 bytes in code size meaning 2 bytes in program size. And the
|
|||
|
processor needs to load and process a smaller instruction that
|
|||
|
needs less processor cycles. And since these 16 bit addresses are
|
|||
|
relative the loader of the OS need not relocate them. Meaning
|
|||
|
that you spare even 8 bytes more in executable size and some
|
|||
|
loading time.
|
|||
|
|
|||
|
And there is another advantage: If all the data is addressed
|
|||
|
relative it is simple to relocate it at program start - which
|
|||
|
means that you can easily get multientrant and reentrant executables
|
|||
|
(the code section is constant and need not be relocated).
|
|||
|
You know these as pure=resident programs.
|
|||
|
|
|||
|
Some details:
|
|||
|
@menu
|
|||
|
* Data models::
|
|||
|
* Code models::
|
|||
|
@end menu
|
|||
|
|
|||
|
@node Data models,Code models,geta4,geta4
|
|||
|
@chapter Data models - large and small
|
|||
|
|
|||
|
Let's take a simple C program:
|
|||
|
|
|||
|
@example
|
|||
|
#include <stdio.h>
|
|||
|
|
|||
|
int max=100;
|
|||
|
int count;
|
|||
|
char string[]="Hello, world\n";
|
|||
|
|
|||
|
int main(void)
|
|||
|
@{ int i;
|
|||
|
for(i=0;i<max;i++)
|
|||
|
@{ count++;
|
|||
|
printf("%s",string); @}
|
|||
|
return 0;
|
|||
|
@}
|
|||
|
@end example
|
|||
|
|
|||
|
If we look at it carefully we see 4 different types of data in it:
|
|||
|
|
|||
|
@enumerate
|
|||
|
@item
|
|||
|
The variable @samp{max} and the array @samp{string} - both are nonconstant
|
|||
|
initialized global data.
|
|||
|
@item
|
|||
|
The variable @samp{count} - this is uninitialized (and therefore nonconstant)
|
|||
|
global data.
|
|||
|
@item
|
|||
|
The string "%s" - this one is constant data.
|
|||
|
@item
|
|||
|
The variable @samp{i} - this one is local data.
|
|||
|
@end enumerate
|
|||
|
|
|||
|
The compiler places these 4 types of data into 4 different places:
|
|||
|
|
|||
|
Data number 4 is local (and exists only in one function call).
|
|||
|
The compiler places such data into registers if possible. On
|
|||
|
the stack if this is not possible. If you do not want the compiler
|
|||
|
to place data into registers declare it volatile - it will be
|
|||
|
on the stack then all the time.
|
|||
|
|
|||
|
Data number 3 is constant - the compiler places it together with
|
|||
|
all the other constant data into the code section (code is also
|
|||
|
constant). Never change any constant data - the weirdest things
|
|||
|
can happen.
|
|||
|
|
|||
|
Data number 2 is not initialized - it would be a bad thing to
|
|||
|
put data without information into an executable. Therefore
|
|||
|
such data goes into a special section - the BSS section. BSS data
|
|||
|
does not increase executable size.
|
|||
|
|
|||
|
And the rest (number 1) goes into the data section :-).
|
|||
|
|
|||
|
To access the data section with machine instructions there are two
|
|||
|
possible methods:
|
|||
|
|
|||
|
You can take the whole 32 bit address and store it into your machine
|
|||
|
code. 32 bit means 4 byte every time you access a global variable.
|
|||
|
This is know as the large (normal) data model because you can access
|
|||
|
the whole bunch of 4 GB address space.
|
|||
|
|
|||
|
A lot of applications do not need such a large data section and it
|
|||
|
would be a waste of memory to do so. So there exists a second
|
|||
|
possibility:
|
|||
|
|
|||
|
You take one address register (a4) and use it as a pointer to your
|
|||
|
data section. You access your data relative to this pointer with
|
|||
|
16 bit references. This is known as small data model.
|
|||
|
|
|||
|
Since there are only 16 bit references you can access a total of
|
|||
|
64k of data (32k in each direction from a4 on). And since you
|
|||
|
use only one address register for this the data and BSS section get
|
|||
|
merged together (BSS data still need not increase executable size -
|
|||
|
there exist some tricks to prevent this - but not all linkers
|
|||
|
support such tricks).
|
|||
|
|
|||
|
Beware: you should never lose the contents of your address register
|
|||
|
(a4) or all the hell breaks lose.
|
|||
|
|
|||
|
If you ever lost them (this can only happen in certain cases when
|
|||
|
using interrupts or hooks) you can restore them by calling
|
|||
|
@samp{geta4()} or you can use no global data at all (and have no
|
|||
|
problems then).
|
|||
|
|
|||
|
The second method is recommended - and it is possible sometimes
|
|||
|
since the OS takes care of this and supports local data areas in
|
|||
|
these nasty cases. But don't call any shared library - or you
|
|||
|
will access (hidden) a library base pointer.
|
|||
|
|
|||
|
It is not possible to have a @samp{geta4()} function with
|
|||
|
resident programs = multiple data sections
|
|||
|
(which one would you choose? You access the data from a different task!)
|
|||
|
|
|||
|
It's in general not possible to mix objects compiled for
|
|||
|
the two data models. There are some exceptions, but people that
|
|||
|
know enough to prevent collosions need no explanation of when
|
|||
|
it's possible ;-).
|
|||
|
|
|||
|
@node Code models,,Data models,geta4
|
|||
|
@chapter Code models
|
|||
|
|
|||
|
All your constant data and all (constant) code form the code section.
|
|||
|
To access the code section there exist two code models:
|
|||
|
|
|||
|
You can take the whole 32 bit address to call a function and can write
|
|||
|
programs 4GB large. This is the large (normal) code model.
|
|||
|
|
|||
|
But you can even call your functions relative to the program counter
|
|||
|
(pc) with 16 bit offsets. This is the small code model.
|
|||
|
The advantages are the same as in the small data model - only the
|
|||
|
disadvantages are different:
|
|||
|
|
|||
|
@itemize @bullet
|
|||
|
@item
|
|||
|
You can only have a total of 32k of code since you need to jump in
|
|||
|
both directions. But even this is enough for a lot of programs.
|
|||
|
@item
|
|||
|
There is no address register that can be lost - the program counter
|
|||
|
is valid the whole time.
|
|||
|
@item
|
|||
|
It is possible to mix large and small code model - but you can
|
|||
|
hardly get more than 32k of code out of it.
|
|||
|
@end itemize
|
|||
|
|
|||
|
@node Library bases,Code size,geta4,Top
|
|||
|
@chapter Library base pointers - and how they work
|
|||
|
|
|||
|
The model of shared libraries on the amiga works as follows:
|
|||
|
|
|||
|
The library is not managed by the linker but by the application.
|
|||
|
You open it through a system function. The advantage of this is clear:
|
|||
|
You open libraries under program control, i.e. you can even check if
|
|||
|
they are there and disable some features if not or take other action.
|
|||
|
|
|||
|
The result of this system function is a pointer to the upper end of a
|
|||
|
jump table (a table of @samp{jmp} instructions to the different functions)
|
|||
|
and the lower end of a library structure containing extra
|
|||
|
information for every library you opened. These pointers are called
|
|||
|
library base pointers. They are usually stored in normal global variables.
|
|||
|
|
|||
|
To call a system function you have to put the library base into
|
|||
|
address register a6 and the parameters into certain other registers.
|
|||
|
Then your program has to jump over the certain address of the jump table.
|
|||
|
The function returns with the result in register d0 (and sometimes some more).
|
|||
|
|
|||
|
A compiler can handle this behaviour by two different methods:
|
|||
|
|
|||
|
@itemize @bullet
|
|||
|
@item
|
|||
|
It can just do the right thing and place everything into the desired
|
|||
|
registers - gcc does this by declaring special assembler inline
|
|||
|
functions that do the job.
|
|||
|
@item
|
|||
|
It can put the arguments on the stack (as in every normal function call)
|
|||
|
and call a glue function that does nothing else then taking the arguments
|
|||
|
from the stack and putting them into the right registers then calling
|
|||
|
the function. This glue code is contained in @samp{amiga.lib}.
|
|||
|
@end itemize
|
|||
|
|
|||
|
Both methods require to access the library base pointer
|
|||
|
(and a valid value in it) so they make up a reference to
|
|||
|
this variable.
|
|||
|
|
|||
|
@node libamiga.a,libstubs.a,libnix.a,Features
|
|||
|
@chapter Glue code and some other things
|
|||
|
|
|||
|
It's not possible for me to redistribute amiga.lib - but you should
|
|||
|
have one if you really want to use the possibilities of your amiga.
|
|||
|
(You don't need one if you only want to use ANSI features or if you
|
|||
|
use the inline headers of gcc)
|
|||
|
If you want to compile resident programs you will need a baserelative
|
|||
|
version too.
|
|||
|
|
|||
|
To solve this problem I decided to build a selfmade version of
|
|||
|
libamiga.a. The gluecode of this library is built out of the
|
|||
|
inline header files of gcc, some of the other functions are written
|
|||
|
from scratch. This does not give you a complete version of libamiga.a
|
|||
|
but a better that nothing version including sources.
|
|||
|
To rebuild it unpack the sources, then type a 'make libamiga'.
|
|||
|
|
|||
|
If you want to have a fully functional version of libamiga.a you
|
|||
|
can use the real one. To do this you will
|
|||
|
have to convert normal amiga objectfile format to a.out format
|
|||
|
(known by the linker). Type:
|
|||
|
|
|||
|
@example
|
|||
|
cd <some empty directory>
|
|||
|
stack 300000
|
|||
|
sh
|
|||
|
Hunk2GCC <path>amiga.lib
|
|||
|
ar -q libamiga.a obj*
|
|||
|
rm *.o
|
|||
|
ranlib libamiga.a
|
|||
|
exit
|
|||
|
@end example
|
|||
|
|
|||
|
Doing this on @samp{RAM:} will improve performance a lot.
|
|||
|
|
|||
|
This doesn't give you the baserelative version blib/libamiga.a -
|
|||
|
you will be unable to compile resident programs. To get a baserelative
|
|||
|
version of amiga.lib try to get the @samp{libtos} program of the
|
|||
|
@samp{DICE} compiler of M. Dillon (from fishdisk or somewhere else) -
|
|||
|
it converts libraries to baserelative ones:
|
|||
|
|
|||
|
@example
|
|||
|
cd <some empty directory>
|
|||
|
lha x amigalibdisk491:dice/dice206_21.lzh #?/libtos
|
|||
|
netdcc/bin/libtos <path>amiga.lib amigas.lib
|
|||
|
@end example
|
|||
|
|
|||
|
Then do the same as above.
|
|||
|
|
|||
|
@node Code size,FAQs,Library bases,Top
|
|||
|
@chapter Writing small programs
|
|||
|
|
|||
|
Writing very small programs is a trivial thing - if you know how to do it.
|
|||
|
Here are some basic tips and tricks:
|
|||
|
|
|||
|
@table @asis
|
|||
|
@item Do not use printf:
|
|||
|
@samp{printf} is the most high-level function of
|
|||
|
usual ANSI C libraries and builds on almost everything else. Often this function
|
|||
|
can be replaced by simpler routines like e.g. @samp{puts}. The same is true
|
|||
|
for the other formatted I/O functions.
|
|||
|
|
|||
|
If you write amiga-only programs you can use the @samp{sprintf} routine of
|
|||
|
libamiga.a by linking with @samp{-lamiga}.
|
|||
|
|
|||
|
@item Try to avoid using buffered I/O:
|
|||
|
A lot of programs can be written using the low-level I/O-functions @samp{read} and
|
|||
|
@samp{write}. This saves the code responsible for buffered I/O.
|
|||
|
|
|||
|
@item Avoid -O3:
|
|||
|
@samp{-O3} activates all those optimizations that sacrifice code size for speed.
|
|||
|
An exception is @samp{-fstrength-reduce} which costs compile time but not
|
|||
|
code size. @samp{-O2 -fstrength-reduce} will give small programs.
|
|||
|
|
|||
|
@item Strip your executables:
|
|||
|
gcc doesn't strip executables by default. Setting @samp{-s} will give worse
|
|||
|
debugable but small programs.
|
|||
|
|
|||
|
@item Use small code and data model:
|
|||
|
@samp{-msmall-code -fbaserel} do this. Be aware that this won't work for
|
|||
|
larger programs.
|
|||
|
|
|||
|
@item Don't use libgcc.a:
|
|||
|
@samp{libgcc.a} which calls global C++ constructors and destructors builds
|
|||
|
on @samp{atexit} which builds on @samp{malloc}, ... If your program doesn't
|
|||
|
use them you can save some memory by linking by hand (@samp{-nostdlib}).
|
|||
|
|
|||
|
@item Set __nocommandline:
|
|||
|
If you use @samp{ReadArgs} instead of @samp{argc, argv} you can save some
|
|||
|
memory by setting a global variable named @samp{__nocommandline} to drop
|
|||
|
parsing of commandline arguments.
|
|||
|
@end table
|
|||
|
|
|||
|
@node FAQs,,Code size,Top
|
|||
|
@chapter FAQs
|
|||
|
|
|||
|
@table @asis
|
|||
|
@item Q:
|
|||
|
I do not get a working executable out of it - my debugger
|
|||
|
tells me the library bases are broken.
|
|||
|
@item A:
|
|||
|
The GNU ld that comes with GCC 2.5.8 (or lower) has some
|
|||
|
serious bugs in conjunction with set elements.
|
|||
|
Use the fixed version of ld that comes with gcc 2.6.0 (or above).
|
|||
|
|
|||
|
@item Q:
|
|||
|
There are some prototypes missing in stdio.h.
|
|||
|
@item A:
|
|||
|
This stdio.h is only for internal use - use the normal GCC
|
|||
|
stdio.h to compile your programs.
|
|||
|
|
|||
|
@item Q:
|
|||
|
While printing floats printf prints weird characters.
|
|||
|
@item A:
|
|||
|
This problem should be fixed with the current release of libnix. It was caused
|
|||
|
by a bug in the system math libraries which happens if you open them in the
|
|||
|
wrong order. You can use the SetMathPatch program by Andreas Wolff to fix this
|
|||
|
and another more serious bug with mc68040 processors.
|
|||
|
@end table
|
|||
|
@printindex cp
|
|||
|
@contents
|
|||
|
@bye
|