| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "linux-static-pie.h" |
| |
| #include <array> |
| #include <string_view> |
| |
| #include "lss.h" |
| |
| namespace { |
| |
| using namespace std::literals; |
| |
| void WriteString(int fd, std::string_view str) { sys_write(fd, str.data(), str.size()); } |
| |
| [[noreturn]] void Exit(int status) { |
| while (true) { |
| sys_exit_group(status); |
| } |
| } |
| |
| [[noreturn]] bool Panic(std::string_view str) { |
| WriteString(2, str); |
| Exit(127); |
| } |
| |
| // This just returns *ptr, but it prevents the compiler from doing dataflow |
| // analysis and realizing that the return value is just *ptr. This makes sure |
| // that compiler can't do things like constant-fold the value because it knows |
| // ptr is the address of a constexpr object. |
| int* Launder(int* const* ptr) { |
| __asm__("" : "=r"(ptr) : "0"(ptr)); |
| return *ptr; |
| } |
| |
| // This is big enough to ensure the RELRO segment will span multiple pages. |
| constexpr size_t kBig = 128 * 1024 / sizeof(int*); |
| constexpr size_t kMiddle = kBig / 2; |
| |
| // constexpr ensures this will be linker-initialized in the RELRO segment. |
| // It's accesseed at runtime via "laundered" address so the reads can't be |
| // constant-folded. |
| constexpr std::array<int*, kBig> kMuchRelro = []() { |
| // This is a zero-initialization, mutation, and copy in constexpr context |
| // just to achieve the effect of a designated array element initializer. |
| std::array<int*, kBig> big{}; |
| big[kMiddle] = &gSyscallErrno; |
| return big; |
| }(); |
| |
| } // namespace |
| |
| // The traditional Unix/Linux entry point protocol is not compatible with the |
| // C/C++ ABI: instead the argc, argv, and envp words are directly on the stack. |
| __asm__( |
| R"""( |
| .pushsection .text._start |
| .globl _start |
| .type _start, %function |
| _start: |
| .cfi_startproc |
| )""" |
| #if defined(__aarch64__) |
| R"""( |
| mov x0, sp |
| bl StaticPieSetup |
| bl TestMain |
| )""" |
| #elif defined(__x86_64__) |
| R"""( |
| mov %rsp, %rdi |
| and $-16, %rsp |
| call StaticPieSetup |
| call TestMain |
| )""" |
| #else |
| #error "what machine?" |
| #endif |
| R"""( |
| .cfi_endproc |
| .size _start, . - _start |
| .popsection |
| )"""); |
| |
| extern "C" [[noreturn]] void TestMain() { |
| static int* data_address = &gSyscallErrno; |
| static int* const relro_address = &gSyscallErrno; |
| |
| // Since gSyscallErrno has internal linkage, the references here will use |
| // pure PC-relative address materialization. |
| |
| int* from_data = Launder(&data_address); |
| if (from_data != &gSyscallErrno) { |
| Panic("address in data not relocated properly"sv); |
| } |
| |
| int* from_relro = Launder(&relro_address); |
| if (from_relro != &gSyscallErrno) { |
| Panic("address in RELRO not relocated properly"sv); |
| } |
| |
| from_relro = Launder(&kMuchRelro[kMiddle]); |
| if (from_relro != &gSyscallErrno) { |
| Panic("second address in RELRO not relocated properly"sv); |
| } |
| |
| WriteString(1, "Hello, world!\n"sv); |
| Exit(0); |
| } |