Skip to content

Commit 6f20fd1

Browse files
authored
Avoid reverse dependencies in JS library code handling time. NFC (#16118)
Specifically this change removes `_get_daylight`, `_get_daylight` and `_get_tzname` which are native symbols that were used by `tzset` which has a lot of different call sites. This made reverse dependencies on these symbols fairly common and easy to miss. See #15958 for a recent example. This change removes these functions completely in favor or just passing these addresses in as arguments. Fixes: #15958
1 parent 1c2c5bf commit 6f20fd1

File tree

7 files changed

+102
-81
lines changed

7 files changed

+102
-81
lines changed

src/library.js

Lines changed: 22 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -464,10 +464,8 @@ LibraryManager.library = {
464464
return time1 - time0;
465465
},
466466

467-
mktime__deps: ['tzset'],
468-
mktime__sig: 'ii',
469-
mktime: function(tmPtr) {
470-
_tzset();
467+
_mktime_js__sig: 'ii',
468+
_mktime_js: function(tmPtr) {
471469
var date = new Date({{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_year, 'i32') }}} + 1900,
472470
{{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'i32') }}},
473471
{{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'i32') }}},
@@ -507,7 +505,6 @@ LibraryManager.library = {
507505

508506
return (date.getTime() / 1000)|0;
509507
},
510-
timelocal: 'mktime',
511508

512509
_gmtime_js__sig: 'iii',
513510
_gmtime_js: function(time, tmPtr) {
@@ -524,10 +521,8 @@ LibraryManager.library = {
524521
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}};
525522
},
526523

527-
timegm__deps: ['tzset'],
528-
timegm__sig: 'ii',
529-
timegm: function(tmPtr) {
530-
_tzset();
524+
_timegm_js__sig: 'ii',
525+
_timegm_js: function(tmPtr) {
531526
var time = Date.UTC({{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_year, 'i32') }}} + 1900,
532527
{{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'i32') }}},
533528
{{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'i32') }}},
@@ -545,10 +540,8 @@ LibraryManager.library = {
545540
return (date.getTime() / 1000)|0;
546541
},
547542

548-
localtime_r__deps: ['tzset'],
549-
localtime_r__sig: 'iii',
550-
localtime_r: function(time, tmPtr) {
551-
_tzset();
543+
_localtime_js__sig: 'iii',
544+
_localtime_js: function(time, tmPtr) {
552545
var date = new Date({{{ makeGetValue('time', 0, 'i32') }}}*1000);
553546
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'date.getSeconds()', 'i32') }}};
554547
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_min, 'date.getMinutes()', 'i32') }}};
@@ -568,16 +561,9 @@ LibraryManager.library = {
568561
var winterOffset = start.getTimezoneOffset();
569562
var dst = (summerOffset != winterOffset && date.getTimezoneOffset() == Math.min(winterOffset, summerOffset))|0;
570563
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_isdst, 'dst', 'i32') }}};
571-
572-
var zonePtr = {{{ makeGetValue('__get_tzname()', 'dst ? ' + Runtime.POINTER_SIZE + ' : 0', 'i32') }}};
573-
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_zone, 'zonePtr', 'i32') }}};
574-
575-
return tmPtr;
576564
},
577-
__localtime_r: 'localtime_r',
578565

579566
// musl-internal function used to implement both `asctime` and `asctime_r`
580-
__asctime_r__deps: ['mktime'],
581567
__asctime_r__sig: 'iii',
582568
__asctime_r: function(tmPtr, buf) {
583569
var date = {
@@ -622,23 +608,24 @@ LibraryManager.library = {
622608

623609
// TODO: Initialize these to defaults on startup from system settings.
624610
// Note: glibc has one fewer underscore for all of these. Also used in other related functions (timegm)
625-
tzset__deps: ['tzset_impl'],
626-
tzset__sig: 'v',
627-
tzset: function() {
611+
_tzset_js__deps: ['tzset_impl'],
612+
_tzset_js__sig: 'viii',
613+
_tzset_js: function(timezone, daylight, tzname) {
628614
// TODO: Use (malleable) environment variables instead of system settings.
629-
if (_tzset.called) return;
630-
_tzset.called = true;
631-
_tzset_impl();
615+
if (__tzset_js.called) return;
616+
__tzset_js.called = true;
617+
_tzset_impl(timezone, daylight, tzname);
632618
},
633619

620+
tzset_impl__internal: true,
634621
tzset_impl__proxy: 'sync',
635-
tzset_impl__sig: 'v',
636-
tzset_impl__deps: ['_get_daylight', '_get_timezone', '_get_tzname',
622+
tzset_impl__sig: 'viii',
623+
tzset_impl__deps: [
637624
#if MINIMAL_RUNTIME
638625
'$allocateUTF8'
639626
#endif
640627
],
641-
tzset_impl: function() {
628+
tzset_impl: function(timezone, daylight, tzname) {
642629
var currentYear = new Date().getFullYear();
643630
var winter = new Date(currentYear, 0, 1);
644631
var summer = new Date(currentYear, 6, 1);
@@ -655,9 +642,9 @@ LibraryManager.library = {
655642
// Coordinated Universal Time (UTC) and local standard time."), the same
656643
// as returned by stdTimezoneOffset.
657644
// See http://pubs.opengroup.org/onlinepubs/009695399/functions/tzset.html
658-
{{{ makeSetValue('__get_timezone()', '0', 'stdTimezoneOffset * 60', 'i32') }}};
645+
{{{ makeSetValue('timezone', '0', 'stdTimezoneOffset * 60', 'i32') }}};
659646

660-
{{{ makeSetValue('__get_daylight()', '0', 'Number(winterOffset != summerOffset)', 'i32') }}};
647+
{{{ makeSetValue('daylight', '0', 'Number(winterOffset != summerOffset)', 'i32') }}};
661648

662649
function extractZone(date) {
663650
var match = date.toTimeString().match(/\(([A-Za-z ]+)\)$/);
@@ -669,11 +656,11 @@ LibraryManager.library = {
669656
var summerNamePtr = allocateUTF8(summerName);
670657
if (summerOffset < winterOffset) {
671658
// Northern hemisphere
672-
{{{ makeSetValue('__get_tzname()', '0', 'winterNamePtr', 'i32') }}};
673-
{{{ makeSetValue('__get_tzname()', Runtime.POINTER_SIZE, 'summerNamePtr', 'i32') }}};
659+
{{{ makeSetValue('tzname', '0', 'winterNamePtr', POINTER_TYPE) }}};
660+
{{{ makeSetValue('tzname', Runtime.POINTER_SIZE, 'summerNamePtr', POINTER_TYPE) }}};
674661
} else {
675-
{{{ makeSetValue('__get_tzname()', '0', 'summerNamePtr', 'i32') }}};
676-
{{{ makeSetValue('__get_tzname()', Runtime.POINTER_SIZE, 'winterNamePtr', 'i32') }}};
662+
{{{ makeSetValue('tzname', '0', 'summerNamePtr', POINTER_TYPE) }}};
663+
{{{ makeSetValue('tzname', Runtime.POINTER_SIZE, 'winterNamePtr', POINTER_TYPE) }}};
677664
}
678665
},
679666

system/lib/libc/emscripten_time.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2022 The Emscripten Authors. All rights reserved.
3+
* Emscripten is available under two separate licenses, the MIT license and the
4+
* University of Illinois/NCSA Open Source License. Both these licenses can be
5+
* found in the LICENSE file.
6+
*/
7+
8+
#include <time.h>
9+
#include <stdbool.h>
10+
#include "libc.h"
11+
12+
// Replaces musl's __tz.c
13+
14+
__attribute__((__weak__)) long timezone = 0;
15+
__attribute__((__weak__)) int daylight = 0;
16+
__attribute__((__weak__)) char *tzname[2] = { 0, 0 };
17+
18+
void _tzset_js(long* timezone, int* daylight, char** tzname);
19+
time_t _timegm_js(struct tm *tm);
20+
time_t _mktime_js(struct tm *tm);
21+
void _localtime_js(const time_t *restrict t, struct tm *restrict tm);
22+
void _gmtime_js(const time_t *restrict t, struct tm *restrict tm);
23+
24+
__attribute__((__weak__))
25+
void tzset() {
26+
_tzset_js(&timezone, &daylight, tzname);
27+
}
28+
29+
__attribute__((__weak__))
30+
time_t timegm(struct tm *tm) {
31+
tzset();
32+
return _timegm_js(tm);
33+
}
34+
35+
__attribute__((__weak__))
36+
time_t mktime(struct tm *tm) {
37+
tzset();
38+
return _mktime_js(tm);
39+
}
40+
41+
__attribute__((__weak__))
42+
struct tm *__localtime_r(const time_t *restrict t, struct tm *restrict tm) {
43+
tzset();
44+
_localtime_js(t, tm);
45+
// __localtime_js sets everything but the tmzone pointer
46+
tm->__tm_zone = tm->tm_isdst ? tzname[1] :tzname[0];
47+
return tm;
48+
}
49+
50+
__attribute__((__weak__))
51+
struct tm *__gmtime_r(const time_t *restrict t, struct tm *restrict tm) {
52+
tzset();
53+
_gmtime_js(t, tm);
54+
tm->tm_isdst = 0;
55+
tm->__tm_gmtoff = 0;
56+
tm->__tm_zone = "GMT";
57+
return tm;
58+
}
59+
60+
weak_alias(__gmtime_r, gmtime_r);
61+
weak_alias(__localtime_r, localtime_r);

system/lib/libc/extras.c

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,6 @@
55
* found in the LICENSE file.
66
*/
77

8-
// Extra libc helper functions
9-
10-
char *tzname[2];
11-
12-
void* _get_tzname() {
13-
return (void*)tzname;
14-
}
15-
16-
int daylight;
17-
18-
int* _get_daylight() {
19-
return &daylight;
20-
}
21-
22-
long timezone;
23-
24-
long* _get_timezone() {
25-
return &timezone;
26-
}
27-
288
// Musl lock internals. As we assume wasi is single-threaded for now, these
299
// are no-ops.
3010

system/lib/libc/musl/src/time/gmtime_r.c

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,15 @@
11
#include "time_impl.h"
22
#include <errno.h>
33

4-
#ifdef __EMSCRIPTEN__
5-
void _gmtime_js(const time_t *restrict t, struct tm *restrict tm);
6-
#endif
7-
84
struct tm *__gmtime_r(const time_t *restrict t, struct tm *restrict tm)
95
{
10-
#ifndef __EMSCRIPTEN__
116
if (__secs_to_tm(*t, tm) < 0) {
127
errno = EOVERFLOW;
138
return 0;
149
}
1510
tm->tm_isdst = 0;
1611
tm->__tm_gmtoff = 0;
1712
tm->__tm_zone = __utc;
18-
#else
19-
_gmtime_js(t, tm);
20-
tm->tm_isdst = 0;
21-
tm->__tm_gmtoff = 0;
22-
tm->__tm_zone = "GMT";
23-
#endif
2413
return tm;
2514
}
2615

tests/test_other.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,29 +1187,30 @@ def test_export_all(self):
11871187

11881188
def test_export_all_and_exported_functions(self):
11891189
# EXPORT_ALL should not export library functions by default.
1190-
# This mans that to export library function you also need to explicitly
1190+
# This means that to export library function you also need to explicitly
11911191
# list them in EXPORTED_FUNCTIONS.
11921192
lib = r'''
11931193
#include <stdio.h>
11941194
#include <emscripten.h>
11951195
EMSCRIPTEN_KEEPALIVE void libfunc() { puts("libfunc\n"); }
1196+
void libfunc2() { puts("libfunc2\n"); }
11961197
'''
11971198
create_file('lib.c', lib)
11981199
create_file('main.js', '''
11991200
var Module = {
12001201
onRuntimeInitialized: function() {
12011202
_libfunc();
1202-
__get_daylight();
1203+
_libfunc2();
12031204
}
12041205
};
12051206
''')
12061207

1207-
# __get_daylight should not be linked by default, even with EXPORT_ALL
1208+
# libfunc2 should not be linked by default, even with EXPORT_ALL
12081209
self.emcc('lib.c', ['-sEXPORT_ALL', '--pre-js', 'main.js'], output_filename='a.out.js')
12091210
err = self.run_js('a.out.js', assert_returncode=NON_ZERO)
1210-
self.assertContained('__get_daylight is not defined', err)
1211+
self.assertContained('_libfunc2 is not defined', err)
12111212

1212-
self.emcc('lib.c', ['-sEXPORTED_FUNCTIONS=__get_daylight', '-sEXPORT_ALL', '--pre-js', 'main.js'], output_filename='a.out.js')
1213+
self.emcc('lib.c', ['-sEXPORTED_FUNCTIONS=_libfunc2', '-sEXPORT_ALL', '--pre-js', 'main.js'], output_filename='a.out.js')
12131214
self.assertContained('libfunc\n', self.run_js('a.out.js'))
12141215

12151216
def test_stdin(self):

tools/deps_info.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,9 @@
7373
'alcGetString': ['malloc'],
7474
'bind': ['ntohs', 'htons'],
7575
'connect': ['ntohs', 'htons'],
76-
'ctime': ['_get_tzname', '_get_daylight', '_get_timezone', 'malloc'],
77-
'ctime_r': ['_get_tzname', '_get_daylight', '_get_timezone', 'malloc'],
76+
'ctime': ['malloc'],
77+
'gmtime': ['malloc'],
78+
'ctime_r': ['malloc'],
7879
'dladdr': ['malloc'],
7980
'dlopen': ['__dl_seterr'],
8081
'dlsym': ['__dl_seterr'],
@@ -167,9 +168,9 @@
167168
'glfwInit': ['malloc', 'free'],
168169
'glfwSleep': ['sleep'],
169170
'glfwGetMonitors': ['malloc'],
170-
'localtime': ['_get_tzname', '_get_daylight', '_get_timezone', 'malloc'],
171-
'localtime_r': ['_get_tzname', '_get_daylight', '_get_timezone', 'malloc'],
172-
'mktime': ['_get_tzname', '_get_daylight', '_get_timezone', 'malloc'],
171+
'localtime': ['malloc'],
172+
'localtime_r': ['malloc'],
173+
'mktime': ['malloc'],
173174
'mmap': ['memalign'],
174175
'munmap': ['free'],
175176
'pthread_create': ['malloc', 'free', 'emscripten_main_thread_process_queued_calls'],
@@ -187,10 +188,10 @@
187188
# dependency.
188189
'setjmp': ['malloc', 'free', 'saveSetjmp', 'setThrew'],
189190
'setprotoent': ['malloc'],
190-
'syslog': ['ntohs', 'htons'],
191-
'vsyslog': ['ntohs', 'htons'],
192-
'timegm': ['_get_tzname', '_get_daylight', '_get_timezone', 'malloc'],
193-
'tzset': ['_get_tzname', '_get_daylight', '_get_timezone', 'malloc'],
191+
'syslog': ['malloc', 'ntohs', 'htons'],
192+
'vsyslog': ['malloc', 'ntohs', 'htons'],
193+
'timegm': ['malloc'],
194+
'tzset': ['malloc'],
194195
'uuid_compare': ['memcmp'],
195196
'uuid_copy': ['memcpy'],
196197
'wgpuBufferGetMappedRange': ['malloc', 'free'],

tools/system_libs.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -906,7 +906,6 @@ def get_files(self):
906906
'asctime.c',
907907
'ctime.c',
908908
'gmtime.c',
909-
'gmtime_r.c',
910909
'localtime.c',
911910
'nanosleep.c',
912911
'clock_nanosleep.c',
@@ -971,6 +970,7 @@ def get_files(self):
971970
'sigtimedwait.c',
972971
'pthread_sigmask.c',
973972
'emscripten_console.c',
973+
'emscripten_time.c',
974974
])
975975

976976
libc_files += files_in_path(
@@ -1577,7 +1577,9 @@ def get_files(self):
15771577
'difftime.c',
15781578
'gettimeofday.c',
15791579
'localtime_r.c',
1580+
'gmtime_r.c',
15801581
'mktime.c',
1582+
'timegm.c',
15811583
'time.c'])
15821584
# It is more efficient to use JS for __assert_fail, as it avoids always
15831585
# including fprintf etc.

0 commit comments

Comments
 (0)