Operating System - OpenVMS
1828342 Members
3279 Online
109976 Solutions
New Discussion

Get function address in BASIC

 
GuentherF
Trusted Contributor

Get function address in BASIC

I am fixing bugs in a 17 year old application written in VAX Basic (oh how deep did I sink). The program passes the address of an external function as:

 

         EXTERNAL INTEGER FUNCTION ast

         CALL sub(..., LOC(astfunc),...)

 

...which procedure sub stores in a data structure.

 

In another function it tries to compare the function address - retrieved from the structure - with the address of the function ast:

 

          SELECT astadr

          CASE LOC(ast)

 

I stepped through the machine code (Itanium/V8.4) and found that astadr had the right address for function ast but the LOC(ast) gives the address of where ast's address is stored. So it never takes this case statement. A comment in the code gave a - desperate - hint that  'something with that ast doesn't work right'. Sure, with this problem it never canceled an outstanding timer AST.

 

I couldn't find a BASIC construct that would return the function address  in the CASE statement. Like a dereference something.

 

I have an idea how to modify the code taking a different approach but out of curiosity...anyone with an idea?

 

Thanks,

Guenther

5 REPLIES 5
H.Becker
Honored Contributor

Re: Get function address in BASIC

I can't really help with Basic, but ... 

>>> address of an external function

On Integrity, the address of a function is the address of a function descriptor, FD, which (ignore the /nonative stuff) consists of two quadwords: the code address and the GP value. There can be official FDs, definitions of functions in an image, and local FDs, references to functions external to an image. Whenever the address of a function is used, the address of the official FD is taken, no matter whether it is within the same image or in another (shareable) image. For compiled and linked code, relocations produced by the compilers and the processing of them by  the linker make sure that this is the case.

 

When debugging the code, you can identify what's stored in the data structure: it should be the address of the official FD, not a code address. The linker creates all the FDs in the short data segment, that is separate from all your (compiler generated) PSECTs.

 

>>> LOC(ast) gives the address of where ast's address is stored

LOC should return the address of the official FD, which may be the case, if I interpret the "ast's address" as code address.

 

On Integrity it is important to have all externals correctly declared, as function/procedure or data. I don't know how you do this in Basic, but the linker should complain if you don't.

 

In the shown code, ast is declared but astfunc is passed to sub. It's not obvious whether this is  a cut&paste problem, or part of the error. 

GuentherF
Trusted Contributor

Re: Get function address in BASIC

Hi Hartmut!

 

Correction to my code sample:

 

         EXTERNAL INTEGER FUNCTION ast

         CALL sub(..., LOC(ast) BY VALUE,...)

 

What puzzles me is that the value received by the called function 'sub' differs from the value returned by LOC(ast) in an assignment. Looks to me like BASIC stored the address of function 'sub' somewhere and returns the pointer to this location. I am not sure if this is a FD where it points to. If I use plain 'ast' w/o LOC() in the assignment it calls that function right away - BUMMER!

 

I think this is a bug in the way LOC() is used by the BASIC compiler. But then maybe not:

 

DBG> exam SC_DO_TIMAST
%DEBUG-I-NOUNIQ, symbol 'SC_DO_TIMAST' is not unique
    data SC_DO_TIMAST\SC_DO_TIMAST\SC_DO_TIMAST
    routine SC_DO_TIMAST\SC_DO_TIMAST

 

It seems there are two entities in use: a data and a function definition for the ast function. Why would that be?

 

Thanks,

Guenther

Craig A Berry
Honored Contributor

Re: Get function address in BASIC

Do a:

 

$ lib/extract=$pdscdef/output=pdscdef.bas sys$library:basic$starlet.tlb

 

and see if it has the clues you need to sort out the procedure descriptors.  There is some discussion in the calling standard:

 

http://h71000.www7.hp.com/doc/82final/5973/5973pro_012.html#index_x_373

 

and elsewhere.

 

I had a simpler version of this problem when we moved to Itanium because we had an AST routine we used for SMG broadcast trapping and were sloppy about whether we considered it to be a subroutine or a function.  As Hartmut said, a function is a different animal on IA64 and you have to declare it as such.  We came by our sloppiness honestly as we were doing exactly what was in the example in the SMG manual at:

 

