Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

math functions, errno and Tcl 8.6

198 views
Skip to first unread message

phil...@gmail.com

unread,
Jan 29, 2018, 7:15:57 PM1/29/18
to
Hi,

We're running into some problems with the use of math functions when we port to Tcl 8.6. It seems to alter the behavior of some of our math functions even though we aren't using Tcl in this code. This small program shows the issue:

#include <iostream>
#include <math.h>
#include <errno.h>


int main( int argc, char* argv[] ) {

std::cout << "acosh(.4)=" << acosh(.4) << std::endl;
std::cout << "errno=" << errno << std::endl;

}

If I link this with only the math library, the output is:

$ ./doit.sh
acosh(.4)=-nan
errno=33


If I link the Tcl 8.6 library into this program, the output is:

$ ./doit.sh
acosh(.4)=-nan
errno=0

This seems true for a number of versions. I tried ActiveTcl 8.6.7 and freshly downloaded Tcl 8.6.8.

Seems like a bug?

Phil


Arjen Markus

unread,
Jan 30, 2018, 2:59:19 AM1/30/18
to
I have tried this with g++ and Tcl 8.6.6 under Cygwin and MSVC and Tcl 8.6.1 under Windows (an older ActiveState distribution) and I do not see any such behaviour.

Can you specify which OS and compiler you are using?

(I did see a warning about exceptions when I added the Tcl library with MSVC, but that made no particular difference to the program's output.)

Regards,

Arjen

Ralf Fassel

unread,
Jan 30, 2018, 5:59:38 AM1/30/18
to
* phil...@gmail.com
| If I link the Tcl 8.6 library into this program, the output is:
>
| $ ./doit.sh
| acosh(.4)=-nan
| errno=0
>
| This seems true for a number of versions. I tried ActiveTcl 8.6.7 and
| freshly downloaded Tcl 8.6.8.

I can reproduce this in OpenSuse 42.3, gcc version 4.8.5 and
tcl-8.6.7-8.1.x86_64.

% g++ -o t t.cxx
% ./t
acosh(.4)=-nan
errno=33

% g++ -o t t.cxx -ltcl8.6
% ./t
acosh(.4)=-nan
errno=0

If I change the program as suggested in math_error(7) to

#include <iostream>
#include <math.h>
#include <errno.h>
#include <fenv.h>
int main( int argc, char* argv[] ) {
errno = 0;
feclearexcept(FE_ALL_EXCEPT);
std::cout << "acosh(.4)=" << acosh(.4) << std::endl;
std::cout << "fetestexcept()=" << fetestexcept(FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW) << std::endl;
std::cout << "errno=" << errno << std::endl;
}

I get:

% g++ -o t t.cxx
% ./t
acosh(.4)=-nan
fetestexcept()=1
errno=33

% g++ -o t t.cxx -ltcl8.6
% ./t
acosh(.4)=-nan
fetestexcept()=1
errno=0

I.e. fetestexcept() indicating an error in both cases, but errno does
not reflect the error.

HTH
R'

Arjen Markus

unread,
Jan 30, 2018, 7:24:56 AM1/30/18
to
Is there any reference to Tcl in the executable because of linking the program that way? I did not see any when I tried it on Windows. That may be the cause of the difference.

Regards,

Arjen

Ralf Fassel

unread,
Jan 30, 2018, 9:02:34 AM1/30/18
to
* Arjen Markus <arjen.m...@gmail.com>
| Is there any reference to Tcl in the executable because of linking the
| program that way? I did not see any when I tried it on Windows. That
| may be the cause of the difference.

Without tcl:

% g++ -o t t.cxx
% ldd ./t
linux-vdso.so.1 (0x00007fff51de9000)
libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00007f26660f3000)
libm.so.6 => /lib64/libm.so.6 (0x00007f2665df6000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f2665bdf000)
libc.so.6 => /lib64/libc.so.6 (0x00007f266583e000)
/lib64/ld-linux-x86-64.so.2 (0x00007f266647c000)

With TCL:

% g++ -o t t.cxx -l tcl8.6
% ldd t
linux-vdso.so.1 (0x00007ffc4724e000)
libtcl8.6.so => /usr/lib64/libtcl8.6.so (0x00007fd2d71b0000)
libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00007fd2d6e27000)
libm.so.6 => /lib64/libm.so.6 (0x00007fd2d6b2a000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fd2d6913000)
libc.so.6 => /lib64/libc.so.6 (0x00007fd2d6572000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007fd2d636e000)
libz.so.1 => /lib64/libz.so.1 (0x00007fd2d6158000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fd2d5f3b000)
/lib64/ld-linux-x86-64.so.2 (0x00007fd2d7562000)

The difference seems to be -lz -ldl -lpthread -ltcl8.6, but neither of
the first three libs triggers the error. Linking against libz, libdl or
libpthread, either alone or in combinations does not trigger the error.
Only when linked against libtcl, the error occurs.


The nm output is identical apart from the offsets, so it is likely the
shared libs init code that triggers the behaviour.

% nm t-no-tcl
U acosh@@GLIBC_2.2.5
0000000000601090 B __bss_start
00000000006011b0 b completed.6385
U __cxa_atexit@@GLIBC_2.2.5
0000000000601080 D __data_start
0000000000601080 W data_start
0000000000400950 t deregister_tm_clones
00000000004009c0 t __do_global_dtors_aux
0000000000600dd8 t __do_global_dtors_aux_fini_array_entry
0000000000601088 D __dso_handle
0000000000600de8 d _DYNAMIC
0000000000601090 D _edata
00000000006011b8 B _end
U __errno_location@@GLIBC_2.2.5
U feclearexcept@@GLIBC_2.2.5
U fetestexcept@@GLIBC_2.2.5
0000000000400bb4 T _fini
00000000004009e0 t frame_dummy
0000000000600dc8 t __frame_dummy_init_array_entry
0000000000400d84 r __FRAME_END__
0000000000601000 d _GLOBAL_OFFSET_TABLE_
0000000000400b28 t _GLOBAL__sub_I_main
w __gmon_start__
0000000000400be8 r __GNU_EH_FRAME_HDR
0000000000400808 T _init
0000000000600dd8 t __init_array_end
0000000000600dc8 t __init_array_start
0000000000400bc0 R _IO_stdin_used
0000000000600de0 d __JCR_END__
0000000000600de0 d __JCR_LIST__
0000000000400bb0 T __libc_csu_fini
0000000000400b40 T __libc_csu_init
U __libc_start_main@@GLIBC_2.2.5
0000000000400a0d T main
0000000000400980 t register_tm_clones
0000000000400920 T _start
0000000000601090 D __TMC_END__
0000000000400aeb t _Z41__static_initialization_and_destruction_0ii
U _ZNSolsEd@@GLIBCXX_3.4
U _ZNSolsEi@@GLIBCXX_3.4
U _ZNSolsEPFRSoS_E@@GLIBCXX_3.4
U _ZNSt8ios_base4InitC1Ev@@GLIBCXX_3.4
U _ZNSt8ios_base4InitD1Ev@@GLIBCXX_3.4
00000000006010a0 B _ZSt4cout@@GLIBCXX_3.4
U _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@@GLIBCXX_3.4
00000000006011b1 b _ZStL8__ioinit
U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@@GLIBCXX_3.4

