[comp.sys.hp] Incremental loading on the HP9000s800

net@tub.UUCP (Oliver Laumann) (09/06/90)

I'm trying to port an application to the HP-PA that dynamically loads
object files into the running program by using "ld -A".  So far I have
been unable to have /bin/ld to anything more useful than printing

    /bin/ld: Internal Error 4027 (Bad symbol address)

What exactly does this mean?

The exact invokation of /bin/ld I used was

    /bin/ld -N -x -A program obj.o -o output -lc

where "program" is the file name of the running program and "obj.o" is
the object file to be loaded (produced by applying "cc -c" to a source
file containing an empty function definition).

I have also tried to omit -N and -x and to explicitly supply an address
(by means of "-R addr" where "addr" is the current program break).

Thanks in advance for any help.  Please e-mail me a copy of your reply;
our news feed is unreliable.

Regards,
--
    Oliver Laumann, Technical University of Berlin, Germany.
    pyramid!tub!net   net@TUB.BITNET   net@tub.cs.tu-berlin.de

mar@hpclmar.HP.COM (Michelle Ruscetta) (09/11/90)

  I did send a reply to Oliver, but for those Netlander's that are interested
  in ld -A usage on the HP 9000s800 here is a copy of my reply:


  Here is a description of how the 'ld -A' option is used:

  The '-A' option to ld was a supported option for HPUX release 3.0 release,
  so I assume that you are on a HPUX 3.0, or later, release.

  The -A options on the series 800 is complicated due to a few hardware
  requirements -- I won't go into the details of why, but I have included
  an an example of how -A must be used on the s800. 

  One problem with your example above is that the -A must be used with the 
  -R and -N options. You must specify an address where the resulting object 
  file will be loaded (read) into memory (-R data_addr).

  In any case, the linker shouldn't be giving you an "Internal Error" response,
  it should be improved to detect improper use of -A, and give a reasonable
  error message.

  The following is a 'canned' ld -A response but it should answer your 
  questions:

  Using -A can be rather tricky, since you need to know a 
  little bit about the a.out object file format, in particular, the fields of
  the HPUX auxiliary header record found in all a.out files. 
  The resulting object is not loaded in, but rather is read into an area 
  of memory allocated by the main program.

  The -A option is used when you want to dynamically link a file 
  from an existing 'main' program. The link command is called from within
  the main program (using 'system()' or 'exec()'), using the main program
  as the basefile (ld -A basefile ...) so that any symbols defined in the 
  basefile will be used to resolve references from the file which is being 
  dynamically linked (for example if you want to make calls from a dynamically 
  linked function to routines which are defined in the main program). 
  Normally, space is allocated in the main program's data area using malloc(),
  but since you don't know the size of the executable file that you will be
  placing into the data area, the malloc size is just a guess.
  The address returned from malloc must be page-aligned, and then can be used
  in the link (ld -A basefile -R data_address ...) command to inform the linker
  to link the file using that address for code placement. The link command 
  should also specify the -N option to tell the linker to place the data
  immediately following the code, since we want code and data to be contiguous
  when we read it into the main program's data area.
  The executable file resulting from the link can then be read into the space 
  allocated using information from the HPUX auxiliary header record, such as 
  size of text, the file location of the program entry point, and the size of 
  data. The executable file is read into data, and then can be executed
  by dereferencing a function pointer which has been set to the address of
  the entry point (found in the HPUX auliary header).
  There are other details to be taken care of as well, such as doing a memset
  for BSS (to initialize all of bss to zero), since the loader (exec()) usually
  does that for you, and we are bypassing the loader. 

  Example program using ld -A:

