From patchwork Fri Mar 1 04:30:32 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "H.J. Lu" X-Patchwork-Id: 208636 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:7301:2097:b0:108:e6aa:91d0 with SMTP id gs23csp855256dyb; Thu, 29 Feb 2024 20:30:48 -0800 (PST) X-Forwarded-Encrypted: i=3; AJvYcCWib5Nja/e2Cyo0gfD48Ql6yx6SZm+I8EaebBC+RlaxfFKO7tcGorNY/8rNPAtTm7kgaJXvZkRL+JC+i65R8koz6CrbBg== X-Google-Smtp-Source: AGHT+IGuNfF97QYzUCQmwaEMeovvn/Xnq0G3Qd7IvuJy0m/49BatBPmTOCMPv5WMop0DjmTekHXd X-Received: by 2002:a05:622a:1716:b0:42e:c69a:3aa8 with SMTP id h22-20020a05622a171600b0042ec69a3aa8mr638430qtk.57.1709267448613; Thu, 29 Feb 2024 20:30:48 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1709267448; cv=pass; d=google.com; s=arc-20160816; b=xN/RjXnkdRYjQKQrSLQiJFWk/elkw08/zN/k3PTfahrTTmYLk/EU+NdoCAfvdwbN6U 9GNkZnNcH1XKb23hRcpJkHRkZlE5zs1dIoOfxTaDL2zgY4TKidsexRwtQpOOszK0pnZa AemQjK0UqP644EKK5WM4i+Qitv4FJmalIauNU2APPsj/AGFJW4rK6KEmWwFuZSGzDK8p yRRXsKtnvvPPBrr6F0Wl/NRin9U6EXxVgsf8RYF03Y2LQIpVQPgbVqYWYivmUShM2OLE uH13AfBq2o96pkK+WoVm9cfxJXiyzznUkZba6Um36NmBfJRKXi+2xd+v4ypPxC0RB25p K1RQ== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:content-transfer-encoding :mime-version:message-id:date:subject:to:from:dkim-signature :arc-filter:dmarc-filter:delivered-to; bh=tP0ZeVyEcevVbFyav8T0OmvnQX+3O+AIjk56GkKwJiQ=; fh=NLxAvL/bDfPg4AGOtxqvQlND8vazkZrNzKLY8+LAbBY=; b=XDmvwOU3mqUNZS1cgKK/s6+AvbRW4srQsytFU/StI1zH6YpaMRFfmvNfPEUR7PnnhJ UHAUhVl0ZKnzE1KVnIjpErfud8G4bt+vS9MsSfL3D+ecAk14rJdfkxKJ6kCs3Y1yZ4Us Zy8hZZIDpr1Z5/iDRQz8md1LjtL8dzNllb/Hs+aWYys1O/ZFQIq8LfzPyqcX1c3mK8zF LMqITeM907X28O3/wo6QQn/xBPlUCZN7i1K1JzAOHHsyojuSHucGo3JKTXErLuOtx1n8 620qpcUG3PXDBJcG1QosvvBm7ObIb//gl2QclM1Whx0xNVIyTvhkBVdmb4GaWDo6CS3S GO1g==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@gmail.com header.s=20230601 header.b=L+VizOWX; arc=pass (i=1); spf=pass (google.com: domain of binutils-bounces+ouuuleilei=gmail.com@sourceware.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="binutils-bounces+ouuuleilei=gmail.com@sourceware.org"; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from server2.sourceware.org (server2.sourceware.org. [2620:52:3:1:0:246e:9693:128c]) by mx.google.com with ESMTPS id w5-20020ac86b05000000b0042e9f4d487esi2740995qts.120.2024.02.29.20.30.48 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 29 Feb 2024 20:30:48 -0800 (PST) Received-SPF: pass (google.com: domain of binutils-bounces+ouuuleilei=gmail.com@sourceware.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) client-ip=2620:52:3:1:0:246e:9693:128c; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20230601 header.b=L+VizOWX; arc=pass (i=1); spf=pass (google.com: domain of binutils-bounces+ouuuleilei=gmail.com@sourceware.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="binutils-bounces+ouuuleilei=gmail.com@sourceware.org"; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 1E1ED385842C for ; Fri, 1 Mar 2024 04:30:48 +0000 (GMT) X-Original-To: binutils@sourceware.org Delivered-To: binutils@sourceware.org Received: from mail-pl1-x62f.google.com (mail-pl1-x62f.google.com [IPv6:2607:f8b0:4864:20::62f]) by sourceware.org (Postfix) with ESMTPS id A41EF3858D34 for ; Fri, 1 Mar 2024 04:30:35 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org A41EF3858D34 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org A41EF3858D34 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::62f ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1709267439; cv=none; b=lz/NysmUx2ZNklJ5CpNSvsNhyOcEuwBZZtdpsm+TdwG1INFsNb4RnPYuXTKTvQ527v+P2T5BCkWqzvyOYkPr7YKOE9s6SBnkX/xB4kTRI0HUoJVeckAKmtuhKLAny+gJoVMKZwlWakE+7i7sJ+noU0AzgpIrREdHcwIgAcWaFxU= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1709267439; c=relaxed/simple; bh=GHU9ijB8pEVYr3XcKgOLl8Sy0TSbOciKM/VuZiELONc=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=TCuB7o9dtOBDIJTFYpqYJSsfuDXfvkFb71+9gjs7wowe2NuSyZIGIgLN9tIGfQ9Yx4NB2lHi8JGRQCclKOPq7Ylf3qK2XB81z1h5QzznCB0pvAVnE/SqTHgUlPiidRZ2+Lp0IOlN5LfXAxjHniIQqjZe+MFNwdjJ/kzyXHWeDeM= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-pl1-x62f.google.com with SMTP id d9443c01a7336-1dc1ff58fe4so14755805ad.1 for ; Thu, 29 Feb 2024 20:30:35 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1709267434; x=1709872234; darn=sourceware.org; h=content-transfer-encoding:mime-version:message-id:date:subject:to :from:from:to:cc:subject:date:message-id:reply-to; bh=tP0ZeVyEcevVbFyav8T0OmvnQX+3O+AIjk56GkKwJiQ=; b=L+VizOWXFxvPjmKrPJHkUf+jxD6WMNgJKcNDo0FhZGdfAL/MQnNL816qjmrWDKgnx+ OVCWrRlibpKRtvkvF4H1cUvxk3WxHfkiPfkqP1f9NAvh71Q89TDmti0MsmEuqASNBVYX 1vC+yW9U5rPzQP8HdDyPv6++x8oXvwt/8NSBWdLeftpGnLFDqK8L8K5toNssZ3erGvYt BEO8GpGze9zx01WV2g4rNlSjpIeAt7/XAcooXUQcJJnKAtjqZ/T4QLG8FeyJJVW4455R kIy1eSBEqxBC68k3uxsh8my/UJGunqBXlu4FZHJc7Ig7RxRUhyyNp0FLZD2V20ljIFIh Fc4A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1709267434; x=1709872234; h=content-transfer-encoding:mime-version:message-id:date:subject:to :from:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=tP0ZeVyEcevVbFyav8T0OmvnQX+3O+AIjk56GkKwJiQ=; b=cAPkkc8btTWxOvmGjwD7uowpsjY7MaaLH2m1jn3yOelVBYkMiiuRMh0uovkroJZapm Ll6mtH8QTTQYFuhAaaBfYjKzrRoJeCjGXblRHlS5zIsfjZ65LGGcSi//HHh+3PrXAU/O bYGt6deS4e81koGKTLMd0baBeIOXboi5n0yk3oXdK1Yf5qQVkAiH8HCXvY/6yupqsP8C hczgvA7WK2aMtE38L1z0+yn7EELEEHy6w0RUFx1k64pTaGflHE3al6H0bIV4X1lOOYtD TEOPUwjoQj1+Kd0NH00XV5XhSppAt+z6dbu6gCvThQ2zmcI62ydJZ5e44RmIYl42QcHz AF4A== X-Gm-Message-State: AOJu0Yxq4Wrr7G8S1eGZWEmKpD47UbsULwbCmtBVL4auEO0JWGu7B8oM G+1sZUqUppJIEJuk+Xs/KGTS+VdABP4uhR/UV/znvTuz8AqsxZRkYuNi4tqx X-Received: by 2002:a17:902:da87:b0:1dc:8ba1:edc3 with SMTP id j7-20020a170902da8700b001dc8ba1edc3mr668783plx.9.1709267434039; Thu, 29 Feb 2024 20:30:34 -0800 (PST) Received: from gnu-tgl-2.localdomain ([172.58.89.72]) by smtp.gmail.com with ESMTPSA id iw9-20020a170903044900b001dc6528d49asm2335647plb.250.2024.02.29.20.30.33 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 29 Feb 2024 20:30:33 -0800 (PST) Received: from gnu-tgl-2.. (localhost [IPv6:::1]) by gnu-tgl-2.localdomain (Postfix) with ESMTP id 5341A300CC8 for ; Thu, 29 Feb 2024 20:30:32 -0800 (PST) From: "H.J. Lu" To: binutils@sourceware.org Subject: [PATCH] elf: Use mmap to map in read-only sections Date: Thu, 29 Feb 2024 20:30:32 -0800 Message-ID: <20240301043032.2957895-1-hjl.tools@gmail.com> X-Mailer: git-send-email 2.44.0 MIME-Version: 1.0 X-Spam-Status: No, score=-3021.2 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_SBL_CSS, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE 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: binutils@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Binutils mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: binutils-bounces+ouuuleilei=gmail.com@sourceware.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1792296824384730003 X-GMAIL-MSGID: 1792296824384730003 There are many linker input files in LLVM debug build with huge string sections. All these string sections can be treated as read-only. But linker copies all of them into memory which consumes huge amount of memory and slows down linker significantly. Add _bfd_mmap_readonly and _bfd_mmap_readonly_local to mmap in reado-only sections. It improves linker performance for LLVM debug build by more than 10x. NB: All string sections in valid ELF inputs must be null terminated. There is no need to terminate it again and string sections are mmapped as read-only. * bfd.c (bfd_mmapped_entry): New. (bfd_mmapped): Likewise. (bfd): Add mmapped. * bfdwin.c (bfd_get_file_window): Use _bfd_pagesize. * cache.c (cache_bmmap): Remove pagesize_m1 and use pagesize_m1 instead. * elf.c (bfd_elf_get_str_section): Call _bfd_mmap_readonly instead of _bfd_alloc_and_read. Don't terminate the string section again. (get_hash_table_data): Call _bfd_mmap_readonly_local and _bfd_munmap_readonly_local instead of _bfd_malloc_and_read and free. (_bfd_elf_get_dynamic_symbols): Call _bfd_mmap_readonly instead of _bfd_alloc_and_read. Don't terminate the string section again. Call _bfd_mmap_readonly_local and _bfd_munmap_readonly_local instead of _bfd_malloc_and_read and free. (_bfd_elf_slurp_version_tables): Call _bfd_mmap_readonly_local and _bfd_munmap_readonly_local instead of _bfd_malloc_and_read and free. * elflink.c (bfd_elf_link_record_dynamic_symbol): Use bfd_malloc to get the unversioned symbol. * libbfd-in.h (_bfd_pagesize): New. (_bfd_pagesize_m1): Likewise. (_bfd_mmap_readonly): Likewise. (_bfd_mmap_readonly_local): Likewise. (_bfd_munmap_readonly_local): Likewise. * libbfd.c (MAP_ANONYMOUS): New. Define if not defined. (bfd_allocate_mmapped_page): New. (bfd_mmap_readonly_local_1): Likewise. (_bfd_mmap_readonly_local): Likewise. (_bfd_munmap_readonly_local): Likewise. (_bfd_mmap_readonly): Likewise. (_bfd_pagesize): Likewise. (_bfd_pagesize_m1): Likewise. (bfd_init_pagesize): Likewise. * ynx-core.c (lynx_core_file_p): Use _bfd_pagesize. * opncls.c: Include . (_bfd_delete_bfd): Munmap mmapped memories. * bfd-in2.h: Regenerated. * libbfd.h: Likewise. --- bfd/bfd-in2.h | 17 +++++++ bfd/bfd.c | 17 +++++++ bfd/bfdwin.c | 8 +-- bfd/cache.c | 11 ++-- bfd/elf.c | 65 +++++++++++++++--------- bfd/elflink.c | 16 +++--- bfd/libbfd-in.h | 18 +++++++ bfd/libbfd.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++++ bfd/libbfd.h | 18 +++++++ bfd/lynx-core.c | 2 +- bfd/opncls.c | 16 ++++++ 11 files changed, 275 insertions(+), 45 deletions(-) diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h index a335df522d2..934c73e14e8 100644 --- a/bfd/bfd-in2.h +++ b/bfd/bfd-in2.h @@ -1951,6 +1951,20 @@ struct bfd_build_id bfd_byte data[1]; }; +struct bfd_mmapped_entry + { + void *addr; + bfd_size_type size; + }; + +struct bfd_mmapped + { + struct bfd_mmapped *next; + unsigned int max_entry; + unsigned int next_entry; + struct bfd_mmapped_entry entries[1]; + }; + struct bfd { /* The filename the application opened the BFD with. */ @@ -2280,6 +2294,9 @@ struct bfd /* For input BFDs, the build ID, if the object has one. */ const struct bfd_build_id *build_id; + + /* For input BFDs, mmapped entries. */ + struct bfd_mmapped *mmapped; }; static inline const char * diff --git a/bfd/bfd.c b/bfd/bfd.c index 6c822656cc8..34c571f8f7b 100644 --- a/bfd/bfd.c +++ b/bfd/bfd.c @@ -74,6 +74,20 @@ EXTERNAL . bfd_byte data[1]; . }; . +.struct bfd_mmapped_entry +. { +. void *addr; +. bfd_size_type size; +. }; +. +.struct bfd_mmapped +. { +. struct bfd_mmapped *next; +. unsigned int max_entry; +. unsigned int next_entry; +. struct bfd_mmapped_entry entries[1]; +. }; +. CODE_FRAGMENT .struct bfd @@ -406,6 +420,9 @@ CODE_FRAGMENT . . {* For input BFDs, the build ID, if the object has one. *} . const struct bfd_build_id *build_id; +. +. {* For input BFDs, mmapped entries. *} +. struct bfd_mmapped *mmapped; .}; . diff --git a/bfd/bfdwin.c b/bfd/bfdwin.c index beb17398744..d9ce0d25e90 100644 --- a/bfd/bfdwin.c +++ b/bfd/bfdwin.c @@ -165,7 +165,7 @@ bfd_get_file_window (bfd *abfd, bool writable) { static int ok_to_map = 1; - static size_t pagesize; + size_t pagesize = _bfd_pagesize; bfd_window_internal *i = windowp->i; bfd_size_type size_to_alloc = size; @@ -175,12 +175,6 @@ bfd_get_file_window (bfd *abfd, windowp, windowp->data, (unsigned long) windowp->size, windowp->i, writable); - /* Make sure we know the page size, so we can be friendly to mmap. */ - if (pagesize == 0) - pagesize = getpagesize (); - if (pagesize == 0) - abort (); - if (i == NULL) { i = bfd_zmalloc (sizeof (bfd_window_internal)); diff --git a/bfd/cache.c b/bfd/cache.c index 4c00c00f8d5..2331262fb2e 100644 --- a/bfd/cache.c +++ b/bfd/cache.c @@ -498,7 +498,6 @@ cache_bmmap (struct bfd *abfd ATTRIBUTE_UNUSED, #ifdef HAVE_MMAP else { - static uintptr_t pagesize_m1; FILE *f; file_ptr pg_offset; bfd_size_type pg_len; @@ -510,12 +509,10 @@ cache_bmmap (struct bfd *abfd ATTRIBUTE_UNUSED, return ret; } - if (pagesize_m1 == 0) - pagesize_m1 = getpagesize () - 1; - /* Align. */ - pg_offset = offset & ~pagesize_m1; - pg_len = (len + (offset - pg_offset) + pagesize_m1) & ~pagesize_m1; + pg_offset = offset & ~_bfd_pagesize_m1; + pg_len = ((len + (offset - pg_offset) + _bfd_pagesize_m1) + & ~_bfd_pagesize_m1); ret = mmap (addr, pg_len, prot, flags, fileno (f), pg_offset); if (ret == (void *) -1) @@ -524,7 +521,7 @@ cache_bmmap (struct bfd *abfd ATTRIBUTE_UNUSED, { *map_addr = ret; *map_len = pg_len; - ret = (char *) ret + (offset & pagesize_m1); + ret = (char *) ret + (offset & _bfd_pagesize_m1); } } #endif diff --git a/bfd/elf.c b/bfd/elf.c index 8bffd3c5141..82f36625d16 100644 --- a/bfd/elf.c +++ b/bfd/elf.c @@ -289,16 +289,23 @@ bfd_elf_get_str_section (bfd *abfd, unsigned int shindex) in case the string table is not terminated. */ if (shstrtabsize + 1 <= 1 || bfd_seek (abfd, offset, SEEK_SET) != 0 - || (shstrtab = _bfd_alloc_and_read (abfd, shstrtabsize + 1, - shstrtabsize)) == NULL) + || (shstrtab = _bfd_mmap_readonly (abfd, shstrtabsize + 1, + shstrtabsize)) == NULL) { /* Once we've failed to read it, make sure we don't keep trying. Otherwise, we'll keep allocating space for the string table over and over. */ i_shdrp[shindex]->sh_size = 0; } - else - shstrtab[shstrtabsize] = '\0'; + else if (shstrtab[shstrtabsize - 1] != '\0') + { + /* It is an error if a string table isn't terminated. */ + _bfd_error_handler + /* xgettext:c-format */ + (_("%pB(%pA): string table is corrupt"), + abfd, i_shdrp[shindex]->bfd_section); + return NULL; + } i_shdrp[shindex]->contents = shstrtab; } return (char *) shstrtab; @@ -1940,7 +1947,7 @@ get_hash_table_data (bfd *abfd, bfd_size_type number, return NULL; } - e_data = _bfd_malloc_and_read (abfd, size, size); + e_data = _bfd_mmap_readonly_local (abfd, size); if (e_data == NULL) return NULL; @@ -1958,7 +1965,7 @@ get_hash_table_data (bfd *abfd, bfd_size_type number, while (number--) i_data[number] = bfd_get_64 (abfd, e_data + number * ent_size); - free (e_data); + _bfd_munmap_readonly_local (e_data, size); return i_data; } @@ -2007,6 +2014,8 @@ _bfd_elf_get_dynamic_symbols (bfd *abfd, Elf_Internal_Phdr *phdr, size_t verneed_size = 0; size_t extsym_size; const struct elf_backend_data *bed; + size_t esymbuf_size = 0; + size_t dynbuf_size = 0; /* Return TRUE if symbol table is bad. */ if (elf_bad_symtab (abfd)) @@ -2024,7 +2033,8 @@ _bfd_elf_get_dynamic_symbols (bfd *abfd, Elf_Internal_Phdr *phdr, if (bfd_seek (abfd, phdr->p_offset, SEEK_SET) != 0) goto error_return; - dynbuf = _bfd_malloc_and_read (abfd, phdr->p_filesz, phdr->p_filesz); + dynbuf_size = phdr->p_filesz; + dynbuf = _bfd_mmap_readonly_local (abfd, dynbuf_size); if (dynbuf == NULL) goto error_return; @@ -2102,11 +2112,17 @@ _bfd_elf_get_dynamic_symbols (bfd *abfd, Elf_Internal_Phdr *phdr, goto error_return; /* Dynamic string table must be valid until ABFD is closed. */ - strbuf = (char *) _bfd_alloc_and_read (abfd, dt_strsz + 1, dt_strsz); + strbuf = (char *) _bfd_mmap_readonly (abfd, dt_strsz + 1, dt_strsz); if (strbuf == NULL) goto error_return; - /* Since this is a string table, make sure that it is terminated. */ - strbuf[dt_strsz] = 0; + if (strbuf[dt_strsz - 1] != 0) + { + /* It is an error if a string table is't terminated. */ + _bfd_error_handler + /* xgettext:c-format */ + (_("%pB: DT_STRTAB table is corrupt"), abfd); + goto error_return; + } /* Get the real symbol count from DT_HASH or DT_GNU_HASH. Prefer DT_HASH since it is simpler than DT_GNU_HASH. */ @@ -2281,7 +2297,8 @@ _bfd_elf_get_dynamic_symbols (bfd *abfd, Elf_Internal_Phdr *phdr, if (filepos == (file_ptr) -1 || bfd_seek (abfd, filepos, SEEK_SET) != 0) goto error_return; - esymbuf = _bfd_malloc_and_read (abfd, amt, amt); + esymbuf_size = amt; + esymbuf = _bfd_mmap_readonly_local (abfd, esymbuf_size); if (esymbuf == NULL) goto error_return; @@ -2325,7 +2342,7 @@ _bfd_elf_get_dynamic_symbols (bfd *abfd, Elf_Internal_Phdr *phdr, goto error_return; /* DT_VERSYM info must be valid until ABFD is closed. */ - versym = _bfd_alloc_and_read (abfd, amt, amt); + versym = _bfd_mmap_readonly (abfd, amt, amt); if (dt_verdef) { @@ -2337,8 +2354,7 @@ _bfd_elf_get_dynamic_symbols (bfd *abfd, Elf_Internal_Phdr *phdr, goto error_return; /* DT_VERDEF info must be valid until ABFD is closed. */ - verdef = _bfd_alloc_and_read (abfd, verdef_size, - verdef_size); + verdef = _bfd_mmap_readonly (abfd, verdef_size, verdef_size); } if (dt_verneed) @@ -2351,8 +2367,8 @@ _bfd_elf_get_dynamic_symbols (bfd *abfd, Elf_Internal_Phdr *phdr, goto error_return; /* DT_VERNEED info must be valid until ABFD is closed. */ - verneed = _bfd_alloc_and_read (abfd, verneed_size, - verneed_size); + verneed = _bfd_mmap_readonly (abfd, verneed_size, + verneed_size); } } @@ -2375,8 +2391,8 @@ _bfd_elf_get_dynamic_symbols (bfd *abfd, Elf_Internal_Phdr *phdr, /* Restore file position for elf_object_p. */ if (bfd_seek (abfd, saved_filepos, SEEK_SET) != 0) res = false; - free (dynbuf); - free (esymbuf); + _bfd_munmap_readonly_local (dynbuf, dynbuf_size); + _bfd_munmap_readonly_local (esymbuf, esymbuf_size); free (gnubuckets); free (gnuchains); free (mipsxlat); @@ -9435,6 +9451,7 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver) bfd_byte *contents = NULL; unsigned int freeidx = 0; size_t amt; + size_t size = 0; if (elf_dynverref (abfd) != 0 || elf_tdata (abfd)->dt_verneed != NULL) { @@ -9471,7 +9488,8 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver) if (bfd_seek (abfd, hdr->sh_offset, SEEK_SET) != 0) goto error_return_verref; - contents = _bfd_malloc_and_read (abfd, hdr->sh_size, hdr->sh_size); + size = hdr->sh_size; + contents = _bfd_mmap_readonly_local (abfd, size); if (contents == NULL) goto error_return_verref; @@ -9604,7 +9622,7 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver) elf_tdata (abfd)->cverrefs = i; if (contents != elf_tdata (abfd)->dt_verneed) - free (contents); + _bfd_munmap_readonly_local (contents, size); contents = NULL; } @@ -9646,7 +9664,8 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver) if (bfd_seek (abfd, hdr->sh_offset, SEEK_SET) != 0) goto error_return_verdef; - contents = _bfd_malloc_and_read (abfd, hdr->sh_size, hdr->sh_size); + size = hdr->sh_size; + contents = _bfd_mmap_readonly_local (abfd, size); if (contents == NULL) goto error_return_verdef; @@ -9800,7 +9819,7 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver) } if (contents != elf_tdata (abfd)->dt_verdef) - free (contents); + _bfd_munmap_readonly_local (contents, size); contents = NULL; } else if (default_imported_symver) @@ -9857,7 +9876,7 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver) error_return: if (contents != elf_tdata (abfd)->dt_verneed && contents != elf_tdata (abfd)->dt_verdef) - free (contents); + _bfd_munmap_readonly_local (contents, size); return false; } diff --git a/bfd/elflink.c b/bfd/elflink.c index 5a6cb07b2ce..42029f29f7a 100644 --- a/bfd/elflink.c +++ b/bfd/elflink.c @@ -549,22 +549,24 @@ bfd_elf_link_record_dynamic_symbol (struct bfd_link_info *info, return false; } + char *unversioned_name = NULL; + /* We don't put any version information in the dynamic string table. */ name = h->root.root.string; p = strchr (name, ELF_VER_CHR); if (p != NULL) - /* We know that the p points into writable memory. In fact, - there are only a few symbols that have read-only names, being - those like _GLOBAL_OFFSET_TABLE_ that are created specially - by the backends. Most symbols will have names pointing into - an ELF string table read from a file, or to objalloc memory. */ - *p = 0; + { + unversioned_name = bfd_malloc (p - name + 1); + memcpy (unversioned_name, name, p - name); + unversioned_name[p - name] = 0; + name = unversioned_name; + } indx = _bfd_elf_strtab_add (dynstr, name, p != NULL); if (p != NULL) - *p = ELF_VER_CHR; + free (unversioned_name); if (indx == (size_t) -1) return false; diff --git a/bfd/libbfd-in.h b/bfd/libbfd-in.h index b8b2ce7ba09..f0a90a61a47 100644 --- a/bfd/libbfd-in.h +++ b/bfd/libbfd-in.h @@ -851,6 +851,9 @@ extern struct bfd_link_info *_bfd_get_link_info (bfd *) extern bool _bfd_link_keep_memory (struct bfd_link_info *) ATTRIBUTE_HIDDEN; +extern uintptr_t _bfd_pagesize ATTRIBUTE_HIDDEN; +extern uintptr_t _bfd_pagesize_m1 ATTRIBUTE_HIDDEN; + #if GCC_VERSION >= 7000 #define _bfd_mul_overflow(a, b, res) __builtin_mul_overflow (a, b, res) #else @@ -888,6 +891,21 @@ _bfd_alloc_and_read (bfd *abfd, bfd_size_type asize, bfd_size_type rsize) return NULL; } +#ifdef HAVE_MMAP +extern void *_bfd_mmap_readonly (bfd *, bfd_size_type, bfd_size_type) + ATTRIBUTE_HIDDEN; +extern void *_bfd_mmap_readonly_local (bfd *, bfd_size_type) + ATTRIBUTE_HIDDEN; +extern void _bfd_munmap_readonly_local (void *, bfd_size_type) + ATTRIBUTE_HIDDEN; +#else +#define _bfd_mmap_readonly(abfd, asize, rsize) \ + _bfd_alloc_and_read (abfd, asize, rsize) +#define _bfd_mmap_readonly_local(abfd, rsize) \ + _bfd_malloc_and_read (abfd, rsize, rsize) +#define _bfd_munmap_readonly_local(ptr, rsize) free (ptr) +#endif + static inline void * _bfd_malloc_and_read (bfd *abfd, bfd_size_type asize, bfd_size_type rsize) { diff --git a/bfd/libbfd.c b/bfd/libbfd.c index f8d148c9677..d24392ceb43 100644 --- a/bfd/libbfd.c +++ b/bfd/libbfd.c @@ -1326,3 +1326,135 @@ _bfd_generic_init_private_section_data (bfd *ibfd ATTRIBUTE_UNUSED, { return true; } + +#ifdef HAVE_MMAP +#include + +#ifndef MAP_ANONYMOUS +#define MAP_ANONYMOUS MAP_ANON +#endif + +/* Allocate a page to track mmapped memory and return the page and + the first entry. Return NULL if mmap fails. */ + +static struct bfd_mmapped * +bfd_allocate_mmapped_page (bfd *abfd, struct bfd_mmapped_entry **entry) +{ + struct bfd_mmapped * mmapped + = (struct bfd_mmapped *) mmap (NULL, _bfd_pagesize, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0); + if (mmapped == MAP_FAILED) + return NULL; + + mmapped->next = abfd->mmapped; + mmapped->max_entry + = ((_bfd_pagesize - offsetof (struct bfd_mmapped, entries)) + / sizeof (struct bfd_mmapped_entry)); + mmapped->next_entry = 1; + abfd->mmapped = mmapped; + *entry = mmapped->entries; + return mmapped; +} + +/* Mmap a readonly memory region of RSIZE bytes at the current offset. + Return mmap address and size in MAP_ADDR and MAP_SIZE. Return NULL + on invalid input and (void *) -1 for mmap failure. */ + +static void * +bfd_mmap_readonly_local_1 (bfd *abfd, bfd_size_type rsize, + void **map_addr, bfd_size_type *map_size) +{ + if (!_bfd_constant_p (rsize)) + { + ufile_ptr filesize = bfd_get_file_size (abfd); + if (filesize != 0 && rsize > filesize) + { + bfd_set_error (bfd_error_file_truncated); + return NULL; + } + } + + void *mem; + ufile_ptr offset = bfd_tell (abfd); + mem = bfd_mmap (abfd, NULL, rsize, PROT_READ, MAP_PRIVATE, offset, + map_addr, map_size); + return mem; +} + +/* Mmap a readonly memory region of RSIZE bytes at the current offset. + Return NULL on invalid input or mmap failure. */ + +void * +_bfd_mmap_readonly_local (bfd *abfd, bfd_size_type rsize) +{ + void *map_addr; + bfd_size_type map_size; + return bfd_mmap_readonly_local_1 (abfd, rsize, &map_addr, &map_size); +} + +/* Munmap RSIZE bytes at PTR. */ + +void +_bfd_munmap_readonly_local (void *ptr, bfd_size_type rsize) +{ + /* NB: Since _bfd_munmap_readonly_local is called like free, + PTR may be NULL. */ + if (ptr != NULL) + munmap (ptr, rsize); +} + +/* Mmap a readonly memory region of RSIZE bytes at the current offset. + Return NULL on invalid input or mmap failure. */ + +void * +_bfd_mmap_readonly (bfd *abfd, bfd_size_type asize, bfd_size_type rsize) +{ + void *mem, *map_addr; + bfd_size_type map_size; + mem = bfd_mmap_readonly_local_1 (abfd, rsize, &map_addr, &map_size); + if (mem == NULL) + return mem; + if (mem == (void *) -1) + return _bfd_alloc_and_read (abfd, asize, rsize); + + struct bfd_mmapped_entry *entry; + unsigned int next_entry; + struct bfd_mmapped *mmapped = abfd->mmapped; + if (mmapped != NULL + && (next_entry = mmapped->next_entry) < mmapped->max_entry) + { + entry = &mmapped->entries[next_entry]; + mmapped->next_entry++; + } + else + { + mmapped = bfd_allocate_mmapped_page (abfd, &entry); + if (mmapped == NULL) + { + munmap (map_addr, map_size); + return NULL; + } + } + + entry->addr = map_addr; + entry->size = map_size; + + return mem; +} + +#endif + +uintptr_t _bfd_pagesize; +uintptr_t _bfd_pagesize_m1; + +__attribute__ ((unused, constructor)) +static void +bfd_init_pagesize (void) +{ + _bfd_pagesize = getpagesize (); + if (_bfd_pagesize == 0) + abort (); + _bfd_pagesize_m1 = _bfd_pagesize - 1; +} diff --git a/bfd/libbfd.h b/bfd/libbfd.h index a60063b5ae1..f8e20d850d9 100644 --- a/bfd/libbfd.h +++ b/bfd/libbfd.h @@ -857,6 +857,9 @@ extern struct bfd_link_info *_bfd_get_link_info (bfd *) extern bool _bfd_link_keep_memory (struct bfd_link_info *) ATTRIBUTE_HIDDEN; +extern uintptr_t _bfd_pagesize ATTRIBUTE_HIDDEN; +extern uintptr_t _bfd_pagesize_m1 ATTRIBUTE_HIDDEN; + #if GCC_VERSION >= 7000 #define _bfd_mul_overflow(a, b, res) __builtin_mul_overflow (a, b, res) #else @@ -894,6 +897,21 @@ _bfd_alloc_and_read (bfd *abfd, bfd_size_type asize, bfd_size_type rsize) return NULL; } +#ifdef HAVE_MMAP +extern void *_bfd_mmap_readonly (bfd *, bfd_size_type, bfd_size_type) + ATTRIBUTE_HIDDEN; +extern void *_bfd_mmap_readonly_local (bfd *, bfd_size_type) + ATTRIBUTE_HIDDEN; +extern void _bfd_munmap_readonly_local (void *, bfd_size_type) + ATTRIBUTE_HIDDEN; +#else +#define _bfd_mmap_readonly(abfd, asize, rsize) \ + _bfd_alloc_and_read (abfd, asize, rsize) +#define _bfd_mmap_readonly_local(abfd, rsize) \ + _bfd_malloc_and_read (abfd, rsize, rsize) +#define _bfd_munmap_readonly_local(ptr, rsize) free (ptr) +#endif + static inline void * _bfd_malloc_and_read (bfd *abfd, bfd_size_type asize, bfd_size_type rsize) { diff --git a/bfd/lynx-core.c b/bfd/lynx-core.c index 44d94ad8745..9ec5a0d2028 100644 --- a/bfd/lynx-core.c +++ b/bfd/lynx-core.c @@ -96,7 +96,7 @@ lynx_core_file_p (bfd *abfd) asection *newsect; size_t amt; - pagesize = getpagesize (); /* Serious cross-target issue here... This + pagesize = _bfd_pagesize; /* Serious cross-target issue here... This really needs to come from a system-specific header file. */ diff --git a/bfd/opncls.c b/bfd/opncls.c index a0a5c40fba8..245e239c8e4 100644 --- a/bfd/opncls.c +++ b/bfd/opncls.c @@ -27,6 +27,10 @@ #include "libiberty.h" #include "elf-bfd.h" +#ifdef HAVE_MMAP +#include +#endif + #ifndef S_IXUSR #define S_IXUSR 0100 /* Execute by owner. */ #endif @@ -176,6 +180,18 @@ _bfd_delete_bfd (bfd *abfd) else free ((char *) bfd_get_filename (abfd)); +#ifdef HAVE_MMAP + struct bfd_mmapped *mmapped, *next; + for (mmapped = abfd->mmapped; mmapped != NULL; mmapped = next) + { + struct bfd_mmapped_entry *entries = mmapped->entries; + next = mmapped->next; + for (unsigned int i = 0; i < mmapped->next_entry; i++) + munmap (entries[i].addr, entries[i].size); + munmap (mmapped, _bfd_pagesize); + } +#endif + free (abfd->arelt_data); free (abfd); }