Discussion:
Fun trick
Add Reply
Arne Vajhøj
2025-01-08 01:58:29 UTC
Reply
Permalink
During some troubleshooting over at VSI forum hb told me that
running a shareable image will execute LIB$INITIALIZE
functions.

That made me write this:

$ type lib.pas
[inherit('sys$library:pascal$lib_routines', 'sys$library:starlet')]
module lib(input, output);

[global]
procedure check;

var
imgnam : varying [1024] of char;

begin
lib$getjpi(item_code := jpi$_imagname, resultant_string :=
imgnam.body, resultant_length := imgnam.length);
if index(imgnam, ']libshr.EXE') > 0 then begin
writeln('This is a shareable image to link against not run');
$exit(ss$_normal);
end;
end;

[global]
procedure say;

begin
writeln('Hi');
end;

end.
$ type prg.pas
program prg(input,output);

[external]
procedure say; external;

begin
say;
end.
$ type trick.mar
.title trick
.extrn lib$initialize
.psect lib$initialize long,nopic,con,gbl,noshr,noexe,nowrt
.address check
.end
$ macro trick
$ pas lib
$ link/share=libshr lib + trick + sys$input/opt
SYMBOL_VECTOR=(say=PROCEDURE)
$
$ define/nolog libshr sys$disk:[]libshr
$ pas prg
$ link prg + sys$input/opt
libshr/share
$
$ run prg
Hi
$ run libshr
This is a shareable image to link against not run

:-)

Arne
Lawrence D'Oliveiro
2025-01-08 02:33:07 UTC
Reply
Permalink
During some troubleshooting over at VSI forum hb told me that running a
shareable image will execute LIB$INITIALIZE functions.
Wonder why shareable images had that .EXE file extension, eh ...
John Reagan
2025-01-08 17:37:44 UTC
Reply
Permalink
Post by Arne Vajhøj
During some troubleshooting over at VSI forum hb told me that
running a shareable image will execute LIB$INITIALIZE
functions.
$ type lib.pas
[inherit('sys$library:pascal$lib_routines', 'sys$library:starlet')]
module lib(input, output);
[global]
procedure check;
var
   imgnam : varying [1024] of char;
begin
   lib$getjpi(item_code := jpi$_imagname, resultant_string :=
imgnam.body, resultant_length := imgnam.length);
   if index(imgnam, ']libshr.EXE') > 0 then begin
      writeln('This is a shareable image to link against not run');
      $exit(ss$_normal);
   end;
end;
[global]
procedure say;
begin
   writeln('Hi');
end;
end.
$ type prg.pas
program prg(input,output);
[external]
procedure say; external;
begin
   say;
end.
$ type trick.mar
        .title  trick
        .extrn  lib$initialize
        .psect  lib$initialize long,nopic,con,gbl,noshr,noexe,nowrt
        .address check
        .end
$ macro trick
$ pas lib
$ link/share=libshr lib + trick + sys$input/opt
SYMBOL_VECTOR=(say=PROCEDURE)
$
$ define/nolog libshr sys$disk:[]libshr
$ pas prg
$ link prg + sys$input/opt
libshr/share
$
$ run prg
Hi
$ run libshr
This is a shareable image to link against not run
:-)
Arne
Pascal has an [INITIALIZE] attribute that you can put on the PROCEDURE
CHECK and the compiler should do the contribution to the LIB$INITIALIZE
PSECT.

And you should include LIB$INITIALIZE (the code module, not the data
PSECT) from STARLET when linking. The x86 linker will do that for you,
but the Alpha and Itanium linkers do not.
Arne Vajhøj
2025-01-08 19:36:13 UTC
Reply
Permalink
Post by John Reagan
Post by Arne Vajhøj
During some troubleshooting over at VSI forum hb told me that
running a shareable image will execute LIB$INITIALIZE
functions.
...
Post by John Reagan
Pascal has an [INITIALIZE] attribute that you can put on the PROCEDURE
CHECK and the compiler should do the contribution to the LIB$INITIALIZE
PSECT.
And you should include LIB$INITIALIZE (the code module, not the data
PSECT) from STARLET when linking.  The x86 linker will do that for you,
but the Alpha and Itanium linkers do not.
Zweite Sehr Verbesserte Ausgabe:

$ type lib.pas
[inherit('sys$library:pascal$lib_routines', 'sys$library:starlet')]
module lib(input, output);

[initialize]
procedure check;

var
imgnam : varying [1024] of char;