main()
{
     char *x; 
     int (*funcptr)();

     x = malloc(some_large_size);

     /* page align since ld expects page-align value for -R */ 
     page_align(x);

     /* Here we want to link in the relocatable object 'dynfunc.o', producing*/
     /* a bound executable 'dynfunc', which can then be read into memory.    */

     /* get the value of 'x' into the ld command that we are going to call */
     sprintf(cmd_buf, "ld -A basefile -R %x -N dynfunc.o -o dynfunc -e foo",x);
     
     /* call the linker to link the file */
     system(cmd_buf);

     /* now we open the resulting executable for reading */
     fileptr = fopen("dynfunc", "r");

     /* seek to and read the auxiliary header record 
     fseek(fileptr, sizeof(struct header), 0);
     fread(&filhdr, sizeof(filhdr), 1, fileptr);

     /* determine the size of the executable -- and see if we allocated enough
        space
     */
    dynfunc_size = filhdr.exec_dmem + filhdr.exec_bsize - filhdr.exec_tmem; 
    if(dynfunc_size > some_large_size) {
           /* do something -- either error, or realloc and relink */
    }

    /* seek to and read in the text area of the dynamically linked file */
    fseek(f, filhdr.exec_tfile, 0);
    fread(filhdr.exec_tmem, filhdr.exec_tsize, 1, f);

    /* seek to and read in the data area of the dynamically linked file */
    fseek(f, filhdr.exec_dfile, 0);
    fread(filhdr.exec_dmem, filhdr.exec_dsize, 1, f);

    /* init the BSS area to zero */
    memset(filhdr.exec_dmem+filhdr.exec_dsize, 0, filhdr.exec_bsize);

    /* set the function ptr to the entry point of the dynamically linked file */
    funcptr = (int (*)()) (filhdr.exec_entry);

    /* flush the data and instruction caches -- not this must be done on the
       series 800 ! -- see the flush_cache assembly routine below */

    flush_cache();

    /* call the dynamically linked function */
    (* funcptr)();
} /* END OF PROGRAM */


The following is the routine that can be used to flush the caches courtesy
of Cary Coutant:

;
; Routine to flush and synchronize data and instruction caches
; for dynamic loading
;
; Copyright Hewlett-Packard Co. 1985
;

	.code

; flush_cache(addr, len) - executes FDC and FIC instructions for every cache
; line in the text region given by the starting address in arg0 and
; the length in arg1.  When done, it executes a SYNC instruction and
; the seven NOPs required to assure that the cache has been flushed.
;
; Assumption:  the cache line size must be at least 16 bytes.

	.proc
	.callinfo
	.export	flush_cache,entry
flush_cache
	.enter
	ldsid	(0,%arg0),%r1
	mtsp	%r1,%sr0
	ldo	-1(%arg1),%arg1
	fdc	%arg1(0,%arg0)
loop	fic	%arg1(%sr0,%arg0)
	addib,>,n	-16,%arg1,loop	; decrement by cache line size
	fdc	%arg1(0,%arg0)

	; flush first word at addr, to handle arbitrary cache line boundary
	fdc	0(0,%arg0)
	fic	0(%sr0,%arg0)
	sync
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	.leave
	.procend

	.end

mar@hpclmar.HP.COM (Michelle Ruscetta) (09/11/90)

  The cache-flushing routine in the precious response was an OLD copy.
  Please use the following (new) cache-flushing routine. (The old 
  routine in the previous posting works on machines with a unfied cache,
  but will not work on machines which have a separate data & instruction
  cache.

;
; Routine to flush and synchronize data and instruction caches
; for dynamic loading
;
; Copyright Hewlett-Packard Co. 1985
;

	.code

; flush_cache(addr, len) - executes FDC and FIC instructions for every cache
; line in the text region given by the starting address in arg0 and
; the length in arg1.  When done, it executes a SYNC instruction and
; the seven NOPs required to assure that the cache has been flushed.
;
; Assumption:  the cache line size must be at least 16 bytes.

	.proc
	.callinfo
	.export	flush_cache,entry
flush_cache
	.enter
	ldsid	(0,%arg0),%r1
	mtsp	%r1,%sr0
	ldo	-1(%arg1),%arg1
	copy	%arg0,%arg2
	copy	%arg1,%arg3

	fdc	%arg1(0,%arg0)
loop1	addib,>,n	-16,%arg1,loop1	; decrement by cache line size
	fdc	%arg1(0,%arg0)
	; flush first word at addr, to handle arbitrary cache line boundary
	fdc	0(0,%arg0)
	sync

	fic	%arg3(%sr0,%arg2)
loop2	addib,>,n	-16,%arg3,loop2	; decrement by cache line size
	fic	%arg3(%sr0,%arg2)
	; flush first word at addr, to handle arbitrary cache line boundary
	fic	0(%sr0,%arg2)

	sync
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	.leave
	.procend

	.end

mar@hpclmar.HP.COM (Michelle Ruscetta) (09/12/90)

/ hpclmar:comp.sys.hp / mar@hpclmar.HP.COM (Michelle Ruscetta) /  3:06 pm  Sep 10, 1990 /

  A netlander has graciously pointed out another small error in my previous
  example. The flush_cache call in the example program is missing the 
  parameters; the 'flush_cache();' call should be changed to:

	flush_cache(filhdr.exec_tmem, filhdr.exec_tsize);
 
-- Michelle Ruscetta