Operating System - OpenVMS
cancel
Showing results for 
Search instead for 
Did you mean: 

C++ v. LIB$INITIALIZE, DECC$ARGV_PARSE_STYLE

Steven Schweda
Honored Contributor

C++ v. LIB$INITIALIZE, DECC$ARGV_PARSE_STYLE

I have a working scheme for using
LIB$INITIALIZE in a C program to set
DECC$ARGV_PARSE_STYLE (among other things).
DECC$ARGV_PARSE_STYLE must be set early to be
effective, hence LIB$INITIALIZE.

I have not found a working scheme to do the
same thing in a (non-trivial) C++ program. I
can use an explicit LIB$INITIALIZE set-up,as
described in "HP C++ V7.3 Release Notes for
OpenVMS Industry Standard 64 (I64) for
IntegrityServers":

http://www.openvms.compaq.com/commercial/cplus/i64_doc/rni64.html

or C++ constructor initialization (whatever
that is), to get my initialization function
called, but not in time to make its setting of
DECC$ARGV_PARSE_STYLE effective. Apparently,
the C++ initialization scheme, which also uses
LIB$INITIALIZE, does its command-line parsing
before it gets to my
DECC$ARGV_PARSE_STYLE-setting code.

Is there a way (preferably easy, simple, and
fool-proof) to get my
DECC$ARGV_PARSE_STYLE-setting code executed
before it's too late? (And if not, why not?)
10 REPLIES
Martin Vorlaender
Honored Contributor

Re: C++ v. LIB$INITIALIZE, DECC$ARGV_PARSE_STYLE

Steven,

I'd think the earliest user code executed in a C++ program would be a global nameless static block. Haven't tried it, though.

http://www.openvms.compaq.com/commercial/cplus/docs/ugv_impl.html#stat_obj_sec

says the initialization of nonlocal static objects uses lib$initialize.

cu,
Martin
Hoff
Honored Contributor

Re: C++ v. LIB$INITIALIZE, DECC$ARGV_PARSE_STYLE

I can think of a couple of ways to deal with this, including hooking into the image activator sequence pretending to be an almost-null debugger.

Probably the cleanest way would be to read off the current setting and (if ill-suited) display a diagnostic and exit.

Various of the C-level parsing of commands occurs in the support "behind" the main() call and before your application code gets control; it might be feasible to sneak in here with a not-main main that then calls VAXC$CRTL_INIT. Basically, don't be a C++ main program until you need to be.

If this setting is getting tagged and the setting changed in another LIB$INITIALIZE psect, I don't know of a way to re-order that sequencing off-hand.

Craig A Berry
Honored Contributor

Re: C++ v. LIB$INITIALIZE, DECC$ARGV_PARSE_STYLE

My somewhat dim understanding of this is that LIB$INITIALIZE is a psect containing any number of routines, and the image activator will call all of them at start-up time. Which is peachy except for the fact that only one of them is yours and you have no way of knowing (and possibly no way of controlling) where in the sequence it will get called. So maybe the real question is how does the image activator decide what order to call them in. Or maybe it's the linker that determines the order in which they'll be called.

If the linker decides the order, maybe poking around with ANALYZE/IMAGE would result in more than just a headache, and maybe explicitly pulling in the module containing your start-up routine in the link command before referencing any libraries would make a difference.

But that's just desperate guessing.

John Gillings
Honored Contributor

Re: C++ v. LIB$INITIALIZE, DECC$ARGV_PARSE_STYLE

Steven,

I seem to remember this is an arms race to see who can add the most $ signs to the beginning of their symbols. Experiment suggests this is incorrect.

Try this experiment, it shows the routines are called in link order:

main.mar
.title MAIN
.EXTRN LIB$INITIALIZE
.PSECT LIB$INITIALIZE,NOPIC,CON,REL,GBL,NOSHR,NOEXE,NOWRT,LONG
.LONG MAIN_INIT

.psect data,rd,wrt,noexe
i: .LONG 0
tab: .LONG 0[10]
.LONG 0
main: .ASCID /MAIN/
.psect code,rd,nowrt,exe
.entry stamp,^M
MOVL i,R2
MOVAB @4(AP),tab[r2]
INCL i
RET
.entry start,^M
CLRL R2
loop: CMPL R2,i
BGEQ fin
PUSHL tab[r2]
BEQL fin
CALLS #1,G^LIB$PUT_OUTPUT
INCL R2
BRB loop
fin: MOVL #1,R0
RET
.ENTRY MAIN_INIT,^M<>
PUSHAB main
CALLS #1,stamp
RET

.END start

