[comp.sys.amiga] Linking without startup code or libraries

deven@pawl.rpi.edu (Deven Corzine) (03/03/89)

In article <92203@sun.uucp> cmcmanis%pepper@Sun.COM (Chuck McManis) writes:
>In article <DEVEN.89Mar1183038@daniel.pawl.rpi.edu> shadow@pawl.rpi.edu writes:
>>One question - is it possible to create a compiled C module which can
>>be linked alone, without being linked to the lib:c.o startup module or
>>the lib: libraries?  (Clearly the libraries can be ditched if nothing
>>in them is used.  The question is whether or not you MUST use assembly
>>for the startup module...)
>
>Yes, with Lattice 5.0x it is possible to have nothing but 
>blink myprog.o to myprog
>(which sometimes you can just execute myprog.o if there are no externals
>at all) 
>
>Essentially, you will have to define your lib pointers internally and use
>the prototypes for direct linkage to the libraries.

Can you give me a short simple example that will work for this?  Say,
the classic "Hello, world!" program?  I tried something like this:

-----
/* hello.c */
#include <proto/exec.h>
#include <proto/dos.h>

struct DosLibrary *DOSBase;
static char msg[]="Hello, world!\n";

void main()
{
   DOSBase=(struct DosLibrary *) OpenLibrary("dos.library",0);
   Write(Output(),msg,sizeof(msg));
   CloseLibrary((struct Library *) DOSBase);
}
-----
lc -b -r -v hello.c
blink hello.o to hello smallcode smalldata

(I'm trying to make the executable as small as possible, but without
using assembly.)

It compiled and linked without errors, with an executable size of
about 100 bytes.  But every time I ran it, it caused a GURU.  Often
GOMF didn't manage to catch it, either.  I also tried moving the
DOSBase and msg declarations to the end of the file and declaring them
as externs in main(), I tried using _main(), (which gave me 604 bytes
linking with lib:c.o+hello.o and lib:lc.lib) I tried declaring DOSBase
as an extern, but the linker complained that it couldn't resolve
_DOSBase (unless I linked with lib:lc.lib) and any time I managed to
get it to compile and link, it wouldn't execute.  At  some point, I
think I managed to get it to print something, half garbage.  Oh, yes.
One more thing - when I have the OpenLibrary() call in, it accesses
df0: once, quickly, and then gurus.  I think it half-worked (printed
<garbage>, world! or something like that) when I took out the
OpenLibrary() and CloseLibrary() calls.  Something. Anyway, I couldn't
get it to compile, link and run correctly.  What am I doing wrong?
Do I need to specially define the entry point?  (or make the entry
function the first function?)  Or am I incorrently defining DOSBase?

HELP!

Deven
--
------- shadow@pawl.rpi.edu ------- Deven Thomas Corzine ---------------------
Cogito  shadow@acm.rpi.edu          2346 15th Street            Pi-Rho America
ergo    userfxb6@rpitsmts.bitnet    Troy, NY 12180-2306         (518) 272-5847
sum...     In the immortal words of Socrates:  "I drank what?"     ...I think.

gay%elde.epfl.ch@cunyvm.cuny.edu (David Gay) (03/05/89)

>/* hello.c */
>#include <proto/exec.h>
>#include <proto/dos.h>
>
>struct DosLibrary *DOSBase;
>static char msg[]="Hello, world!\n";
>
>void main()
>{
>   DOSBase=(struct DosLibrary *) OpenLibrary("dos.library",0);
>   Write(Output(),msg,sizeof(msg));
>   CloseLibrary((struct Library *) DOSBase);
>}
>-----
>lc -b -r -v hello.c
>blink hello.o to hello smallcode smalldata

The problem is that A4 (which points to global data) is not initialised.
2 solutions:

- compile with lc -b0 -v hello.c

- add a call to geta4() at the start of main() (if you include <dos.h>, this
 will be expanded "inline").

>Deven
>--
>------- shadow@pawl.rpi.edu ------- Deven Thomas Corzine ---------------------
>Cogito  shadow@acm.rpi.edu          2346 15th Street            Pi-Rho America
>ergo    userfxb6@rpitsmts.bitnet    Troy, NY 12180-2306         (518) 272-5847
>sum...     In the immortal words of Socrates:  "I drank what?"     ...I think.


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
David Gay                                  6 x 9 = 42

