Bug 102992 - fortran: redirecting standard out to a file does not work on macOS 12.0
Summary: fortran: redirecting standard out to a file does not work on macOS 12.0
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: libfortran (show other bugs)
Version: 11.0
: P3 normal
Target Milestone: 12.2
Assignee: Not yet assigned to anyone
URL:
Keywords:
: 103043 (view as bug list)
Depends on:
Blocks:
 
Reported: 2021-10-28 22:34 UTC by Jürgen Reuter
Modified: 2022-05-29 19:27 UTC (History)
6 users (show)

See Also:
Host:
Target: x86_64-apple-darwin21
Build:
Known to work:
Known to fail:
Last reconfirmed: 2021-10-28 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jürgen Reuter 2021-10-28 22:34:36 UTC
I don't know whether this is really an issue with gfortran or a screwed setting of my macOS installation, I have macOS 12.0.1 Monterey, Darwin 21.1.0, with XCode 13.1, and a simple file

main.f90:

program main
 print *, "42"
end program main

gfortran -o conftest main.f90

prints 42 in the shell, so to stdout. But when tryng pipe to a file this does not work, the file is always empty, i.e.

./conftest > bla.txt
result in an empty file.
Comment 1 Jürgen Reuter 2021-10-28 22:37:08 UTC
Using a C program compiled with the same version (recent trunk with the fix by Iain Sandoe for Monterey) leads to a program that can pipe to a file.
Comment 2 Iain Sandoe 2021-10-28 23:11:48 UTC
confirmed - I have no idea how to categorise this issue at present.

