From patchwork Sun Feb 2 21:13:44 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 105882 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 760CE3858C35 for ; Sun, 2 Feb 2025 21:22:49 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 760CE3858C35 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=MCgQR8hj X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id A999F3858C50 for ; Sun, 2 Feb 2025 21:13:51 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org A999F3858C50 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org A999F3858C50 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1738530831; cv=none; b=ZMyKpQm4ADeazVFgyaUuMpnZcUft3Knc1EZVpQG7xmbstqe1rN9YotESRojLwht1G37QmnjxADaCJw6FNQgknMZBukbV9gChl/Yg8HU5dMYay3Xa8JN+pRp5cQGJ0M2V5pDqwHo6e8psrJOVAiKe5Z9VSW/8R4N8Sw7gYsAlWHo= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1738530831; c=relaxed/simple; bh=WIjlT0fEI2nU9KnK2/d3lTr6BpbK1iZaD6/WpNQoVX4=; h=DKIM-Signature:From:To:Subject:Message-ID:Date:MIME-Version; b=xgsz9DTSYjqq9vhffmdZOD6JEm4LWh4OramsFEl7kuUeYEszkjBBDZjIs7UK0TvgCHk1CmG41DGOsaRbAuHTzuKgqDjZLvd9k2yYaLH94+E1mt5y7exZaxG83Cw0lug+CXOTr0vW9m3MYK9BZyUL3nExAuLi456KxjBi2pyKaMo= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org A999F3858C50 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1738530831; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=4QhA2IWQLxQmzG7YIQjidbO6xEuDGI5F8kiqTUOywJQ=; b=MCgQR8hjWU5p5OYjljlvjcebxnVwrI/8PCRQI0iBZsU+ur22VhMB5L0oE8iHERjVKywoZJ gE0iRqThYnbGw/6YDGG32iNaLYtssGb4nd72qoCr+7XmLjQ9+neiYzJCFg0iyXHcEnV25B bq/FlSGaie0+9O27dxlXeWsYyMhc44M= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-216-PTaCHeLLPui2LTsL0jMn-w-1; Sun, 02 Feb 2025 16:13:50 -0500 X-MC-Unique: PTaCHeLLPui2LTsL0jMn-w-1 X-Mimecast-MFC-AGG-ID: PTaCHeLLPui2LTsL0jMn-w Received: from mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.15]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 0289119560AA for ; Sun, 2 Feb 2025 21:13:49 +0000 (UTC) Received: from oldenburg.str.redhat.com (unknown [10.2.16.2]) by mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 9C7101956094 for ; Sun, 2 Feb 2025 21:13:47 +0000 (UTC) From: Florian Weimer To: libc-alpha@sourceware.org Subject: [PATCH v4 10/14] elf: Bootstrap allocation for future protected memory allocator In-Reply-To: Message-ID: <79fc25bb098ab1671534a83c201df942d7fba5e1.1738530302.git.fweimer@redhat.com> References: X-From-Line: 79fc25bb098ab1671534a83c201df942d7fba5e1 Mon Sep 17 00:00:00 2001 Date: Sun, 02 Feb 2025 22:13:44 +0100 User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.15 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: GFrH5I6nuvhNN89Jdv1uSrs2ao_az1bOVw8IFh_TSHQ_1738530829 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-10.8 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org A subsequent change will place link maps into memory which is read-only most of the time. This means that the link map for ld.so itself (GLPM (dl_rtld_map)) needs to be put there as well, which requires allocating it dynamically. --- elf/Makefile | 1 + elf/dl-protmem_bootstrap.h | 29 ++++ elf/rtld.c | 87 ++++++---- elf/tst-rtld-nomem.c | 177 ++++++++++++++++++++ sysdeps/generic/dl-early_mmap.h | 35 ++++ sysdeps/generic/ldsodefs.h | 6 +- sysdeps/mips/Makefile | 6 + sysdeps/unix/sysv/linux/dl-early_allocate.c | 17 +- sysdeps/unix/sysv/linux/dl-early_mmap.h | 41 +++++ 9 files changed, 345 insertions(+), 54 deletions(-) create mode 100644 elf/dl-protmem_bootstrap.h create mode 100644 elf/tst-rtld-nomem.c create mode 100644 sysdeps/generic/dl-early_mmap.h create mode 100644 sysdeps/unix/sysv/linux/dl-early_mmap.h diff --git a/elf/Makefile b/elf/Makefile index 5c833871d0..1d93993241 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -463,6 +463,7 @@ tests += \ tst-rtld-no-malloc \ tst-rtld-no-malloc-audit \ tst-rtld-no-malloc-preload \ + tst-rtld-nomem \ tst-rtld-run-static \ tst-single_threaded \ tst-single_threaded-pthread \ diff --git a/elf/dl-protmem_bootstrap.h b/elf/dl-protmem_bootstrap.h new file mode 100644 index 0000000000..a2fc267a2d --- /dev/null +++ b/elf/dl-protmem_bootstrap.h @@ -0,0 +1,29 @@ +/* Bootstrap allocation for the protected memory area. + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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 + . */ + +#include + +/* Return a pointer to the protected memory area, or NULL if + allocation fails. This function is called before self-relocation, + and the system call needs to be inlined for (most) + HIDDEN_VAR_NEEDS_DYNAMIC_RELOC targets. */ +static inline __attribute__ ((always_inline)) struct rtld_protmem * +_dl_protmem_bootstrap (void) +{ + return _dl_early_mmap (sizeof (struct rtld_protmem)); +} diff --git a/elf/rtld.c b/elf/rtld.c index 0bc7d9dbcd..de9e87cd0b 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -53,6 +53,7 @@ #include #include #include +#include #include @@ -345,8 +346,6 @@ struct rtld_global _rtld_global = extern struct rtld_global _rtld_local __attribute__ ((alias ("_rtld_global"), visibility ("hidden"))); -struct rtld_protmem _rtld_protmem; - /* This variable is similar to _rtld_local, but all values are read-only after relocation. */ struct rtld_global_ro _rtld_global_ro attribute_relro = @@ -421,6 +420,7 @@ static ElfW(Addr) _dl_start_final (void *arg); struct dl_start_final_info { struct link_map l; + struct rtld_protmem *protmem; RTLD_TIMING_VAR (start_time); }; static ElfW(Addr) _dl_start_final (void *arg, @@ -455,6 +455,14 @@ _dl_start_final (void *arg, struct dl_start_final_info *info) { ElfW(Addr) start_addr; +#ifndef DONT_USE_BOOTSTRAP_MAP + GLRO (dl_protmem) = info->protmem; +#endif + + /* Delayed error reporting after relocation processing. */ + if (GLRO (dl_protmem) == NULL) + _dl_fatal_printf ("Fatal glibc error: Cannot allocate link map\n"); + __rtld_malloc_init_stubs (); /* Do not use an initializer for these members because it would @@ -478,21 +486,10 @@ _dl_start_final (void *arg, struct dl_start_final_info *info) RTLD_TIMING_SET (start_time, info->start_time); #endif - /* Transfer data about ourselves to the permanent link_map structure. */ -#ifndef DONT_USE_BOOTSTRAP_MAP - GLPM(dl_rtld_map).l_addr = info->l.l_addr; - GLPM(dl_rtld_map).l_ld = info->l.l_ld; - GLPM(dl_rtld_map).l_ld_readonly = info->l.l_ld_readonly; - memcpy (GLPM(dl_rtld_map).l_info, info->l.l_info, - sizeof GLPM(dl_rtld_map).l_info); - GLPM(dl_rtld_map).l_mach = info->l.l_mach; - GLPM(dl_rtld_map).l_relocated = 1; -#endif _dl_setup_hash (&GLPM(dl_rtld_map)); GLPM(dl_rtld_map).l_real = &GLPM(dl_rtld_map); GLPM(dl_rtld_map).l_map_start = (ElfW(Addr)) &__ehdr_start; GLPM(dl_rtld_map).l_map_end = (ElfW(Addr)) _end; - /* Copy the TLS related data if necessary. */ #ifndef DONT_USE_BOOTSTRAP_MAP # if NO_TLS_OFFSET != 0 GLPM(dl_rtld_map).l_rw->l_tls_offset = NO_TLS_OFFSET; @@ -537,43 +534,59 @@ _dl_start (void *arg) rtld_timer_start (&info.start_time); #endif - /* Partly clean the `bootstrap_map' structure up. Don't use - `memset' since it might not be built in or inlined and we cannot - make function calls at this point. Use '__builtin_memset' if we - know it is available. We do not have to clear the memory if we - do not have to use the temporary bootstrap_map. Global variables - are initialized to zero by default. */ -#ifndef DONT_USE_BOOTSTRAP_MAP -# ifdef HAVE_BUILTIN_MEMSET - __builtin_memset (bootstrap_map.l_info, '\0', sizeof (bootstrap_map.l_info)); -# else - for (size_t cnt = 0; - cnt < sizeof (bootstrap_map.l_info) / sizeof (bootstrap_map.l_info[0]); - ++cnt) - bootstrap_map.l_info[cnt] = 0; -# endif + struct rtld_protmem *protmem = _dl_protmem_bootstrap (); + bool protmem_failed = protmem == NULL; + if (protmem_failed) + { + /* Allocate some space for a stub protected memory area on the + stack, to get to the point when we can report the error. */ + protmem = alloca (sizeof (*protmem)); + + /* Partly clean the `bootstrap_map' structure up. Don't use + `memset' since it might not be built in or inlined and we + cannot make function calls at this point. Use + '__builtin_memset' if we know it is available. */ +#ifdef HAVE_BUILTIN_MEMSET + __builtin_memset (protmem->_dl_rtld_map.l_info, + '\0', sizeof (protmem->_dl_rtld_map.l_info)); +#else + for (size_t i = 0; i < array_length (protmem->_dl_rtld_map.l_info); ++i) + protmem->_dl_rtld_map.l_info[i] = NULL; #endif + } /* Figure out the run-time load address of the dynamic linker itself. */ - bootstrap_map.l_addr = elf_machine_load_address (); + protmem->_dl_rtld_map.l_addr = elf_machine_load_address (); /* Read our own dynamic section and fill in the info array. */ - bootstrap_map.l_ld = (void *) bootstrap_map.l_addr + elf_machine_dynamic (); - bootstrap_map.l_ld_readonly = DL_RO_DYN_SECTION; - elf_get_dynamic_info (&bootstrap_map, true, false); + protmem->_dl_rtld_map.l_ld = ((void *) protmem->_dl_rtld_map.l_addr + + elf_machine_dynamic ()); + protmem->_dl_rtld_map.l_ld_readonly = DL_RO_DYN_SECTION; + elf_get_dynamic_info (&protmem->_dl_rtld_map, true, false); #ifdef ELF_MACHINE_BEFORE_RTLD_RELOC - ELF_MACHINE_BEFORE_RTLD_RELOC (&bootstrap_map, bootstrap_map.l_info); + ELF_MACHINE_BEFORE_RTLD_RELOC (&protmem->_dl_rtld_map, + protmem->_dl_rtld_map.l_info); #endif - if (bootstrap_map.l_addr) + if (protmem->_dl_rtld_map.l_addr) { /* Relocate ourselves so we can do normal function calls and data access using the global offset table. */ - ELF_DYNAMIC_RELOCATE (&bootstrap_map, NULL, 0, 0, 0); + ELF_DYNAMIC_RELOCATE (&protmem->_dl_rtld_map, NULL, 0, 0, 0); } - bootstrap_map.l_relocated = 1; + protmem->_dl_rtld_map.l_relocated = 1; + + /* Communicate the original mmap failure to _dl_start_final. */ + if (protmem_failed) + protmem = NULL; + +#ifdef DONT_USE_BOOTSTRAP_MAP + GLRO (dl_protmem) = protmem; +#else + info.protmem = protmem; +#endif /* Please note that we don't allow profiling of this object and therefore need not test whether we have to allocate the array @@ -1024,7 +1037,7 @@ ERROR: audit interface '%s' requires version %d (maximum supported version %d); else *last_audit = (*last_audit)->next = &newp->ifaces; - /* The dynamic linker link map is statically allocated, so the + /* The dynamic linker link map is allocated separately, so the cookie in _dl_new_object has not happened. */ link_map_audit_state (&GLPM(dl_rtld_map), GLRO (dl_naudit))->cookie = (intptr_t) &GLPM(dl_rtld_map); diff --git a/elf/tst-rtld-nomem.c b/elf/tst-rtld-nomem.c new file mode 100644 index 0000000000..b8caf5d8fe --- /dev/null +++ b/elf/tst-rtld-nomem.c @@ -0,0 +1,177 @@ +/* Test that out-of-memory during early ld.so startup reports an error. + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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 + . */ + +/* This test invokes execve with increasing RLIMIT_AS limits, to + trigger the early _dl_protmem_bootstrap memory allocation failure + and check that a proper error is reported for it. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int +do_test (void) +{ + long int page_size = sysconf (_SC_PAGE_SIZE); + TEST_VERIFY (page_size > 0); + + struct rlimit rlim; + TEST_COMPARE (getrlimit (RLIMIT_AS, &rlim), 0); + + /* Reduced once we encounter success. */ + int kb_limit = 2048; + + /* Exit status in case of test error. */ + enum { unexpected_error = 17 }; + + /* Used to verify that at least one execve crash is encountered. + This is how execve reports late memory allocation failures due + to rlimit. */ + bool crash_seen = false; + + /* Set to true if the early out-of-memory error message is + encountered. */ + bool oom_error_seen = false; + + /* Set to true once success (the usage message) is encountered. + This is expected to happen only after oom_error_seen turns true, + otherwise the rlimit does not work. */ + bool success_seen = false; + + /* Try increasing rlimits. The kernel rounds down to page sizes, so + try only page size increments. */ + for (int kb = 128; kb <= kb_limit; kb += page_size / 1024) + { + printf ("info: trying %d KiB\n", kb); + + int pipe_stdout[2]; + xpipe (pipe_stdout); + int pipe_stderr[2]; + xpipe (pipe_stderr); + + pid_t pid = xfork (); + if (pid == 0) + { + /* Restrict address space for the ld.so invocation. */ + rlim.rlim_cur = kb * 1024; + int ret = setrlimit (RLIMIT_AS, &rlim); + TEST_COMPARE (ret, 0); + if (ret != 0) + _exit (unexpected_error); + + /* Redirect output for capture. */ + TEST_COMPARE (dup2 (pipe_stdout[1], STDOUT_FILENO), + STDOUT_FILENO); + TEST_COMPARE (dup2 (pipe_stderr[1], STDERR_FILENO), + STDERR_FILENO); + + /* Try to invoke ld.so with the resource limit in place. */ + char ldso[] = "ld.so"; + char *const argv[] = { ldso, NULL }; + execve (support_objdir_elf_ldso, argv, &argv[1]); + TEST_COMPARE (errno, ENOMEM); + _exit (unexpected_error); + } + + int status; + xwaitpid (pid, &status, 0); + + xclose (pipe_stdout[1]); + xclose (pipe_stderr[1]); + + /* No output on stdout. */ + char actual[1024]; + ssize_t count = read (pipe_stdout[0], actual, sizeof (actual)); + if (count < 0) + FAIL_EXIT1 ("read stdout: %m"); + TEST_COMPARE_BLOB ("", 0, actual, count); + + /* Read the standard error output. */ + count = read (pipe_stderr[0], actual, sizeof (actual)); + if (count < 0) + FAIL_EXIT1 ("read stderr: %m"); + + if (WIFEXITED (status) && WEXITSTATUS (status) == 1) + { + TEST_VERIFY (oom_error_seen); + static const char expected[] = "\ +ld.so: missing program name\n\ +Try 'ld.so --help' for more information.\n\ +"; + TEST_COMPARE_BLOB (expected, strlen (expected), actual, count); + if (!success_seen) + { + puts ("info: first success"); + /* Four more tries with increasing rlimit, to catch + potential secondary crashes. */ + kb_limit = kb + page_size / 1024 * 4; + } + success_seen = true; + continue; + } + if (WIFEXITED (status) && WEXITSTATUS (status) == 127) + { + TEST_VERIFY (crash_seen); + TEST_VERIFY (!success_seen); + static const char expected[] = + "Fatal glibc error: Cannot allocate link map\n"; + TEST_COMPARE_BLOB (expected, strlen (expected), actual, count); + if (!oom_error_seen) + puts ("info: first memory allocation error"); + oom_error_seen = true; + continue; + } + + TEST_VERIFY (!success_seen); + TEST_VERIFY (!oom_error_seen); + + if (WIFEXITED (status)) + { + /* Unexpected regular exit status. */ + TEST_COMPARE (WIFEXITED (status), 1); + TEST_COMPARE_BLOB ("", 0, actual, count); + } + else if (WIFSIGNALED (status) && WTERMSIG (status) == SIGSEGV) + { + /* Very early out of memory. No output expected. */ + TEST_COMPARE_BLOB ("", 0, actual, count); + if (!crash_seen) + puts ("info: first expected crash observed"); + crash_seen = true; + } + else + { + /* Unexpected status. */ + printf ("error: unexpected exit status %d\n", status); + support_record_failure (); + TEST_COMPARE_BLOB ("", 0, actual, count); + } + } + + return 0; +} + +#include diff --git a/sysdeps/generic/dl-early_mmap.h b/sysdeps/generic/dl-early_mmap.h new file mode 100644 index 0000000000..75eb8eb30c --- /dev/null +++ b/sysdeps/generic/dl-early_mmap.h @@ -0,0 +1,35 @@ +/* Early anonymous mmap for ld.so, before self-relocation. Generic version. + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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 + . */ + +#ifndef DL_EARLY_MMAP_H +#define DL_EARLY_MMAP_H + +/* The generic version assumes that regular mmap works. It returns + NULL on failure. */ +static inline void * +_dl_early_mmap (size_t size) +{ + void *ret = __mmap (NULL, size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (ret == MAP_FAILED) + return NULL; + else + return ret; +} + +#endif /* DL_EARLY_MMAP_H */ diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h index ac71668f29..0ff0650cb1 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -515,12 +515,11 @@ struct rtld_protmem /* Structure describing the dynamic linker itself. */ struct link_map _dl_rtld_map; }; -extern struct rtld_protmem _rtld_protmem attribute_hidden; #endif /* SHARED */ /* GLPM(FIELD) denotes the FIELD in the protected memory area. */ #ifdef SHARED -# define GLPM(name) _rtld_protmem._##name +# define GLPM(name) GLRO (dl_protmem)->_##name #else # define GLPM(name) _##name #endif @@ -660,6 +659,9 @@ struct rtld_global_ro EXTERN enum dso_sort_algorithm _dl_dso_sort_algo; #ifdef SHARED + /* Pointer to the protected memory area. */ + EXTERN struct rtld_protmem *_dl_protmem; + /* We add a function table to _rtld_global which is then used to call the function instead of going through the PLT. The result is that we can avoid exporting the functions and we do not jump diff --git a/sysdeps/mips/Makefile b/sysdeps/mips/Makefile index d189973aa0..2ec9bf2a6c 100644 --- a/sysdeps/mips/Makefile +++ b/sysdeps/mips/Makefile @@ -32,6 +32,12 @@ test-xfail-tst-audit24d = yes test-xfail-tst-audit25a = yes test-xfail-tst-audit25b = yes +# _dl_start performs a system call before self-relocation, to allocate +# the link map for ld.so itself. This involves a direct function +# call. Build rtld.c in MIPS32 mode, so that this function call does +# not require a run-time relocation. +CFLAGS-rtld.c += -mno-mips16 + ifneq ($(o32-fpabi),) tests += tst-abi-interlink diff --git a/sysdeps/unix/sysv/linux/dl-early_allocate.c b/sysdeps/unix/sysv/linux/dl-early_allocate.c index 257519b789..ca7121d52e 100644 --- a/sysdeps/unix/sysv/linux/dl-early_allocate.c +++ b/sysdeps/unix/sysv/linux/dl-early_allocate.c @@ -29,7 +29,7 @@ #include #include -#include +#include /* Defined in brk.c. */ extern void *__curbrk; @@ -63,20 +63,7 @@ _dl_early_allocate (size_t size) unfortunate ASLR layout decisions and kernel bugs, particularly for static PIE. */ if (result == NULL) - { - long int ret; - int prot = PROT_READ | PROT_WRITE; - int flags = MAP_PRIVATE | MAP_ANONYMOUS; -#ifdef __NR_mmap2 - ret = MMAP_CALL_INTERNAL (mmap2, 0, size, prot, flags, -1, 0); -#else - ret = MMAP_CALL_INTERNAL (mmap, 0, size, prot, flags, -1, 0); -#endif - if (INTERNAL_SYSCALL_ERROR_P (ret)) - result = NULL; - else - result = (void *) ret; - } + result = _dl_early_mmap (size); return result; } diff --git a/sysdeps/unix/sysv/linux/dl-early_mmap.h b/sysdeps/unix/sysv/linux/dl-early_mmap.h new file mode 100644 index 0000000000..1d83daa6a6 --- /dev/null +++ b/sysdeps/unix/sysv/linux/dl-early_mmap.h @@ -0,0 +1,41 @@ +/* Early anonymous mmap for ld.so, before self-relocation. Linux version. + Copyright (C) 2022-2023 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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 + . */ + +#ifndef DL_EARLY_MMAP_H +#define DL_EARLY_MMAP_H + +#include + +static inline __attribute__ ((always_inline)) void * +_dl_early_mmap (size_t size) +{ + long int ret; + int prot = PROT_READ | PROT_WRITE; + int flags = MAP_PRIVATE | MAP_ANONYMOUS; +#ifdef __NR_mmap2 + ret = MMAP_CALL_INTERNAL (mmap2, 0, size, prot, flags, -1, 0); +#else + ret = MMAP_CALL_INTERNAL (mmap, 0, size, prot, flags, -1, 0); +#endif + if (INTERNAL_SYSCALL_ERROR_P (ret)) + return NULL; + else + return (void *) ret; +} + +#endif /* DL_EARLY_MMAP_H */