GAY@ELDE.EPFL.CH, or GAY%ELDE.EPFL.CH@CLSEPF51.bitnet

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

cmcmanis%pepper@Sun.COM (Chuck McManis) (03/07/89)

In article <92203@sun.uucp> I wrote:
> Yes, with Lattice 5.0x it is possible to have nothing but 
> blink myprog.o to myprog

This is correct.

> (which sometimes you can just execute myprog.o if there are no externals
> at all) 

This is incorrect.

> Essentially, you will have to define your lib pointers internally and use
> the prototypes for direct linkage to the libraries.

Then in a followup article shadow@pawl.rpi.edu writes:
> Can you give me a short simple example that will work for this?  Say,
> the classic "Hello, world!" program?  I tried something like this:

To which I reply "Certainly" and here it is :

To make this program run compile it like so :
lc -v -b0 sm.c
blink sm.o to sm

You must use -b0 or somehow set up A4 to point to your data segment 
ahead of time. (Relative addressing on code still works fine)

-------
/* Hello world very simply */
#include <exec/types.h>		/* always useful 		*/
#include <exec/memory.h>	/* probably extraneous 		*/
#include <libraries/dos.h> 	/* Defines dosname 		*/
#include <proto/exec.h>		/* Essential for inline calls 	*/
#include <proto/dos.h>		/* ditto 			*/

ULONG **SysBase = (ULONG **) 4;	/* The one "known" address */
ULONG *ExecBase;		/* Needed for Exec functions */
struct DOSBase  *DOSBase;	/* Needed for DOS functions  */

char	msg[] = "Hello World\n"; /* Ye old message */

_main()
{
  ExecBase = *SysBase;
  DOSBase = (struct DOSBase *)OpenLibrary(DOSNAME,0);
  Write(Output(), msg, sizeof(msg));
  (void) CloseLibrary(DOSBase);
  return (0);
}

And the assembly that Lattice generates for this is :

-------

Lattice AMIGA 68000-68020 OBJ Module Disassembler V5.00
Copyright ) 1988 Lattice Inc.  All Rights Reserved.


Amiga Object File Loader V1.00
68000 Instruction Set

EXTERNAL DEFINITIONS

__main 0000-00    _SysBase 0000-01    _msg 0004-01    _ExecBase 0000-02    
_DOSBase 0004-02

SECTION 00 "text" 00000050 BYTES
       | 0000  48 E7 30 02                    MOVEM.L   D2-D3/A6,-(A7)
       | 0004  20 79  00 00 00 00-01          MOVEA.L   01.00000000,A0
       | 000A  23 D0  00 00 00 00-02          MOVE.L    (A0),02.00000000
       | 0010  43 F9  00 00 00 12-01          LEA       01.00000012,A1
       | 0016  70 00                          MOVEQ     #00,D0
       | 0018  2C 78 00 04                    MOVEA.L   0004,A6
       | 001C  4E AE FD D8                    JSR       FDD8(A6)
       | 0020  23 C0  00 00 00 04-02          MOVE.L    D0,02.00000004
       | 0026  C1 8E                          EXG       D0,A6
       | 0028  4E AE FF C4                    JSR       FFC4(A6)
       | 002C  22 00                          MOVE.L    D0,D1
       | 002E  41 F9  00 00 00 04-01          LEA       01.00000004,A0
       | 0034  24 08                          MOVE.L    A0,D2
       | 0036  76 0D                          MOVEQ     #0D,D3
       | 0038  4E AE FF D0                    JSR       FFD0(A6)
       | 003C  22 4E                          MOVEA.L   A6,A1
       | 003E  2C 78 00 04                    MOVEA.L   0004,A6
       | 0042  4E AE FE 62                    JSR       FE62(A6)
       | 0046  70 00                          MOVEQ     #00,D0
       | 0048  4C DF 40 0C                    MOVEM.L   (A7)+,D2-D3/A6
       | 004C  4E 75                          RTS

SECTION 01 "data" 00000020 BYTES
0000 00 00 00 04 48 65 6C 6C 6F 20 57 6F 72 6C 64 0A ....Hello World.
0010 00 00 64 6F 73 2E 6C 69 62 72 61 72 79 00 00 00 ..dos.library...

