[libc-commits] [libc] 5d59385 - [libc] Setup TLS in x86_64 loader.

Siva Chandra Reddy via libc-commits libc-commits at lists.llvm.org
Fri Aug 7 23:19:16 PDT 2020


Author: Siva Chandra Reddy
Date: 2020-08-07T23:19:03-07:00
New Revision: 5d59385ba67ec20dc4a3e13b9a7088ace970df4d

URL: https://github.com/llvm/llvm-project/commit/5d59385ba67ec20dc4a3e13b9a7088ace970df4d
DIFF: https://github.com/llvm/llvm-project/commit/5d59385ba67ec20dc4a3e13b9a7088ace970df4d.diff

LOG: [libc] Setup TLS in x86_64 loader.

The new code added is still very x86_64 specific. AArch64 support will
be added very soon and refactoring of the loader code will be done as
part of the patches adding it.

Reviewed By: asteinhauser

Differential Revision: https://reviews.llvm.org/D82700

Added: 
    libc/config/linux/app.h
    libc/test/loader/linux/tls_test.cpp

Modified: 
    libc/config/linux/CMakeLists.txt
    libc/loader/linux/x86_64/CMakeLists.txt
    libc/loader/linux/x86_64/start.cpp
    libc/test/loader/CMakeLists.txt
    libc/test/loader/linux/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libc/config/linux/CMakeLists.txt b/libc/config/linux/CMakeLists.txt
index c8f496bf3f07..50e98379cf53 100644
--- a/libc/config/linux/CMakeLists.txt
+++ b/libc/config/linux/CMakeLists.txt
@@ -9,3 +9,9 @@ add_gen_header(
   DEPENDS
     libc.src.__support.common
 )
+
+add_header(
+  app_h
+  HDR
+    app.h
+)