begin
lib$getjpi(item_code := jpi$_imagname, resultant_string :=
imgnam.body, resultant_length := imgnam.length);
if index(imgnam, ']libshr.EXE') > 0 then begin
writeln('This is a shareable image to link against not run');
$exit(ss$_normal);
end;
end;

[global]
procedure say;

begin
writeln('Hi');
end;

end.
$ type prg.pas
program prg(input,output);

[external]
procedure say; external;

begin
say;
end.
$ pas lib
$ link/share=libshr lib + sys$library:starlet/lib/incl=lib$initialize +
sys$input/opt
SYMBOL_VECTOR=(say=PROCEDURE)
$
$ define/nolog libshr sys$disk:[]libshr
$ pas prg
$ link prg + sys$input/opt
libshr/share
$
$ run prg
Hi
$ run libshr
This is a shareable image to link against not run

Arne
John Reagan
2025-01-08 20:42:07 UTC
Reply
Permalink
Post by Arne Vajhøj
Post by John Reagan
Post by Arne Vajhøj
During some troubleshooting over at VSI forum hb told me that
running a shareable image will execute LIB$INITIALIZE
functions.
...
Post by John Reagan
Pascal has an [INITIALIZE] attribute that you can put on the PROCEDURE
CHECK and the compiler should do the contribution to the
LIB$INITIALIZE PSECT.
And you should include LIB$INITIALIZE (the code module, not the data
PSECT) from STARLET when linking.  The x86 linker will do that for
you, but the Alpha and Itanium linkers do not.
$ type lib.pas
[inherit('sys$library:pascal$lib_routines', 'sys$library:starlet')]
module lib(input, output);
[initialize]
procedure check;
var
   imgnam : varying [1024] of char;
begin
   lib$getjpi(item_code := jpi$_imagname, resultant_string :=
imgnam.body, resultant_length := imgnam.length);
   if index(imgnam, ']libshr.EXE') > 0 then begin
      writeln('This is a shareable image to link against not run');
      $exit(ss$_normal);
   end;
end;
[global]
procedure say;
begin
   writeln('Hi');
end;
end.
$ type prg.pas
program prg(input,output);
[external]
procedure say; external;
begin
   say;
end.
$ pas lib
$ link/share=libshr lib + sys$library:starlet/lib/incl=lib$initialize +
sys$input/opt
SYMBOL_VECTOR=(say=PROCEDURE)
$
$ define/nolog libshr sys$disk:[]libshr
$ pas prg
$ link prg + sys$input/opt
libshr/share
$
$ run prg
Hi
$ run libshr
This is a shareable image to link against not run
Arne
And the compiler also supports the Extended Pascal TO BEGIN DO and TO
END DO statements. TO BEGIN DO is just the same as [INITIALIZE] but
allows any statement. TO END DO is an exit handler (registered with an
initialization routine).
Arne Vajhøj
2025-01-08 21:32:41 UTC
Reply
Permalink
Post by John Reagan
And the compiler also supports the Extended Pascal TO BEGIN DO and TO
END DO statements.  TO BEGIN DO is just the same as [INITIALIZE] but
allows any statement.  TO END DO is an exit handler (registered with an
initialization routine).
$ type prg.pas
program prg(input,output);

[external]
procedure say; external;

begin
writeln('2');
say;
writeln('4');
end.
$ type lib.pas
module lib(input, output);

[global]
procedure say;

begin
writeln('3');
end;

to begin do writeln('1');
to end do writeln('5');

end.
$ pas lib
$ link/share=libshr lib + sys$library:starlet/lib/incl=lib$initialize +
sys$input/opt
SYMBOL_VECTOR=(say=PROCEDURE)
$
$ define/nolog libshr sys$disk:[]libshr
$ pas prg
$ link prg + sys$input/opt
libshr/share
$
$ run prg
1
2
3
4
5

Arne
Arne Vajhøj
2025-01-08 21:37:21 UTC
Reply
Permalink
Post by Arne Vajhøj
Post by John Reagan
And the compiler also supports the Extended Pascal TO BEGIN DO and TO
END DO statements.  TO BEGIN DO is just the same as [INITIALIZE] but
allows any statement.  TO END DO is an exit handler (registered with
an initialization routine).
$ type prg.pas
program prg(input,output);
[external]
procedure say; external;
begin
    writeln('2');
    say;
    writeln('4');
end.
$ type lib.pas
module lib(input, output);
[global]
procedure say;
begin
    writeln('3');