a.mar
.title A
.EXTRN LIB$INITIALIZE
.PSECT LIB$INITIALIZE,NOPIC,CON,REL,GBL,NOSHR,NOEXE,NOWRT,LONG
.LONG A_INIT
.psect data,rd,wrt,noexe
name: .ASCID /A/
.psect code,rd,nowrt,exe
.ENTRY A_INIT,^M<>
PUSHAB name
CALLS #1,stamp
RET
.END

b.mar
.title B
.EXTRN LIB$INITIALIZE
.PSECT LIB$INITIALIZE,NOPIC,CON,REL,GBL,NOSHR,NOEXE,NOWRT,LONG
.LONG B_INIT
.psect data,rd,wrt,noexe
name: .ASCID /B/
.psect code,rd,nowrt,exe
.ENTRY B_INIT,^M<>
PUSHAB name
CALLS #1,stamp
RET
.END

$ link main,a,b
$ run main
MAIN
A
B
$ link main,b,a
$ run main
MAIN
B
A
$ link a,b,main
$ run a
A
B
MAIN

Check the link map to see the order.

What may be happening is your call to DECC$ARGV_PARSE_STYLE, or perhaps just the routine prolog is triggering a first time flag mechanism that's resulting in the C++ init routine being called, effectively FROM your routine, rather than some mechanism where they push themselves higher up the queue.

Perhaps you should write your initializer routine in a language other than C++?
A crucible of informative mistakes
Martin Vorlaender
Honored Contributor

Re: C++ v. LIB$INITIALIZE, DECC$ARGV_PARSE_STYLE

Okay, so the "nameless" crept in from Java - sorry. Using the attached test program I tried to prove the point WRT to the "global static objects". And indeed:

$ cxx/debug/noopt test
$ link/debug test
$ mcr []test HALLO

OpenVMS I64 Debug64 Version V8.3-014

%DEBUG-I-INITIAL, Language: C++, Module: TEST
%DEBUG-I-NOTATMAIN, Type GO to reach MAIN program

DBG> set break INIT::INIT
DBG> g
break at routine INIT::INIT
11066: show_features("INIT");
DBG> sho call
module name routine name line
*TEST INIT 11066
*TEST $complete$INIT
11065
*TEST __init_$E 11070
MAIN
LIB$INITIALIZE

DBG>

But unfortunately, this is only after command line parsing:

INIT
Feature DECC$ARGV_PARSE_STYLE (6): 0 (default)
INIT after set
Feature DECC$ARGV_PARSE_STYLE (6): 1 (set by decc$feature*)
main
Feature DECC$ARGV_PARSE_STYLE (6): 1 (set by decc$feature*)
arg1: hallo

cu,
Martin
Martin Vorlaender
Honored Contributor

Re: C++ v. LIB$INITIALIZE, DECC$ARGV_PARSE_STYLE

...and here's the attachment - sorry.
x2084
Trusted Contributor

Re: C++ v. LIB$INITIALIZE, DECC$ARGV_PARSE_STYLE

The lib$initialize mechanism is more or less an array with function pointers. Such an array can be in any image, the main (aka executable) image and in all shareable images. The init code of all the shareable images is run before the init code of the main image. The order of the init array entries determines the order of calling init
routines. The order of shareable image initializarion depends on the the activation of the shareable image. That depends on the link order and the dependencies within the shareable image list.

It looks like the C++ command line parsing is done in init code of a shareable image.

Create your own shareable image with init code to set the logical..Make sure it does NOT depend on any C++ shareable image. Link your main with an option file and name all the C++ shareable images before your init image. This should convince the image activator to activate your shareable first and run your init code before all the C++ init code.

>>> including hooking into the image activator sequence pretending to be an almost-null debugger.

Please note, on I64 the linker does not support user supplied, aka linked-in debuggers. That means you need an $ DEFINE LIB$DEBUG mumblemumble
Craig A Berry
Honored Contributor

Re: C++ v. LIB$INITIALIZE, DECC$ARGV_PARSE_STYLE

I'm reviving this old thread because I've never seen a complete working answer and I've been trying to solve the same problem.  After reading through the post by x2084 a few times it finally started to click and I've attached a simple example that seems to do the trick.  The critical statement in x2084's post was, "The init code of all the shareable images is run before the init code of the main image." 

 

The zip attachment contains 3 files:

 

$ unzip -l vms_test_init.zip
Archive:  D0:[craig]vms_test_init.zip;1
  Length      Date    Time    Name
---------  ---------- -----   ----
     1226  06-08-2012 15:26   VMS_TEST_INIT.COM
      129  06-08-2012 14:25   VMS_TEST_INIT.C
      928  06-08-2012 14:24   VMS_INIT.C
