rschupp / PAR-Packer

(perl) Generate stand-alone executables, perl scripts and PAR files https://metacpan.org/pod/PAR::Packer
Other
48 stars 13 forks source link

Error: Attempt to reload DynaLoader.pm aborted. #76

Closed hoppfrosch closed 7 months ago

hoppfrosch commented 1 year ago

Hi,

as described in issue 75, I had some trouble in using PAR-packed Exes on some Windows-Machines due to missing DLLs . The issue was solved by using Shawn Laffan's APP::PP::Autolink - as suggested by R.Schupp.

But I still have some problems: same Environment as in issue 75. I compile my script within following Environment:

Whilst the PAR-packed exe runs on most Windows-Machines in our organization without problems, there still occurs the following problem on some machines:

Attempt to reload DynaLoader.pm aborted.

Compilation failed in require at c:/usr/misc/PerlEnvs/strawberry-5.26.0.1/perl/lib/XSLoader.pm line 118.

Compilation failed in require at c:/usr/misc/PerlEnvs/strawberry-5.26.0.1/perl/lib/IO/Handle.pm line 269.

BEGIN failed--compilation aborted at c:/usr/misc/PerlEnvs/strawberry-5.26.0.1/perl/lib/IO/Handle.pm line 269.

Compilation failed in require at c:/usr/misc/PerlEnvs/strawberry-5.26.0.1/perl/lib/IO/Seekable.pm line 101.

BEGIN failed--compilation aborted at c:/usr/misc/PerlEnvs/strawberry-5.26.0.1/perl/lib/IO/Seekable.pm line 101.

Compilation failed in require at c:/usr/misc/PerlEnvs/strawberry-5.26.0.1/perl/lib/IO/File.pm line 133.

BEGIN failed--compilation aborted at c:/usr/misc/PerlEnvs/strawberry-5.26.0.1/perl/lib/IO/File.pm line 133.

Compilation failed in require at -e line 351.

Any ideas/help to overcome this problem?

TIA hoppfrosch

rschupp commented 1 year ago

Compilation failed in require at c:/usr/misc/PerlEnvs/strawberry-5.26.0.1/perl/lib/XSLoader.pm line 118.

The paths in these error messages look wrong, you should either expect a file extracted below the inc directory in the cache directory, e.g. /tmp/par-726f646572696368/cache-f3a7cc68193a9ed7fd5af0f594c923b8a2a52039/inc/lib/File/Slurp.pm, or something like <embedded>/Archive/Zip/DirectoryMember.pm for "embedded" files extracted to the toplevel cache directory with mangled names.

These paths appear to be from the machine where the executable runs. The executable isn't supposed to look there, you can check with

pp -o show-inc.exe -E 'say "\@INC = @INC"'
./show-inc
hoppfrosch commented 1 year ago

You are right - it was my fault. The error messages above are from a previous version, where I linked withtout using APP::PP::Autolink.

Nevertheless still errors occur on some machines, executing the compiled code - here are the correct error messages: aKhDynRUe5

Sorry for the wrong error messages in the original posting.

Any idea how to fix this issue?

rschupp commented 1 year ago

I'm trying to make sense of the error message.

Attempt to reload DynaLoader.pm aborted: this means there was a prior attempt to require DynaLoader, the corresponding file DynaLoader.pm was found, but for some reason couldn't be loaded (e.g. because it contains a syntax error, but there could be other reasons). This would normally abort the program, but could be trapped via eval. Anyway, at this point $INC{"DynaLoader.pm"} has already been set to undef (if the require would have been successful it would have been set to the pathname of the file just loaded. If there is another require DynaLoader later, it first checks $INC{"DynaLoader.pm"} to see if the module has already been loaded. If the value exists, but is undef, the program aborts with this message. You can provoke this error with:

# write a perl module Foo.pm with a syntax error
$ echo "{" > Foo.pm
$ perl -I. -E "eval { require Foo }; say qq[first require: $@] if $@; require Foo;"
first require: Missing right curly or square bracket at Foo.pm line 1, at end of line
syntax error at Foo.pm line 1, at EOF
Compilation failed in require at -e line 1.