SECTION 02 "udata" 00000008 BYTES
----------

When blinked results in an executable that is 224 bytes long. You can
probably trim that some. (It doesn't have to return 0). 

--Chuck McManis
uucp: {anywhere}!sun!cmcmanis   BIX: cmcmanis  ARPAnet: cmcmanis@sun.com
These opinions are my own and no one elses, but you knew that didn't you.

deven@pawl.rpi.edu (Deven Corzine) (03/09/89)

In article <92710@sun.uucp> cmcmanis%pepper@Sun.COM (Chuck McManis) writes:
>In article <92203@sun.uucp> I wrote:
>To make this program run compile it like so :
>lc -v -b0 sm.c
>blink sm.o to sm
>
>You must use -b0 or somehow set up A4 to point to your data segment 
>ahead of time. (Relative addressing on code still works fine)
>
>-------
>/* Hello world very simply */
>#include <exec/types.h>		/* always useful 		*/
>#include <exec/memory.h>		/* probably extraneous 		*/
>#include <libraries/dos.h> 		/* Defines dosname 		*/
>#include <proto/exec.h>		/* Essential for inline calls 	*/
>#include <proto/dos.h>			/* ditto 			*/
>
>ULONG **SysBase = (ULONG **) 4;	/* The one "known" address */
>ULONG *ExecBase;			/* Needed for Exec functions */
>struct DOSBase  *DOSBase;		/* Needed for DOS functions  */
>
>char	msg[] = "Hello World\n";	/* Ye old message */
>
>_main()
>{
>  ExecBase = *SysBase;
>  DOSBase = (struct DOSBase *)OpenLibrary(DOSNAME,0);
>  Write(Output(), msg, sizeof(msg));
>  (void) CloseLibrary(DOSBase);
>  return (0);
>}
>
>And the assembly that Lattice generates for this is :
>
>-------
>
>Lattice AMIGA 68000-68020 OBJ Module Disassembler V5.00
>Copyright ) 1988 Lattice Inc.  All Rights Reserved.
>
>
>Amiga Object File Loader V1.00
>68000 Instruction Set
>
>EXTERNAL DEFINITIONS
>
>__main 0000-00    _SysBase 0000-01    _msg 0004-01    _ExecBase 0000-02    
>_DOSBase 0004-02
>
>SECTION 00 "text" 00000050 BYTES
>	  | 0000  48 E7 30 02                    MOVEM.L   D2-D3/A6,-(A7)
>	  | 0004  20 79  00 00 00 00-01          MOVEA.L   01.00000000,A0
>	  | 000A  23 D0  00 00 00 00-02          MOVE.L    (A0),02.00000000
>	  | 0010  43 F9  00 00 00 12-01          LEA       01.00000012,A1
>	  | 0016  70 00                          MOVEQ     #00,D0
>	  | 0018  2C 78 00 04                    MOVEA.L   0004,A6
>	  | 001C  4E AE FD D8                    JSR       FDD8(A6)
>	  | 0020  23 C0  00 00 00 04-02          MOVE.L    D0,02.00000004
>	  | 0026  C1 8E                          EXG       D0,A6
>	  | 0028  4E AE FF C4                    JSR       FFC4(A6)
>	  | 002C  22 00                          MOVE.L    D0,D1
>	  | 002E  41 F9  00 00 00 04-01          LEA       01.00000004,A0
>	  | 0034  24 08                          MOVE.L    A0,D2
>	  | 0036  76 0D                          MOVEQ     #0D,D3
>	  | 0038  4E AE FF D0                    JSR       FFD0(A6)
>	  | 003C  22 4E                          MOVEA.L   A6,A1
>	  | 003E  2C 78 00 04                    MOVEA.L   0004,A6
>	  | 0042  4E AE FE 62                    JSR       FE62(A6)
>	  | 0046  70 00                          MOVEQ     #00,D0
>	  | 0048  4C DF 40 0C                    MOVEM.L   (A7)+,D2-D3/A6
>	  | 004C  4E 75                          RTS
>
>SECTION 01 "data" 00000020 BYTES
>0000 00 00 00 04 48 65 6C 6C 6F 20 57 6F 72 6C 64 0A ....Hello World.
>0010 00 00 64 6F 73 2E 6C 69 62 72 61 72 79 00 00 00 ..dos.library...
>
>SECTION 02 "udata" 00000008 BYTES
>----------
>
>When blinked results in an executable that is 224 bytes long. You can
>probably trim that some. (It doesn't have to return 0). 
>
>--Chuck McManis
>uucp: {anywhere}!sun!cmcmanis>BIX: cmcmanis  ARPAnet: cmcmanis@sun.com
>These opinions are my own and no one elses, but you knew that didn't you.

Hmm.  Well, here's what I finally got to work correctly.  (The problem
was indeed that A4 didn't point to the data segment)

The program code: hello.c
----------
#include <proto/exec.h>
#include <proto/dos.h>

struct DosLibrary *DOSBase;
static char msg[]="Hello, world!\n";

void main()
{
   DOSBase=(struct DosLibrary *) OpenLibrary(DOSNAME,0);
   Write(Output(),msg,sizeof(msg));
   CloseLibrary((struct Library *) DOSBase);
}
----------
<proto/exec.h> will include <exec/types.h> if it hasn't been included
yet, <proto/dos.h> will include <libraries/dosextens.h> (if it hasn't
been) which will include <exec/types.h>, <exec/tasks.h>,
<exec/ports.h>, <exec/libraries.h> and <libraries/dos.h>.
<exec/tasks.h> will include <exec/nodes.h> and <exec/lists.h>.
<exec/lists.h> will include <exec/nodes.h>.  <exec/ports.h> will
include <exec/nodes.h>, <exec/lists.h> and <exec/tasks.h>.
<exec/libraries.h> will include <exec/nodes.h>.  <libraries/dos.h>
will include <exec/types.h>.

Therefore, this:
----------
#include <proto/exec.h>
#include <proto/dos.h>
----------
is equivalent to:
----------
#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/tasks.h>
#include <exec/ports.h>
#include <exec/libraries.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <proto/exec.h>
#include <proto/dos.h>
----------

Compiled with: lc -b -r -v -y hello.c
----------
Lattice AmigaDOS C Compiler, Version 5.0
Copyright (C) 1988 Lattice, Inc.  All rights reserved.

Compiling hello.c
Module size P=00000040 D=0000001C U=00000004

Total files: 1, Compiled OK: 1
----------
(-b for base-relative addressing, -r for pc-relative branches
(probably irrelevant here), -v to disable stack checking, and -y to
load A4 with the data segment at the start of each function.  (I
forgot the -y before so it didn't work.))

Linked with: blink hello.o to hello
----------
Blink - Version 5.0
CopyRight (c) 1988 Lattice, Inc.  All Rights Reserved.


BLINK Complete - Maximum code size = 96 ($00000060) bytes

Final output file size = 168 ($000000a8) bytes
----------

Disassembled with: omd hello.o
[note: this one actually had a copyright symbol.  shown here as (c).]
----------
Lattice AMIGA 68000-68020 OBJ Module Disassembler V5.00
Copyright (c) 1988 Lattice Inc.  All Rights Reserved.


Amiga Object File Loader V1.00
68000 Instruction Set

EXTERNAL DEFINITIONS

_main 0000-00    _DOSBase 0000-02

SECTION 00 "text" 00000040 BYTES
       | 0000  48 E7 30 0A                    MOVEM.L   D2-D3/A4/A6,-(A7)
       | 0004  49 F9  00 00 00 00-XX          LEA       _LinkerDB,A4
       | 000A  43 EC  00 10-01.2              LEA       01.00000010(A4),A1
       | 000E  70 00                          MOVEQ     #00,D0
       | 0010  2C 78 00 04                    MOVEA.L   0004,A6
       | 0014  4E AE FD D8                    JSR       FDD8(A6)
       | 0018  29 40  00 00-02.2              MOVE.L    D0,02.00000000(A4)
       | 001C  C1 8E                          EXG       D0,A6
       | 001E  4E AE FF C4                    JSR       FFC4(A6)
       | 0022  22 00                          MOVE.L    D0,D1
       | 0024  41 EC  00 00-01.2              LEA       01.00000000(A4),A0
       | 0028  24 08                          MOVE.L    A0,D2
       | 002A  76 0F                          MOVEQ     #0F,D3
       | 002C  4E AE FF D0                    JSR       FFD0(A6)
       | 0030  22 4E                          MOVEA.L   A6,A1
       | 0032  2C 78 00 04                    MOVEA.L   0004,A6
       | 0036  4E AE FE 62                    JSR       FE62(A6)
       | 003A  4C DF 50 0C                    MOVEM.L   (A7)+,D2-D3/A4/A6
       | 003E  4E 75                          RTS

SECTION 01 "__MERGED" 0000001C BYTES
0000 48 65 6C 6C 6F 2C 20 77 6F 72 6C 64 21 0A 00 00 Hello, world!...
0010 64 6F 73 2E 6C 69 62 72 61 72 79 00 dos.library.

SECTION 02 "__MERGED" 00000004 BYTES
----------

Listed with: ls -l hello*
----------
-----rwed 89-03-08 21:28:44    1      168 hello
-----rwed 89-03-08 21:27:25    1      268 hello.c
-----rwed 89-03-08 21:28:30    1      296 hello.o
Dirs:0   Files:3    Blocks:3     Bytes:732
----------

Ran with: hello
(well, might as well be consistent...) :-)
----------
Hello, world!
----------

She works.  I think 168 bytes is a pretty decent executable size,
especially considering the first time I tried compiling a "Hello,
world!" program, using Lattice C V3.03, (code was:
"main(){printf("Hello, world!\n");}", compiled with "lc -L hello.c")
the executable was on the order of about 10-11K...

Actually, I left out -O on the compile command line (to run the
optimizer "go" between the passes), and options nodebug (strip
debugging information), smallcode (merge code hunks), and smalldata
(merge data hunks) to blink.  In this example, none of these change
either the object file or the executable.

In summary, it seems that compiling with:

lc -b -r -v -y file.c

and linking with:

blink file.o to file nodebug smallcode smalldata

will get about the smallest executable possible without modifying the
code.  (when using prototypes, defining lib pointers, etc.)

Gee, maybe I ought to post an article on "how to be verbose"...  :-)