end;
to begin do writeln('1');
to end do writeln('5');
end.
$ pas lib
$ link/share=libshr lib + sys$library:starlet/lib/incl=lib$initialize +
sys$input/opt
SYMBOL_VECTOR=(say=PROCEDURE)
$
$ define/nolog libshr sys$disk:[]libshr
$ pas prg
$ link prg + sys$input/opt
libshr/share
$
$ run prg
1
2
3
4
5
Or ditching the shareable image that are no longer relevant:

$ type prg2.pas
[inherit('lib2')]
program prg2(input,output);

begin
writeln('2');
say;
writeln('4');
end.
$ type lib2.pas
module lib2(input, output);

procedure say;

begin
writeln('3');
end;

to begin do writeln('1');
to end do writeln('5');

end.
$ pas/env lib2
$ pas prg2
$ link prg2 + lib2
$ run prg2
1
2
3
4
5

Arne
Arne Vajhøj
2025-01-08 21:38:51 UTC
Reply
Permalink
Post by John Reagan
And the compiler also supports the Extended Pascal TO BEGIN DO and TO
END DO statements.  TO BEGIN DO is just the same as [INITIALIZE] but
allows any statement.  TO END DO is an exit handler (registered with an
initialization routine).
The compiler tell me that those two are only valid in modules
not in programs.

They are probably most useful for modules, but why not allow
them for programs?

Arne
hb0815
2025-01-08 22:11:46 UTC
Reply
Permalink
Post by Arne Vajhøj
During some troubleshooting over at VSI forum hb told me that
running a shareable image will execute LIB$INITIALIZE
functions.
Yeah, but ... You do not need init code for this.
Arne Vajhøj
2025-01-08 22:20:50 UTC
Reply
Permalink
Post by hb0815
Post by Arne Vajhøj
During some troubleshooting over at VSI forum hb told me that
running a shareable image will execute LIB$INITIALIZE
functions.
Yeah, but ... You do not need init code for this.
Oh.

Can one get a transfer address into a shareable image?

Arne
hb0815
2025-01-08 22:31:03 UTC
Reply
Permalink
Post by Arne Vajhøj
Post by hb0815
Post by Arne Vajhøj
During some troubleshooting over at VSI forum hb told me that
running a shareable image will execute LIB$INITIALIZE
functions.
Yeah, but ... You do not need init code for this.
Oh.
Can one get a transfer address into a shareable image?
Arne
$ sh sys/noproc/full
OpenVMS V8.4-2L2 on node EISNER 8-JAN-2025 17:29:41.07 Uptime 3
22:58:56
AlphaServer ES40
$
$ ty s.c
#include <stdio.h>
static int firstFunctionInThisModuleBecomesTheEntryPoint () {
printf ("It's the shareable, stupid!\n");
return 1;
}
int foo () {
printf ("This is %s\n", __func__);
return 1;
}
$ cc s/stand=rel
$ link/share s,tt:/opt ! symbol_v=(foo=proc)
symbol_v=(foo=proc)
Exit
$ r s
It's the shareable, stupid!
$
Arne Vajhøj
2025-01-09 01:00:59 UTC
Reply
Permalink
Post by hb0815
Post by Arne Vajhøj
Post by hb0815
Post by Arne Vajhøj
During some troubleshooting over at VSI forum hb told me that
running a shareable image will execute LIB$INITIALIZE
functions.
Yeah, but ... You do not need init code for this.
Oh.
Can one get a transfer address into a shareable image?
$ ty s.c
#include <stdio.h>
static int firstFunctionInThisModuleBecomesTheEntryPoint () {
  printf ("It's the shareable, stupid!\n");
  return 1;
}
int foo () {
  printf ("This is %s\n", __func__);
  return 1;
}
$ cc s/stand=rel
$ link/share s,tt:/opt ! symbol_v=(foo=proc)
symbol_v=(foo=proc)
 Exit
$ r s
It's the shareable, stupid!
Hmmm.

It works in C, but it does not seem to work in
any other language (tested with Pascal and Fortran).

$ type prg.c
#include <stdio.h>

extern void say();

int main(int argc, char *argv[])
{
say();
return 0;
}

$ type lib.c
#include <stdio.h>

int ooops()
{
printf("This is a shareable image to link against not run\n");
return 1;
}

void say()
{
printf("Hi\n");
}

$ cc lib
$ link/share=libshr lib + sys$input/opt
symbol_vector=(say=procedure)
$
$ define/nolog libshr sys$disk:[]libshr
$ cc prg
$ link prg + sys$input/opt
libshr/share
$
$ run prg
Hi
$ run libshr
This is a shareable image to link against not run
$ type prg.pas
program prg(input,output);