Next message in the exception stack is Compilation failed in require at <embedded>/XSLoader.pm line 118. As expected that line is: require DynaLoader. Next up is <embedded>/IO/Handle.pm line 269 which is the line use IO (which will invoke XSloader to load its XS part.

The rest also checks out, but the top message Compilation failed in require at -e line 351. doesn't. The -e is actually script/par.pl which is passed to the special perl interpreter of the packed executable as of on the command line. The line numbers are a bit tricky as it's not the original script/par.pl, but the condensed version found in myldr/my_par_pl.c (as a multiline C string). But there's nothing like a require (or use) in the vicinity of that line.

So I'm still puzzled. I'm pretty sure that this problem isn't caused by the script you packed, but could you confirm this by running a packed "hello world" script on a machine where the problem occurs and that it exhibits the same behaviour? Perhaps you could run this "hello world" with PERL_DL_DEBUG=1 set in the environment, this might give a clue.

hoppfrosch commented 12 months ago

I did as recommended: packed a simple "hello world" on my packing machine and ran the packed exe on one of the problematic machines:

use strict;
use warnings;
print "Hello, World!\n";

The packed exe ran without any issues on the problematic machine.

Just a pure guesswork: Might there be any problems with missing access rights for perl on the problematic machines?

rschupp commented 12 months ago

Just a pure guesswork: Might there be any problems with missing access rights for perl on the problematic machines?

Possible, but unlikely as I fail to explain the error message with permission problems.

Maybe the PAR cache directory is (almost) full?

The error messages seem to indicate that there's a problem early in the start up of the packed executable, i.e. before your packed script even gets control. On the other hand, the same start up process succeeds with the "hello world" executable...

Does the problematic machine have an installation of Strawberry perl? And if yes, is the installation path the sames as on the machine you do the packing?

hoppfrosch commented 12 months ago

Finally found the solution - it was a combination of two things:


A last question: Do you know any reason, why the unpacked folder parXXXX could not be deleted? That's an very long running problem here - so we tried many different locations for the parXXXX folder. Our assumption was, that windows specific subfolders as for example the standard location of the windows folder beneath %USERPROFILE% might be problematic due to UAC or something like this. Therefore we used folders like C:\Temp or similiar as %TEMP% .... but we have seen that automatic removing of the parxxxx folder by newer versions of the exe fails in most cases. (on our Windows Systems - problem exists at last from Win7 up ...) Any clue? We also have seen the phenomen occasionally. where the exe was unmodified and failed to run. Only after deleting the parxxxx folder, the exe run again. It looked like, that the system does not recognize that the exe was unmodified and tried to cleanup the parxxx folder before running the exe - but the cleanup failed. I don't have any clue, why the exe is assumed to be modified ....

rschupp commented 12 months ago

A last question: Do you know any reason, why the unpacked folder parXXXX could not be deleted?

OK, you probably know this, but just that we're on the same page here: this is how a packed executable works.

  1. The executable contains a HASH (basically the SHA1 of the whole thing), so that after any modification of the script or any of its depencies gets a different hash, even though the packed exectuable gets the same name.
  2. When the executable starts, it reads the HASH and checks if the directory %TEMP%/par-USER/HASH (the "cache directory") already exists (USER is something derived from the identity of the user running the executabe).
  3. If it doesn't exist, it is created (including parents if necessary) and the "zip contents" of the packed executable (the stuff you can see with unzip -l executable) is extracted into its sub directory inc. Also it extracts a specialized perl interpreter (using the same name as the packed executable) into the cache directory (and some other stuff). If the cache directory exists, this step is skipped.
  4. Run the perl interpreter with the extracted copy of your script.

The second and later invocations of the executable skip step 3, hence will start up faster.

If you often deploy new versions of your packed executable, %TEMP%/par-USER will fill up with stuff from obsolete versions. If you can live with the slower start up (e.g. because people typically run every version of the executable only once or because the start up time is negligible compare to the typical running time of the executable), you can pack with pp --clean .... This will automatically remove the cache directory after the executable finishes. Note that this removal is a simple-minded recursive directory traversal. So if you have problems removing the cache directory by hand, this might fail also.

And yeah, I've also seen Windows refusing to delete files or directories for indiscernible reasons. The usual suspects (file is open in another application or directory is used as working directory by another application) shouldn't apply.

rschupp commented 11 months ago

And yeah, I've also seen Windows refusing to delete files or directories for indiscernible reasons

More specifically: while any packed executable foo.exe is running on a machine, the following files are "busy" and can't be deleted: