17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 541efec22Sraf * Common Development and Distribution License (the "License"). 641efec22Sraf * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 21e8031f0aSraf 227c478bd9Sstevel@tonic-gate /* 2341efec22Sraf * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 247c478bd9Sstevel@tonic-gate * Use is subject to license terms. 257c478bd9Sstevel@tonic-gate */ 267c478bd9Sstevel@tonic-gate 277c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate /* Copyright (c) 1988 AT&T */ 307c478bd9Sstevel@tonic-gate /* All Rights Reserved */ 317c478bd9Sstevel@tonic-gate 32e8031f0aSraf #pragma weak atexit = _atexit 337c478bd9Sstevel@tonic-gate 34e8031f0aSraf #include "synonyms.h" 357c478bd9Sstevel@tonic-gate #include "thr_uberdata.h" 367c478bd9Sstevel@tonic-gate #include "libc_int.h" 377c478bd9Sstevel@tonic-gate #include "atexit.h" 387c478bd9Sstevel@tonic-gate #include "stdiom.h" 397c478bd9Sstevel@tonic-gate 407c478bd9Sstevel@tonic-gate /* 417c478bd9Sstevel@tonic-gate * Note that memory is managed by lmalloc()/lfree(). 427c478bd9Sstevel@tonic-gate * 437c478bd9Sstevel@tonic-gate * Among other reasons, this is occasioned by the insistence of our 447c478bd9Sstevel@tonic-gate * brothers sh(1) and csh(1) that they can do malloc, etc., better than 457c478bd9Sstevel@tonic-gate * libc can. Those programs define their own malloc routines, and 467c478bd9Sstevel@tonic-gate * initialize the underlying mechanism in main(). This means that calls 477c478bd9Sstevel@tonic-gate * to malloc occuring before main will crash. The loader calls atexit(3C) 487c478bd9Sstevel@tonic-gate * before calling main, so we'd better avoid malloc() when it does. 497c478bd9Sstevel@tonic-gate * 507c478bd9Sstevel@tonic-gate * Another reason for using lmalloc()/lfree() is that the atexit() 517c478bd9Sstevel@tonic-gate * list must transcend all link maps. See the Linker and Libraries 527c478bd9Sstevel@tonic-gate * Guide for information on alternate link maps. 537c478bd9Sstevel@tonic-gate * 547c478bd9Sstevel@tonic-gate * See "thr_uberdata.h" for the definitions of structures used here. 557c478bd9Sstevel@tonic-gate */ 567c478bd9Sstevel@tonic-gate 577c478bd9Sstevel@tonic-gate static int in_range(_exithdlr_func_t, Lc_addr_range_t[], uint_t count); 587c478bd9Sstevel@tonic-gate 597c478bd9Sstevel@tonic-gate extern caddr_t _getfp(void); 607c478bd9Sstevel@tonic-gate 617c478bd9Sstevel@tonic-gate /* 627c478bd9Sstevel@tonic-gate * exitfns_lock is declared to be a recursive mutex so that we 637c478bd9Sstevel@tonic-gate * can hold it while calling out to the registered functions. 647c478bd9Sstevel@tonic-gate * If they call back to us, we are self-consistent and everything 657c478bd9Sstevel@tonic-gate * works, even the case of calling exit() from functions called 667c478bd9Sstevel@tonic-gate * by _exithandle() (recursive exit()). All that is required is 677c478bd9Sstevel@tonic-gate * that the registered functions actually return (no longjmp()s). 687c478bd9Sstevel@tonic-gate * 697c478bd9Sstevel@tonic-gate * Because exitfns_lock is declared to be a recursive mutex, we 707c478bd9Sstevel@tonic-gate * cannot use it with lmutex_lock()/lmutex_unlock() and we must use 717c478bd9Sstevel@tonic-gate * rmutex_lock()/rmutex_unlock() (which are defined to be simply 727c478bd9Sstevel@tonic-gate * mutex_lock()/mutex_unlock()). This means that atexit() and 737c478bd9Sstevel@tonic-gate * exit() are not async-signal-safe. We make them fork1-safe 747c478bd9Sstevel@tonic-gate * via the atexit_locks()/atexit_unlocks() functions, called from 757c478bd9Sstevel@tonic-gate * libc_prepare_atfork()/libc_child_atfork()/libc_parent_atfork() 767c478bd9Sstevel@tonic-gate */ 777c478bd9Sstevel@tonic-gate 787c478bd9Sstevel@tonic-gate /* 797c478bd9Sstevel@tonic-gate * atexit_locks() and atexit_unlocks() are called on every link map. 807c478bd9Sstevel@tonic-gate * Do not use curthread->ul_uberdata->atexit_root for these. 817c478bd9Sstevel@tonic-gate */ 827c478bd9Sstevel@tonic-gate void 837c478bd9Sstevel@tonic-gate atexit_locks() 847c478bd9Sstevel@tonic-gate { 857c478bd9Sstevel@tonic-gate (void) rmutex_lock(&__uberdata.atexit_root.exitfns_lock); 867c478bd9Sstevel@tonic-gate } 877c478bd9Sstevel@tonic-gate 887c478bd9Sstevel@tonic-gate void 897c478bd9Sstevel@tonic-gate atexit_unlocks() 907c478bd9Sstevel@tonic-gate { 917c478bd9Sstevel@tonic-gate (void) rmutex_unlock(&__uberdata.atexit_root.exitfns_lock); 927c478bd9Sstevel@tonic-gate } 937c478bd9Sstevel@tonic-gate 947c478bd9Sstevel@tonic-gate /* 957c478bd9Sstevel@tonic-gate * atexit() is called before the primordial thread is fully set up. 967c478bd9Sstevel@tonic-gate * Be careful about dereferencing self->ul_uberdata->atexit_root. 977c478bd9Sstevel@tonic-gate */ 987c478bd9Sstevel@tonic-gate int 997c478bd9Sstevel@tonic-gate _atexit(void (*func)(void)) 1007c478bd9Sstevel@tonic-gate { 1017c478bd9Sstevel@tonic-gate ulwp_t *self; 1027c478bd9Sstevel@tonic-gate atexit_root_t *arp; 1037c478bd9Sstevel@tonic-gate _exthdlr_t *p; 1047c478bd9Sstevel@tonic-gate 1057c478bd9Sstevel@tonic-gate if ((p = lmalloc(sizeof (_exthdlr_t))) == NULL) 1067c478bd9Sstevel@tonic-gate return (-1); 1077c478bd9Sstevel@tonic-gate 1087c478bd9Sstevel@tonic-gate if ((self = __curthread()) == NULL) 1097c478bd9Sstevel@tonic-gate arp = &__uberdata.atexit_root; 1107c478bd9Sstevel@tonic-gate else { 1117c478bd9Sstevel@tonic-gate arp = &self->ul_uberdata->atexit_root; 1127c478bd9Sstevel@tonic-gate (void) rmutex_lock(&arp->exitfns_lock); 1137c478bd9Sstevel@tonic-gate } 1147c478bd9Sstevel@tonic-gate p->hdlr = func; 1157c478bd9Sstevel@tonic-gate p->next = arp->head; 1167c478bd9Sstevel@tonic-gate arp->head = p; 1177c478bd9Sstevel@tonic-gate if (self != NULL) 1187c478bd9Sstevel@tonic-gate (void) rmutex_unlock(&arp->exitfns_lock); 1197c478bd9Sstevel@tonic-gate return (0); 1207c478bd9Sstevel@tonic-gate } 1217c478bd9Sstevel@tonic-gate 1227c478bd9Sstevel@tonic-gate void 1237c478bd9Sstevel@tonic-gate _exithandle(void) 1247c478bd9Sstevel@tonic-gate { 1257c478bd9Sstevel@tonic-gate atexit_root_t *arp = &curthread->ul_uberdata->atexit_root; 1267c478bd9Sstevel@tonic-gate _exthdlr_t *p; 1277c478bd9Sstevel@tonic-gate 1287c478bd9Sstevel@tonic-gate (void) rmutex_lock(&arp->exitfns_lock); 1297c478bd9Sstevel@tonic-gate arp->exit_frame_monitor = _getfp() + STACK_BIAS; 1307c478bd9Sstevel@tonic-gate p = arp->head; 1317c478bd9Sstevel@tonic-gate while (p != NULL) { 1327c478bd9Sstevel@tonic-gate arp->head = p->next; 1337c478bd9Sstevel@tonic-gate p->hdlr(); 1347c478bd9Sstevel@tonic-gate lfree(p, sizeof (_exthdlr_t)); 1357c478bd9Sstevel@tonic-gate p = arp->head; 1367c478bd9Sstevel@tonic-gate } 1377c478bd9Sstevel@tonic-gate (void) rmutex_unlock(&arp->exitfns_lock); 1387c478bd9Sstevel@tonic-gate } 1397c478bd9Sstevel@tonic-gate 1407c478bd9Sstevel@tonic-gate /* 1417c478bd9Sstevel@tonic-gate * _get_exit_frame_monitor is called by the C++ runtimes. 1427c478bd9Sstevel@tonic-gate */ 1437c478bd9Sstevel@tonic-gate void * 1447c478bd9Sstevel@tonic-gate _get_exit_frame_monitor(void) 1457c478bd9Sstevel@tonic-gate { 1467c478bd9Sstevel@tonic-gate atexit_root_t *arp = &curthread->ul_uberdata->atexit_root; 1477c478bd9Sstevel@tonic-gate return (&arp->exit_frame_monitor); 1487c478bd9Sstevel@tonic-gate } 1497c478bd9Sstevel@tonic-gate 1507c478bd9Sstevel@tonic-gate /* 1517c478bd9Sstevel@tonic-gate * The following is a routine which the loader (ld.so.1) calls when it 1527c478bd9Sstevel@tonic-gate * processes a dlclose call on an object. It resets all signal handlers 1537c478bd9Sstevel@tonic-gate * which fall within the union of the ranges specified by the elements 1547c478bd9Sstevel@tonic-gate * of the array range to SIG_DFL. 1557c478bd9Sstevel@tonic-gate */ 1567c478bd9Sstevel@tonic-gate static void 1577c478bd9Sstevel@tonic-gate _preexec_sig_unload(Lc_addr_range_t range[], uint_t count) 1587c478bd9Sstevel@tonic-gate { 1597c478bd9Sstevel@tonic-gate uberdata_t *udp = curthread->ul_uberdata; 1607c478bd9Sstevel@tonic-gate int sig; 16141efec22Sraf rwlock_t *rwlp; 1627c478bd9Sstevel@tonic-gate struct sigaction *sap; 1637c478bd9Sstevel@tonic-gate struct sigaction oact; 1647c478bd9Sstevel@tonic-gate void (*handler)(); 1657c478bd9Sstevel@tonic-gate 1667c478bd9Sstevel@tonic-gate for (sig = 1; sig < NSIG; sig++) { 1677c478bd9Sstevel@tonic-gate sap = (struct sigaction *)&udp->siguaction[sig].sig_uaction; 1687c478bd9Sstevel@tonic-gate again: 1697c478bd9Sstevel@tonic-gate handler = sap->sa_handler; 1707c478bd9Sstevel@tonic-gate if (handler != SIG_DFL && handler != SIG_IGN && 1717c478bd9Sstevel@tonic-gate in_range(handler, range, count)) { 17241efec22Sraf rwlp = &udp->siguaction[sig].sig_lock; 17341efec22Sraf lrw_wrlock(rwlp); 1747c478bd9Sstevel@tonic-gate if (handler != sap->sa_handler) { 17541efec22Sraf lrw_unlock(rwlp); 1767c478bd9Sstevel@tonic-gate goto again; 1777c478bd9Sstevel@tonic-gate } 1787c478bd9Sstevel@tonic-gate sap->sa_handler = SIG_DFL; 1797c478bd9Sstevel@tonic-gate sap->sa_flags = SA_SIGINFO; 1807c478bd9Sstevel@tonic-gate (void) sigemptyset(&sap->sa_mask); 1817c478bd9Sstevel@tonic-gate if (__sigaction(sig, NULL, &oact) == 0 && 1827c478bd9Sstevel@tonic-gate oact.sa_handler != SIG_DFL && 1837c478bd9Sstevel@tonic-gate oact.sa_handler != SIG_IGN) 1847c478bd9Sstevel@tonic-gate (void) __sigaction(sig, sap, NULL); 18541efec22Sraf lrw_unlock(rwlp); 1867c478bd9Sstevel@tonic-gate } 1877c478bd9Sstevel@tonic-gate } 1887c478bd9Sstevel@tonic-gate } 1897c478bd9Sstevel@tonic-gate 1907c478bd9Sstevel@tonic-gate /* 1917c478bd9Sstevel@tonic-gate * The following is a routine which the loader (ld.so.1) calls when it 1927c478bd9Sstevel@tonic-gate * processes a dlclose call on an object. It cancels all atfork() entries 1937c478bd9Sstevel@tonic-gate * whose prefork, parent postfork, or child postfork functions fall within 1947c478bd9Sstevel@tonic-gate * the union of the ranges specified by the elements of the array range. 1957c478bd9Sstevel@tonic-gate */ 1967c478bd9Sstevel@tonic-gate static void 1977c478bd9Sstevel@tonic-gate _preexec_atfork_unload(Lc_addr_range_t range[], uint_t count) 1987c478bd9Sstevel@tonic-gate { 199*2e145884Sraf ulwp_t *self = curthread; 200*2e145884Sraf uberdata_t *udp = self->ul_uberdata; 2017c478bd9Sstevel@tonic-gate atfork_t *atfork_q; 2027c478bd9Sstevel@tonic-gate atfork_t *atfp; 2037c478bd9Sstevel@tonic-gate atfork_t *next; 2047c478bd9Sstevel@tonic-gate void (*func)(void); 2057c478bd9Sstevel@tonic-gate int start_again; 2067c478bd9Sstevel@tonic-gate 207*2e145884Sraf (void) _private_mutex_lock(&udp->atfork_lock); 2087c478bd9Sstevel@tonic-gate if ((atfork_q = udp->atforklist) != NULL) { 2097c478bd9Sstevel@tonic-gate atfp = atfork_q; 2107c478bd9Sstevel@tonic-gate do { 2117c478bd9Sstevel@tonic-gate next = atfp->forw; 2127c478bd9Sstevel@tonic-gate start_again = 0; 2137c478bd9Sstevel@tonic-gate 2147c478bd9Sstevel@tonic-gate if (((func = atfp->prepare) != NULL && 2157c478bd9Sstevel@tonic-gate in_range(func, range, count)) || 2167c478bd9Sstevel@tonic-gate ((func = atfp->parent) != NULL && 2177c478bd9Sstevel@tonic-gate in_range(func, range, count)) || 2187c478bd9Sstevel@tonic-gate ((func = atfp->child) != NULL && 2197c478bd9Sstevel@tonic-gate in_range(func, range, count))) { 220*2e145884Sraf if (self->ul_fork) { 2217c478bd9Sstevel@tonic-gate /* 2227c478bd9Sstevel@tonic-gate * dlclose() called from a fork handler. 2237c478bd9Sstevel@tonic-gate * Deleting the entry would wreak havoc. 2247c478bd9Sstevel@tonic-gate * Just null out the function pointers 2257c478bd9Sstevel@tonic-gate * and leave the entry in place. 2267c478bd9Sstevel@tonic-gate */ 2277c478bd9Sstevel@tonic-gate atfp->prepare = NULL; 2287c478bd9Sstevel@tonic-gate atfp->parent = NULL; 2297c478bd9Sstevel@tonic-gate atfp->child = NULL; 2307c478bd9Sstevel@tonic-gate continue; 2317c478bd9Sstevel@tonic-gate } 2327c478bd9Sstevel@tonic-gate if (atfp == atfork_q) { 2337c478bd9Sstevel@tonic-gate /* deleting the list head member */ 2347c478bd9Sstevel@tonic-gate udp->atforklist = atfork_q = next; 2357c478bd9Sstevel@tonic-gate start_again = 1; 2367c478bd9Sstevel@tonic-gate } 2377c478bd9Sstevel@tonic-gate atfp->forw->back = atfp->back; 2387c478bd9Sstevel@tonic-gate atfp->back->forw = atfp->forw; 2397c478bd9Sstevel@tonic-gate lfree(atfp, sizeof (atfork_t)); 2407c478bd9Sstevel@tonic-gate if (atfp == atfork_q) { 2417c478bd9Sstevel@tonic-gate /* we deleted the whole list */ 2427c478bd9Sstevel@tonic-gate udp->atforklist = NULL; 2437c478bd9Sstevel@tonic-gate break; 2447c478bd9Sstevel@tonic-gate } 2457c478bd9Sstevel@tonic-gate } 2467c478bd9Sstevel@tonic-gate } while ((atfp = next) != atfork_q || start_again); 2477c478bd9Sstevel@tonic-gate } 248*2e145884Sraf (void) _private_mutex_unlock(&udp->atfork_lock); 2497c478bd9Sstevel@tonic-gate } 2507c478bd9Sstevel@tonic-gate 2517c478bd9Sstevel@tonic-gate /* 2527c478bd9Sstevel@tonic-gate * The following is a routine which the loader (ld.so.1) calls when it 2537c478bd9Sstevel@tonic-gate * processes a dlclose call on an object. It sets the destructor 2547c478bd9Sstevel@tonic-gate * function pointer to NULL for all keys whose destructors fall within 2557c478bd9Sstevel@tonic-gate * the union of the ranges specified by the elements of the array range. 2567c478bd9Sstevel@tonic-gate * We don't assign TSD_UNALLOCATED (the equivalent of pthread_key_destroy()) 2577c478bd9Sstevel@tonic-gate * because the thread may use the key's TSD further on in fini processing. 2587c478bd9Sstevel@tonic-gate */ 2597c478bd9Sstevel@tonic-gate static void 2607c478bd9Sstevel@tonic-gate _preexec_tsd_unload(Lc_addr_range_t range[], uint_t count) 2617c478bd9Sstevel@tonic-gate { 2627c478bd9Sstevel@tonic-gate tsd_metadata_t *tsdm = &curthread->ul_uberdata->tsd_metadata; 2637c478bd9Sstevel@tonic-gate void (*func)(void *); 2647c478bd9Sstevel@tonic-gate int key; 2657c478bd9Sstevel@tonic-gate 2667c478bd9Sstevel@tonic-gate lmutex_lock(&tsdm->tsdm_lock); 2677c478bd9Sstevel@tonic-gate for (key = 1; key < tsdm->tsdm_nused; key++) { 2687c478bd9Sstevel@tonic-gate if ((func = tsdm->tsdm_destro[key]) != NULL && 2697c478bd9Sstevel@tonic-gate func != TSD_UNALLOCATED && 2707c478bd9Sstevel@tonic-gate in_range((_exithdlr_func_t)func, range, count)) 2717c478bd9Sstevel@tonic-gate tsdm->tsdm_destro[key] = NULL; 2727c478bd9Sstevel@tonic-gate } 2737c478bd9Sstevel@tonic-gate lmutex_unlock(&tsdm->tsdm_lock); 2747c478bd9Sstevel@tonic-gate } 2757c478bd9Sstevel@tonic-gate 2767c478bd9Sstevel@tonic-gate /* 2777c478bd9Sstevel@tonic-gate * The following is a routine which the loader (ld.so.1) calls when it 2787c478bd9Sstevel@tonic-gate * processes dlclose calls on objects with atexit registrations. It 2797c478bd9Sstevel@tonic-gate * executes the exit handlers that fall within the union of the ranges 2807c478bd9Sstevel@tonic-gate * specified by the elements of the array range in the REVERSE ORDER of 2817c478bd9Sstevel@tonic-gate * their registration. Do not change this characteristic; it is REQUIRED 2827c478bd9Sstevel@tonic-gate * BEHAVIOR. 2837c478bd9Sstevel@tonic-gate */ 2847c478bd9Sstevel@tonic-gate int 2857c478bd9Sstevel@tonic-gate _preexec_exit_handlers(Lc_addr_range_t range[], uint_t count) 2867c478bd9Sstevel@tonic-gate { 2877c478bd9Sstevel@tonic-gate atexit_root_t *arp = &curthread->ul_uberdata->atexit_root; 2887c478bd9Sstevel@tonic-gate _exthdlr_t *o; /* previous node */ 2897c478bd9Sstevel@tonic-gate _exthdlr_t *p; /* this node */ 2907c478bd9Sstevel@tonic-gate 2917c478bd9Sstevel@tonic-gate (void) rmutex_lock(&arp->exitfns_lock); 2927c478bd9Sstevel@tonic-gate o = NULL; 2937c478bd9Sstevel@tonic-gate p = arp->head; 2947c478bd9Sstevel@tonic-gate while (p != NULL) { 2957c478bd9Sstevel@tonic-gate if (in_range(p->hdlr, range, count)) { 2967c478bd9Sstevel@tonic-gate /* We need to execute this one */ 2977c478bd9Sstevel@tonic-gate if (o != NULL) 2987c478bd9Sstevel@tonic-gate o->next = p->next; 2997c478bd9Sstevel@tonic-gate else 3007c478bd9Sstevel@tonic-gate arp->head = p->next; 3017c478bd9Sstevel@tonic-gate p->hdlr(); 3027c478bd9Sstevel@tonic-gate lfree(p, sizeof (_exthdlr_t)); 3037c478bd9Sstevel@tonic-gate o = NULL; 3047c478bd9Sstevel@tonic-gate p = arp->head; 3057c478bd9Sstevel@tonic-gate } else { 3067c478bd9Sstevel@tonic-gate o = p; 3077c478bd9Sstevel@tonic-gate p = p->next; 3087c478bd9Sstevel@tonic-gate } 3097c478bd9Sstevel@tonic-gate } 3107c478bd9Sstevel@tonic-gate (void) rmutex_unlock(&arp->exitfns_lock); 3117c478bd9Sstevel@tonic-gate 3127c478bd9Sstevel@tonic-gate _preexec_tsd_unload(range, count); 3137c478bd9Sstevel@tonic-gate _preexec_atfork_unload(range, count); 3147c478bd9Sstevel@tonic-gate _preexec_sig_unload(range, count); 3157c478bd9Sstevel@tonic-gate 3167c478bd9Sstevel@tonic-gate return (0); 3177c478bd9Sstevel@tonic-gate } 3187c478bd9Sstevel@tonic-gate 3197c478bd9Sstevel@tonic-gate static int 3207c478bd9Sstevel@tonic-gate in_range(_exithdlr_func_t addr, Lc_addr_range_t ranges[], uint_t count) 3217c478bd9Sstevel@tonic-gate { 3227c478bd9Sstevel@tonic-gate uint_t idx; 3237c478bd9Sstevel@tonic-gate 3247c478bd9Sstevel@tonic-gate for (idx = 0; idx < count; idx++) { 3257c478bd9Sstevel@tonic-gate if ((void *)addr >= ranges[idx].lb && 3267c478bd9Sstevel@tonic-gate (void *)addr < ranges[idx].ub) { 3277c478bd9Sstevel@tonic-gate return (1); 3287c478bd9Sstevel@tonic-gate } 3297c478bd9Sstevel@tonic-gate } 3307c478bd9Sstevel@tonic-gate 3317c478bd9Sstevel@tonic-gate return (0); 3327c478bd9Sstevel@tonic-gate } 333