diff  --git a/libc/config/linux/app.h b/libc/config/linux/app.h
new file mode 100644
index 000000000000..48e42ae2c80a
--- /dev/null
+++ b/libc/config/linux/app.h
@@ -0,0 +1,44 @@
+//===-- Classes to capture properites of linux applications -----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_CONFIG_LINUX_APP_H
+#define LLVM_LIBC_CONFIG_LINUX_APP_H
+
+#include <stdint.h>
+
+namespace __llvm_libc {
+
+// Data structure to capture properties of the linux/ELF TLS.
+struct TLS {
+  // The load address of the TLS.
+  uintptr_t address;
+
+  // The bytes size of the TLS.
+  uintptr_t size;
+
+  // The alignment of the TLS layout. It assumed that the alignment
+  // value is a power of 2.
+  uintptr_t align;
+};
+
+// Data structure which captures properties of a linux application.
+struct AppProperties {
+  // Page size used for the application.
+  uintptr_t pageSize;
+
+  // The properties of an application's TLS.
+  TLS tls;
+};
+
+// Creates and initializes the TLS area for the current thread. Should not
+// be called before app.tls has been initialized.
+void initTLS();
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_CONFIG_LINUX_APP_H

diff  --git a/libc/loader/linux/x86_64/CMakeLists.txt b/libc/loader/linux/x86_64/CMakeLists.txt
index aa118167a06a..ceaa94ee11a3 100644
--- a/libc/loader/linux/x86_64/CMakeLists.txt
+++ b/libc/loader/linux/x86_64/CMakeLists.txt
@@ -4,7 +4,10 @@ add_loader_object(
     start.cpp
   DEPENDS
     libc.config.linux.linux_syscall_h
+    libc.config.linux.app_h
     libc.include.sys_syscall
+    libc.src.string.memcpy
+    libc.src.sys.mman.mmap
   COMPILE_OPTIONS
     -fno-omit-frame-pointer
     -ffreestanding # To avoid compiler warnings about calling the main function.

diff  --git a/libc/loader/linux/x86_64/start.cpp b/libc/loader/linux/x86_64/start.cpp
index dbd0b367bfc6..320fda96105b 100644
--- a/libc/loader/linux/x86_64/start.cpp
+++ b/libc/loader/linux/x86_64/start.cpp
@@ -6,14 +6,76 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "config/linux/app.h"
 #include "config/linux/syscall.h"
+#include "include/sys/mman.h"
 #include "include/sys/syscall.h"
+#include "src/string/memcpy.h"
+#include "src/sys/mman/mmap.h"
 
+#include <asm/prctl.h>
 #include <linux/auxvec.h>
+#include <linux/elf.h>
 #include <stdint.h>
 
 extern "C" int main(int, char **, char **);
 
+namespace __llvm_libc {
+
+#ifdef SYS_mmap2
+static constexpr long mmapSyscallNumber = SYS_mmap2;
+#elif SYS_mmap
+static constexpr long mmapSyscallNumber = SYS_mmap;
+#else
+#error "Target platform does not have SYS_mmap or SYS_mmap2 defined"
+#endif
+
+// TODO: Declare var an extern var in config/linux/app.h so that other
+// libc functions can make use of the application wide information. For
+// example, mmap can pick up the page size from here.
+AppProperties app;
+
+// TODO: The function is x86_64 specific. Move it to config/linux/app.h
+// and generalize it. Also, dynamic loading is not handled currently.
+void initTLS() {
+  if (app.tls.size == 0)
+    return;
+
+  // We will assume the alignment is always a power of two.
+  uintptr_t tlsSize = (app.tls.size + app.tls.align) & -app.tls.align;
+
+  // Per the x86_64 TLS ABI, the entry pointed to by the thread pointer is the
+  // address of the TLS block. So, we add more size to accomodate this address
+  // entry.
+  size_t tlsSizeWithAddr = tlsSize + sizeof(uintptr_t);
+
+  // We cannot call the mmap function here as the functions set errno on
+  // failure. Since errno is implemented via a thread local variable, we cannot
+  // use errno before TLS is setup.
+  long mmapRetVal = __llvm_libc::syscall(
+      mmapSyscallNumber, nullptr, tlsSizeWithAddr, PROT_READ | PROT_WRITE,
+      MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+  // We cannot check the return value with MAP_FAILED as that is the return
+  // of the mmap function and not the mmap syscall.
+  if (mmapRetVal < 0 && static_cast<uintptr_t>(mmapRetVal) > -app.pageSize)
+    __llvm_libc::syscall(SYS_exit, 1);
+  uintptr_t *tlsAddr = reinterpret_cast<uintptr_t *>(mmapRetVal);
+
+  // x86_64 TLS faces down from the thread pointer with the first entry
+  // pointing to the address of the first real TLS byte.
+  uintptr_t endPtr = reinterpret_cast<uintptr_t>(tlsAddr) + tlsSize;
+  *reinterpret_cast<uintptr_t *>(endPtr) = endPtr;
+
+  __llvm_libc::memcpy(tlsAddr, reinterpret_cast<const void *>(app.tls.address),
+                      app.tls.size);
+  if (__llvm_libc::syscall(SYS_arch_prctl, ARCH_SET_FS, endPtr) == -1)
+    __llvm_libc::syscall(SYS_exit, 1);
+}
+
+} // namespace __llvm_libc
+
+using __llvm_libc::app;
+
 struct Args {
   // At the language level, argc is an int. But we use uint64_t as the x86_64
   // ABI specifies it as an 8 byte value.
@@ -53,12 +115,37 @@ extern "C" void _start() {
 
   // After the env array, is the aux-vector. The end of the aux-vector is
   // denoted by an AT_NULL entry.
+  Elf64_Phdr *programHdrTable = nullptr;
+  uintptr_t programHdrCount;
   for (AuxEntry *aux_entry = reinterpret_cast<AuxEntry *>(env_end_marker + 1);
        aux_entry->type != AT_NULL; ++aux_entry) {
-    // TODO: Read the aux vector and store necessary information in a libc wide
-    // data structure.
+    switch (aux_entry->type) {
+    case AT_PHDR:
+      programHdrTable = reinterpret_cast<Elf64_Phdr *>(aux_entry->value);
+      break;
+    case AT_PHNUM:
+      programHdrCount = aux_entry->value;
+      break;
+    case AT_PAGESZ:
+      app.pageSize = aux_entry->value;
+      break;
+    default:
+      break; // TODO: Read other useful entries from the aux vector.
+    }
   }
 
+  for (uintptr_t i = 0; i < programHdrCount; ++i) {
+    Elf64_Phdr *phdr = programHdrTable + i;
+    if (phdr->p_type != PT_TLS)
+      continue;
+    // TODO: p_vaddr value has to be adjusted for static-pie executables.
+    app.tls.address = phdr->p_vaddr;
+    app.tls.size = phdr->p_memsz;
+    app.tls.align = phdr->p_align;
+  }
+
+  __llvm_libc::initTLS();
+
   __llvm_libc::syscall(SYS_exit,
                        main(args->argc, reinterpret_cast<char **>(args->argv),
                             reinterpret_cast<char **>(env_ptr)));

diff  --git a/libc/test/loader/CMakeLists.txt b/libc/test/loader/CMakeLists.txt
index a38d3884ded6..5123bed8aaa9 100644
--- a/libc/test/loader/CMakeLists.txt
+++ b/libc/test/loader/CMakeLists.txt
@@ -37,9 +37,12 @@ function(add_loader_test target_name)
       ${LIBC_BUILD_DIR}/include
   )
 
-  get_fq_deps_list(fq_deps_list ${ADD_LOADER_TEST_DEPENDS})
-  get_object_files_for_test(link_object_files has_skipped_entrypoint_list ${fq_deps_list})
-  target_link_libraries(${fq_target_name} ${link_object_files})
+  if(ADD_LOADER_TEST_DEPENDS)
+    get_fq_deps_list(fq_deps_list ${ADD_LOADER_TEST_DEPENDS})
+    add_dependencies(${fq_target_name} ${fq_deps_list})
+    get_object_files_for_test(link_object_files has_skipped_entrypoint_list ${fq_deps_list})
+    target_link_libraries(${fq_target_name} ${link_object_files})
+  endif()
 
   target_link_options(
     ${fq_target_name}

diff  --git a/libc/test/loader/linux/CMakeLists.txt b/libc/test/loader/linux/CMakeLists.txt
index 1f47b2ce3f06..b7899fa4c5c1 100644
--- a/libc/test/loader/linux/CMakeLists.txt
+++ b/libc/test/loader/linux/CMakeLists.txt
@@ -35,3 +35,17 @@ add_loader_test(
   DEPENDS
     libc.loader.linux.crt1
 )
+
+add_loader_test(
+  loader_tls_test
+  SRC
+    tls_test.cpp
+  DEPENDS
+    libc.config.linux.app_h
+    libc.include.errno
+    libc.include.sys_mman
+    libc.loader.linux.crt1
+    libc.src.assert.__assert_fail
+    libc.src.errno.__errno_location
+    libc.src.sys.mman.mmap
+)

diff  --git a/libc/test/loader/linux/tls_test.cpp b/libc/test/loader/linux/tls_test.cpp
new file mode 100644
index 000000000000..144d894a2caf
--- /dev/null
+++ b/libc/test/loader/linux/tls_test.cpp
@@ -0,0 +1,40 @@
+//===-- Loader test to check if tls size is read correctly ----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "include/errno.h"
+#include "include/sys/mman.h"
+
+#undef NDEBUG
+#include "src/assert/assert.h"
+
+#include "src/errno/llvmlibc_errno.h"
+#include "src/sys/mman/mmap.h"
+
+constexpr int threadLocalDataSize = 101;
+_Thread_local int a[threadLocalDataSize] = {123};
+
+int main(int argc, char **argv, char **envp) {
+  assert(a[0] == 123);
+
+  for (int i = 1; i < threadLocalDataSize; ++i)
+    a[i] = i;
+  for (int i = 1; i < threadLocalDataSize; ++i)
+    assert(a[i] == i);
+
+  // Call mmap with bad params so that an error value is
+  // set in errno. Since errno is implemented using a thread
+  // local var, this helps us test setting of errno and
+  // reading it back.
+  assert(llvmlibc_errno == 0);
+  void *addr = __llvm_libc::mmap(nullptr, 0, PROT_READ,
+                                 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+  assert(addr == MAP_FAILED);
+  assert(llvmlibc_errno == EINVAL);
+
+  return 0;
+}


        


More information about the libc-commits mailing list