This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
[PATCH v4 03/11] nds32: Thread-Local Storage Support
- From: Vincent Chen <vincentc at andestech dot com>
- To: <libc-alpha at sourceware dot org>, <joseph at codesourcery dot com>
- Cc: <deanbo422 at gmail dot com>, <cnoize at andestech dot com>, <vincentc at andestech dot com>
- Date: Wed, 5 Jun 2019 19:56:47 +0800
- Subject: [PATCH v4 03/11] nds32: Thread-Local Storage Support
- References: <1559735815-20093-1-git-send-email-vincentc@andestech.com>
This patch implements TLS mechanism for nds32. 4 TLS addressing mode
(LE, IE, LD, GD) is supported when running on Linux via NPTL.
2019-06-03 Vincent Chen <vincentc@andestech.com>
2019-06-03 CheWei Chuang <cnoize@andestech.com>
* sysdeps/nds32/dl-tls.h: New file.
* sysdeps/nds32/dl-tlsdesc.S: Likewise.
* sysdeps/nds32/dl-tlsdesc.h: Likewise.
* sysdeps/nds32/nptl/tcb-offsets.sym: Likewise.
* sysdeps/nds32/nptl/tls.h: Likewise.
* sysdeps/nds32/tls-macros.h: Likewise.
* sysdeps/nds32/tlsdesc.c: Likewise.
* sysdeps/nds32/tlsdesc.sym: Likewise.
---
sysdeps/nds32/dl-tls.h | 28 +++++++
sysdeps/nds32/dl-tlsdesc.S | 104 +++++++++++++++++++++++++
sysdeps/nds32/dl-tlsdesc.h | 62 +++++++++++++++
sysdeps/nds32/nptl/tcb-offsets.sym | 7 ++
sysdeps/nds32/nptl/tls.h | 155 +++++++++++++++++++++++++++++++++++++
sysdeps/nds32/tls-macros.h | 75 ++++++++++++++++++
sysdeps/nds32/tlsdesc.c | 38 +++++++++
sysdeps/nds32/tlsdesc.sym | 16 ++++
8 files changed, 485 insertions(+)
create mode 100644 sysdeps/nds32/dl-tls.h
create mode 100644 sysdeps/nds32/dl-tlsdesc.S
create mode 100644 sysdeps/nds32/dl-tlsdesc.h
create mode 100644 sysdeps/nds32/nptl/tcb-offsets.sym
create mode 100644 sysdeps/nds32/nptl/tls.h
create mode 100644 sysdeps/nds32/tls-macros.h
create mode 100644 sysdeps/nds32/tlsdesc.c
create mode 100644 sysdeps/nds32/tlsdesc.sym
diff --git a/sysdeps/nds32/dl-tls.h b/sysdeps/nds32/dl-tls.h
new file mode 100644
index 0000000..a5e37b7
--- /dev/null
+++ b/sysdeps/nds32/dl-tls.h
@@ -0,0 +1,28 @@
+/* Thread-local storage handling in the ELF dynamic linker. Andes nds32 version.
+ Copyright (C) 2018-2019 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+
+/* Type used for the representation of TLS information in the GOT. */
+typedef struct
+{
+ unsigned long int ti_module;
+ unsigned long int ti_offset;
+} tls_index;
+
+
+extern void *__tls_get_addr (tls_index *ti);
diff --git a/sysdeps/nds32/dl-tlsdesc.S b/sysdeps/nds32/dl-tlsdesc.S
new file mode 100644
index 0000000..981d1b8
--- /dev/null
+++ b/sysdeps/nds32/dl-tlsdesc.S
@@ -0,0 +1,104 @@
+/* Thread-local storage handling in the ELF dynamic linker, Andes nds32 version.
+ Copyright (C) 2018-2019 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <sysdep.h>
+#include <tls.h>
+#include "tlsdesc.h"
+
+ .text
+ .hidden _dl_tlsdesc_return
+ .global _dl_tlsdesc_return
+ .type _dl_tlsdesc_return,#function
+ cfi_startproc
+ .align 2
+_dl_tlsdesc_return:
+ lwi $r0, [$r0 + 4]
+ add $r0, $r0, $r25
+ ret
+ cfi_endproc
+ .size _dl_tlsdesc_return, .-_dl_tlsdesc_return
+
+ .hidden _dl_tlsdesc_undefweak
+ .global _dl_tlsdesc_undefweak
+ .type _dl_tlsdesc_undefweak,#function
+ cfi_startproc
+ .align 2
+_dl_tlsdesc_undefweak:
+ ret
+ cfi_endproc
+ .size _dl_tlsdesc_undefweak, .-_dl_tlsdesc_undefweak
+
+#ifdef SHARED
+ /* Handler for dynamic TLS symbols.
+ Prototype:
+ _dl_tlsdesc_dynamic (tlsdesc *) ;
+
+ ptrdiff_t
+ _dl_tlsdesc_dynamic(struct tlsdesc *tdp)
+ {
+ struct tlsdesc_dynamic_arg *td = tdp->argument.pointer;
+ dtv_t *dtv = (dtv_t *)THREAD_DTV();
+ if (__builtin_expect (td->gen_count <= dtv[0].counter
+ && (dtv[td->tlsinfo.ti_module].pointer.val
+ != TLS_DTV_UNALLOCATED),
+ 1))
+ return dtv[td->tlsinfo.ti_module].pointer.val +
+ td->tlsinfo.ti_offset;
+
+ return __tls_get_addr (&td->tlsinfo);
+ }
+ */
+ .hidden _dl_tlsdesc_dynamic
+ .global _dl_tlsdesc_dynamic
+ .type _dl_tlsdesc_dynamic,#function
+ cfi_startproc
+ .pic
+ .align 2
+
+_dl_tlsdesc_dynamic:
+ lwi $r0, [$r0 + 4] /* $r0 = td. */
+ lwi $r1, [$r0 + #TLSDESC_GEN_COUNT] /* $r1 = td->gen_count. */
+ lwi $r2, [$r25 + #DTV_OFFSET] /* $r2 = &dtv[0]. */
+ lwi $r3, [$r2] /* $r3 = module id. */
+ sub $r1, $r1, $r3
+ bgtz $r1, 2f
+ lwi $r3, [$r0 + #TLSDESC_MODID]
+ slli $r3, $r3, #3 /* $r3 = module offset = ID * 8. */
+ lw $r3, [$r2 + $r3] /* $r3 = &dtc[ID] = &dtv[0]+ module offset. */
+ movi $r1, #TLS_DTV_UNALLOCATED
+ beq $r3, $r1, 2f
+ lwi $r1, [$r0 + #TLSDESC_MODOFF]
+ add $r0, $r3, $r1
+1:
+ ret
+2:
+ smw.adm $sp,[$sp],$sp,#0x6
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset gp, 0
+ .cfi_rel_offset lp, 4
+ GET_GTABLE ($gp)
+ la $r15, HIDDEN_JUMPTARGET (__tls_get_addr@PLT)
+ jral $r15
+ lmw.bim $sp,[$sp],$sp,#0x6
+ .cfi_adjust_cfa_offset -8
+ .cfi_restore gp
+ .cfi_restore lp
+ j 1b
+ cfi_endproc
+ .size _dl_tlsdesc_dynamic, .-_dl_tlsdesc_dynamic
+#endif /* SHARED. */
diff --git a/sysdeps/nds32/dl-tlsdesc.h b/sysdeps/nds32/dl-tlsdesc.h
new file mode 100644
index 0000000..605327d
--- /dev/null
+++ b/sysdeps/nds32/dl-tlsdesc.h
@@ -0,0 +1,62 @@
+/* Thread-local storage descriptor handling in the ELF dynamic linker.
+ Andes nds32 version.
+ Copyright (C) 2018-2019 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; witout even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#ifndef _NDS32_DL_TLSDESC_H
+# define _NDS32_DL_TLSDESC_H 1
+
+/* Type used to represent a TLS descriptor in the GOT. */
+struct tlsdesc
+{
+ ptrdiff_t (*entry)(struct tlsdesc *);
+ union
+ {
+ void *pointer;
+ long int value;
+ } argument;
+};
+
+
+typedef struct dl_tls_index
+{
+ unsigned long int ti_module;
+ unsigned long int ti_offset;
+} tls_index;
+
+/* Type used as the argument in a TLS descriptor for a symbol that
+ needs dynamic TLS offsets. */
+struct tlsdesc_dynamic_arg
+{
+ tls_index tlsinfo;
+ size_t gen_count;
+};
+
+extern ptrdiff_t attribute_hidden
+_dl_tlsdesc_return(struct tlsdesc *);
+
+extern ptrdiff_t attribute_hidden
+_dl_tlsdesc_undefweak(struct tlsdesc *);
+
+# ifdef SHARED
+extern void *_dl_make_tlsdesc_dynamic (struct link_map *map, size_t ti_offset);
+
+extern ptrdiff_t attribute_hidden
+_dl_tlsdesc_dynamic(struct tlsdesc *);
+# endif
+
+#endif /* nds32/dl-tlsdesc.h. */
diff --git a/sysdeps/nds32/nptl/tcb-offsets.sym b/sysdeps/nds32/nptl/tcb-offsets.sym
new file mode 100644
index 0000000..333dd53
--- /dev/null
+++ b/sysdeps/nds32/nptl/tcb-offsets.sym
@@ -0,0 +1,7 @@
+#include <sysdep.h>
+#include <tls.h>
+
+#define thread_offsetof(mem) (long)(offsetof (struct pthread, mem) - TLS_TCB_OFFSET - TLS_PRE_TCB_SIZE)
+
+MULTIPLE_THREADS_OFFSET thread_offsetof (header.multiple_threads)
+TID_OFFSET thread_offsetof (tid)
diff --git a/sysdeps/nds32/nptl/tls.h b/sysdeps/nds32/nptl/tls.h
new file mode 100644
index 0000000..f391370
--- /dev/null
+++ b/sysdeps/nds32/nptl/tls.h
@@ -0,0 +1,155 @@
+/* Definition for thread-local data handling. Andes NPTL/nds32 version.
+ Copyright (C) 2018-2019 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#ifndef _NDS32_TLS_H
+#define _NDS32_TLS_H 1
+
+#include <dl-sysdep.h>
+
+#ifdef __ASSEMBLER__
+# include <tcb-offsets.h>
+#else
+# include <stdbool.h>
+# include <stddef.h>
+# include <stdint.h>
+# include <dl-dtv.h>
+/* Get system call information. */
+# include <sysdep.h>
+
+/* The TP points to the start of the thread blocks. */
+# define TLS_DTV_AT_TP 1
+# define TLS_TCB_AT_TP 0
+
+/* We use the multiple_threads field in the pthread struct. */
+# define TLS_MULTIPLE_THREADS_IN_TCB 1
+
+/* Get the thread descriptor definition. */
+# include <nptl/descr.h>
+
+typedef struct
+{
+ dtv_t *dtv;
+ void *private;
+} tcbhead_t;
+
+/* This is the size of the initial TCB. */
+# define TLS_INIT_TCB_SIZE 0
+
+/* Alignment requirements for the initial TCB. */
+# define TLS_INIT_TCB_ALIGN __alignof__ (struct pthread)
+
+/* This is the size of the TCB. */
+# define TLS_TCB_SIZE 0
+
+/* Alignment requirements for the TCB. */
+# define TLS_TCB_ALIGN __alignof__ (struct pthread)
+
+
+
+/* This is the size we need before TCB - actually, it includes the TCB. */
+# define TLS_PRE_TCB_SIZE \
+ (sizeof (struct pthread) \
+ + ((sizeof (tcbhead_t) + TLS_TCB_ALIGN - 1) & ~(TLS_TCB_ALIGN - 1)))
+
+/* Return the thread descriptor (tp) for the current thread. */
+register void *__thread_pointer asm ("$r25");
+
+
+/* The thread pointer tp points to the end of the TCB.
+ * The pthread_descr structure is immediately in front of the TCB. */
+# define TLS_TCB_OFFSET 0
+
+/* Install the dtv pointer. The pointer passed is to the element with
+ index -1 which contain the length. */
+# define INSTALL_DTV(tcbp, dtvp) \
+ (((tcbhead_t *) (tcbp))[-1].dtv = (dtvp) + 1)
+
+/* Install new dtv for current thread. */
+# define INSTALL_NEW_DTV(dtv) (THREAD_DTV() = (dtv))
+
+/* Return dtv of given thread descriptor. */
+# define GET_DTV(tcbp) (((tcbhead_t *) (tcbp))[-1].dtv)
+
+/* Code to initially initialize the thread pointer (tp). */
+# define TLS_INIT_TP(tcbp) \
+ (__thread_pointer = (char *)(tcbp) + TLS_TCB_OFFSET, NULL)
+
+/* Value passed to 'clone' for initialization of the thread register. */
+# define TLS_DEFINE_INIT_TP(tp, pd) \
+ void *tp = (void *) (pd) + TLS_TCB_OFFSET + TLS_PRE_TCB_SIZE
+
+/* Return the address of the dtv for the current thread. */
+# define THREAD_DTV() \
+ (((tcbhead_t *) (__thread_pointer - TLS_TCB_OFFSET))[-1].dtv)
+
+/* Return the thread descriptor for the current thread. */
+# define THREAD_SELF \
+ ((struct pthread *) (__thread_pointer \
+ - TLS_TCB_OFFSET - TLS_PRE_TCB_SIZE))
+
+/* Magic for libthread_db to know how to do THREAD_SELF. */
+# define DB_THREAD_SELF \
+ REGISTER (32, 32, 26 * 4, - TLS_TCB_OFFSET - TLS_PRE_TCB_SIZE)
+
+/* Read member of the thread descriptor directly. */
+# define THREAD_GETMEM(descr, member) (descr->member)
+
+/* Same as THREAD_GETMEM, but the member offset can be non-constant. */
+# define THREAD_GETMEM_NC(descr, member, idx) \
+ (descr->member[idx])
+
+/* Set member of the thread descriptor directly. */
+# define THREAD_SETMEM(descr, member, value) \
+ (descr->member = (value))
+
+/* Same as THREAD_SETMEM, but the member offset can be non-constant. */
+# define THREAD_SETMEM_NC(descr, member, idx, value) \
+ (descr->member[idx] = (value))
+
+
+/* l_tls_offset == 0 is perfectly valid, so we have to use some different
+ value to mean unset l_tls_offset. */
+# define NO_TLS_OFFSET -1
+
+/* Get and set the global scope generation counter in struct pthread. */
+# define THREAD_GSCOPE_IN_TCB 1
+# define THREAD_GSCOPE_FLAG_UNUSED 0
+# define THREAD_GSCOPE_FLAG_USED 1
+# define THREAD_GSCOPE_FLAG_WAIT 2
+# define THREAD_GSCOPE_RESET_FLAG() \
+ do \
+ { int __res \
+ = atomic_exchange_rel (&THREAD_SELF->header.gscope_flag, \
+ THREAD_GSCOPE_FLAG_UNUSED); \
+ if (__res == THREAD_GSCOPE_FLAG_WAIT) \
+ lll_futex_wake (&THREAD_SELF->header.gscope_flag, 1, LLL_PRIVATE); \
+ } \
+ while (0)
+# define THREAD_GSCOPE_SET_FLAG() \
+ do \
+ { \
+ THREAD_SELF->header.gscope_flag = THREAD_GSCOPE_FLAG_USED; \
+ atomic_write_barrier (); \
+ } \
+ while (0)
+# define THREAD_GSCOPE_WAIT() \
+ GL(dl_wait_lookup_done) ()
+
+#endif /* __ASSEMBLER__. */
+
+#endif /* _NDS32_TLS_H. */
diff --git a/sysdeps/nds32/tls-macros.h b/sysdeps/nds32/tls-macros.h
new file mode 100644
index 0000000..104fdf6
--- /dev/null
+++ b/sysdeps/nds32/tls-macros.h
@@ -0,0 +1,75 @@
+/* Macros to support TLS testing in times of missing compiler support.
+ Andes nds32 version.
+ Copyright (C) 2018-2019 Free Software Foundation, Inc.
+
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#define TLS_LE(x) \
+({ int *__l; \
+ asm("sethi %0, hi20("#x"@TPOFF)\n\t" \
+ "ori %0, %0, lo12("#x"@TPOFF)\n\t" \
+ "add %0, %0, $r25\n\t" \
+ : "=r" (__l)); \
+ __l;})
+
+#ifdef PIC
+# define TLS_IE(x) \
+({ int *__l; \
+ asm(".relax_hint begin\n\t" \
+ "sethi %0, hi20("#x"@GOTTPOFF)\n\t" \
+ ".relax_hint\n\t" \
+ "ori %0, %0, lo12("#x"@GOTTPOFF)\n\t" \
+ ".relax_hint end\n\t" \
+ "lw %0, [%0 + $gp]\n\t" \
+ "add %0, %0, $r25\n\t" \
+ : "=r" (__l)); \
+ __l;})
+#else
+# define TLS_IE(x) \
+({ int *__l; \
+ asm(".relax_hint begin\n\t" \
+ "sethi %0, hi20("#x"@GOTTPOFF)\n\t" \
+ ".relax_hint end\n\t" \
+ "lwi %0, [%0 + lo12("#x"@GOTTPOFF)]\n\t" \
+ "add %0, %0, $r25\n\t" \
+ : "=r" (__l)); \
+ __l;})
+#endif
+
+#define TLS_LD(x) TLS_GD(x)
+
+#define TLS_GD(x) \
+({ int *__l; \
+ asm("smw.adm $r1,[$sp],$r5,#0\n\t" \
+ "smw.adm $r16,[$sp],$r24,#0\n\t" \
+ ".relax_hint begin\n\t" \
+ "sethi $r0, hi20("#x"@TLSDESC)\n\t" \
+ ".relax_hint\n\t" \
+ "ori $r0, $r0, lo12("#x"@TLSDESC)\n\t" \
+ ".relax_hint\n\t" \
+ "lw $r15, [$r0 + $gp]\n\t" \
+ ".relax_hint\n\t" \
+ "add $r0, $r0, $gp\n\t" \
+ ".relax_hint end\n\t" \
+ "jral $r15\n\t" \
+ "lmw.bim $r16,[$sp],$r24,#0\n\t" \
+ "lmw.bim $r1,[$sp],$r5,#0\n\t" \
+ "move %0, $r0\n\t" \
+ : "=r" (__l) \
+ : \
+ : "$r0", "$r15"); \
+ __l;})
diff --git a/sysdeps/nds32/tlsdesc.c b/sysdeps/nds32/tlsdesc.c
new file mode 100644
index 0000000..2e61ce3
--- /dev/null
+++ b/sysdeps/nds32/tlsdesc.c
@@ -0,0 +1,38 @@
+/* Manage TLS descriptors, Andes nds32 version.
+ Copyright (C) 2018-2019 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <ldsodefs.h>
+#include <tls.h>
+#include <dl-tlsdesc.h>
+#include <dl-unmap-segments.h>
+#define _dl_tlsdesc_resolve_hold 0
+#include <tlsdeschtab.h>
+
+/* Unmap the dynamic object, but also release its TLS descriptor table
+ if there is one. */
+
+void
+_dl_unmap (struct link_map *map)
+{
+ _dl_unmap_segments (map);
+
+#ifdef SHARED
+ if (map->l_mach.tlsdesc_table)
+ htab_delete (map->l_mach.tlsdesc_table);
+#endif
+}
diff --git a/sysdeps/nds32/tlsdesc.sym b/sysdeps/nds32/tlsdesc.sym
new file mode 100644
index 0000000..0a34a74
--- /dev/null
+++ b/sysdeps/nds32/tlsdesc.sym
@@ -0,0 +1,16 @@
+#include <stddef.h>
+#include <sysdep.h>
+#include <tls.h>
+#include <link.h>
+#include <dl-tlsdesc.h>
+
+#define dtv_offsetof(dtv) (offsetof (tcbhead_t, dtv) - TLS_TCB_OFFSET - sizeof (tcbhead_t))
+
+DTV_OFFSET dtv_offsetof (dtv)
+
+TLSDESC_ARG offsetof (struct tlsdesc, argument.pointer)
+
+TLSDESC_GEN_COUNT offsetof (struct tlsdesc_dynamic_arg, gen_count)
+TLSDESC_MODID offsetof (struct tlsdesc_dynamic_arg, tlsinfo.ti_module)
+TLSDESC_MODOFF offsetof (struct tlsdesc_dynamic_arg, tlsinfo.ti_offset)
+TLS_DTV_UNALLOCATED TLS_DTV_UNALLOCATED
--
1.9.5