% nm t-with-tcl
U acosh@@GLIBC_2.2.5
0000000000602090 B __bss_start
00000000006021b0 b completed.6385
U __cxa_atexit@@GLIBC_2.2.5
0000000000602080 D __data_start
0000000000602080 W data_start
0000000000400a60 t deregister_tm_clones
0000000000400ad0 t __do_global_dtors_aux
0000000000601dc8 t __do_global_dtors_aux_fini_array_entry
0000000000602088 D __dso_handle
0000000000601dd8 d _DYNAMIC
0000000000602090 D _edata
00000000006021b8 B _end
U __errno_location@@GLIBC_2.2.5
U feclearexcept@@GLIBC_2.2.5
U fetestexcept@@GLIBC_2.2.5
0000000000400cc4 T _fini
0000000000400af0 t frame_dummy
0000000000601db8 t __frame_dummy_init_array_entry
0000000000400e94 r __FRAME_END__
0000000000602000 d _GLOBAL_OFFSET_TABLE_
0000000000400c38 t _GLOBAL__sub_I_main
w __gmon_start__
0000000000400cf8 r __GNU_EH_FRAME_HDR
0000000000400918 T _init
0000000000601dc8 t __init_array_end
0000000000601db8 t __init_array_start
0000000000400cd0 R _IO_stdin_used
0000000000601dd0 d __JCR_END__
0000000000601dd0 d __JCR_LIST__
0000000000400cc0 T __libc_csu_fini
0000000000400c50 T __libc_csu_init
U __libc_start_main@@GLIBC_2.2.5
0000000000400b1d T main
0000000000400a90 t register_tm_clones
0000000000400a30 T _start
0000000000602090 D __TMC_END__
0000000000400bfb t _Z41__static_initialization_and_destruction_0ii
U _ZNSolsEd@@GLIBCXX_3.4
U _ZNSolsEi@@GLIBCXX_3.4
U _ZNSolsEPFRSoS_E@@GLIBCXX_3.4
U _ZNSt8ios_base4InitC1Ev@@GLIBCXX_3.4
U _ZNSt8ios_base4InitD1Ev@@GLIBCXX_3.4
00000000006020a0 B _ZSt4cout@@GLIBCXX_3.4
U _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@@GLIBCXX_3.4
00000000006021b1 b _ZStL8__ioinit
U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@@GLIBCXX_3.4


Strace shows the loading of /usr/lib64/libtcl8.6.so and some additional
calls (look for set_tid_address ff):

