From 2c5153b043b44e9935a334ae9b2d5a4bc5258b40 Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Fri, 11 Jan 2013 14:43:05 -0800 Subject: [PATCH] libc: add getauxval() Add support for getauxval(). This method allows a program an easy way to retrieve information from the kernel auxiliary vector, and will hopefully replace other clumsy ways of accessing this same information. This particular function was also added to glibc in glibc 2.16. See the following URLs for more details. * http://lwn.net/Articles/519085/ * http://www.gnu.org/software/libc/manual/html_node/Auxiliary-Vector.html This change is a prerequisite for bug 7959813. Bug: http://code.google.com/p/android/issues/detail?id=38441 Change-Id: Iba19d899df334bddc6f4899077ece2fc87564ea8 --- libc/Android.mk | 1 + libc/bionic/getauxval.cpp | 48 ++++++++++++++++++++++++++++++++++++ libc/bionic/libc_init_common.c | 10 ++++++++ libc/bionic/libc_init_static.c | 34 +++++--------------------- libc/include/elf.h | 15 ++++++++++++ libc/include/sys/auxv.h | 39 ++++++++++++++++++++++++++++++ libc/private/bionic_auxv.h | 39 ++++++++++++++++++++++++++++++ tests/Android.mk | 1 + tests/getauxval_test.cpp | 55 ++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 214 insertions(+), 28 deletions(-) create mode 100644 libc/bionic/getauxval.cpp create mode 100644 libc/include/sys/auxv.h create mode 100644 libc/private/bionic_auxv.h create mode 100644 tests/getauxval_test.cpp diff --git a/libc/Android.mk b/libc/Android.mk index 360bdc1cf..c98d2ff59 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -275,6 +275,7 @@ libc_bionic_src_files := \ bionic/dirent.cpp \ bionic/eventfd.cpp \ bionic/__fgets_chk.cpp \ + bionic/getauxval.cpp \ bionic/getcwd.cpp \ bionic/libgen.cpp \ bionic/__memcpy_chk.cpp \ diff --git a/libc/bionic/getauxval.cpp b/libc/bionic/getauxval.cpp new file mode 100644 index 000000000..38a05fc21 --- /dev/null +++ b/libc/bionic/getauxval.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +__LIBC_HIDDEN__ +Elf32_auxv_t* __libc_auxv = NULL; + +extern "C" unsigned long int getauxval(unsigned long int type) { + Elf32_auxv_t* v; + + for (v = __libc_auxv; v->a_type != AT_NULL; v++) { + if (v->a_type == type) { + return v->a_un.a_val; + } + } + + return 0; +} diff --git a/libc/bionic/libc_init_common.c b/libc/bionic/libc_init_common.c index fb164f447..86e1eb574 100644 --- a/libc/bionic/libc_init_common.c +++ b/libc/bionic/libc_init_common.c @@ -38,6 +38,7 @@ #include #include +#include extern unsigned __get_sp(void); extern pid_t gettid(void); @@ -95,6 +96,15 @@ void __libc_init_common(uintptr_t* elf_data) { __progname = argv[0] ? argv[0] : ""; environ = envp; + // The auxiliary vector is at the end of the environment block + while(*envp != NULL) { + envp++; + } + /* The end of the environment block is marked by two NULL pointers */ + envp++; + + __libc_auxv = (Elf32_auxv_t*) envp; + __system_properties_init(); // Requires 'environ'. } diff --git a/libc/bionic/libc_init_static.c b/libc/bionic/libc_init_static.c index ba7f008e9..bb3a41021 100644 --- a/libc/bionic/libc_init_static.c +++ b/libc/bionic/libc_init_static.c @@ -49,6 +49,7 @@ #include #include #include +#include // Returns the address of the page containing address 'x'. #define PAGE_START(x) ((x) & PAGE_MASK) @@ -65,27 +66,13 @@ static void call_array(void(**list)()) } } -/* - * Find the value of the AT_* variable passed to us by the kernel. - */ -static unsigned find_aux(unsigned *vecs, unsigned type) { - while (vecs[0]) { - if (vecs[0] == type) { - return vecs[1]; - } - vecs += 2; - } - - return 0; // should never happen -} - -static void apply_gnu_relro(unsigned *vecs) { +static void apply_gnu_relro() { Elf32_Phdr *phdr_start; - unsigned phdr_ct; + unsigned long int phdr_ct; Elf32_Phdr *phdr; - phdr_start = (Elf32_Phdr *) find_aux(vecs, AT_PHDR); - phdr_ct = find_aux(vecs, AT_PHNUM); + phdr_start = (Elf32_Phdr *) getauxval(AT_PHDR); + phdr_ct = getauxval(AT_PHNUM); for (phdr = phdr_start; phdr < (phdr_start + phdr_ct); phdr++) { if (phdr->p_type != PT_GNU_RELRO) @@ -108,7 +95,6 @@ __noreturn void __libc_init(uintptr_t *elfdata, { int argc; char **argv, **envp; - unsigned *vecs; __libc_init_tls(NULL); @@ -129,14 +115,6 @@ __noreturn void __libc_init(uintptr_t *elfdata, argv = (char**)(elfdata + 1); envp = argv + argc + 1; - // The auxiliary vector is at the end of the environment block - vecs = (unsigned *) envp; - while (vecs[0] != 0) { - vecs++; - } - /* The end of the environment block is marked by two NULL pointers */ - vecs++; - /* The executable may have its own destructors listed in its .fini_array * so we need to ensure that these are called when the program exits * normally. @@ -144,6 +122,6 @@ __noreturn void __libc_init(uintptr_t *elfdata, if (structors->fini_array) __cxa_atexit(__libc_fini,structors->fini_array,NULL); - apply_gnu_relro(vecs); + apply_gnu_relro(); exit(slingshot(argc, argv, envp)); } diff --git a/libc/include/elf.h b/libc/include/elf.h index c3e32264c..ac0f1d2c3 100644 --- a/libc/include/elf.h +++ b/libc/include/elf.h @@ -28,8 +28,23 @@ #ifndef _ELF_H #define _ELF_H +#include #include #include +typedef struct { + uint32_t a_type; + union { + uint32_t a_val; + } a_un; +} Elf32_auxv_t; + +typedef struct { + uint64_t a_type; + union { + uint64_t a_val; + } a_un; +} Elf64_auxv_t; + #endif /* _ELF_H */ diff --git a/libc/include/sys/auxv.h b/libc/include/sys/auxv.h new file mode 100644 index 000000000..918442f54 --- /dev/null +++ b/libc/include/sys/auxv.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef _SYS_AUXV_H_ +#define _SYS_AUXV_H_ + +#include + +__BEGIN_DECLS + +unsigned long int getauxval(unsigned long int type); + +__END_DECLS + +#endif /* _SYS_AUXV_H_ */ diff --git a/libc/private/bionic_auxv.h b/libc/private/bionic_auxv.h new file mode 100644 index 000000000..d3b0038d8 --- /dev/null +++ b/libc/private/bionic_auxv.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef _SYS_AUXV_H_ +#define _SYS_AUXV_H_ + +#include + +__BEGIN_DECLS + +extern Elf32_auxv_t* __libc_auxv; + +__END_DECLS + +#endif /* _SYS_AUXV_H_ */ diff --git a/tests/Android.mk b/tests/Android.mk index 635c0214c..25da12084 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -55,6 +55,7 @@ test_c_flags = \ test_src_files = \ dirent_test.cpp \ fenv_test.cpp \ + getauxval_test.cpp \ getcwd_test.cpp \ libgen_test.cpp \ pthread_test.cpp \ diff --git a/tests/getauxval_test.cpp b/tests/getauxval_test.cpp new file mode 100644 index 000000000..01c11c3c2 --- /dev/null +++ b/tests/getauxval_test.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +// getauxval() was only added as of glibc version 2.16. +// See: http://lwn.net/Articles/519085/ +// Don't try to compile this code on older glibc versions. + +#if defined(__BIONIC__) + #define GETAUXVAL_CAN_COMPILE 1 +#elif defined(__GLIBC_PREREQ) + #if __GLIBC_PREREQ(2, 16) + #define GETAUXVAL_CAN_COMPILE 1 + #endif +#endif + +#if defined(GETAUXVAL_CAN_COMPILE) + +#include + +TEST(getauxval, expected_values) { + ASSERT_EQ((unsigned long int) 0, getauxval(AT_SECURE)); + ASSERT_EQ(getuid(), getauxval(AT_UID)); + ASSERT_EQ(geteuid(), getauxval(AT_EUID)); + ASSERT_EQ(getgid(), getauxval(AT_GID)); + ASSERT_EQ(getegid(), getauxval(AT_EGID)); + ASSERT_EQ((unsigned long int) getpagesize(), getauxval(AT_PAGESZ)); + + ASSERT_NE((unsigned long int) 0, getauxval(AT_PHDR)); + ASSERT_NE((unsigned long int) 0, getauxval(AT_PHNUM)); + ASSERT_NE((unsigned long int) 0, getauxval(AT_ENTRY)); + ASSERT_NE((unsigned long int) 0, getauxval(AT_PAGESZ)); +} + +TEST(getauxval, unexpected_values) { + ASSERT_EQ((unsigned long int) 0, getauxval(0xdeadbeef)); +} + +#endif /* GETAUXVAL_CAN_COMPILE */ -- 2.11.0