Does gfortran do something special opening/cloning stdin/out/err?
Comment 3 Andrew Pinski 2021-10-28 23:22:13 UTC
(In reply to Iain Sandoe from comment #2)
> confirmed - I have no idea how to categorise this issue at present.
> 
> Does gfortran do something special opening/cloning stdin/out/err?

Libfortran does a few things with respect to 0, 1 and 2 file fds. Maybe something it does is something that Darwin no longer likes.
This is why I moved it to libfortran component.
Comment 4 Jürgen Reuter 2021-10-29 06:41:43 UTC
The problem is not related to XCode 13.1 which appeared at roughly the same time. On Big Sur with XCode 13.1 still all works as expected.
Comment 5 Jürgen Reuter 2021-10-29 10:04:13 UTC
I checked that the assembler code on macOS Big Sur and Monterey is identical (up to the date in the .ident line). So either the assembler works differently, or one of the routines from the libgfortran (_gfortrran_st_write, _gfortran_set_options, _gfortran_set_args, _gfortran_transfer_character_write) acts differently now.
Comment 6 Iain Sandoe 2021-10-29 12:40:11 UTC
I had a brief look at some new fails on macOS12 / Darwin21 for gcov.

It seems that .mod_term_funcs entries are not being run - so if libgfortran relies on something defined as __attribute__((destructor)) [e.g. to flush file output] that might explain the issue.
Comment 7 Iain Sandoe 2021-10-29 12:51:00 UTC
(although , the output is fine when not redirected, so perhaps that's irrelevant)
Comment 8 Iain Sandoe 2021-10-29 13:12:52 UTC
I have checked Ada, C++, C, Objective-C++ and Objective-C and they all behave as expected.
Comment 9 Jürgen Reuter 2021-10-29 14:05:07 UTC
I also tried that for a Fortran program ./a.out | less (pipe to less) works. It's just the redirection that does not work. I'm waiting for the compilation to check whether gfortran 11.2 from Macports shares the same problem.
Comment 10 Jürgen Reuter 2021-10-29 15:50:51 UTC
Reassuringly, the gfortran 11.2 from Macports has the same problem as the gfortran 12.0.0 installed by hand: no redirecting into files.
Comment 11 Rich Townsend 2021-10-31 00:52:26 UTC
I can confirm this issue on macOS 12.0.1 and gcc/gfortran 10.2.
Comment 12 Jürgen Reuter 2021-11-01 09:09:20 UTC
I'm not an expert on the I/O system, but could it be that the unit to which the stdout of a compiled Fortran program goes does not provide the unit that the redirect function (now) expects under macOS 12?
Comment 13 Thomas Koenig 2021-11-01 10:51:43 UTC
Here is a complete strace of a "Hello, world" program on Linux, compiled
with -static-libgfortran (to remove some of the shared library loading :-)
and executed with

$ strace ./a.out > hello.dat

execve("./a.out", ["./a.out"], 0x7fff23679850 /* 52 vars */) = 0
brk(NULL)                               = 0x55795af28000
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffef37f68b0) = -1 EINVAL (Invalid argument)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=137154, ...}) = 0
mmap(NULL, 137154, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f6b9f05a000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libgcc_s.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\3405\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=104984, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f6b9f058000
mmap(NULL, 107592, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f6b9f03d000
mmap(0x7f6b9f040000, 73728, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3000) = 0x7f6b9f040000
mmap(0x7f6b9f052000, 16384, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x15000) = 0x7f6b9f052000
mmap(0x7f6b9f056000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x18000) = 0x7f6b9f056000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libquadmath.so.0", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\200:\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=297784, ...}) = 0
mmap(NULL, 299712, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f6b9eff3000
mmap(0x7f6b9eff6000, 184320, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3000) = 0x7f6b9eff6000
mmap(0x7f6b9f023000, 98304, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x30000) = 0x7f6b9f023000
mmap(0x7f6b9f03b000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x47000) = 0x7f6b9f03b000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libm.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300\363\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=1369352, ...}) = 0
mmap(NULL, 1368336, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f6b9eea4000
mmap(0x7f6b9eeb3000, 684032, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xf000) = 0x7f6b9eeb3000
mmap(0x7f6b9ef5a000, 618496, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xb6000) = 0x7f6b9ef5a000
mmap(0x7f6b9eff1000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x14c000) = 0x7f6b9eff1000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\360q\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32, 848) = 32
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\t\233\222%\274\260\320\31\331\326\10\204\276X>\263"..., 68, 880) = 68
fstat(3, {st_mode=S_IFREG|0755, st_size=2029224, ...}) = 0
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32, 848) = 32
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\t\233\222%\274\260\320\31\331\326\10\204\276X>\263"..., 68, 880) = 68
mmap(NULL, 2036952, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f6b9ecb2000
mprotect(0x7f6b9ecd7000, 1847296, PROT_NONE) = 0
mmap(0x7f6b9ecd7000, 1540096, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x25000) = 0x7f6b9ecd7000
mmap(0x7f6b9ee4f000, 303104, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x19d000) = 0x7f6b9ee4f000
mmap(0x7f6b9ee9a000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7f6b9ee9a000
mmap(0x7f6b9eea0000, 13528, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f6b9eea0000
close(3)                                = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f6b9ecb0000
arch_prctl(ARCH_SET_FS, 0x7f6b9ecb0b80) = 0
mprotect(0x7f6b9ee9a000, 12288, PROT_READ) = 0
mprotect(0x7f6b9eff1000, 4096, PROT_READ) = 0
mprotect(0x7f6b9f03b000, 4096, PROT_READ) = 0
mprotect(0x7f6b9f056000, 4096, PROT_READ) = 0
mprotect(0x557959277000, 4096, PROT_READ) = 0
mprotect(0x7f6b9f0a9000, 4096, PROT_READ) = 0
munmap(0x7f6b9f05a000, 137154)          = 0
brk(NULL)                               = 0x55795af28000
brk(0x55795af49000)                     = 0x55795af49000
fstat(0, {st_mode=S_IFCHR|0600, st_rdev=makedev(0x88, 0x1), ...}) = 0
fstat(1, {st_mode=S_IFREG|0664, st_size=0, ...}) = 0
fstat(2, {st_mode=S_IFCHR|0600, st_rdev=makedev(0x88, 0x1), ...}) = 0
rt_sigaction(SIGQUIT, {sa_handler=0x5579592418a0, sa_mask=[QUIT], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f6b9ecf8210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGILL, {sa_handler=0x5579592418a0, sa_mask=[ILL], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f6b9ecf8210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGABRT, {sa_handler=0x5579592418a0, sa_mask=[ABRT], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f6b9ecf8210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGFPE, {sa_handler=0x5579592418a0, sa_mask=[FPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f6b9ecf8210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGSEGV, {sa_handler=0x5579592418a0, sa_mask=[SEGV], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f6b9ecf8210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGBUS, {sa_handler=0x5579592418a0, sa_mask=[BUS], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f6b9ecf8210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGSYS, {sa_handler=0x5579592418a0, sa_mask=[SYS], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f6b9ecf8210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGTRAP, {sa_handler=0x5579592418a0, sa_mask=[TRAP], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f6b9ecf8210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGXCPU, {sa_handler=0x5579592418a0, sa_mask=[XCPU], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f6b9ecf8210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGXFSZ, {sa_handler=0x5579592418a0, sa_mask=[XFSZ], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f6b9ecf8210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
write(1, "Hello, world!\n", 14)         = 14
exit_group(0)                           = ?
+++ exited with 0 +++

I see nothing untoward there, the only times file hande 1 is
referenced is by

fstat(1, {st_mode=S_IFREG|0664, st_size=0, ...}) = 0

and

write(1, "Hello, world!\n", 14)         = 14

Is it possible to run an equivalent command on MacOS to see
what is going on there?
Comment 14 Thomas Koenig 2021-11-01 11:10:56 UTC
(In reply to Iain Sandoe from comment #6)
> I had a brief look at some new fails on macOS12 / Darwin21 for gcov.
> 
> It seems that .mod_term_funcs entries are not being run - so if libgfortran
> relies on something defined as __attribute__((destructor)) [e.g. to flush
> file output] that might explain the issue.

Well, there is (at the end of main.c)

/* Cleanup the runtime library.  */

static void __attribute__((destructor))
cleanup (void)
{
  close_units ();
}

I guess that could be put into main, after the call to __MAIN, instead.
Comment 15 Thomas Koenig 2021-11-01 11:17:28 UTC
Hmm, to test this further: What does

program main
  use, intrinsic :: iso_fortran_env, only: output_unit
  write (*,'(A)') 'Hello, world!'
  flush output_unit
end program main

yield, does that create the file as expected?
Comment 16 Iain Sandoe 2021-11-01 12:22:12 UTC
(In reply to Thomas Koenig from comment #14)
> (In reply to Iain Sandoe from comment #6)
> > I had a brief look at some new fails on macOS12 / Darwin21 for gcov.
> > 
> > It seems that .mod_term_funcs entries are not being run - so if libgfortran
> > relies on something defined as __attribute__((destructor)) [e.g. to flush
> > file output] that might explain the issue.
> 
> Well, there is (at the end of main.c)
> 
> /* Cleanup the runtime library.  */
> 
> static void __attribute__((destructor))
> cleanup (void)
> {
>   close_units ();
> }

That would seem a very likely candidate for the issue.

NOTE: I have been discussing the non-running of mod_term_funcs with my "downstream" and Apple folks.  The consensus is that this is 99.99% likely an unintentional bug that just happens not to show with clang-based toolchains because they queue DTORs on atexit (as does GCC for C++).

> I guess that could be put into main, after the call to __MAIN, instead.

Well, unless there's a good reason to have it in a DTOR, it makes for a more robust solution to call it directly (there are various other differences that could occur in mixed code bases esp. c++ + fortran even without the Darwin21.1 bug).

will try your alternate code later.
Comment 17 Iain Sandoe 2021-11-01 12:25:14 UTC
(In reply to Thomas Koenig from comment #15)
> Hmm, to test this further: What does
> 
> program main
>   use, intrinsic :: iso_fortran_env, only: output_unit
>   write (*,'(A)') 'Hello, world!'
>   flush output_unit
> end program main
> 
> yield, does that create the file as expected?

yup, indeed it does.
Comment 18 Jürgen Reuter 2021-11-01 12:49:18 UTC
(In reply to Thomas Koenig from comment #13)
> Here is a complete strace of a "Hello, world" program on Linux, compiled
> with -static-libgfortran (to remove some of the shared library loading :-)
> and executed with
> 
> $ strace ./a.out > hello.dat
> 
> execve("./a.out", ["./a.out"], 0x7fff23679850 /* 52 vars */) = 0
> brk(NULL)                               = 0x55795af28000
>
> Is it possible to run an equivalent command on MacOS to see
> what is going on there?

Seems that dtruss is doing the same under macOS, here I get this output for the Hello, world! program:
SYSCALL(args) 		 = return
 Hello, world!\n
access("/AppleInternal/XBS/.isChrooted\0", 0x0, 0x0)		 = -1 Err#2
bsdthread_register(0x7FF805E18020, 0x7FF805E1800C, 0x2000)		 = 1073742303 0
shm_open(0x7FF805CE6F5D, 0x0, 0x5CE57BA)		 = 3 0
fstat64(0x3, 0x7FF7BDDDE820, 0x0)		 = 0 0
mmap(0x0, 0x2000, 0x1, 0x40001, 0x3, 0x0)		 = 0x102231000 0
close(0x3)		 = 0 0
ioctl(0x2, 0x4004667A, 0x7FF7BDDDE8D4)		 = 0 0
mprotect(0x102238000, 0x1000, 0x0)		 = 0 0
mprotect(0x10223F000, 0x1000, 0x0)		 = 0 0
mprotect(0x102240000, 0x1000, 0x0)		 = 0 0
mprotect(0x102247000, 0x1000, 0x0)		 = 0 0
mprotect(0x102233000, 0x90, 0x1)		 = 0 0
mprotect(0x102233000, 0x90, 0x3)		 = 0 0
mprotect(0x102233000, 0x90, 0x1)		 = 0 0
mprotect(0x102248000, 0x1000, 0x1)		 = 0 0
mprotect(0x102249000, 0x90, 0x1)		 = 0 0
mprotect(0x102249000, 0x90, 0x3)		 = 0 0
mprotect(0x102249000, 0x90, 0x1)		 = 0 0
mprotect(0x102233000, 0x90, 0x3)		 = 0 0
mprotect(0x102233000, 0x90, 0x1)		 = 0 0
mprotect(0x102248000, 0x1000, 0x3)		 = 0 0
mprotect(0x102248000, 0x1000, 0x1)		 = 0 0
issetugid(0x0, 0x0, 0x0)		 = 0 0
getentropy(0x7FF7BDDDE6D0, 0x20, 0x0)		 = 0 0
getentropy(0x7FF7BDDDE730, 0x40, 0x0)		 = 0 0
getpid(0x0, 0x0, 0x0)		 = 61321 0
stat64("/AppleInternal\0", 0x7FF7BDDDEDF0, 0x0)		 = -1 Err#2
csops_audittoken(0xEF89, 0x7, 0x7FF7BDDDE920)		 = -1 Err#22
proc_info(0x2, 0xEF89, 0xD)		 = 64 0
csops_audittoken(0xEF89, 0x7, 0x7FF7BDDDEA10)		 = -1 Err#22
sysctlbyname(kern.osvariant_status, 0x15, 0x7FF7BDDDEE40, 0x7FF7BDDDEE38, 0x0)		 = 0 0
csops(0xEF89, 0x0, 0x7FF7BDDDEE74)		 = 0 0
fstat64(0x0, 0x7FF7BDDDEBF0, 0x0)		 = 0 0
fstat64(0x1, 0x7FF7BDDDEBF0, 0x0)		 = 0 0
fstat64(0x2, 0x7FF7BDDDEBF0, 0x0)		 = 0 0
mprotect(0x10212F000, 0x100000, 0x1)		 = 0 0
sigaction(0x3, 0x7FF7BDDDFFA8, 0x7FF7BDDDFFD0)		 = 0 0
sigaction(0x4, 0x7FF7BDDDFFA8, 0x7FF7BDDDFFD0)		 = 0 0
sigaction(0x6, 0x7FF7BDDDFFA8, 0x7FF7BDDDFFD0)		 = 0 0
sigaction(0x8, 0x7FF7BDDDFFA8, 0x7FF7BDDDFFD0)		 = 0 0
sigaction(0xB, 0x7FF7BDDDFFA8, 0x7FF7BDDDFFD0)		 = 0 0
sigaction(0xA, 0x7FF7BDDDFFA8, 0x7FF7BDDDFFD0)		 = 0 0
sigaction(0xC, 0x7FF7BDDDFFA8, 0x7FF7BDDDFFD0)		 = 0 0
sigaction(0x5, 0x7FF7BDDDFFA8, 0x7FF7BDDDFFD0)		 = 0 0
sigaction(0x18, 0x7FF7BDDDFFA8, 0x7FF7BDDDFFD0)		 = 0 0
sigaction(0x19, 0x7FF7BDDDFFB8, 0x7FF7BDDDFFE0)		 = 0 0
write(0x1, " Hello, world!\\n\n\0", 0x11)		 = 17 0
Comment 19 Thomas Koenig 2021-11-01 17:37:34 UTC
(In reply to Jürgen Reuter from comment #18)

> write(0x1, " Hello, world!\\n\n\0", 0x11)		 = 17 0

Hmm, was this actually the string that you put into the Fortran
program, or is something very strange going on here?
Comment 20 Thomas Koenig 2021-11-01 17:41:59 UTC
(In reply to Iain Sandoe from comment #16)
> (In reply to Thomas Koenig from comment #14)

> NOTE: I have been discussing the non-running of mod_term_funcs with my
> "downstream" and Apple folks.  The consensus is that this is 99.99% likely
> an unintentional bug that just happens not to show with clang-based
> toolchains because they queue DTORs on atexit (as does GCC for C++).
> 
> > I guess that could be put into main, after the call to __MAIN, instead.
> 
> Well, unless there's a good reason to have it in a DTOR, it makes for a more
> robust solution to call it directly (there are various other differences
> that could occur in mixed code bases esp. c++ + fortran even without the
> Darwin21.1 bug).

There is always the reason of not breaking compatibility, a quick look
at close_units() shows that it is not meant to be called twice,
so combining both methods is likely to cause headaches.

So, something for the next incompatible libgfortran update, maybe.

Maybe flushing all units on program termination would be safer, but
I am not sure we should put in a workaround for what appears to
be a bug in the underlying system (hopefully soon to be fixed),
especially since there seems to be a workaround in user code.
Comment 21 Jürgen Reuter 2021-11-01 17:44:44 UTC
(In reply to Thomas Koenig from comment #19)
> (In reply to Jürgen Reuter from comment #18)
> 
> > write(0x1, " Hello, world!\\n\n\0", 0x11)		 = 17 0
> 
> Hmm, was this actually the string that you put into the Fortran
> program, or is something very strange going on here?

Yes, the program was 

program main
   print *, "Hello, world!\n"
end program main
sorry, about the backslash \n, that was accidental. 
Doing just
program main
   print *, "Hello, world!"
end program main

# dtruss ./a.out > out.008
SYSCALL(args)          = return
 Hello, world!
access("/AppleInternal/XBS/.isChrooted\0", 0x0, 0x0)         = -1 Err#2
bsdthread_register(0x7FF805E18020, 0x7FF805E1800C, 0x2000)         = 1073742303 0
shm_open(0x7FF805CE6F5D, 0x0, 0x5CE57BA)         = 3 0
fstat64(0x3, 0x7FF7BE646820, 0x0)         = 0 0
mmap(0x0, 0x2000, 0x1, 0x40001, 0x3, 0x0)         = 0x1019C9000 0
close(0x3)         = 0 0
ioctl(0x2, 0x4004667A, 0x7FF7BE6468D4)         = 0 0
mprotect(0x1019D0000, 0x1000, 0x0)         = 0 0
mprotect(0x1019D7000, 0x1000, 0x0)         = 0 0
mprotect(0x1019D8000, 0x1000, 0x0)         = 0 0
mprotect(0x1019DF000, 0x1000, 0x0)         = 0 0
mprotect(0x1019CB000, 0x90, 0x1)         = 0 0
mprotect(0x1019CB000, 0x90, 0x3)         = 0 0
mprotect(0x1019CB000, 0x90, 0x1)         = 0 0
mprotect(0x1019E0000, 0x1000, 0x1)         = 0 0
mprotect(0x1019E1000, 0x90, 0x1)         = 0 0
mprotect(0x1019E1000, 0x90, 0x3)         = 0 0
mprotect(0x1019E1000, 0x90, 0x1)         = 0 0
mprotect(0x1019CB000, 0x90, 0x3)         = 0 0
mprotect(0x1019CB000, 0x90, 0x1)         = 0 0
mprotect(0x1019E0000, 0x1000, 0x3)         = 0 0
mprotect(0x1019E0000, 0x1000, 0x1)         = 0 0
issetugid(0x0, 0x0, 0x0)         = 0 0
getentropy(0x7FF7BE6466D0, 0x20, 0x0)         = 0 0
getentropy(0x7FF7BE646730, 0x40, 0x0)         = 0 0
getpid(0x0, 0x0, 0x0)         = 61408 0
stat64("/AppleInternal\0", 0x7FF7BE646DF0, 0x0)         = -1 Err#2
csops_audittoken(0xEFE0, 0x7, 0x7FF7BE646920)         = -1 Err#22
proc_info(0x2, 0xEFE0, 0xD)         = 64 0
csops_audittoken(0xEFE0, 0x7, 0x7FF7BE646A10)         = -1 Err#22
sysctlbyname(kern.osvariant_status, 0x15, 0x7FF7BE646E40, 0x7FF7BE646E38, 0x0)         = 0 0
csops(0xEFE0, 0x0, 0x7FF7BE646E74)         = 0 0
fstat64(0x0, 0x7FF7BE646BF0, 0x0)         = 0 0
fstat64(0x1, 0x7FF7BE646BF0, 0x0)         = 0 0
fstat64(0x2, 0x7FF7BE646BF0, 0x0)         = 0 0
mprotect(0x1018C7000, 0x100000, 0x1)         = 0 0
sigaction(0x3, 0x7FF7BE647FA8, 0x7FF7BE647FD0)         = 0 0
sigaction(0x4, 0x7FF7BE647FA8, 0x7FF7BE647FD0)         = 0 0
sigaction(0x6, 0x7FF7BE647FA8, 0x7FF7BE647FD0)         = 0 0
sigaction(0x8, 0x7FF7BE647FA8, 0x7FF7BE647FD0)         = 0 0
sigaction(0xB, 0x7FF7BE647FA8, 0x7FF7BE647FD0)         = 0 0
sigaction(0xA, 0x7FF7BE647FA8, 0x7FF7BE647FD0)         = 0 0
sigaction(0xC, 0x7FF7BE647FA8, 0x7FF7BE647FD0)         = 0 0
sigaction(0x5, 0x7FF7BE647FA8, 0x7FF7BE647FD0)         = 0 0
sigaction(0x18, 0x7FF7BE647FA8, 0x7FF7BE647FD0)         = 0 0
sigaction(0x19, 0x7FF7BE647FB8, 0x7FF7BE647FE0)         = 0 0
write(0x1, " Hello, world!\n\0", 0xF)         = 15 0
Comment 22 Jürgen Reuter 2021-11-01 17:50:56 UTC
(In reply to Thomas Koenig from comment #20)
> (In reply to Iain Sandoe from comment #16)
> > (In reply to Thomas Koenig from comment #14)

> 
> There is always the reason of not breaking compatibility, a quick look
> at close_units() shows that it is not meant to be called twice,
> so combining both methods is likely to cause headaches.
> 
> So, something for the next incompatible libgfortran update, maybe.
> 
> Maybe flushing all units on program termination would be safer, but
> I am not sure we should put in a workaround for what appears to
> be a bug in the underlying system (hopefully soon to be fixed),
> especially since there seems to be a workaround in user code.

I agree that if this an OS bug, then workarounds in libgfortran that might jeopardize the integrity are hard to justify. Do we know that this will be fixed let's say for macOS 12.0.2 or so, and when will that come? At the moment these things do break quite a lot of build scripts for software that rely on redirecting output from test programs. Changing all those test programs to iso_fortan_env is tedious (but doable).
Comment 23 Iain Sandoe 2021-11-01 18:08:14 UTC
(In reply to Jürgen Reuter from comment #22)
> (In reply to Thomas Koenig from comment #20)
> > (In reply to Iain Sandoe from comment #16)
> > > (In reply to Thomas Koenig from comment #14)
> 
> > 
> > There is always the reason of not breaking compatibility, a quick look
> > at close_units() shows that it is not meant to be called twice,
> > so combining both methods is likely to cause headaches.
> > 
> > So, something for the next incompatible libgfortran update, maybe.
> > 
> > Maybe flushing all units on program termination would be safer, but
> > I am not sure we should put in a workaround for what appears to
> > be a bug in the underlying system (hopefully soon to be fixed),
> > especially since there seems to be a workaround in user code.
> 
> I agree that if this an OS bug, then workarounds in libgfortran that might
> jeopardize the integrity are hard to justify. 

Well, we certainly would not want to do compromise integrity, in any event; that would not be a solution.  However, ensuring a single call to the function ought to be feasible.  the secondary point I made was that the phasing of DTORs called from cxa_atexit (C++) c.f. directly from a destructor can cause subtle issues in any case.

>Do we know that this will be
> fixed let's say for macOS 12.0.2 or so, and when will that come? 

I have filed a bug report, (FB9733712).  It is impossible to know what the OS release priorities are (or schedules), but I do know that the Apple OSS folks are very supportive of gfortran, so I expect they will help.

> At the
> moment these things do break quite a lot of build scripts for software that
> rely on redirecting output from test programs. Changing all those test
> programs to iso_fortan_env is tedious (but doable).
Comment 24 Jürgen Reuter 2021-11-01 18:53:50 UTC
(In reply to Iain Sandoe from comment #23)
> (In reply to Jürgen Reuter from comment #22)
> > (In reply to Thomas Koenig from comment #20)
> > > (In reply to Iain Sandoe from comment #16)
> > > > (In reply to Thomas Koenig from comment #14)

> 
> >Do we know that this will be
> > fixed let's say for macOS 12.0.2 or so, and when will that come? 
> 
> I have filed a bug report, (FB9733712).  It is impossible to know what the
> OS release priorities are (or schedules), but I do know that the Apple OSS
> folks are very supportive of gfortran, so I expect they will help.
> 

Could you post the link to this bug report? I cannot find it. Or is this not publicly visible.

> > At the
> > moment these things do break quite a lot of build scripts for software that
> > rely on redirecting output from test programs. Changing all those test
> > programs to iso_fortan_env is tedious (but doable).

I just started to look a bit, changing all of your configure script would be really tedious. So I really would prefer a fixed OS or a workaround inside gcc/gfortran.
Comment 25 Iain Sandoe 2021-11-01 19:00:34 UTC
(In reply to Jürgen Reuter from comment #24)
> (In reply to Iain Sandoe from comment #23)

> > I have filed a bug report, (FB9733712).  It is impossible to know what the
> > OS release priorities are (or schedules), but I do know that the Apple OSS
> > folks are very supportive of gfortran, so I expect they will help.

> Could you post the link to this bug report? I cannot find it. Or is this not
> publicly visible.

"radars" are not publicly visible, but the ID is significant if you are reporting something to Apple / an Apple OSS person.
Comment 26 Iain Sandoe 2021-11-01 20:47:48 UTC
(In reply to Iain Sandoe from comment #25)
> (In reply to Jürgen Reuter from comment #24)
> > (In reply to Iain Sandoe from comment #23)
> 
> > > I have filed a bug report, (FB9733712).  It is impossible to know what the
> > > OS release priorities are (or schedules), but I do know that the Apple OSS
> > > folks are very supportive of gfortran, so I expect they will help.
> 
> > Could you post the link to this bug report? I cannot find it. Or is this not
> > publicly visible.
> 
> "radars" are not publicly visible, but the ID is significant if you are
> reporting something to Apple / an Apple OSS person.

I added to the openradars site here:
https://openradar.appspot.com/FB9733712
(that doesn't make any difference to visibility of if/when it will be fixed, of course)
Comment 27 Dominique d'Humieres 2021-11-02 10:39:21 UTC
*** Bug 103043 has been marked as a duplicate of this bug. ***
Comment 28 Francois-Xavier Coudert 2021-11-12 12:06:08 UTC
> Well, unless there's a good reason to have it in a DTOR, it makes for a more
> robust solution to call it directly (there are various other differences
> that could occur in mixed code bases esp. c++ + fortran even without the
> Darwin21.1 bug).

Mixed code is in fact the reason why it's in a DTOR and not called at the end of Fortran main program. We need this clean-up routine to run even when the main code is not in Fortran, and but Fortran routines are called by other means (Fortran object files linked, or Fortran-based library loaded at run-time).
Comment 29 Iain Sandoe 2021-11-12 12:31:37 UTC
(In reply to Francois-Xavier Coudert from comment #28)
> > Well, unless there's a good reason to have it in a DTOR, it makes for a more
> > robust solution to call it directly (there are various other differences
> > that could occur in mixed code bases esp. c++ + fortran even without the
> > Darwin21.1 bug).
> 
> Mixed code is in fact the reason why it's in a DTOR and not called at the
> end of Fortran main program. We need this clean-up routine to run even when
> the main code is not in Fortran, and but Fortran routines are called by
> other means (Fortran object files linked, or Fortran-based library loaded at
> run-time).

I've posted a fix for this (it is the fix for darwin21 DTORs in general)
however CAVEAT : there is No guarantee given by the GCC DTOR machinery about the relative ordering of DTORs between different TUs, my fix will not solve that.
Comment 30 Francois-Xavier Coudert 2021-11-12 12:40:30 UTC
(In reply to Iain Sandoe from comment #29)
> I've posted a fix for this (it is the fix for darwin21 DTORs in general)

Yes I've seen the patch, thanks!

> however CAVEAT : there is No guarantee given by the GCC DTOR machinery about
> the relative ordering of DTORs between different TUs, my fix will not solve
> that.

That was already the case before (if I understand right) and this is fine for our use: we have only one DTOR in libgfortran, and Fortran I/O and I/O in other languages should not operate on the same files or descriptors.
Comment 31 Jürgen Reuter 2021-11-15 19:32:27 UTC
(In reply to Iain Sandoe from comment #29)
> (In reply to Francois-Xavier Coudert from comment #28)

> I've posted a fix for this (it is the fix for darwin21 DTORs in general)
> however CAVEAT : there is No guarantee given by the GCC DTOR machinery about
> the relative ordering of DTORs between different TUs, my fix will not solve
> that.

Does this now mean that this is fixed within the gcc trunk/master? Or just in
a branch which has yet to be merged into the master? And will it be backported
for 11.3, 10.4 as well?
Comment 32 Francois-Xavier Coudert 2021-11-15 19:43:39 UTC
(In reply to Jürgen Reuter from comment #31)
> Does this now mean that this is fixed within the gcc trunk/master? Or just in
> a branch which has yet to be merged into the master?

The patch was posted for review, and okayed on 13 November at https://gcc.gnu.org/pipermail/gcc-patches/2021-November/584359.html
Comment 33 GCC Commits 2021-11-15 19:51:33 UTC
The master branch has been updated by Iain D Sandoe <iains@gcc.gnu.org>:

https://gcc.gnu.org/g:fabe8cc41e9b01913e2016861237d1d99d7567bf

commit r12-5281-gfabe8cc41e9b01913e2016861237d1d99d7567bf
Author: Iain Sandoe <iain@sandoe.co.uk>
Date:   Thu Nov 4 09:37:14 2021 +0000

    IPA: Provide a mechanism to register static DTORs via cxa_atexit.
    
    For at least one target (Darwin) the platform convention is to
    register static destructors (i.e. __attribute__((destructor)))
    with __cxa_atexit rather than placing them into a list that is
    run by some other mechanism.
    
    This patch provides a target hook that allows a target to opt
    into this and handling for the process in ipa_cdtor_merge ().
    
    When the mode is enabled (dtors_from_cxa_atexit is set) we:
    
     * Generate new CTORs to register static destructors with
       __cxa_atexit and add them to the existing list of CTORs;
       we then process the revised CTORs list.
    
     * We sort the DTORs into priority and then TU order, this
       means that they are registered in that order with
       __cxa_atexit () and therefore will be run in the reverse
       order.
    
     * Likewise, CTORs are sorted into priority and then TU order,
       which means that they will run in that order.
    
    This matches the behavior of using init/fini (or
    mod_init_func/mod_term_func) sections.
    
    This also fixes a bug where Fortran needs a DTOR to be run to
    close IO.
    
    Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
    
            PR fortran/102992
    
    gcc/ChangeLog:
    
            * config/darwin.h (TARGET_DTORS_FROM_CXA_ATEXIT): New.
            * doc/tm.texi: Regenerated.
            * doc/tm.texi.in: Add TARGET_DTORS_FROM_CXA_ATEXIT hook.
            * ipa.c (cgraph_build_static_cdtor_1): Return the built
            function decl.
            (build_cxa_atexit_decl): New.
            (build_dso_handle_decl): New.
            (build_cxa_dtor_registrations): New.
            (compare_cdtor_tu_order): New.
            (build_cxa_atexit_fns): New.
            (ipa_cdtor_merge): If dtors_from_cxa_atexit is set,
            process the DTORs/CTORs accordingly.
            (pass_ipa_cdtor_merge::gate): Also run if
            dtors_from_cxa_atexit is set.
            * target.def (dtors_from_cxa_atexit): New hook.
Comment 34 Iain Sandoe 2021-11-15 19:55:34 UTC
I did ask for OK on back-ports

We should let it bake on master for now - I see reports that 12.1 ia under test - but no idea when it will come out (or if this particular issue will be fixed in it).

In any event, it was designed to be an add-on so that the behaviour of everything else (that does not set the target option) would be unchanged - so IMO it would be safe to use the back ported patch ahead of "reality" in emergency.
Comment 35 Jürgen Reuter 2021-12-14 18:39:32 UTC
Now that macOS 12.1 is out (and XCode 13.2) could someone please check whether the problem has been solved from the side of the Darwin kernel?
Comment 36 Iain Sandoe 2021-12-15 13:06:59 UTC
(In reply to Jürgen Reuter from comment #35)
> Now that macOS 12.1 is out (and XCode 13.2) could someone please check
> whether the problem has been solved from the side of the Darwin kernel?

I didn't see the issue listed in the 12.1 description and my radar is not acknowledged officially (although some of the OSS folks agree it is a bug).

So .. right now I would not expect it to be resolved without the master change and I intend to back port that change too - since clang sets the effective ABI, it is more compatible for us to use the same approach (even if the term funcs would work in principle).

Note 103080 also (and I would think that impossible to solve without using the current approach of registering on cxa-atexit).
Comment 37 Francois-Xavier Coudert 2021-12-15 14:06:53 UTC
OS bug confirmed to be still present on macOS 12.1 (darwin 21.2.0), at least on Intel. I don't have a non-patched compiler on ARM right now to confirm, but I don't expect it to be arch-dependent.
Comment 38 GCC Commits 2022-04-14 05:26:45 UTC
The releases/gcc-11 branch has been updated by Iain D Sandoe <iains@gcc.gnu.org>:

https://gcc.gnu.org/g:6841e9fc63b260186f8c980c7e0534b6376b073f

commit r11-9873-g6841e9fc63b260186f8c980c7e0534b6376b073f
Author: Iain Sandoe <iain@sandoe.co.uk>
Date:   Thu Nov 4 09:37:14 2021 +0000

    IPA: Provide a mechanism to register static DTORs via cxa_atexit.
    
    For at least one target (Darwin) the platform convention is to
    register static destructors (i.e. __attribute__((destructor)))
    with __cxa_atexit rather than placing them into a list that is
    run by some other mechanism.
    
    This patch provides a target hook that allows a target to opt
    into this and handling for the process in ipa_cdtor_merge ().
    
    When the mode is enabled (dtors_from_cxa_atexit is set) we:
    
     * Generate new CTORs to register static destructors with
       __cxa_atexit and add them to the existing list of CTORs;
       we then process the revised CTORs list.
    
     * We sort the DTORs into priority and then TU order, this
       means that they are registered in that order with
       __cxa_atexit () and therefore will be run in the reverse
       order.
    
     * Likewise, CTORs are sorted into priority and then TU order,
       which means that they will run in that order.
    
    This matches the behavior of using init/fini (or
    mod_init_func/mod_term_func) sections.
    
    This also fixes a bug where Fortran needs a DTOR to be run to
    close IO.
    
    Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
    
            PR fortran/102992
    
    gcc/ChangeLog:
    
            * config/darwin.h (TARGET_DTORS_FROM_CXA_ATEXIT): New.
            * doc/tm.texi: Regenerated.
            * doc/tm.texi.in: Add TARGET_DTORS_FROM_CXA_ATEXIT hook.
            * ipa.c (cgraph_build_static_cdtor_1): Return the built
            function decl.
            (build_cxa_atexit_decl): New.
            (build_dso_handle_decl): New.
            (build_cxa_dtor_registrations): New.
            (compare_cdtor_tu_order): New.
            (build_cxa_atexit_fns): New.
            (ipa_cdtor_merge): If dtors_from_cxa_atexit is set,
            process the DTORs/CTORs accordingly.
            (pass_ipa_cdtor_merge::gate): Also run if
            dtors_from_cxa_atexit is set.
            * target.def (dtors_from_cxa_atexit): New hook.
    
    (cherry picked from commit fabe8cc41e9b01913e2016861237d1d99d7567bf)
Comment 39 Jakub Jelinek 2022-05-06 08:31:38 UTC
GCC 12.1 is being released, retargeting bugs to GCC 12.2.
Comment 40 GCC Commits 2022-05-29 19:15:19 UTC
The releases/gcc-10 branch has been updated by Iain D Sandoe <iains@gcc.gnu.org>:

https://gcc.gnu.org/g:16db663772c03eded30d8ede7ffbc2b9de25f648

commit r10-10802-g16db663772c03eded30d8ede7ffbc2b9de25f648
Author: Iain Sandoe <iain@sandoe.co.uk>
Date:   Thu Nov 4 09:37:14 2021 +0000

    IPA: Provide a mechanism to register static DTORs via cxa_atexit.
    
    For at least one target (Darwin) the platform convention is to
    register static destructors (i.e. __attribute__((destructor)))
    with __cxa_atexit rather than placing them into a list that is
    run by some other mechanism.
    
    This patch provides a target hook that allows a target to opt
    into this and handling for the process in ipa_cdtor_merge ().
    
    When the mode is enabled (dtors_from_cxa_atexit is set) we:
    
     * Generate new CTORs to register static destructors with
       __cxa_atexit and add them to the existing list of CTORs;
       we then process the revised CTORs list.
    
     * We sort the DTORs into priority and then TU order, this
       means that they are registered in that order with
       __cxa_atexit () and therefore will be run in the reverse
       order.
    
     * Likewise, CTORs are sorted into priority and then TU order,
       which means that they will run in that order.
    
    This matches the behavior of using init/fini (or
    mod_init_func/mod_term_func) sections.
    
    This also fixes a bug where Fortran needs a DTOR to be run to
    close IO.
    
    Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
    
            PR fortran/102992
    
    gcc/ChangeLog:
    
            * config/darwin.h (TARGET_DTORS_FROM_CXA_ATEXIT): New.
            * doc/tm.texi: Regenerated.
            * doc/tm.texi.in: Add TARGET_DTORS_FROM_CXA_ATEXIT hook.
            * ipa.c (cgraph_build_static_cdtor_1): Return the built
            function decl.
            (build_cxa_atexit_decl): New.
            (build_dso_handle_decl): New.
            (build_cxa_dtor_registrations): New.
            (compare_cdtor_tu_order): New.
            (build_cxa_atexit_fns): New.
            (ipa_cdtor_merge): If dtors_from_cxa_atexit is set,
            process the DTORs/CTORs accordingly.
            (pass_ipa_cdtor_merge::gate): Also run if
            dtors_from_cxa_atexit is set.
            * target.def (dtors_from_cxa_atexit): New hook.
    
    (cherry picked from commit fabe8cc41e9b01913e2016861237d1d99d7567bf)
Comment 41 Iain Sandoe 2022-05-29 19:27:15 UTC
fixed for open branches, the equivalent is needed to build working compilers on darwin21 for earlier gcc versions.