Deven
--
------- shadow@pawl.rpi.edu ------- Deven Thomas Corzine ---------------------
Cogito  shadow@acm.rpi.edu          2346 15th Street            Pi-Rho America
ergo    userfxb6@rpitsmts.bitnet    Troy, NY 12180-2306         (518) 272-5847
sum...     In the immortal words of Socrates:  "I drank what?"     ...I think.

toebes@sas.UUCP (John Toebes) (03/17/89)

In article <92710@sun.uucp> cmcmanis@sun.UUCP (Chuck McManis) writes:
>In article <92203@sun.uucp> I wrote:
>> Yes, with Lattice 5.0x it is possible to have nothing but 
>> blink myprog.o to myprog
>This is correct.
>> Essentially, you will have to define your lib pointers internally and use
>> the prototypes for direct linkage to the libraries.
>
>Then in a followup article shadow@pawl.rpi.edu writes:
>> Can you give me a short simple example that will work for this?  Say,
>> the classic "Hello, world!" program?  I tried something like this:
>
>To which I reply "Certainly" and here it is :
>  ... example deleted ...
Actually this question has come up quite frequently and there turns out to
be an even smaller solution that is made possible with 5.0 that has the
additional advantage of being RESIDENTABLE!

#include <exec/types.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <strings.h>

#define MSG(f,s) Write(f,s,strlen(s))

void tinyhello()
{
struct DosLibrary *DOSBase;  /* NOTE it is a local variable */
if ((DOSBase = (struct DosLibrary *)OpenLibrary("dos.library",0)) != NULL)
   {
   MSG(Output(), "Hello World\n");
   CloseLibrary((struct Library *)DOSBase);
   }
}

Compile this with
  LC -cs -v tinyhello
  blink tinyhello.o

Note that if you use the -cs option, the strings will be put into the code
section automatically.  Since the library base for the call is a local 
variable (a trick added under 5.0) and the strings are accessed pc-relative,
there is no need to have A4 set up.  This trick is used in POPCLI 4 and
also in the Avail program on the Lattice diskettes.  Enjoy.

>--Chuck McManis
>uucp: {anywhere}!sun!cmcmanis   BIX: cmcmanis  ARPAnet: cmcmanis@sun.com
>These opinions are my own and no one elses, but you knew that didn't you.