/* * Copyright (c) 2012 The Native Client Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef NATIVE_CLIENT_SRC_UNTRUSTED_NACL_TLS_PARAMS_H_ #define NATIVE_CLIENT_SRC_UNTRUSTED_NACL_TLS_PARAMS_H_ 1 /* * Native Client support for thread local storage * * Support for TLS in NaCl depends upon the cooperation of the * compiler's code generator, the linker (and linker script), the * startup code (_start), and the CPU-specific routines defined here. * * Each thread is associated with both a TLS region, comprising the .tdata * and .tbss (zero-initialized) sections of the ELF file, and a thread * descriptor block (TDB), a structure containing information about the * thread that is mostly private to the thread library implementation. * The details of the TLS and TDB regions vary by platform; this module * provides a generic abstraction which may be supported by any platform. * * The "combined area" is an opaque region of memory associated with a * thread, containing its TDB and TLS and sufficient internal padding so * that it can be allocated anywhere without regard to alignment. We * provide here the CPU-specific parametrization routines that control how * that should be layed out. The src/untrusted/nacl/tls.c module * provides some routines for setting up the combined area, which use * these parameters. * * Each time a thread is created (including the main thread via * _start), a combined area is allocated and initialized for it. * * Once the main thread's TLS area is initialized, a nacl_tls_init() call * is made to save its location in the "thread register" (aka $tp). How * this is stored in the machine state varies by platform. Additional * threads set up $tp in the thread creation call and don't need to use * nacl_tls_init() explicitly. * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * * Example 1: x86-32. This diagram shows the combined area: * * +-----+-----------------+------+-----+ * | pad | TLS data, bss | TDB | pad | * +-----+-----------------+------+-----+ * ^ * | * +--- %gs * * The first word of the TDB is its own address, relative to the default * segment register (DS). Negative offsets from %gs will not work since * NaCl enforces segmentation violations, so TLS references explicitly * retrieve this TLS base pointer and then indirect relative to it (using * DS). This first word the only part of the TDB that is part of any * public ABI; the rest is private to the thread library's implementation. * * The TLS section is aligned as needed by the program's particular TLS * data. Since $tp (%gs) points right after it, the TDB also gets the * same alignment, though the TDB itself needs only word-alignment. * * mov %gs:0, %eax ; load TDB's (DS-)address from TDB. * mov -0x20(%eax), %ebx ; load TLS object into ebx * * It's also possible to use direct %gs:OFFSET accesses (with positive * OFFSETS only) to refer to the TDB, though we do not make use of that. * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * * Example 2: x86-64. * * The layout of the combined area is the same as for x86-32; the TDB * address is accessed via an intrinsic, __nacl_read_tp(). * * callq __nacl_read_tp ; load TDB address into eax. * mov -0x20(%r15,%rax,1),%eax ; sandboxed load from r15 "segment". * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * * Example 3: ARM. The positions of TDB and TLS are reversed. * * +-----------+--------+----------------+ * | TDB | header | TLS data, bss | * +-----------+--------+----------------+ * ^ ^ * | | * | +--- __aeabi_read_tp()+8 * +--- __aeabi_read_tp() * * The header is fixed at 8 bytes by the ARM ELF TLS ABI. The linker * automatically lays out TLS symbols starting at offset 8. (We do not * actually make use of the header space in this implementation, though * other, more thorough TLS implementations do.) The size of the TDB is * not part of the ABI, and is private to the thread library. Code * supported by this runtime uses only the non-PIC style, known as the * "local exec" TLS model ("initial exec" is similar). The generated * code calls __aeabi_read_tp() to locate the TLS area, then uses * positive offsets from this address. Our implementation of this * function just fetches r9. * * mov r1, #192 @ offset of symbol from $tp, i.e. var(tpoff) * bl __aeabi_read_tp * ldr r2, [r0, r1] * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * * The trivial inline functions below indicate the TLS layout parameters * specific to the machine. The rest of the runtime code is generic to * any machine, by using the values of these functions. */ #include #if defined(__pnacl__) /* * These are compiler intrinsics in NaCl's variant of LLVM. * * See: * hg/llvm/llvm-trunk/include/llvm/CodeGen/ISDOpcodes.h * hg/llvm/llvm-trunk/include/llvm/Intrinsics.td * hg/llvm/llvm-trunk/lib/CodeGen/SelectionDAG/SelectionDAG.cpp * hg/llvm/llvm-trunk/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp * hg/llvm/llvm-trunk/lib/Target/ARM/ARMISelLowering.h * hg/llvm/llvm-trunk/lib/Target/ARM/ARMISelLowering.cpp * hg/llvm/llvm-trunk/lib/Target/X86/X86ISelLowering.h * hg/llvm/llvm-trunk/lib/Target/X86/X86ISelLowering.cpp */ /* * Signed offset from $tp to the beginning of TLS data. * This is where the actual TLS for a thread is found. * The address ($tp + __nacl_tp_tls_offset(tls_size)) * is what gets initialized with the .tdata image. */ ptrdiff_t __nacl_tp_tls_offset(size_t tls_size) asm("llvm.nacl.tp.tls.offset"); /* * Signed offset from $tp to the thread library's private thread data block. * This is where implementation-private data for the thread library (if any) * is stored. On some machines it's required that the first word of this * be a pointer with value $tp. */ ptrdiff_t __nacl_tp_tdb_offset(size_t tdb_size) asm("llvm.nacl.tp.tdb.offset"); #elif defined(__i386__) || defined(__x86_64__) /* * +-----------------+------+ * | TLS data, bss | TDB | * +-----------------+------+ * ^ * | * +--- $tp points here * | * +--- first word's value is $tp address * * In x86-32, %gs segment prefix gets the $tp address, as does fetching %gs:0. * In x86-64, __nacl_read_tp() must be called; it returns the $tp address. */ static inline __attribute__((__unused__)) ptrdiff_t __nacl_tp_tls_offset(size_t tls_size) { return -(ptrdiff_t) tls_size; } static inline __attribute__((__unused__)) ptrdiff_t __nacl_tp_tdb_offset(size_t tdb_size) { return 0; } #elif defined(__arm__) /* * +-----------+--------+----------------+ * | TDB | header | TLS data, bss | * +-----------+--------+----------------+ * ^ ^ * | | * | +--- $tp+8 points here * | * +--- $tp points here * * In ARM EABI, __aeabi_read_tp() gets $tp address. * In NaCl, this is defined as register r9. */ static inline __attribute__((__unused__)) ptrdiff_t __nacl_tp_tls_offset(size_t tls_size) { return 8; } static inline __attribute__((__unused__)) ptrdiff_t __nacl_tp_tdb_offset(size_t tdb_size) { return -(ptrdiff_t) tdb_size; } #else #error "unknown platform" #endif #endif /* NATIVE_CLIENT_SRC_UNTRUSTED_NACL_TLS_PARAMS_H_ */