% strace ./t-with-tcl
execve("./t", ["./t"], [/* 143 vars */]) = 0
brk(0) = 0xc4f000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=182580, ...}) = 0
mmap(NULL, 182580, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fc89ae4d000
close(3) = 0
open("/usr/lib64/libtcl8.6.so", 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\240\335\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0555, st_size=1774976, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc89ae4c000
mmap(NULL, 3873840, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fc89a8a8000
mprotect(0x7fc89aa4a000, 2093056, PROT_NONE) = 0
mmap(0x7fc89ac49000, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1a1000) = 0x7fc89ac49000
mmap(0x7fc89ac59000, 3120, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fc89ac59000
close(3) = 0
open("/usr/lib64/libstdc++.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\0p7\t\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1598552, ...}) = 0
mmap(NULL, 3706880, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fc89a51f000
mprotect(0x7fc89a69a000, 2093056, PROT_NONE) = 0
mmap(0x7fc89a899000, 49152, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x17a000) = 0x7fc89a899000
mmap(0x7fc89a8a5000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fc89a8a5000
close(3) = 0
open("/lib64/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\260T\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1100480, ...}) = 0
mmap(NULL, 3129672, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fc89a222000
mprotect(0x7fc89a31d000, 2097152, PROT_NONE) = 0
mmap(0x7fc89a51d000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xfb000) = 0x7fc89a51d000
close(3) = 0
open("/lib64/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\260/\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=92552, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc89ae4b000
mmap(NULL, 2188336, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fc89a00b000
mprotect(0x7fc89a021000, 2093056, PROT_NONE) = 0
mmap(0x7fc89a220000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x15000) = 0x7fc89a220000
close(3) = 0
open("/lib64/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\360\7\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1916856, ...}) = 0
mmap(NULL, 3803616, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fc899c6a000
mprotect(0x7fc899e02000, 2093056, PROT_NONE) = 0
mmap(0x7fc89a001000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x197000) = 0x7fc89a001000
mmap(0x7fc89a007000, 14816, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fc89a007000
close(3) = 0
open("/lib64/libdl.so.2", 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\220\r\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=18712, ...}) = 0
mmap(NULL, 2109680, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fc899a66000
mprotect(0x7fc899a68000, 2097152, PROT_NONE) = 0
mmap(0x7fc899c68000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7fc899c68000
close(3) = 0
open("/lib64/libz.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\0P#\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=88216, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc89ae4a000
mmap(NULL, 2183304, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fc899850000
mprotect(0x7fc899865000, 2093056, PROT_NONE) = 0
mmap(0x7fc899a64000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x14000) = 0x7fc899a64000
close(3) = 0
open("/lib64/libpthread.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\320r\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=135952, ...}) = 0
mmap(NULL, 2213008, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fc899633000
mprotect(0x7fc89964b000, 2093056, PROT_NONE) = 0
mmap(0x7fc89984a000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x17000) = 0x7fc89984a000
mmap(0x7fc89984c000, 13456, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fc89984c000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc89ae49000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc89ae48000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc89ae46000
arch_prctl(ARCH_SET_FS, 0x7fc89ae46740) = 0
mprotect(0x7fc89a001000, 16384, PROT_READ) = 0
mprotect(0x7fc89984a000, 4096, PROT_READ) = 0
mprotect(0x7fc899a64000, 4096, PROT_READ) = 0
mprotect(0x7fc899c68000, 4096, PROT_READ) = 0
mprotect(0x7fc89a220000, 4096, PROT_READ) = 0
mprotect(0x7fc89a51d000, 4096, PROT_READ) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc89ae45000
mprotect(0x7fc89a899000, 40960, PROT_READ) = 0
mprotect(0x7fc89ac49000, 57344, PROT_READ) = 0
mprotect(0x601000, 4096, PROT_READ) = 0
mprotect(0x7fc89ae7a000, 4096, PROT_READ) = 0
munmap(0x7fc89ae4d000, 182580) = 0
set_tid_address(0x7fc89ae46a10) = 32025
set_robust_list(0x7fc89ae46a20, 24) = 0
rt_sigaction(SIGRTMIN, {0x7fc899639d60, [], SA_RESTORER|SA_SIGINFO, 0x7fc899643b20}, NULL, 8) = 0
rt_sigaction(SIGRT_1, {0x7fc899639df0, [], SA_RESTORER|SA_RESTART|SA_SIGINFO, 0x7fc899643b20}, NULL, 8) = 0
rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
getrlimit(RLIMIT_STACK, {rlim_cur=8720000, rlim_max=RLIM64_INFINITY}) = 0
brk(0) = 0xc4f000
brk(0xc81000) = 0xc81000
futex(0x7fc89a8a605c, FUTEX_WAKE_PRIVATE, 2147483647) = 0
futex(0x7fc89a8a6068, FUTEX_WAKE_PRIVATE, 2147483647) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 4), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc89ae79000
write(1, "acosh(.4)=-nan\n", 15acosh(.4)=-nan
) = 15
write(1, "fetestexcept()=1\n", 17fetestexcept()=1
) = 17
write(1, "errno=0\n", 8errno=0
) = 8
exit_group(0) = ?
+++ exited with 0 +++


% strace ./t-no-tcl
execve("./t-no-tcl", ["./t-no-tcl"], [/* 143 vars */]) = 0
brk(0) = 0x1d12000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=182580, ...}) = 0
mmap(NULL, 182580, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fbaa9b21000
close(3) = 0
open("/usr/lib64/libstdc++.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\0p7\t\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1598552, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fbaa9b20000
mmap(NULL, 3706880, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fbaa95a5000
mprotect(0x7fbaa9720000, 2093056, PROT_NONE) = 0
mmap(0x7fbaa991f000, 49152, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x17a000) = 0x7fbaa991f000
mmap(0x7fbaa992b000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fbaa992b000
close(3) = 0
open("/lib64/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\260T\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1100480, ...}) = 0
mmap(NULL, 3129672, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fbaa92a8000
mprotect(0x7fbaa93a3000, 2097152, PROT_NONE) = 0
mmap(0x7fbaa95a3000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xfb000) = 0x7fbaa95a3000
close(3) = 0
open("/lib64/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\260/\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=92552, ...}) = 0
mmap(NULL, 2188336, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fbaa9091000
mprotect(0x7fbaa90a7000, 2093056, PROT_NONE) = 0
mmap(0x7fbaa92a6000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x15000) = 0x7fbaa92a6000
close(3) = 0
open("/lib64/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\360\7\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1916856, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fbaa9b1f000
mmap(NULL, 3803616, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fbaa8cf0000
mprotect(0x7fbaa8e88000, 2093056, PROT_NONE) = 0
mmap(0x7fbaa9087000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x197000) = 0x7fbaa9087000
mmap(0x7fbaa908d000, 14816, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fbaa908d000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fbaa9b1e000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fbaa9b1c000
arch_prctl(ARCH_SET_FS, 0x7fbaa9b1c740) = 0
mprotect(0x7fbaa9087000, 16384, PROT_READ) = 0
mprotect(0x7fbaa92a6000, 4096, PROT_READ) = 0
mprotect(0x7fbaa95a3000, 4096, PROT_READ) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fbaa9b1b000
mprotect(0x7fbaa991f000, 40960, PROT_READ) = 0
mprotect(0x600000, 4096, PROT_READ) = 0
mprotect(0x7fbaa9b4e000, 4096, PROT_READ) = 0
munmap(0x7fbaa9b21000, 182580) = 0
brk(0) = 0x1d12000
brk(0x1d44000) = 0x1d44000
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 4), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fbaa9b4d000
write(1, "acosh(.4)=-nan\n", 15acosh(.4)=-nan
) = 15
write(1, "fetestexcept()=1\n", 17fetestexcept()=1
) = 17
write(1, "errno=33\n", 9errno=33
) = 9
exit_group(0) = ?
+++ exited with 0 +++

HTH
R'

phil...@gmail.com

unread,
Jan 30, 2018, 5:50:28 PM1/30/18
to
Thanks for the responses - it does seem to be related to Linux shared library implementation - possibly some issue with the way Tcl is doing stubs etc? It is new between Tcl 8.4 and Tcl 8.6 - I haven't tried Tcl 8.5.

I am running on Redhat Enterprise Linux 6.8.

When I build Tcl with the --disable-shared flag to configure, the problem goes away.

I also note, as suggested above, that the fenv floating point exception library does still function as normal, it's the errno handling that is broken.

Phil

phil...@gmail.com

unread,
Jan 30, 2018, 5:59:41 PM1/30/18
to
I just tried Tcl 8.5, and it appears that it is also present there.

Ralf Fassel

unread,
Jan 31, 2018, 4:21:47 AM1/31/18
to
* phil...@gmail.com
| Thanks for the responses - it does seem to be related to Linux shared
| library implementation - possibly some issue with the way Tcl is doing
| stubs etc? It is new between Tcl 8.4 and Tcl 8.6 - I haven't tried
| Tcl 8.5.
>
| I am running on Redhat Enterprise Linux 6.8.
>
| When I build Tcl with the --disable-shared flag to configure, the
| problem goes away.

I see the problem with tcl85 in a -disabled-shared configuration, too:

$ tclsh
% info patchlevel
8.5.19
% parray tcl_platform
tcl_platform(byteOrder) = littleEndian
tcl_platform(engine) = Tcl
tcl_platform(machine) = x86_64
tcl_platform(os) = Linux
tcl_platform(osVersion) = 4.4.104-39-default
tcl_platform(platform) = unix
tcl_platform(pointerSize) = 8
tcl_platform(user) = ralf
tcl_platform(wordSize) = 8


$ g++ -o t85 t.cxx -ltcl8.5 -L/path/to/tcl85
$ env LD_LIBRARY_PATH=/path/to/tcl85 ./t85
acosh(.4)=-nan
fetestexcept()=1
errno=0

HTH
R'

briang

unread,
Jan 31, 2018, 10:17:59 AM1/31/18
to
"errno" as a global can't work in a threaded environment. Because Tcl is bringing in pthreads, the definition of "errno" is changed. I suspect the way the test program is compiled is not accounting for the threaded nature of errno, leading to the errant behavior. See this post for more info: https://stackoverflow.com/questions/1694164/is-errno-thread-safe

-Brian

briang

unread,
Jan 31, 2018, 12:06:21 PM1/31/18
to
On Monday, January 29, 2018 at 4:15:57 PM UTC-8, phil...@gmail.com wrote:
> Hi,
>
> We're running into some problems with the use of math functions when we port to Tcl 8.6. It seems to alter the behavior of some of our math functions even though we aren't using Tcl in this code. This small program shows the issue:
>
> #include <iostream>
> #include <math.h>
> #include <errno.h>
>
>
> int main( int argc, char* argv[] ) {
>
> std::cout << "acosh(.4)=" << acosh(.4) << std::endl;
> std::cout << "errno=" << errno << std::endl;
>
> }

Are you certain that none of the crap between "acosh(.4)" and "errno", i.e. "<< ... << ... <<", never ever touch errno? I would find that hard to believe.

Another thought I had: are only floating point errors affected, or all errors?

-Brian

phil...@gmail.com

unread,
Jan 31, 2018, 1:54:27 PM1/31/18
to

> "errno" as a global can't work in a threaded environment. Because Tcl is bringing in pthreads, the definition of "errno" is changed. I suspect the way the test program is compiled is not accounting for the threaded nature of errno, leading to the errant behavior.

threading is not the issue. No threads are active in the program and adding or removing the pthread library has no effect.

> Are you certain that none of the crap between "acosh(.4)" and "errno", i.e. "<< ... << ... <<", never ever touch errno? I would find that hard to believe.

The stream insertion operators are not affecting errno.

>
> Another thought I had: are only floating point errors affected, or all errors?

So far, it only looks like math functions cause this mishandling of errno. fopen, for example, still sets errno appropriately.

I note that the Tcl shared library has an extra errno reference:

$ nm /usr/local/tcl8.6.8/lib/libtcl8.6.so | grep errno
U __errno_location@@GLIBC_2.2.5
U __h_errno_location@@GLIBC_2.2.5

I don't know if that has something to do with it. Perhaps something in the shared library stub init process is inadvertently pointing math things at the wrong errno?

Phil


phil...@gmail.com

unread,
Jan 31, 2018, 2:56:56 PM1/31/18
to
Interestingly, the C++ library also seems to be involved. Here is a new version of the test program:


#include <math.h>
#include <errno.h>
#include <stdio.h>

int main( int argc, char* argv[] ) {
errno = 0;
double ac = acosh(.4);
int errno_copy = errno;
printf( "acosh(.4)=%g errno=%d\n", ac, errno_copy );
}

The main program can be compiled with gcc. If the link is done with c++, the bug is present. If it is done with gcc, then it is not:

C linkage
acosh(.4)=-nan errno=33
C++ linkage
acosh(.4)=-nan errno=0

Phil

phil...@gmail.com

unread,
Jan 31, 2018, 3:08:28 PM1/31/18
to
The C/C++ environments that exhibit this are both

GCC 4.4.7 (stock Red Hat Enterprise 6.8 compiler)
GCC 6.2.1 (RedHat Developer Toolset 6)

Phil

briang

unread,
Jan 31, 2018, 5:22:24 PM1/31/18
to
On Wednesday, January 31, 2018 at 10:54:27 AM UTC-8, phil...@gmail.com wrote:
> > "errno" as a global can't work in a threaded environment. Because Tcl is bringing in pthreads, the definition of "errno" is changed. I suspect the way the test program is compiled is not accounting for the threaded nature of errno, leading to the errant behavior.
>
> threading is not the issue. No threads are active in the program and adding or removing the pthread library has no effect.

errno implementation is not dependent on active threads, but rather a compile time decision whether the code is to be thread safe or not. To be thread-safe, each thread has it's own private errno.

>
> > Are you certain that none of the crap between "acosh(.4)" and "errno", i.e. "<< ... << ... <<", never ever touch errno? I would find that hard to believe.
>
> The stream insertion operators are not affecting errno.

Except maybe if there were a stream error, including retries (EINTR, EAGAIN).

>
> >
> > Another thought I had: are only floating point errors affected, or all errors?
>
> So far, it only looks like math functions cause this mishandling of errno. fopen, for example, still sets errno appropriately.

I believe Tcl has a configure option to make floating point operations not produce errors so that +/-Inf, NaN can be handled cleanly in Tcl scripts.

>
> I note that the Tcl shared library has an extra errno reference:
>
> $ nm /usr/local/tcl8.6.8/lib/libtcl8.6.so | grep errno
> U __errno_location@@GLIBC_2.2.5
> U __h_errno_location@@GLIBC_2.2.5

These are pointers. You can't tell if they point to the same place or not.

>
> I don't know if that has something to do with it. Perhaps something in the shared library stub init process is inadvertently pointing math things at the wrong errno?

If this is the case, then I suspect it will boil down to a compile time decision.

errno is one of those poorly designed unix-ism that should never have been a global value. It should have been a function call.

-Brian

Ralf Fassel

unread,
Feb 1, 2018, 4:54:22 AM2/1/18
to
* phil...@gmail.com
Not here, I get the error in both cases:

$ gcc -o t2 t2.cxx -ltcl8.6 -lm
$ ./t2
acosh(.4)=-nan errno=0
$ g++ -o t2 t2.cxx -ltcl8.6 -lm
$ ./t2
acosh(.4)=-nan errno=0

R'

Ralf Fassel

unread,
Feb 1, 2018, 5:09:51 AM2/1/18
to
* Ralf Fassel <ral...@gmx.de>
| | The main program can be compiled with gcc. If the link is done with
| | c++, the bug is present. If it is done with gcc, then it is not:
| >
| | C linkage
| | acosh(.4)=-nan errno=33
| | C++ linkage
| | acosh(.4)=-nan errno=0
>
| Not here, I get the error in both cases:
>
| $ gcc -o t2 t2.cxx -ltcl8.6 -lm
| $ ./t2
| acosh(.4)=-nan errno=0
| $ g++ -o t2 t2.cxx -ltcl8.6 -lm
| $ ./t2
| acosh(.4)=-nan errno=0

'Interesting':

$ gcc -o t2 t2.c -ltcl8.6 -lm
$ ./t2
acosh(.4)=-nan errno=0

$ gcc -o t2 t2.c -lm -ltcl8.6
$ ./t2
acosh(.4)=-nan errno=33

I.e. the order of libraries on the link line changes the program
behaviour...

R'

Ralf Fassel

unread,
Feb 1, 2018, 5:21:46 AM2/1/18
to
* briang <bgriffin...@gmail.com>
| "errno" as a global can't work in a threaded environment. Because Tcl
| is bringing in pthreads, the definition of "errno" is changed. I
| suspect the way the test program is compiled is not accounting for the
| threaded nature of errno, leading to the errant behavior. See this
| post for more info:
| https://stackoverflow.com/questions/1694164/is-errno-thread-safe

If I understand correctly, all this says is that errno is no longer a
'simple' global variable but rather a macro resulting in a function call
to the location of the underlying thread-specific storage. This is why
you #include <errno.h> instead of "extern int errno" or somesuch.

Even if I start a thread and change errno in it, the program still
prints the expected result:

#include <math.h>
#include <errno.h>
#include <stdio.h>
#include <pthread.h>

void *func(void *arg) {
errno = -1;
printf("errno in thread %d\n", errno);
return 0;
}

int main( int argc, char* argv[] ) {

errno = 0;
double ac = acosh(.4);

pthread_t t;
if (0 == pthread_create(&t, 0, func, 0)
&& 0 == pthread_join(t,0)) {
printf( "acosh(.4)=%g errno=%d\n", ac, errno );
} else {
perror("threads failed");
}
return 0;
}
// End of file

$ gcc -o t3 t3.c -lm -lpthread
$ ./t3
errno in thread -1
acosh(.4)=-nan errno=33

R'

phil...@gmail.com

unread,
Feb 1, 2018, 5:45:10 PM2/1/18
to
Running through the preprocessor shows that errno is, in fact, a function here:


from the .i file produced by gcc (GCC) 4.4.7:
...

int main( int argc, char* argv[] ) {

(*__errno_location ()) = 0;

double ac = acosh(.4);
int errno_copy = (*__errno_location ());

printf( "acosh(.4)=%g errno=%d\n", ac, errno_copy );

}


Ralf Fassel

unread,
Feb 6, 2018, 6:34:26 AM2/6/18
to
* phil...@gmail.com
| We're running into some problems with the use of math functions when
| we port to Tcl 8.6. It seems to alter the behavior of some of our
| math functions even though we aren't using Tcl in this code.

Digging deeper into the libc source code for acosh reveals:

#include <math.h>
#include <errno.h>
#include <stdio.h>

int main( int argc, char* argv[] ) {
if (argc > 1) _LIB_VERSION = _POSIX_;
errno = 0;
double ac = acosh(.4);
int errno_copy = errno;
printf( "acosh(.4)=%g errno=%d\n", ac, errno_copy );
return 0;
}

$ gcc -o t2 t2.c -ltcl8.6 -lm
$ ./t2
acosh(.4)=-nan errno=0
$ ./t2 1
acosh(.4)=-nan errno=33

I.e. it seems this is related to _LIB_VERSION not being _IEEE_ (which is
a check in the acosh() function to return either an error or the
builtin_acosh()).

double
__acosh (double x)
{
if (__builtin_expect (isless (x, 1.0), 0) && _LIB_VERSION != _IEEE_)
/* acosh(x<1) */
return __kernel_standard (x, x, 29);

return __ieee754_acosh (x);
}

I have no clue what _LIB_VERSION is and why it changes when linking
against libtcl:

#include <iostream>
#include <cmath>
int main()
{
std::cout << "_LIB_VERSION " << _LIB_VERSION << std::endl;
}

$ g++ -o t3 t3.cxx
$ ./t3
_LIB_VERSION 2

$ g++ -o t3 t3.cxx -ltcl8.6
$ ./t3
_LIB_VERSION -1

HTH
R'

phil...@gmail.com

unread,
Feb 6, 2018, 7:58:06 PM2/6/18
to
Interesting - it is defined in the Tcl shared library:

$ nm .../libtcl8.6.so | grep _LIB_VERSION
0000000000393f60 D _LIB_VERSION

phil...@gmail.com

unread,
Feb 6, 2018, 8:13:57 PM2/6/18
to
Also interestingly, taking the -leee out of the link removes the _LIB_VERSION symbol from the shared library and it makes the problem go away. The note in the configure script says:

# Also, Linux requires the "ieee" library for math to work
# right (and it must appear before "-lm").

phil...@gmail.com

unread,
Feb 6, 2018, 8:18:48 PM2/6/18
to
> Also interestingly, taking the -leee

that should read -lieee

Ralf Fassel

unread,
Feb 7, 2018, 4:47:15 AM2/7/18
to
* phil...@gmail.com
| > Also interestingly, taking the -leee
>
| that should read -lieee

From the glibc source code, -lieee's only purpose is to set _LIB_VERSION
to _IEEE_, which would explain the effect.

glibc-2.22/math/Makefile:

...
# The -lieee module sets the _LIB_VERSION_ switch to IEEE mode
# for error handling in the -lm functions.

Ugly...

R'

Ralf Fassel

unread,
Feb 7, 2018, 4:52:30 AM2/7/18
to
* Ralf Fassel <ral...@gmx.de>
| From the glibc source code, -lieee's only purpose is to set _LIB_VERSION
| to _IEEE_, which would explain the effect.
>
| glibc-2.22/math/Makefile:
>
| ...
| # The -lieee module sets the _LIB_VERSION_ switch to IEEE mode
| # for error handling in the -lm functions.
>
| Ugly...

Plus, this module seems no longer to be present in glibc-2.27, at least
not in math/. glibc's Changelog indicates that this is intentionally
removed...

Uglier...
R'
Message has been deleted

phil...@gmail.com

unread,
Feb 9, 2018, 6:47:45 PM2/9/18
to
Yes, the -lieee seems to be the culprit - I can induce the static link into the same behavior if I say:

c++ foo.o -lieee -lm libtcl8.6.a ...

Also, this seems to have been present since at least the late Tcl 8.4 cycle - possibly long before that. We just didn't run into it because we built the Tcl files into our own separate shared library that didn't link with -lieee.
0 new messages