summaryrefslogtreecommitdiffstats
path: root/usr/klibc/libc_init.c
blob: c5b9bab4ef0c8733c56a49e87deff287c918a64c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/*
 * libc_init.c
 *
 * This function takes the raw data block set up by the ELF loader
 * in the kernel and parses it.  It is invoked by crt0.S which makes
 * any necessary adjustments and passes calls this function using
 * the standard C calling convention.
 *
 * The arguments are:
 *  uintptr_t *elfdata	 -- The ELF loader data block; usually from the stack.
 *                          Basically a pointer to argc.
 *  void (*onexit)(void) -- Function to install into onexit
 */

/*
 * Several Linux ABIs don't pass the onexit pointer, and the ones that
 * do never use it.  Therefore, unless USE_ONEXIT is defined, we just
 * ignore the onexit pointer.
 */
/* #define USE_ONEXIT */

#include <stddef.h>
#include <stdlib.h>
#include <stdint.h>
#include <klibc/compiler.h>
#include <elf.h>
#include <sys/auxv.h>
#include <klibc/sysconfig.h>
#include "atexit.h"

#if _KLIBC_HAS_ARCHINIT
# include "klibc/archinit.h"
#else
# define __libc_archinit() ((void)0)
#endif

/* This file is included from __static_init.c or __shared_init.c */
#ifndef SHARED
# error "SHARED should be defined to 0 or 1"
#endif

char **environ;
unsigned int __page_size, __page_shift;

struct auxentry {
	unsigned long type;
	unsigned long v;
};

extern void __libc_init_stdio(void);

unsigned long __auxval[_AUXVAL_MAX];

__noreturn __libc_init(uintptr_t * elfdata, void (*onexit) (void))
{
	int argc;
	char **argv, **envp, **envend;
	struct auxentry *auxentry;
#if SHARED
	typedef int (*main_t) (int, char **, char **);
	main_t MAIN = NULL;
#else
	extern int main(int, char **, char **);
#define MAIN main
#endif
	unsigned int page_size = 0, page_shift = 0;

#ifdef USE_ONEXIT
	if (onexit) {
		static struct atexit at_exit;

		at_exit.fctn = (void (*)(int, void *))onexit;
		/* at_exit.next = NULL already */
		__atexit_list = &at_exit;
	}
#else
	(void)onexit;		/* Ignore this... */
#endif

	argc = (int)*elfdata++;
	argv = (char **)elfdata;
	envp = argv + (argc + 1);

	/* The auxillary entry vector is after all the environment vars */
	for (envend = envp; *envend; envend++) ;
	auxentry = (struct auxentry *)(envend + 1);

	while (auxentry->type) {
		if (auxentry->type < _AUXVAL_MAX)
			__auxval[auxentry->type] = auxentry->v;
		auxentry++;
	}

#if SHARED
	MAIN = (main_t) __auxval[AT_ENTRY];
#endif

	__page_size = page_size = __auxval[AT_PAGESZ];

#if __GNUC__ >= 4
	/* unsigned int is 32 bits on all our architectures */
	page_shift = __builtin_clz(page_size) ^ 31;
#elif defined(__i386__) || defined(__x86_64__)
	asm("bsrl %1,%0" : "=r" (page_shift) : "r" (page_size));
#else
	while (page_size > 1) {
		page_shift++;
		page_size >>= 1;
	}
#endif
	__page_shift = page_shift;

#if _KLIBC_HAS_ARCHINIT
	__libc_archinit();
#endif

	__libc_init_stdio();

	environ = envp;
	exit(MAIN(argc, argv, envp));
}