---------                     -------
     2283                     3 files

 

vms_init.c is a nearly verbatim copy of the LIB$INITIALIZE example from the C++ release notes but it adds a simple initialization function that displays the value of DECC$ARGV_PARSE_STYLE, sets it, and then displays it again.  vms_test_init.c is a dead-simple main program that displays the first argument passed to it on the command line.

 

vms_test_init.com compiles, links, and runs the program, but it builds it two different ways: once as a single image, and once with the init code in a separate shareable image.  Only the second way gets the LIB$INITIALIZE psect visible to the image activator soon enough to do any good.

 

Here is the command procedure inline for those who are merely curious and don't want to download the zip file:

 

$ type vms_test_init.com
$! Compile the main program and init module.
$!
$ cxx/version
$ cxx vms_test_init.c
$ cxx vms_init.c
$!
$! Put the LIB$INITIALIZE psect in a shareable image and 
$! export the psect so the image activator can see it. Then
$! link our program to the shareable image.
$!
$ cxxlink/share=vms_init_shr.exe vms_init.obj, sys$input:/option
PSECT_ATTR=LIB$INITIALIZE,GBL,NOEXE,NOWRT,NOSHR,LONG
$ cxxlink/exe=worky.exe vms_test_init, sys$input:/options
vms_init_shr/share
$!
$ define/nolog vms_init_shr sys$disk:[]vms_init_shr.exe
$!
$! must be using extended parse for DECC$ARGV_PARSE_STYLE to work
$ set process/parse=extended
$!
$! We're testing the ability to set it ourselves, so don't cheat
$! and have it in the environment.
$!
$ if f$trnlnm("DECC$ARGV_PARSE_STYLE") .nes. "" then deassign DECC$ARGV_PARSE_STYLE
$!
$ write sys$output "Should see init calls and mixed-case Foo"
$ mcr []worky Foo
$!
$! Now just do a standard link into a single image, which
$! doesn't get LIB$INITIALIZE noticed by the image activator
$! soon enough to do any good.  Foo will get lower-cased.
$!
$ write sys$output ""
$ write sys$output "Will see init calls but lower-cased Foo"
$ cxxlink/exe=noworky.exe vms_test_init, vms_init
$ mcr []noworky Foo
$!
$ exit

 