[external]
procedure say; external;

begin
say;
end.
$ type lib.pas
[inherit('sys$library:starlet')]
module lib(input, output);

[global]
procedure ooops;

begin
writeln('This is a shareable image to link against not run');
$exit(ss$_normal);
end;

[global]
procedure say;

begin
writeln('Hi');
end;

end.
$ pas lib
$ link/share=libshr lib + sys$input/opt
symbol_vector=(say=procedure)
$
$ define/nolog libshr sys$disk:[]libshr
$ pas prg
$ link prg + sys$input/opt
libshr/share
$
$ run prg
Hi
$ run libshr
%DCL-E-NOTFR, no transfer address
$ type prg.for
program prg
call say
end
$ type lib.for
subroutine ooops
include '($ssdef)'
write(*,*) 'This is a shareable image to link against not run'
call sys$exit(ss$_normal)
end
c
subroutine say
write(*,*) 'Hi'
end
$ for lib
$ link/share=libshr lib + sys$input/opt
symbol_vector=(say=procedure)
$
$ define/nolog libshr sys$disk:[]libshr
$ for prg
$ link prg + sys$input/opt
libshr/share
$
$ run prg
Hi
$ run libshr
%DCL-E-NOTFR, no transfer address

I assume the difference relates to user code main not being
the real program entry.

Arne
hb0815
2025-01-09 13:01:51 UTC
Reply
Permalink
Post by Arne Vajhøj
Hmmm.
It works in C, but it does not seem to work in
any other language (tested with Pascal and Fortran).
...
I assume the difference relates to user code main not being
the real program entry.
So, make it a main entry.

This is on Alpha. It should work on IA64 and x86. I don't have access to
any of the latter systems. If this does not work on these systems, I
know how to make it work, anyway.

$ gdiff -ub lib.pas-orig lib.pas
--- lib.pas-orig 2025-01-09 07:34:25 -0500
+++ lib.pas 2025-01-09 07:37:14 -0500
@@ -1,19 +1,11 @@
-[inherit('sys$library:starlet')]
-module lib(input, output);
-
-[global]
-procedure ooops;
-
-begin
- writeln('This is a shareable image to link against not run');
- $exit(ss$_normal);
-end;
+program lib(input, output);

[global]
procedure say;
-
begin
writeln('Hi');
end;

+begin
+ writeln('This is a shareable image to link against not run');
end.
$
...
$ def/user libshr sys$disk:[]libshr.exe;
$ r prg
Hi
$ r libshr
This is a shareable image to link against not run
$

and

$ gdiff -ub lib.for-orig lib.for
--- lib.for-orig 2025-01-09 07:43:03 -0500
+++ lib.for 2025-01-09 07:43:25 -0500
@@ -1,7 +1,5 @@
- subroutine ooops
- include '($ssdef)'
+ program ooops
write(*,*) 'This is a shareable image to link against not run'
- call sys$exit(ss$_normal)
end
c
subroutine say
$
...
$ def/user libshr sys$disk:[]libshr.exe;
$ r prg
Hi
$ r libshr
This is a shareable image to link against not run
$

Also, I should have mentioned ... Your shareable with printing a message
from init code can not be used as a "normal" shareable image. The
message will always be printed. That's probably not what you want.

Init code of an image is always run. For a shareable image it is run at
activation time, for a main image it is run at image startup time. For
some reasons I distinguish these two phases although others prefer to
say that "startup" is part of activation.

My shareable image only prints its message if a user (accidently) runs
it as main image.

All this works because VMS defines a weak transfer (or entry) address.
The C compiler in absence of a "main" assigns this weak transfer to the
first function seen in a source module. (I admit, I initially didn't
test this with other compilers; obviously FORTRAN and PASCAL do not
define this). The linker keeps track of the first weak transfer it
encounters and uses it as image transfer, if there is no "strong"
transfer, in C no "main". This makes the shareable image "runnable". If
This often ends up in an unexpected error, if a user (accidently) runs
it as main image: the first (static) function may expect arguments and
may therefore miserably fail. I bet that there are only a few shareable
images that are prepared for being run as a main image.

For C this works at least on Alpha, IA64 and x86. On the latter two this
is more visible, because there is a VMS weak symbol in the symbol table
- of the object module. On Alpha the information is in the EOBJ$C_EEOM
record (and on VAX I assume it is the OBJ$C_EOM record).

Loading...