#define _DYN_LOADER
#include <sys/types.h>
#include <sys/syscall.h>
#include <sys/timetc.h>
#ifndef PIC
#include <sys/mman.h>
#endif
#include <tib.h>
#include <limits.h>
#include <link.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "init.h"
#define MAX(a,b) (((a)>(b))?(a):(b))
#ifdef TIB_EXTRA_ALIGN
# define TIB_ALIGN MAX(__alignof__(struct tib), TIB_EXTRA_ALIGN)
#else
# define TIB_ALIGN __alignof__(struct tib)
#endif
char ***_csu_finish(char **_argv, char **_envp, void (*_cleanup)(void));
int _pagesize = 0;
struct timekeep *_timekeep;
unsigned long _hwcap, _hwcap2;
int _hwcap_avail, _hwcap2_avail;
char **environ __attribute__((weak)) = NULL;
char *__progname __attribute__((weak)) = NULL;
#ifndef PIC
struct dl_phdr_info _static_phdr_info __relro = { .dlpi_name = "a.out" };
static inline void early_static_init(char **_argv, char **_envp);
static inline void setup_static_tib(Elf_Phdr *_phdr, int _phnum);
extern Elf_Ehdr __executable_start[] __attribute__((weak));
#endif
const dl_cb *_dl_cb __relro = NULL;
int HIDDEN(execve)(const char *, char *const *, char *const *)
__attribute__((weak));
void _libc_preinit(int, char **, char **, dl_cb_cb *) __dso_hidden;
void
_libc_preinit(int argc, char **argv, char **envp, dl_cb_cb *cb)
{
AuxInfo *aux;
#ifndef PIC
Elf_Phdr *phdr = NULL;
int phnum = 0;
if (cb == NULL)
early_static_init(argv, envp);
#endif
if (cb != NULL)
_dl_cb = cb(DL_CB_CUR);
while (*envp++ != NULL)
;
for (aux = (void *)envp; aux->au_id != AUX_null; aux++) {
switch (aux->au_id) {
case AUX_hwcap:
_hwcap = aux->au_v;
_hwcap_avail = 1;
break;
case AUX_hwcap2:
_hwcap2 = aux->au_v;
_hwcap2_avail = 1;
break;
case AUX_pagesz:
_pagesize = aux->au_v;
break;
#ifndef PIC
case AUX_base:
_static_phdr_info.dlpi_addr = aux->au_v;
break;
case AUX_phdr:
phdr = (void *)aux->au_v;
break;
case AUX_phnum:
phnum = aux->au_v;
break;
#endif
case AUX_openbsd_timekeep:
if (_tc_get_timecount) {
_timekeep = (void *)aux->au_v;
if (_timekeep &&
_timekeep->tk_version != TK_VERSION)
_timekeep = NULL;
}
if (issetugid() == 0 && getenv("LIBC_NOUSERTC"))
_timekeep = NULL;
break;
}
}
#ifndef PIC
if (cb == NULL && phdr == NULL && __executable_start != NULL) {
phdr = (void *)((char *)__executable_start +
__executable_start->e_phoff);
phnum = __executable_start->e_phnum;
}
_static_phdr_info.dlpi_phdr = phdr;
_static_phdr_info.dlpi_phnum = phnum;
if (cb == NULL)
setup_static_tib(phdr, phnum);
if (phdr) {
int i;
for (i = 0; i < phnum; i++) {
if (phdr[i].p_type == PT_LOAD &&
(phdr[i].p_flags & PF_W) == 0)
mimmutable((void *)(_static_phdr_info.dlpi_addr +
phdr[i].p_vaddr), phdr[i].p_memsz);
}
}
#endif
}
#ifndef __arm__
# define TYPE "@"
#else
# define TYPE "%"
#endif
#ifdef __LP64__
# define VALUE_ALIGN ".balign 8"
# define VALUE_DIRECTIVE ".quad"
#else
# define VALUE_ALIGN ".balign 4"
# ifdef __hppa__
# define VALUE_DIRECTIVE ".int P%"
# else
# define VALUE_DIRECTIVE ".int"
# endif
#endif
#ifdef PIC
__asm(" .section .init_array.50,\"a\","TYPE"init_array\n " \
VALUE_ALIGN"\n "VALUE_DIRECTIVE" _libc_preinit\n .previous");
#else
__asm(" .section .preinit_array,\"a\","TYPE"preinit_array\n " \
VALUE_ALIGN"\n "VALUE_DIRECTIVE" _libc_preinit\n .previous");
#endif
char ***
_csu_finish(char **argv, char **envp, void (*cleanup)(void))
{
if (_dl_cb != NULL && _dl_cb->dl_clean_boot != NULL)
_dl_cb->dl_clean_boot();
if (cleanup != NULL)
atexit(cleanup);
return &environ;
}
#ifndef PIC
static inline void
early_static_init(char **argv, char **envp)
{
static char progname_storage[NAME_MAX+1];
environ = envp;
if (*argv != NULL) {
const char *p = strrchr(*argv, '/');
if (p == NULL)
p = *argv;
else
p++;
strlcpy(progname_storage, p, sizeof(progname_storage));
}
__progname = progname_storage;
}
#define ELF_ROUND(x,malign) (((x) + (malign)-1) & ~((malign)-1))
static void *static_tls __relro;
static size_t static_tls_fsize __relro;
size_t _static_tls_size __relro = 0;
int _static_tls_align __relro;
int _static_tls_align_offset __relro;
static inline void
setup_static_tib(Elf_Phdr *phdr, int phnum)
{
struct tib *tib;
char *base;
int i;
_static_tls_align = TIB_ALIGN;
if (phdr != NULL) {
for (i = 0; i < phnum; i++) {
if (phdr[i].p_type != PT_TLS)
continue;
if (phdr[i].p_memsz == 0)
break;
if (phdr[i].p_memsz < phdr[i].p_filesz)
break;
if (phdr[i].p_align > getpagesize())
break;
_static_tls_align = MAX(phdr[i].p_align, TIB_ALIGN);
#if TLS_VARIANT == 1
_static_tls_size = phdr[i].p_memsz;
_static_tls_align_offset =
ELF_ROUND(sizeof(struct tib), _static_tls_align) -
sizeof(struct tib);
#elif TLS_VARIANT == 2
_static_tls_size = ELF_ROUND(phdr[i].p_memsz,
phdr[i].p_align);
_static_tls_align_offset = ELF_ROUND(_static_tls_size,
_static_tls_align) - _static_tls_size;
#endif
if (phdr[i].p_vaddr != 0 && phdr[i].p_filesz != 0) {
static_tls = (void *)phdr[i].p_vaddr +
_static_phdr_info.dlpi_addr;
static_tls_fsize = phdr[i].p_filesz;
}
break;
}
}
base = mmap(NULL, _static_tls_size + _static_tls_align_offset
+ sizeof *tib, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
tib = _static_tls_init(base, NULL);
tib->tib_tid = getthrid();
TCB_SET(TIB_TO_TCB(tib));
#if ! TCB_HAVE_MD_GET
_libc_single_tcb = TIB_TO_TCB(tib);
#endif
}
struct tib *
_static_tls_init(char *base, void *thread)
{
struct tib *tib;
base += _static_tls_align_offset;
# if TLS_VARIANT == 1
tib = (struct tib *)base;
base += sizeof(struct tib);
# elif TLS_VARIANT == 2
tib = (struct tib *)(base + _static_tls_size);
# endif
if (_static_tls_size) {
if (static_tls != NULL)
memcpy(base, static_tls, static_tls_fsize);
memset(base + static_tls_fsize, 0,
_static_tls_size - static_tls_fsize);
}
TIB_INIT(tib, NULL, thread);
return tib;
}
#endif