crash of 32-bit powerpc -r347549 kernel built via system-clang-8, _init_tls is where the initial DIAGNOSTICS-reported SIGSEGV happens
Mark Millard
marklmi at yahoo.com
Sun Jun 9 08:49:53 UTC 2019
So far I've not been able to find the code that is supposed
to establish the value of environ in /sbin/init as matching
the value of arginfo->ps_envstr from the exec_copyout_strings
use by do_execve in the kernel.
Anyone know where to point me to for what I seem to have
missed?
The issue driving the question is having the *sp++ in
_init_tls code below get SIGSEGV on 32-bit FreeBSD when
built via system-clang-8 and devel/powerpc64-binutils:
sp = (Elf_Addr *) environ;
while (*sp++ != 0)
;
The below is relevant detail that I've found.
_start in /sbin/init 's instance of lib/csu/powerpc/crt1.c
calls _init_tls that is from lib/libc/gen/tls.c but first
might assign to environ :
. . .
#include "ignore_init.c"
. . .
void
_start(int argc, char **argv, char **env,
const struct Struct_Obj_Entry *obj __unused, void (*cleanup)(void),
struct ps_strings *ps_strings)
{
handle_argv(argc, argv, env);
if (ps_strings != (struct ps_strings *)0)
__ps_strings = ps_strings;
if (&_DYNAMIC != NULL)
atexit(cleanup);
else
_init_tls();
#ifdef GCRT
atexit(_mcleanup);
monstartup(&eprol, &etext);
#endif
handle_static_init(argc, argv, env);
exit(main(argc, argv, env));
}
lib/csu/common/ignore_init.c has:
char **environ;
. . .
static inline void
handle_argv(int argc, char *argv[], char **env)
{
const char *s;
if (environ == NULL)
environ = env;
if (argc > 0 && argv[0] != NULL) {
__progname = argv[0];
for (s = __progname; *s != '\0'; s++) {
if (*s == '/')
__progname = s + 1;
}
}
}
So _start's char**env argument might be used to assign
environ. But either way I've not managed to find the
binding to the kernel exec_copyout_strings operation.
_init_tls has the *sp++ loop that I referenced earlier:
extern char **environ;
void
_init_tls(void)
{
#ifndef PIC
Elf_Addr *sp;
Elf_Auxinfo *aux, *auxp;
Elf_Phdr *phdr;
size_t phent, phnum;
int i;
void *tls;
sp = (Elf_Addr *) environ;
while (*sp++ != 0)
;
. . .
On the kernel side for invoking /sbin/init is . . .
From /usr/src/sys/sys/imgact.h :
struct image_args {
char *buf; /* pointer to string buffer */
void *bufkva; /* cookie for string buffer KVA */
char *begin_argv; /* beginning of argv in buf */
char *begin_envv; /* (interal use only) beginning of envv in buf,
* access with exec_args_get_begin_envv(). */
char *endp; /* current `end' pointer of arg & env strings */
char *fname; /* pointer to filename of executable (system space) */
char *fname_buf; /* pointer to optional malloc(M_TEMP) buffer */
int stringspace; /* space left in arg & env buffer */
int argc; /* count of argument strings */
int envc; /* count of environment strings */
int fd; /* file descriptor of the executable */
struct filedesc *fdp; /* new file descriptor table */
};
do_execve from sys/kern/kern_exec.c has use, including envc
but avoiding begin_envv (via starting from begin_argv):
static int
do_execve(struct thread *td, struct image_args *args, struct mac *mac_p)
{
. . .
/*
* Copy out strings (args and env) and initialize stack base.
*/
stack_base = (*p->p_sysent->sv_copyout_strings)(imgp);
The exec_copyout_strings code (accessed via ->sv_copyout_strings)
does
stack_base = (register_t *)vectp;
stringp = imgp->args->begin_argv;
argc = imgp->args->argc;
envc = imgp->args->envc;
. . .
/* a null vector table pointer separates the argp's from the envp's */
suword(vectp++, 0);
suword(&arginfo->ps_envstr, (long)(intptr_t)vectp);
suword32(&arginfo->ps_nenvstr, envc);
/*
* Fill in environment portion of vector table.
*/
for (; envc > 0; --envc) {
suword(vectp++, (long)(intptr_t)destp);
while (*stringp++ != 0)
destp++;
destp++;
}
/* end of vector table is a null pointer */
suword(vectp, 0);
. . .
(From what I've seen for /sbin/init being invoked, envc==0 .)
The use involves struct ps_strings from /usr/src/sys/sys/exec.h :
struct ps_strings {
char **ps_argvstr; /* first of 0 or more argument strings */
unsigned int ps_nargvstr; /* the number of argument strings */
char **ps_envstr; /* first of 0 or more environment strings */
unsigned int ps_nenvstr; /* the number of environment strings */
};
The initialization of the begin_envv and envc for much of
the code seems to trace back to:
static void
start_init(void *dummy)
{
struct image_args args;
. . .
while ((path = strsep(&tmp_init_path, ":")) != NULL) {
if (bootverbose)
printf("start_init: trying %s\n", path);
memset(&args, 0, sizeof(args));
. . .
===
Mark Millard
marklmi at yahoo.com
( dsl-only.net went
away in early 2018-Mar)
More information about the freebsd-ppc
mailing list