Skip to content

Commit

Permalink
POSIX: generic wrappers for all setjmp.h symbols
Browse files Browse the repository at this point in the history
This patch extends the generic wrappers of sigsetjmp to all of the *jmp
wrapper functions in <setjmp.h>

The C standard allows these to be defined as macros, rather than
explicit functions, which cannot be referenced by Fortran C bindings, so
we cannot assume that these functions exist, even when using a compliant
libc.

As with sigsetjmp, these functions are now disabled on default, and
raise a runtime error if called by the program.  Realistically, they
will only be defined by an autoconf-configured build.

This is required for older Linux distributions where libc does not
define longjmp.  Some Cray compilers also report incompatible longjmp
function signatures, which may indicate a similar problem (although this
needs further investigation).
  • Loading branch information
marshallward committed Dec 10, 2022
1 parent 9bc8772 commit 15b5eef
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 15 deletions.
31 changes: 25 additions & 6 deletions ac/configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -235,12 +235,24 @@ AC_CONFIG_COMMANDS(Makefile.dep, [make depend])

# POSIX verification tests

# These symbols may be defined as macros, making them inaccessible by Fortran.
# These three exist in modern BSD and Linux libc, so we just confirm them.
# But one day, we many need to handle them more carefully.
AX_FC_CHECK_BIND_C([setjmp], [], [AC_MSG_ERROR([Could not find setjmp.])])
AX_FC_CHECK_BIND_C([longjmp], [], [AC_MSG_ERROR([Could not find longjmp.])])
AX_FC_CHECK_BIND_C([siglongjmp], [], [AC_MSG_ERROR([Could not find siglongjmp.])])
# Symbols in <setjmp.h> may be defined as macros, making them inaccessible by
# Fortran C bindings. `sigsetjmp` is known to have an internal symbol in
# glibc, so we check for this possibility. For the others, we only check for
# existence.

# If the need arises, we may want to define these under a standalone macro.

# Validate the setjmp symbol
AX_FC_CHECK_BIND_C([setjmp],
[SETJMP="setjmp"], [SETJMP="setjmp_missing"]
)
AC_DEFINE_UNQUOTED([SETJMP_NAME], ["${SETJMP}"])

# Validate the longjmp symbol
AX_FC_CHECK_BIND_C([longjmp],
[LONGJMP="longjmp"], [LONGJMP="longjmp_missing"]
)
AC_DEFINE_UNQUOTED([LONGJMP_NAME], ["${LONGJMP}"])

# Determine the sigsetjmp symbol. If missing, then point to sigsetjmp_missing.
#
Expand All @@ -256,6 +268,13 @@ for sigsetjmp_fn in sigsetjmp __sigsetjmp; do
done
AC_DEFINE_UNQUOTED([SIGSETJMP_NAME], ["${SIGSETJMP}"])

# Validate the siglongjmp symbol
AX_FC_CHECK_BIND_C([siglongjmp],
[SIGLONGJMP="siglongjmp"], [SETJMP="siglongjmp_missing"]
)
AC_DEFINE_UNQUOTED([SIGLONGJMP_NAME], ["${SIGLONGJMP}"])


# Verify the size of nonlocal jump buffer structs
# NOTE: This requires C compiler, but can it be done with a Fortran compiler?
AC_LANG_PUSH([C])
Expand Down
51 changes: 44 additions & 7 deletions src/framework/posix.F90
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ function sleep_posix(seconds) result(rc) bind(c, name="sleep")
!! returns 0. When `longjmp` is later called, the program is restored to the
!! point where `setjmp` was called, except it now returns a value (rc) as
!! specified by `longjmp`.
function setjmp(env) result(rc) bind(c, name="setjmp")
function setjmp(env) result(rc) bind(c, name=SETJMP_NAME)
! #include <setjmp.h>
! int setjmp(jmp_buf env);
import :: jmp_buf, c_int
Expand Down Expand Up @@ -175,7 +175,7 @@ end function sigsetjmp