http://h71000.www7.hp.com/doc/73final/5935/5935pro_015.html#5935disable_broadcast_trapping

 

That example does not work on Itanium without the following changes:

 

$ gdiff -pu trapping.bas;-0 trapping.bas
--- trapping.bas;-0     2009-09-30 10:07:25 -0500
+++ trapping.bas        2009-10-03 18:45:00 -0500
@@ -32,8 +32,8 @@
         !GET_INPUT is the routine which spawns the subprocess
         !-

-        EXTERNAL INTEGER GET_MSG
-        EXTERNAL INTEGER GET_INPUT
+        EXTERNAL INTEGER FUNCTION GET_MSG
+        EXTERNAL INTEGER FUNCTION GET_INPUT

         DECLARE LONG pb_id, ret_status, display_id, display2_id, display3_id, &
                      key_id, key_tab_id, counter
@@ -238,7 +238,7 @@
         !are disabled.
         !-

-        SUB GET_INPUT (paste_id, param, nl_1, nl_2, nl_3, nl_4)
+        FUNCTION INTEGER GET_INPUT (paste_id, param, nl_1, nl_2, nl_3, nl_4)

         MAP (params) LONG disp_info(2), LONG keyboard_info(4), LONG done_flag

@@ -252,8 +252,8 @@
         %INCLUDE "LIB$ROUTINES" %FROM %LIBRARY "SYS$LIBRARY:BASIC$STARLET"
         %INCLUDE "$SMGMSG" %FROM %LIBRARY "SYS$LIBRARY:BASIC$STARLET"

-        EXTERNAL INTEGER GET_MSG
-        EXTERNAL INTEGER GET_INPUT
+        EXTERNAL INTEGER FUNCTION GET_MSG
+        EXTERNAL INTEGER FUNCTION GET_INPUT

         !+
         !Assign to the local variables the values that were stored from
@@ -371,7 +371,7 @@
         done_flag = 1

       Out_of_sub:
-        END SUB
+        END FUNCTION

 30      !+
         !Start of AST routine GET_MSG. This AST is called whenever there
@@ -379,7 +379,7 @@
         !MESSAGES virtual display.
         !-

-        SUB GET_MSG (paste_id, nl_1, nl_2, nl_3, nl_4)
+        FUNCTION INTEGER GET_MSG (paste_id, nl_1, nl_2, nl_3, nl_4)
         DECLARE LONG status1, pasteboard, second_disp
         DECLARE WORD msg_len
         DECLARE STRING msg
@@ -421,6 +421,5 @@
               END IF
         NEXT
     Exitloop:
-        END SUB
-
+        END FUNCTION

 

Guenther seems to be declaring things right, so it's not exactly the same problem we had.  We never had to worry about how to invoke the call-back function since SMG does that.  Hopefully $PDSCDEF will offer something to go on for how to unpack the descriptor.

John Gillings
Honored Contributor

Re: Get function address in BASIC

Guenther,

 

    From what you've posted, you don't really care about the address, what you're really trying to do is compare objects to see if they're the same. For that you just need some kind of "fingerprint" which can be reliably and consistently derived. I'd hide the details. Something like this (note - I've probably got the Basic syntax wrong!):

 

function FingerPrint(rtn)
  perform whatever seems necessary here
  FingerPrint=some manipulation of rtn
end

...

EXTERNAL INTEGER FUNCTION ast

  somestrucure.IDField=FingerPrint(LOC(ast) BY VALUE)

...

  SELECT somestructure.IDField

  CASE FingerPrint(LOC(ast) BY VALUE)


 

  Depending on what you do in FingerPrint, you may find you can call it as FingerPrint(ast). If necessary, implement the fingerprint function in MACRO so you have full control over how the argument is treated.

A crucible of informative mistakes
GuentherF
Trusted Contributor

Using small helper function

Doesn't look like a FD where the LOC() is pointing to. Just a plain LONG.

 

I made a small helper function:

 

        FUNCTION INTEGER SC_RET_FUNC_ADDR( INTEGER ADDR )

        SC_RET_FUNC_ADDR = ADDR

        FUNCTIONEND

 

And use it like this:

 

              case sc_ret_func_addr(loc(sc_do_timast) by value)

/Guenther