From patchwork Mon Apr 3 09:04:10 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: stsp X-Patchwork-Id: 1764348 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=sourceware.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=sourceware.org; envelope-from=libc-alpha-bounces+incoming=patchwork.ozlabs.org@sourceware.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; secure) header.d=sourceware.org header.i=@sourceware.org header.a=rsa-sha256 header.s=default header.b=G2h8SPYj; dkim-atps=neutral Received: from sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-384) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4PqlNy65jxz1yYb for ; Mon, 3 Apr 2023 19:05:54 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id CF3EA3854803 for ; Mon, 3 Apr 2023 09:05:52 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org CF3EA3854803 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1680512752; bh=Gbhr4/BAMK7RGCoMxuNF9NkE9VIWN/m5gPh2+tT0Bmw=; h=To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=G2h8SPYjKbakX56esiEvuEXZElmOaBpPlIcuOhRrvTBMN5l14eapmtCs4RTSqnYYS YdhKtoF/24UhTzQzMaFdQjvR+s+20YumHnnXsU88fbceEJe3PF/0wAHM/ubnbsE1/b 9Aqd6VPlwseE2ZTn0/WUCkLCsPNlM4QHSOTRrwqg= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from forward202c.mail.yandex.net (forward202c.mail.yandex.net [IPv6:2a02:6b8:c03:500:1:45:d181:d202]) by sourceware.org (Postfix) with ESMTPS id 5D16838768B1 for ; Mon, 3 Apr 2023 09:04:49 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 5D16838768B1 Received: from mail-nwsmtp-smtp-production-main-73.iva.yp-c.yandex.net (mail-nwsmtp-smtp-production-main-73.iva.yp-c.yandex.net [IPv6:2a02:6b8:c0c:a810:0:640:6b9b:0]) by forward202c.mail.yandex.net (Yandex) with ESMTP id C5D20600D1 for ; Mon, 3 Apr 2023 12:04:46 +0300 (MSK) Received: by mail-nwsmtp-smtp-production-main-73.iva.yp-c.yandex.net (smtp/Yandex) with ESMTPSA id h4J5UKjDcmI0-1nQged5G; Mon, 03 Apr 2023 12:04:46 +0300 X-Yandex-Fwd: 1 To: libc-alpha@sourceware.org Cc: Stas Sergeev Subject: [PATCH 01/12] elf: split _dl_map_object_from_fd() into reusable parts Date: Mon, 3 Apr 2023 14:04:10 +0500 Message-Id: <20230403090421.560208-2-stsp2@yandex.ru> X-Mailer: git-send-email 2.37.2 In-Reply-To: <20230403090421.560208-1-stsp2@yandex.ru> References: <20230403090421.560208-1-stsp2@yandex.ru> MIME-Version: 1.0 X-Spam-Status: No, score=-11.0 required=5.0 tests=BAYES_00, DKIM_INVALID, DKIM_SIGNED, FREEMAIL_ENVFROM_END_DIGIT, FREEMAIL_FROM, GIT_PATCH_0, KAM_DMARC_STATUS, SPF_HELO_NONE, SPF_PASS, 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.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Stas Sergeev via Libc-alpha From: stsp Reply-To: Stas Sergeev Errors-To: libc-alpha-bounces+incoming=patchwork.ozlabs.org@sourceware.org Sender: "Libc-alpha" This is mostly a mechanical split, with just a very minor moves of 2 small code fragments. This change should introduce no functional differences. The test-suite was run on x86_64/64 and showed no regressions. Signed-off-by: Stas Sergeev --- elf/dl-load.c | 329 ++++++++++++++++++++++++++++---------------------- 1 file changed, 185 insertions(+), 144 deletions(-) diff --git a/elf/dl-load.c b/elf/dl-load.c index 9a0e40c0e9..6b2c3ac6bf 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -929,137 +929,46 @@ _dl_process_pt_gnu_property (struct link_map *l, int fd, const ElfW(Phdr) *ph) } } +static void +_dl_process_phdrs (struct link_map *l, int fd) +{ + const ElfW(Phdr) *ph; -/* Map in the shared object NAME, actually located in REALNAME, and already - opened on FD. */ + /* Process program headers again after load segments are mapped in + case processing requires accessing those segments. Scan program + headers backward so that PT_NOTE can be skipped if PT_GNU_PROPERTY + exits. */ + for (ph = &l->l_phdr[l->l_phnum]; ph != l->l_phdr; --ph) + switch (ph[-1].p_type) + { + case PT_NOTE: + _dl_process_pt_note (l, fd, &ph[-1]); + break; + case PT_GNU_PROPERTY: + _dl_process_pt_gnu_property (l, fd, &ph[-1]); + break; + } +} -#ifndef EXTERNAL_MAP_FROM_FD -static -#endif -struct link_map * -_dl_map_object_from_fd (const char *name, const char *origname, int fd, - struct filebuf *fbp, char *realname, - struct link_map *loader, int l_type, int mode, - void **stack_endp, Lmid_t nsid) +static int +_dl_map_object_1 (struct link_map *l, int fd, + struct filebuf *fbp, + int mode, struct link_map *loader, + void **stack_endp, int *errval_p, + const char **errstring_p) { - struct link_map *l = NULL; const ElfW(Ehdr) *header; const ElfW(Phdr) *phdr; const ElfW(Phdr) *ph; size_t maplength; int type; /* Initialize to keep the compiler happy. */ - const char *errstring = NULL; - int errval = 0; - - /* Get file information. To match the kernel behavior, do not fill - in this information for the executable in case of an explicit - loader invocation. */ - struct r_file_id id; - if (mode & __RTLD_OPENEXEC) - { - assert (nsid == LM_ID_BASE); - memset (&id, 0, sizeof (id)); - } - else - { - if (__glibc_unlikely (!_dl_get_file_id (fd, &id))) - { - errstring = N_("cannot stat shared object"); - lose_errno: - errval = errno; - lose: - /* The file might already be closed. */ - if (fd != -1) - __close_nocancel (fd); - if (l != NULL && l->l_map_start != 0) - _dl_unmap_segments (l); - if (l != NULL && l->l_origin != (char *) -1l) - free ((char *) l->l_origin); - if (l != NULL && !l->l_libname->dont_free) - free (l->l_libname); - if (l != NULL && l->l_phdr_allocated) - free ((void *) l->l_phdr); - free (l); - free (realname); - _dl_signal_error (errval, name, NULL, errstring); - } - - /* Look again to see if the real name matched another already loaded. */ - for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next) - if (!l->l_removed && _dl_file_id_match_p (&l->l_file_id, &id)) - { - /* The object is already loaded. - Just bump its reference count and return it. */ - __close_nocancel (fd); - - /* If the name is not in the list of names for this object add - it. */ - free (realname); - add_name_to_object (l, name); - - return l; - } - } - -#ifdef SHARED - /* When loading into a namespace other than the base one we must - avoid loading ld.so since there can only be one copy. Ever. */ - if (__glibc_unlikely (nsid != LM_ID_BASE) - && (_dl_file_id_match_p (&id, &GL(dl_rtld_map).l_file_id) - || _dl_name_match_p (name, &GL(dl_rtld_map)))) - { - /* This is indeed ld.so. Create a new link_map which refers to - the real one for almost everything. */ - l = _dl_new_object (realname, name, l_type, loader, mode, nsid); - if (l == NULL) - goto fail_new; - - /* Refer to the real descriptor. */ - l->l_real = &GL(dl_rtld_map); - - /* Copy l_addr and l_ld to avoid a GDB warning with dlmopen(). */ - l->l_addr = l->l_real->l_addr; - l->l_ld = l->l_real->l_ld; - - /* No need to bump the refcount of the real object, ld.so will - never be unloaded. */ - __close_nocancel (fd); - - /* Add the map for the mirrored object to the object list. */ - _dl_add_to_namespace_list (l, nsid); - - return l; - } -#endif - - if (mode & RTLD_NOLOAD) - { - /* We are not supposed to load the object unless it is already - loaded. So return now. */ - free (realname); - __close_nocancel (fd); - return NULL; - } - - /* Print debugging message. */ - if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)) - _dl_debug_printf ("file=%s [%lu]; generating link map\n", name, nsid); +#define errstring (*errstring_p) +#define errval (*errval_p) /* This is the ELF header. We read it in `open_verify'. */ header = (void *) fbp->buf; - /* Enter the new object in the list of loaded objects. */ - l = _dl_new_object (realname, name, l_type, loader, mode, nsid); - if (__glibc_unlikely (l == NULL)) - { -#ifdef SHARED - fail_new: -#endif - errstring = N_("cannot create shared object descriptor"); - goto lose_errno; - } - /* Extract the remaining details we need from the ELF header and then read in the program header table. */ l->l_entry = header->e_entry; @@ -1083,7 +992,7 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd, /* On most platforms presume that PT_GNU_STACK is absent and the stack is * executable. Other platforms default to a nonexecutable stack and don't * need PT_GNU_STACK to do so. */ - unsigned int stack_flags = DEFAULT_STACK_PERMS; + unsigned int stack_flags = DEFAULT_STACK_PERMS; { /* Scan the program header table, collecting its load commands. */ @@ -1361,30 +1270,6 @@ cannot enable executable stack as shared object requires"); if (l->l_tls_initimage != NULL) l->l_tls_initimage = (char *) l->l_tls_initimage + l->l_addr; - /* Process program headers again after load segments are mapped in - case processing requires accessing those segments. Scan program - headers backward so that PT_NOTE can be skipped if PT_GNU_PROPERTY - exits. */ - for (ph = &l->l_phdr[l->l_phnum]; ph != l->l_phdr; --ph) - switch (ph[-1].p_type) - { - case PT_NOTE: - _dl_process_pt_note (l, fd, &ph[-1]); - break; - case PT_GNU_PROPERTY: - _dl_process_pt_gnu_property (l, fd, &ph[-1]); - break; - } - - /* We are done mapping in the file. We no longer need the descriptor. */ - if (__glibc_unlikely (__close_nocancel (fd) != 0)) - { - errstring = N_("cannot close file descriptor"); - goto lose_errno; - } - /* Signal that we closed the file. */ - fd = -1; - /* Failures before this point are handled locally via lose. There are no more failures in this function until return, to change that the cleanup handling needs to be updated. */ @@ -1409,6 +1294,22 @@ cannot enable executable stack as shared object requires"); (unsigned long int) l->l_phdr, (int) sizeof (void *) * 2, l->l_phnum); + return 0; + +lose_errno: + errval = errno; +lose: + return -1; + +#undef errval +#undef errstring +} + +static void +_dl_map_object_2 (struct link_map *l, int mode, + struct r_file_id id, const char *origname, + Lmid_t nsid) +{ /* Set up the symbol hash table. */ _dl_setup_hash (l); @@ -1510,10 +1411,150 @@ cannot enable executable stack as shared object requires"); if (!GL(dl_ns)[l->l_ns]._ns_loaded->l_auditing) _dl_audit_objopen (l, nsid); #endif +} + +/* Map in the shared object NAME, actually located in REALNAME, and already + opened on FD. */ + +#ifndef EXTERNAL_MAP_FROM_FD +static +#endif +struct link_map * +_dl_map_object_from_fd (const char *name, const char *origname, int fd, + struct filebuf *fbp, char *realname, + struct link_map *loader, int l_type, int mode, + void **stack_endp, Lmid_t nsid) +{ + struct link_map *l = NULL; + /* Initialize to keep the compiler happy. */ + const char *errstring = NULL; + int errval = 0; + + /* Get file information. To match the kernel behavior, do not fill + in this information for the executable in case of an explicit + loader invocation. */ + struct r_file_id id; + if (mode & __RTLD_OPENEXEC) + { + assert (nsid == LM_ID_BASE); + memset (&id, 0, sizeof (id)); + } + else + { + if (__glibc_unlikely (!_dl_get_file_id (fd, &id))) + { + errstring = N_("cannot stat shared object"); + lose_errno: + errval = errno; + lose: + /* The file might already be closed. */ + if (fd != -1) + __close_nocancel (fd); + if (l != NULL && l->l_map_start != 0) + _dl_unmap_segments (l); + if (l != NULL && l->l_origin != (char *) -1l) + free ((char *) l->l_origin); + if (l != NULL && !l->l_libname->dont_free) + free (l->l_libname); + if (l != NULL && l->l_phdr_allocated) + free ((void *) l->l_phdr); + free (l); + free (realname); + + _dl_signal_error (errval, name, NULL, errstring); + } + + /* Look again to see if the real name matched another already loaded. */ + for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next) + if (!l->l_removed && _dl_file_id_match_p (&l->l_file_id, &id)) + { + /* The object is already loaded. + Just bump its reference count and return it. */ + __close_nocancel (fd); + + /* If the name is not in the list of names for this object add + it. */ + free (realname); + add_name_to_object (l, name); + + return l; + } + } + +#ifdef SHARED + /* When loading into a namespace other than the base one we must + avoid loading ld.so since there can only be one copy. Ever. */ + if (__glibc_unlikely (nsid != LM_ID_BASE) + && (_dl_file_id_match_p (&id, &GL(dl_rtld_map).l_file_id) + || _dl_name_match_p (name, &GL(dl_rtld_map)))) + { + /* This is indeed ld.so. Create a new link_map which refers to + the real one for almost everything. */ + l = _dl_new_object (realname, name, l_type, loader, mode, nsid); + if (l == NULL) + goto fail_new; + + /* Refer to the real descriptor. */ + l->l_real = &GL(dl_rtld_map); + + /* Copy l_addr and l_ld to avoid a GDB warning with dlmopen(). */ + l->l_addr = l->l_real->l_addr; + l->l_ld = l->l_real->l_ld; + + /* No need to bump the refcount of the real object, ld.so will + never be unloaded. */ + __close_nocancel (fd); + + /* Add the map for the mirrored object to the object list. */ + _dl_add_to_namespace_list (l, nsid); + + return l; + } +#endif + + if (mode & RTLD_NOLOAD) + { + /* We are not supposed to load the object unless it is already + loaded. So return now. */ + free (realname); + __close_nocancel (fd); + return NULL; + } + + /* Print debugging message. */ + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)) + _dl_debug_printf ("file=%s [%lu]; generating link map\n", name, nsid); + + /* Enter the new object in the list of loaded objects. */ + l = _dl_new_object (realname, name, l_type, loader, mode, nsid); + if (__glibc_unlikely (l == NULL)) + { +#ifdef SHARED + fail_new: +#endif + errstring = N_("cannot create shared object descriptor"); + goto lose_errno; + } + + if (_dl_map_object_1 (l, fd, fbp, mode, loader, stack_endp, &errval, + &errstring)) + goto lose; + + _dl_process_phdrs (l, fd); + /* We are done mapping in the file. We no longer need the descriptor. */ + if (__glibc_unlikely (__close_nocancel (fd) != 0)) + { + errstring = N_("cannot close file descriptor"); + goto lose_errno; + } + /* Signal that we closed the file. */ + fd = -1; + + _dl_map_object_2 (l, mode, id, origname, nsid); return l; } - + /* Print search path. */ static void print_search_path (struct r_search_path_elem **list,