RTLD_START passes correct args to _dl_init (XCC)
authorKevin Klues <klueska@cs.berkeley.edu>
Thu, 9 Jul 2015 17:43:01 +0000 (10:43 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 14 Jul 2015 14:33:16 +0000 (10:33 -0400)
Previously, we used the default RTLD_START for x86_64, which relied on
argv and friends being passed in on the stack. In Akaros, we pass these
values in via procinfo, not on the stack.

RTLD_START is called after dl_open() is called, which uses our existing
implementation of DL_FIND_ARG_COMPONENTS in dl-sysdep.c to extract the
arguments properly from procinfo.  However, RTLD_START wasn't using
these extracted values when calling _dl_init(). It was attempting to
find these values on the stack (where they don't exist in Akaros!) and
then passing them to _dl_init(). In the new version, we now pass the
values extracted from our DL_FIND_ARG_COMPONENTS macro to
_dl_init() properly.

This problem was first noticed when I wanted to use the glibc-defined
variable 'program_invocation_name' in a test I was writing. Under the
hood, this variable takes the value of argv[0]. Statically linked
programs had this variable set properly.  Dynamically linked programs
didn't (because incorrect args were being passed to _dl_init()).

In fixing this stuff up, I noticed that we don't properly handle the
skipping of argments, as specified by _dl_skip_args when running
_dl_open().  The old implementation of RTLD_START took care to skip the
right number of arguments based on this variable and then patch up the
stack appropriately.  Since we get our arguments from procinfo, we can't
patch things up so easily (procinfo is read-only).  We'll either need to
come up with something cleverer, or somehow interpose on _start and push
our extracted arguments from procinfo onto the stack before calling
_start(). This may be the better approach overall since having the args
on the stack is technically part of the ELF ABI.

We also don't properly handle passing our _dl_fini() function as the 6th
argument to __libc_start_main(), or passing the start address of the
user-accessable stack as the 7th argument. We are passing a pointer to
_dl_fini() into _start properly (via %rdx as specified by the ELF ABI),
we just ignore it currently. We should revisit this in the (near)

The new tests/progname.c should now work for both statically and
dynamically linked binaries. Previusly it only worked for statically
linked ones.

tests/progname.c [new file with mode: 0644]
tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/dl-machine.h [new file with mode: 0644]

diff --git a/tests/progname.c b/tests/progname.c
new file mode 100644 (file)
index 0000000..da7e156
--- /dev/null
@@ -0,0 +1,12 @@
+#define __GNU_SOURCE
+#include <errno.h>
+#include <stdio.h>
+extern char *program_invocation_name;
+extern char *program_invocation_short_name;
+int main(int argc, char **argv)
+       printf("argc: %d, argv[0]: %s\n", argc, argv[0]);
+       printf("program_invocation_name: %s\n", program_invocation_name);
+       return 0;
diff --git a/tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/dl-machine.h b/tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/dl-machine.h
new file mode 100644 (file)
index 0000000..101356c
--- /dev/null
@@ -0,0 +1,53 @@
+/* Machine-dependent ELF dynamic relocation inline functions.  x86-64 version.
+   Copyright (C) 2001-2014 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Andreas Jaeger <aj@suse.de>.
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   Lesser General Public License for more details.
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+#include_next "dl-machine.h"
+#undef RTLD_START
+/* Initial entry point code for the dynamic linker.
+ *    The C function `_dl_start' is the real entry point;
+ *        its return value is the user program's entry point.  */
+#define RTLD_START \
+static ElfW(Addr) __attribute_used__ internal_function _dl_start (void *arg);\
+extern char **_environ attribute_hidden;\
+void __attribute__((noreturn)) _start(void)\
+       /* Pass our stack pointer to _dl_start. */\
+       /* Retreive our user entry point. */\
+       void *rsp, *user_entry;\
+       asm volatile("movq %%rsp, %0" : "=r" (rsp));\
+       user_entry = (void*)_dl_start(rsp);\
+       \
+       /* Clear %rbp to mark outermost frame even for initializers. */\
+       asm volatile("xorq %rbp, %rbp");\
+       \
+       /* Call _dl_init to run the initializers. */\
+       _dl_init(GL(dl_ns[0]._ns_loaded), _dl_argc, INTUSE(_dl_argv), _environ);\
+       \
+       /* Pass our finalizer function to the user in %rdx, as per ELF ABI. */\
+       /* And jump to the user! */\
+       asm volatile(\
+               "movq %0, %%rdx;"\
+               "jmp *%1;"\
+               : : "r" (_dl_fini), "r" (user_entry) : "rdx"\
+       );\
+       __builtin_unreachable();\