!> C interface to POSIX longjmp()
!! Users should use the Fortran-defined longjmp() function.
subroutine longjmp_posix(env, val) bind(c, name="longjmp")
subroutine longjmp_posix(env, val) bind(c, name=LONGJMP_NAME)
! #include <setjmp.h>
! int longjmp(jmp_buf env, int val);
import :: jmp_buf, c_int
Expand All @@ -188,7 +188,7 @@ end subroutine longjmp_posix

!> C interface to POSIX siglongjmp()
!! Users should use the Fortran-defined siglongjmp() function.
subroutine siglongjmp_posix(env, val) bind(c, name="longjmp")
subroutine siglongjmp_posix(env, val) bind(c, name=SIGLONGJMP_NAME)
! #include <setjmp.h>
! int siglongjmp(jmp_buf env, int val);
import :: sigjmp_buf, c_int
Expand Down Expand Up @@ -344,11 +344,36 @@ subroutine siglongjmp(env, val)
call siglongjmp_posix(env, val_c)
end subroutine siglongjmp


! Symbols in <setjmp.h> may be platform-dependent and may not exist if defined
! as a macro. The following functions permit compilation when they are
! unavailable, and report a runtime error if used in the program.

!> Placeholder function for a missing or unconfigured setjmp
function setjmp_missing(env) result(rc) bind(c)
type(jmp_buf), intent(in) :: env
!< Current process state (unused)
integer(kind=c_int) :: rc
!< Function return code (unused)

print '(a)', 'ERROR: setjmp() is not implemented in this build.'
print '(a)', 'Recompile with autoconf or -DSETJMP_NAME=\"<symbol name>\".'
error stop
end function setjmp_missing

!> Placeholder function for a missing or unconfigured longjmp
subroutine longjmp_missing(env, val) bind(c)
type(jmp_buf), intent(in) :: env
!< Current process state (unused)
integer(kind=c_int), value, intent(in) :: val
!< Enable signal state flag (unused)

print '(a)', 'ERROR: longjmp() is not implemented in this build.'
print '(a)', 'Recompile with autoconf or -DLONGJMP_NAME=\"<symbol name>\".'
error stop
end subroutine longjmp_missing

!> Placeholder function for a missing or unconfigured sigsetjmp
!!
!! The symbol for sigsetjmp can be platform-dependent and may not exist if
!! defined as a macro. This function allows compilation, and reports a runtime
!! error if used in the program.
function sigsetjmp_missing(env, savesigs) result(rc) bind(c)
type(sigjmp_buf), intent(in) :: env
!< Current process state (unused)
Expand All @@ -362,4 +387,16 @@ function sigsetjmp_missing(env, savesigs) result(rc) bind(c)
error stop
end function sigsetjmp_missing

!> Placeholder function for a missing or unconfigured siglongjmp
subroutine siglongjmp_missing(env, val) bind(c)
type(sigjmp_buf), intent(in) :: env
!< Current process state (unused)
integer(kind=c_int), value, intent(in) :: val
!< Enable signal state flag (unused)

print '(a)', 'ERROR: siglongjmp() is not implemented in this build.'
print '(a)', 'Recompile with autoconf or -DSIGLONGJMP_NAME=\"<symbol name>\".'
error stop
end subroutine siglongjmp_missing

end module posix
16 changes: 14 additions & 2 deletions src/framework/posix.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,24 @@
#define SIZEOF_SIGJMP_BUF SIZEOF_JMP_BUF
#endif

! glibc defines sigsetjmp as __sigsetjmp via macro readable from <setjmp.h>.
! Wrappers to <setjmp.h> are disabled on default.
#ifndef SETJMP_NAME
#define SETJMP_NAME "setjmp_missing"
#endif

#ifndef LONGJMP_NAME
#define LONGJMP_NAME "longjmp_missing"
#endif

#ifndef SIGSETJMP_NAME
#define SIGSETJMP_NAME "sigsetjmp_missing"
#endif

! This should be defined by /usr/include/signal.h
#ifndef SIGLONGJMP_NAME
#define SIGLONGJMP_NAME "siglongjmp_missing"
#endif

! This should be defined by <signal.h>;
! If unset, we use the most common (x86) value
#ifndef POSIX_SIGUSR1
#define POSIX_SIGUSR1 10
Expand Down

0 comments on commit 15b5eef

Please sign in to comment.