And here is what it looks like when you run it, where worky.exe, linked against the shareable image, preserves the case of the Foo argument, but noworky.exe, where all the code is in a single image, lower cases Foo despite having triggered the init code (it's just happening too late).

 

$ @vms_test_init
HP C++ V7.4-004 on OpenVMS IA64 V8.4    
Should see init calls and mixed-case Foo
 ---------- C RTL Feature  Name ----------   Cur    Def    Min    Max Ini
 DECC$ARGV_PARSE_STYLE                         0      0      0      1  -1
 ---------- C RTL Feature  Name ----------   Cur    Def    Min    Max Ini
 DECC$ARGV_PARSE_STYLE                         1      1      0      1  -1
DSA0:[craig]worky.exe;1: argv[1] looks like Foo to me

Will see init calls but lower-cased Foo
 ---------- C RTL Feature  Name ----------   Cur    Def    Min    Max Ini
 DECC$ARGV_PARSE_STYLE                         0      0      0      1  -1
 ---------- C RTL Feature  Name ----------   Cur    Def    Min    Max Ini
 DECC$ARGV_PARSE_STYLE                         1      1      0      1  -1
dsa0:[craig]noworky.exe;1: argv[1] looks like foo to me

 

One odd thing is that the single-image version works fine with:

 

$ cxx/vers
Compaq C++ V6.5-046 for OpenVMS Alpha V8.3

 

Don't know if that's CXX 6.x versus 7.x or Alpha versus Itanium or what.  Also, on Alpha, cxxlink is required or I get:

 

$ link/exe=noworky.exe vms_test_init, vms_init
%LINK-W-NUDFSYMS, 1 undefined symbol:
%LINK-I-UDFSYM, 	CXXL$V60_MAIN_DISPATCH 
%LINK-W-USEUNDEF, undefined symbol CXXL$V60_MAIN_DISPATCH referenced
	in psect $LINK$ offset %X00000020
	in module VMS_TEST_INIT file D0:[CRAIG]VMS_TEST_INIT.OBJ;4

 

but plain old link works fine on Itanium.  Hmm.

 

And finally, I should note that I'm not actually doing C++.  I'm just using the C++ compiler as an eccentric C compiler with interesting warnings and a purportedly better code generation capability on Itanium.  If one were actually doing C++, and especially if one had one's own shareable images for other purposes than the init code, then one might well need to pay more attention than I did to x2084's other comment about "link order and dependencies."

 

 

 

H.Becker
Honored Contributor

Re: C++ v. LIB$INITIALIZE, DECC$ARGV_PARSE_STYLE

>>> 

$! export the psect so the image activator can see it. Then
$! link our program to the shareable image.
$!
$ cxxlink/share=vms_init_shr.exe vms_init.obj, sys$input:/option
PSECT_ATTR=LIB$INITIALIZE,GBL,NOEXE,NOWRT,NOSHR,LONG
 
The image activator doesn't care about any PSECT: you don't have to export the PSECT and you don't do it with this linker option, anyway. However, for the linker and for the image initialization mechanism to work, you have to match the PSECT attributes of the LIB$INITIALIZE PSECT from the LIB$INITIALIZE object module. Whether you do this with language contructs or with a linker option is up to you. The linker has to collect your init array, containing the function pointer for set_parse_style into the very same image segment (I64) as the corresponding PSECTs from the LIB$INITIALIZE object module. Hey, the linker can produce a map showing all the gory details :-)
 
Btw, I would make set_parse_style static, here, remove all the __cplusplus stuff and compile the source with the C compiler, at least for the shareable image example. Then you can make vmsinit_unused_global_1 static, too and you don't have to worry about C++ using LIB$INITALIZE itself. Not to mention, that you no longer need cxxlink, on any platform.
 
>>> One odd thing is that the single-image version works fine with:
 
Yes, C++ on I64 is totally different from C++ on Alpha. I don't know why on I64 the command line parsing was moved into the init code of the C++ shareable image. There may be good reasons. The C++ code gerenator is different from the other compilers, which are GEM based. Whether the code is better or not, beats me. The other big difference is how templates are handled. On I64 this is done - more or less - by the linker. And yes, that's the reason why the plain "new" linker on I64 works. In other words, the main purpose of cxxlink on I64 is the compatibility with your build procedures from/on Alpha.
 
>>> And finally, I should note that I'm not actually doing C++.  I'm just using the C++ compiler as an eccentric C compiler with interesting warnings and a purportedly better code generation capability on Itanium.  If one were actually doing C++, and especially if one had one's own shareable images for other purposes than the init code, then one might well need to pay more attention than I did to x2084's other comment about "link order and dependencies."
 
I would keep just the init code for setting the DECC, aka CRTL features in one single C module and link that into a shareable image to avoid other dependencies.
 
x2084 told me to post his greetings :-)
Craig A Berry
Honored Contributor

Re: C++ v. LIB$INITIALIZE, DECC$ARGV_PARSE_STYLE

----
$! export the psect so the image activator can see it. Then
$! link our program to the shareable image.
$!
$ cxxlink/share=vms_init_shr.exe vms_init.obj, sys$input:/option
PSECT_ATTR=LIB$INITIALIZE,GBL,NOEXE,NOWRT,NOSHR,LONG

The image activator doesn't care about any PSECT: you don't have to export the PSECT and you don't do it with this linker option, anyway. However, for the linker and for the image initialization mechanism to work, you have to match the PSECT attributes of the LIB$INITIALIZE PSECT from the LIB$INITIALIZE object module.
----
Hmm.  Retesting just now it's true: I don't need that linker option.  For a more complicated example, I did appear to need it, which probably means I had some attributes wrong in the code or something.   It doesn't seem to do any harm, though.
----
Btw, I would make set_parse_style static, here, remove all the __cplusplus stuff and compile the source with the C compiler, at least for the shareable image example. Then you can make vmsinit_unused_global_1 static, too and you don't have to worry about C++ using LIB$INITALIZE itself. Not to mention, that you no longer need cxxlink, on any platform.
----
This might make sense if I were an ISV building a package that would only be built by me.  But it would defeat the whole purpose of the exercise, which is to provide common code that works the same for someone who has only the C++ compiler OR only the C compiler and might be using whatever they have to build open source software.  People who have to pay real money for compilers may only have one or the other, or may have to jump through hoops to get someone to install the one they don't have.  And build procedures would be more complicated to support building some parts of a package with one compiler and some parts with another. 
I don't know which of Steven's projects caused him to start this thread.  What I'm working on is getting Perl to where it can be built with the C++ compiler.  It's almost there, once I figure out what to do about globadef/globalref, which I'll likely open a new thread about one of these days.
----
x2084 told me to post his greetings :-)
----
I had a feeling it might be you but I wasn't going to out you :-).  Thanks for the help.  Once one hits the limits of the sometimes sparse documentation, there is no substitute for finding someone who actually knows how it works, and I appreciate your taking the time.