From patchwork Thu Aug 27 05:28:06 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexey Lapshin X-Patchwork-Id: 142278 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 3FA1D4BA2E31 for ; Thu, 27 Aug 2026 05:29:33 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 3FA1D4BA2E31 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=espressifsystems.onmicrosoft.com header.i=@espressifsystems.onmicrosoft.com header.a=rsa-sha256 header.s=selector1-espressifsystems-onmicrosoft-com header.b=af4Ih7tQ X-Original-To: binutils@sourceware.org Delivered-To: binutils@sourceware.org Received: from TYDPR03CU002.outbound.protection.outlook.com (mail-japaneastazon11023127.outbound.protection.outlook.com [52.101.127.127]) by sourceware.org (Postfix) with ESMTPS id C890D4BA79A9 for ; Thu, 27 Aug 2026 05:28:49 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org C890D4BA79A9 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=espressif.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=espressif.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org C890D4BA79A9 Authentication-Results: sourceware.org; arc=pass smtp.remote-ip=52.101.127.127 ARC-Seal: i=2; a=rsa-sha256; d=sourceware.org; s=key; t=1787808530; cv=pass; b=Y2RMsIMNZfhl2MkprJ9nWJD4Xy1RRzGFs+LbENagCGhQVX2C7SXTYWfyb3/i1HpMNJrwbqhvLwe0lxFgn3M8pjXz7h0caklWUlSE17Az6SzBy5MYWwutBxthRF1vq73NM3b0zjHO4BzFdHEclZJemCoT+jeQChbm5UoaL9Wv0jw= ARC-Message-Signature: i=2; a=rsa-sha256; d=sourceware.org; s=key; t=1787808530; c=relaxed/simple; bh=+TmU2OX6ZycdbsQEtP7qiE/EItY3d4XgwaMk4vzThAw=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=WvXwslwipO8DO8fz7GiUybWfWXicdcW5c8jVtDY+VYJxpATfNgrLgwF+DDLLr/I9j4tUeK39+4MTDLjHQgNWm83YGK82TIA6E94GdsRbaBNpECb+5NGCzDrvF6EcImZ/zgd2z/ALyCBC0gNg+EBIu56AVwL8/Wfw2YUJ1XPHvQk= ARC-Authentication-Results: i=2; sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=espressifsystems.onmicrosoft.com header.i=@espressifsystems.onmicrosoft.com header.a=rsa-sha256 header.s=selector1-espressifsystems-onmicrosoft-com header.b=af4Ih7tQ DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org C890D4BA79A9 ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=vfSzUL6dCsxZ01tkD37he6GKOVaN9gMuF4dWAt4hM6aWl4entmHVGD6KWwp2SuyCylfAD7pzZ+nJGil0D9iMOc5kYUeN//ukumbQurbjg62JnE0ISbDEiiq2ueu0t/ZpwCvzInUVUQdl/s3r9REac4mSOO6pmfVfCrGE4Q+AfaIV4kDyYtVGNMBcBZ3ZNo5K22vxcQEwydRRJhi75s7jYWK4Osaar1+eUzTNRnaoxPmHbbV1BQcwxuMAHmPckrl5lbfOTBn2Jwy550PGPheokts9/Kh0R1lUG+Yh2z6yuhIkj9eQWX00lN67++U6bdcBjGlNcpIr0kJGFt8M/V/cuw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=Ft4DDJGE4ut8lNNOT70dykhTMOpbmdOg/YDbU1HZX1E=; b=G4RO1IYZtjrX7fbp1QUHAQ5oyf93Z8HNuXVPWmNUk2ggsfaN2GsshJBTiTYOa0+p5eRhPVbcOnjYg2qkmJN6svlWeWk51ah3fz/5IUq4H58cOqd19r/SIq/X164j4W/GFyA0tCAZvVxeVCP0Hlg1AOF78XigtZxlm+c1Yu0ydapKRdmhNUgDRUVpTLFMxNfr8UQ/Rjc6T7eDrG5R7HtYnsINuVlizXdf8qOwQ8vXFtTW867UEMJL+kNPiJ69/hSOAdv3lpUrDtGbrQ4fZjtv4QNdQ5FZE6HFLxcZ16lqQPI4HO5NAO5+uhdoQlR0gCVFn1PEvwA+RCzCI3560zvS/g== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=espressif.com; dmarc=pass action=none header.from=espressif.com; dkim=pass header.d=espressif.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=espressifsystems.onmicrosoft.com; s=selector1-espressifsystems-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=Ft4DDJGE4ut8lNNOT70dykhTMOpbmdOg/YDbU1HZX1E=; b=af4Ih7tQ8xRFEtmIhV5dTua9WrpPYc97DvTnDa81WlAdM3RLfu3boIcof+7xFFhi9bD5l+dSaiWtUJDMXB8UKe1CKUIbyM8L2zU+m6mvDweh0vOeXlncLzWhTmWTOAnMCHEzPq9WzB7c9HJjUEb+AUGXXUTk+TtCv49g9diyGqE= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=espressif.com; Received: from KL1PR04MB7210.apcprd04.prod.outlook.com (2603:1096:820:fe::7) by SI4PR04MB9330.apcprd04.prod.outlook.com (2603:1096:4:29a::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.360.10; Thu, 27 Aug 2026 05:28:44 +0000 Received: from KL1PR04MB7210.apcprd04.prod.outlook.com ([fe80::4c9e:728f:a1a8:4be8]) by KL1PR04MB7210.apcprd04.prod.outlook.com ([fe80::4c9e:728f:a1a8:4be8%3]) with mapi id 15.21.0360.008; Thu, 27 Aug 2026 05:28:44 +0000 From: Alexey Lapshin To: binutils@sourceware.org Cc: jcmvbkbc@gmail.com, alexey.gerenkov@espressif.com, jjsuwa_sys3175@yahoo.co.jp, Alexey Lapshin Subject: [PATCH v3] xtensa: add R_XTENSA_PDIFF_ULEB128 for DWARF uleb128 diffs Date: Thu, 27 Aug 2026 12:28:06 +0700 Message-ID: <20260827052835.181157-1-alexey.lapshin@espressif.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: References: X-ClientProxiedBy: JH0PR03CA0003.apcprd03.prod.outlook.com (2603:1096:990:6::10) To KL1PR04MB7210.apcprd04.prod.outlook.com (2603:1096:820:fe::7) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: KL1PR04MB7210:EE_|SI4PR04MB9330:EE_ X-MS-Office365-Filtering-Correlation-Id: d91c5d9e-3004-4b28-3b58-08df03fc0f75 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; ARA:13230040|376014|366016|1800799024|10070799003|23010399003|10067099003|6133799003|3023799007|18002099003|22082099003|56012099006|5023799004; X-Microsoft-Antispam-Message-Info: 1wIjJ7gtarrTGxsBh/GRhBwzz2WK068xLdDJ68w3cmcNw18TLFW7QqF2RLM2WXB91/fGhd+549gPioGLPq2YzNUvvbuoJfqcWbdYmioZjpRoc4uBSkNPXyMFLhN6r1rFM6jEZEUu5z8IDj0VzXCA4OrmuQqWKDxE8+aYynbtUtDT6T95Qh9IBHnGvPUwRW6F/+dG21xZaQBYHhQOEITLF+T5F/9Qor51aGQwNBduLrQZwU60gxBNpHVvEJwO/NEB0VeHhO7y4mhpRxbaOKkfy9Pj3JlJI8uEYE8jPqsoGT0QLNYgvTEuxMIalriCsYZ6PnT11McHBDQFO1MCP1w6ulULOUeF6IGSPIJXyO8a5KNzew7YmE7qWHuY3f7eIjdi7H8afH5TI/5M8v4Nm/mB0obNMCM010as74RmigHETFci81x/BAfJ+dnZmNLX42HVkQgg9C+E5wZwBvbQnfCRLSYEAUWwaEI1hmQOMV4haLhrom8gvZ9g7+CTiezc11In3G/C58uIjZ3rErUnz+8kC+C//onYoN4kYMSVI57kPZhievoD1bSNMgILTNLiaPhkLoHvWUdij2IUw7/rC/rQcmaF9yqWvt9q49Q374HPjq3sXKRFe4is/07BeCXtgQ5h6VQ6mVUf9Iv19+r4xjZt57hkYX79mh392j7U1HxTmQs= X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:KL1PR04MB7210.apcprd04.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230040)(376014)(366016)(1800799024)(10070799003)(23010399003)(10067099003)(6133799003)(3023799007)(18002099003)(22082099003)(56012099006)(5023799004); DIR:OUT; SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 2 X-MS-Exchange-AntiSpam-MessageData-0: uuQDN85qP34s2+Wx/OZDkeGLFqNTNiX/EjiTNo8N+VCFy1YlVLicQNQUD7svBZ/XCpxbUf2wxFACr2iEaCFNyMcM+9swadBDUT28lu30HPoBy6oJOkv/vgVh6HLWLndqXWoS7yWr+4Gy3lBC+wo3nFnzy2GIhOk6RaJdqSHitesl88/mXRjge+hjQ/DCf2mFWi9DAR1ifHzk+RPRvOhKqu9YNk1opniGemr9cXg/8+z7k6+28b2IcQrnRBCPsAJKTKBOpjGhofBSXLg3MHhv7DMHOQG41wd9CJjDivQtvKmGUN1x9igMEZJ57QUYU+BZh9YTBf7n/V2IWMpRRORUNk0M+v60qUwakQSt9hRwfapV3shWC9vORICJrUBMlaa7bRLfrj7FICb4ANvm8NbPNWybw8zHxpB5kLuXFGHwD+24917NFL3zClA36w+OzP8AlErP5cNQ6ZueyIrl2ePHxNlVF9ipiWaj0/eVy09kz+ajD4sY0tMO//gXck04GXGJhYQDw1Cm+tzTr6Qae9WfZRxQdgxn0+Okc0D0dWM6YxaNDjIel+9eYRI2zo15BFfbqJjFbwbhV+nsFSFKSYz2SvC7sofaAmICUUpFh1Qa2rxGytzv1qXqY5GLhk6RkTV9y40XcZbbcnCAJyOMZX8K6IWAD7ZOXH7zm3ntdi0jABpaxtqHUvwmun66QbsK6lr+1Wtas+wU1qEvHvcjgSbjEqmXo8PVkLWsjaa41necsy41Fmh1bnrg2miLtPsgA5+f9To8Ci9Z4KNowaGMvDdoDawcM3GsJ78p3r66MQoEjoco4azLMyX8hmdIKKOTNWfz4Aq9WE9IkVs2qyChMO7ny3N1IZY5axmX8pRTqai0ARTTlcsoVs1kguqC4bVbGZ/0dedOdd9uQOP/OHBAH+EQ4ZkmJTjE0Slx65v9+RaaKCrzg98Naz8BEQKBxm/K6D2c/AZBfWbgEVUUAFpGw0QP5URigjoibtKMN6jtQ/a+eX6BNcSEgKqHMPrKQGeji2CDk9fCAVXwxY6YoCLxCfZfviycVDBhJbzctj3z11U+BgWk7/NT8xlhZzxvKFvkGhR+/nERBGivDSAcx6RKCQ3O3e3yn0ZR8lu3zkB/SN3OGME2Rmrf+pfy0I44Doa1pGu5Na2FywWq+Hzm5fLGRhyXrg0u6R3117/oNRyM1Cl0hT8FGh6BCdwqBwF25whuJoRUuum5fU7rkfXfQrjx8n6fyfesQsQtjB/nWNZjBklpOalOTxA8YXDFFiTVo4p3jzt7s5rHD4XhoVa9EKcGQwb3CNAPuwz8h5nKPuwEvRbmQjB2B04oh5+8hUvo7L4jLVakYm55tQUd6g8xwHh5/Rkjj398SCzl0rGx2EZh8PEI7uviUtfqjp+BspJY54s9dOuaiyGHa5sp331SeaOYlriy/wkO7eVxwzcQQU210MAxn1y+kfSJUMq9kiKStSqKOpw7wUKh8Uuqb8CEMxbhwoucAnQFKjLRS2wYUO2cH8vEhIDjpSXGUPOHh9Qh6T/vLuvItf/ECXThDmkGR5J7AKmSzNWf73T1oCl6skUSUdbT9zDTZ2yI7ckFDbQKrH4pV2PWzS4PlTrTXehpE0yKPR0jt0M1Qmce75tYzEnKS0B/zABjuLoYJFqsdGwuRtFzOHTttJ2JeI44YW1SqZBlHiVO1KhpgsF3vGcRIE57yyWP6mMKAUWQV13yCR6gpZY5AnxzjzZdGJVdUmIglYhPtLHrWCEp/JylGCMKNk8BDZSn/4hJ06+4SDm/XN5ejsnMekyAVINSlKp4 X-MS-Exchange-AntiSpam-MessageData-1: RlKZ+FdKac35dQA+ij/dS12/R0ISSYK4nmk= X-OriginatorOrg: espressif.com X-MS-Exchange-CrossTenant-Network-Message-Id: d91c5d9e-3004-4b28-3b58-08df03fc0f75 X-MS-Exchange-CrossTenant-AuthSource: KL1PR04MB7210.apcprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 27 Aug 2026 05:28:43.9265 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 5faf27fd-3557-4294-9545-8ea74a409f39 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: ERJO6SOLN32g+hsJmogCvAgzGSYISfJEXHUGKFZt9+RiT4NvrdnnZBTwfM0ZIwLdOm8LtCeFj7YuxGLHKbNj58SYKgXGizs15BFi9eqn8hA= X-MS-Exchange-Transport-CrossTenantHeadersStamped: SI4PR04MB9330 X-Spam-Status: No, score=-12.2 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_PASS, TXREP shortcircuit=no autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on 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~patchwork=sourceware.org@sourceware.org DWARF 5 location and range lists encode many same-section symbol differences as uleb128. Today the assembler must mark all covered code no_transform so linker relaxation cannot change those differences, which effectively disables relaxation for nearly every function that has DWARF 5 debug info. Emit R_XTENSA_PDIFF_ULEB128 instead: like R_XTENSA_PDIFF32 the difference stays in the section contents and the reloc records the subtracted symbol, but the assembler pads the uleb128 to a fixed width (minimal encoding plus one spare byte) and the linker rewrites the value in place after relaxation. sleb128 and cross-section differences still fall back to no_transform. Growth that needs more than the reserved spare byte is reported as a relocation overflow. bfd/ * bfd-in2.h: Regenerated. * elf32-xtensa.c (elf_howto_table): New entry for R_XTENSA_PDIFF_ULEB128. (elf_xtensa_reloc_type_lookup, elf_xtensa_do_reloc): Handle it. (relax_section): Rewrite padded uleb128 differences in place. * libbfd.h (bfd_reloc_code_real_names): Add BFD_RELOC_XTENSA_PDIFF_ULEB128. * reloc.c: Document BFD_RELOC_XTENSA_PDIFF_ULEB128. binutils/ * readelf.c (is_none_reloc): Recognize R_XTENSA_PDIFF_ULEB128. gas/ * write.c (cvt_frag_to_fill): Pad leb128 frags to the reserved size when md_leb128_frag_size grew them. (relax_segment): Call md_leb128_frag_size if defined. * doc/internals.texi (rs_leb128): Note reserved size and padding. (md_leb128_frag_size): Document. * config/tc-xtensa.h (xtensa_symfield_type): Add has_leb128_diff_reloc. (md_leb128_frag_size): Define. * config/tc-xtensa.c (xtensa_md_finish): Insert uleb128 fixes. (xtensa_insert_uleb128_fixes, xtensa_leb128_frag_size): New. (xtensa_mark_difference_of_two_symbols): Skip relocated diffs. (md_apply_fix): Ignore BFD_RELOC_XTENSA_PDIFF_ULEB128. * testsuite/gas/xtensa/pdiff-uleb128.s: New test. * testsuite/gas/xtensa/pdiff-uleb128.d: New test. * testsuite/gas/xtensa/pdiff-uleb128-norelax.d: New test. include/ * elf/xtensa.h (elf_xtensa_reloc_type): Add R_XTENSA_PDIFF_ULEB128. ld/ * testsuite/ld-xtensa/relax-uleb128.s: New test. * testsuite/ld-xtensa/relax-uleb128.d: New test. * testsuite/ld-xtensa/relax-uleb128-2.s: New test. * testsuite/ld-xtensa/relax-uleb128-2.d: New test. * testsuite/ld-xtensa/xtensa.exp: Run them. --- bfd/bfd-in2.h | 10 +++ bfd/elf32-xtensa.c | 80 +++++++++++++++++ bfd/libbfd.h | 1 + bfd/reloc.c | 12 +++ binutils/readelf.c | 3 +- gas/config/tc-xtensa.c | 90 ++++++++++++++++++- gas/config/tc-xtensa.h | 5 ++ gas/doc/internals.texi | 23 ++++- .../gas/xtensa/pdiff-uleb128-norelax.d | 20 +++++ gas/testsuite/gas/xtensa/pdiff-uleb128.d | 12 +++ gas/testsuite/gas/xtensa/pdiff-uleb128.s | 7 ++ gas/write.c | 20 +++++ include/elf/xtensa.h | 3 + ld/testsuite/ld-xtensa/relax-uleb128-2.d | 12 +++ ld/testsuite/ld-xtensa/relax-uleb128-2.s | 18 ++++ ld/testsuite/ld-xtensa/relax-uleb128.d | 11 +++ ld/testsuite/ld-xtensa/relax-uleb128.s | 16 ++++ ld/testsuite/ld-xtensa/xtensa.exp | 2 + 18 files changed, 339 insertions(+), 6 deletions(-) create mode 100644 gas/testsuite/gas/xtensa/pdiff-uleb128-norelax.d create mode 100644 gas/testsuite/gas/xtensa/pdiff-uleb128.d create mode 100644 gas/testsuite/gas/xtensa/pdiff-uleb128.s create mode 100644 ld/testsuite/ld-xtensa/relax-uleb128-2.d create mode 100644 ld/testsuite/ld-xtensa/relax-uleb128-2.s create mode 100644 ld/testsuite/ld-xtensa/relax-uleb128.d create mode 100644 ld/testsuite/ld-xtensa/relax-uleb128.s diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h index 073d45a6858..711006471d1 100644 --- a/bfd/bfd-in2.h +++ b/bfd/bfd-in2.h @@ -6168,6 +6168,16 @@ enum bfd_reloc_code_real BFD_RELOC_XTENSA_NDIFF16, BFD_RELOC_XTENSA_NDIFF32, + /* Xtensa relocation to mark a positive difference of two local symbols + encoded as an unsigned LEB128 value. This works like + BFD_RELOC_XTENSA_PDIFF32, but the linker rewrites the difference in + place using the number of bytes the assembler originally emitted, so + that the size of the containing section never changes. Without it the + assembler would have to inhibit linker relaxation of all code covered + by such a difference, which DWARF 5 location and range lists produce + for virtually every function. */ + BFD_RELOC_XTENSA_PDIFF_ULEB128, + /* 8 bit signed offset in (ix+d) or (iy+d). */ BFD_RELOC_Z80_DISP8, diff --git a/bfd/elf32-xtensa.c b/bfd/elf32-xtensa.c index b1733345fc8..2c279bcbe95 100644 --- a/bfd/elf32-xtensa.c +++ b/bfd/elf32-xtensa.c @@ -346,6 +346,8 @@ static reloc_howto_type elf_howto_table[] = bfd_elf_xtensa_reloc, "R_XTENSA_NDIFF16", false, 0, 0xffff, false), HOWTO (R_XTENSA_NDIFF32, 0, 4, 32, false, 0, complain_overflow_bitfield, bfd_elf_xtensa_reloc, "R_XTENSA_NDIFF32", false, 0, 0xffffffff, false), + HOWTO (R_XTENSA_PDIFF_ULEB128, 0, 0, 0, false, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_PDIFF_ULEB128", false, 0, 0, false), }; #if DEBUG_GEN_RELOC @@ -409,6 +411,10 @@ elf_xtensa_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, TRACE ("BFD_RELOC_XTENSA_NDIFF32"); return &elf_howto_table[(unsigned) R_XTENSA_NDIFF32 ]; + case BFD_RELOC_XTENSA_PDIFF_ULEB128: + TRACE ("BFD_RELOC_XTENSA_PDIFF_ULEB128"); + return &elf_howto_table[(unsigned) R_XTENSA_PDIFF_ULEB128 ]; + case BFD_RELOC_XTENSA_RTLD: TRACE ("BFD_RELOC_XTENSA_RTLD"); return &elf_howto_table[(unsigned) R_XTENSA_RTLD ]; @@ -1891,6 +1897,7 @@ elf_xtensa_do_reloc (reloc_howto_type *howto, case R_XTENSA_NDIFF8: case R_XTENSA_NDIFF16: case R_XTENSA_NDIFF32: + case R_XTENSA_PDIFF_ULEB128: case R_XTENSA_TLS_FUNC: case R_XTENSA_TLS_ARG: case R_XTENSA_TLS_CALL: @@ -9650,6 +9657,79 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info) r_reloc new_reloc; target_sec = translate_reloc (&r_rel, &new_reloc, target_sec); + if (r_type == R_XTENSA_PDIFF_ULEB128) + { + bfd_vma diff_value, new_end_offset; + unsigned int len, new_len = 0; + bfd_byte *p, *q, *endp; + + if (sec_size <= old_source_offset) + { + (*link_info->callbacks->reloc_dangerous) + (link_info, _("invalid relocation address"), + abfd, sec, old_source_offset); + goto error_return; + } + + /* The value is read back from the section contents, so + it must not be trusted to be a well formed uleb128 + that ends before the end of the section. */ + p = q = &contents[old_source_offset]; + diff_value = _bfd_safe_read_leb128 (abfd, &q, false, + contents + sec_size); + len = q - p; + if (len == 0 || (q[-1] & 0x80) != 0) + { + (*link_info->callbacks->reloc_dangerous) + (link_info, _("invalid uleb128 difference"), + abfd, sec, old_source_offset); + goto error_return; + } + + new_end_offset = offset_with_removed_text_map + (&target_relax_info->action_list, + r_rel.target_offset + diff_value); + if (new_end_offset < new_reloc.target_offset) + { + (*link_info->callbacks->reloc_dangerous) + (link_info, _("uleb128 difference is negative after " + "relaxation"), + abfd, sec, old_source_offset); + goto error_return; + } + diff_value = new_end_offset - new_reloc.target_offset; + + /* Byte length of the minimal uleb128 encoding of + DIFF_VALUE. */ + for (bfd_vma v = diff_value; new_len++, v >>= 7; ) + ; + /* The assembler reserves one spare byte for these + differences, but reject growth that still needs more + bytes than were emitted. */ + if (new_len > len) + { + (*link_info->callbacks->reloc_dangerous) + (link_info, _("uleb128 difference overflows after " + "relaxation"), + abfd, sec, old_source_offset); + goto error_return; + } + + /* Rewrite the difference in place, always using the + number of bytes the assembler emitted so that the size + of this section does not change. A value that now + needs fewer bytes is padded with uleb128 zeroes. */ + endp = p + len - 1; + memset (p, 0x80, len - 1); + *endp = 0; + p = _bfd_write_unsigned_leb128 (p, endp, diff_value); + BFD_ASSERT (p); + if (--p < endp) + *p |= 0x80; + + pin_contents (sec, contents); + } + if (r_type == R_XTENSA_DIFF8 || r_type == R_XTENSA_DIFF16 || r_type == R_XTENSA_DIFF32 diff --git a/bfd/libbfd.h b/bfd/libbfd.h index bc0d62f94dc..c3fa76125e2 100644 --- a/bfd/libbfd.h +++ b/bfd/libbfd.h @@ -2796,6 +2796,7 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@", "BFD_RELOC_XTENSA_NDIFF8", "BFD_RELOC_XTENSA_NDIFF16", "BFD_RELOC_XTENSA_NDIFF32", + "BFD_RELOC_XTENSA_PDIFF_ULEB128", "BFD_RELOC_Z80_DISP8", "BFD_RELOC_Z80_BYTE0", "BFD_RELOC_Z80_BYTE1", diff --git a/bfd/reloc.c b/bfd/reloc.c index d0bcb30b97e..555028bdf19 100644 --- a/bfd/reloc.c +++ b/bfd/reloc.c @@ -6031,6 +6031,18 @@ ENUMDOC differences. The difference value is treated as unsigned with these relocation types, giving full 8/16 value ranges. +ENUM + BFD_RELOC_XTENSA_PDIFF_ULEB128 +ENUMDOC + Xtensa relocation to mark a positive difference of two local symbols + encoded as an unsigned LEB128 value. This works like + BFD_RELOC_XTENSA_PDIFF32, but the linker rewrites the difference in + place using the number of bytes the assembler originally emitted, so + that the size of the containing section never changes. Without it the + assembler would have to inhibit linker relaxation of all code covered + by such a difference, which DWARF 5 location and range lists produce + for virtually every function. + ENUM BFD_RELOC_Z80_DISP8 ENUMDOC diff --git a/binutils/readelf.c b/binutils/readelf.c index b5ccc675af6..a5bfc37a7ee 100644 --- a/binutils/readelf.c +++ b/binutils/readelf.c @@ -16271,7 +16271,8 @@ is_none_reloc (Filedata * filedata, unsigned int reloc_type) || reloc_type == 59 /* R_XTENSA_PDIFF32. */ || reloc_type == 60 /* R_XTENSA_NDIFF8. */ || reloc_type == 61 /* R_XTENSA_NDIFF16. */ - || reloc_type == 62 /* R_XTENSA_NDIFF32. */); + || reloc_type == 62 /* R_XTENSA_NDIFF32. */ + || reloc_type == 63 /* R_XTENSA_PDIFF_ULEB128. */); } return false; } diff --git a/gas/config/tc-xtensa.c b/gas/config/tc-xtensa.c index 397744f742b..631b61c5128 100644 --- a/gas/config/tc-xtensa.c +++ b/gas/config/tc-xtensa.c @@ -6104,6 +6104,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg) break; case BFD_RELOC_XTENSA_ASM_EXPAND: + case BFD_RELOC_XTENSA_PDIFF_ULEB128: case BFD_RELOC_XTENSA_TLS_FUNC: case BFD_RELOC_XTENSA_TLS_ARG: case BFD_RELOC_XTENSA_TLS_CALL: @@ -7361,6 +7362,7 @@ static void xtensa_fix_target_frags (void); static void xtensa_mark_narrow_branches (void); static void xtensa_mark_zcl_first_insns (void); static void xtensa_mark_difference_of_two_symbols (void); +static void xtensa_insert_uleb128_fixes (bfd *, asection *, void *); static void xtensa_fix_a0_b_retw_frags (void); static void xtensa_fix_b_j_loop_end_frags (void); static void xtensa_fix_close_loop_end_frags (void); @@ -7401,6 +7403,9 @@ xtensa_md_finish (void) xtensa_add_config_info (); xtensa_check_frag_count (); + + if (linkrelax) + bfd_map_over_sections (stdoutput, xtensa_insert_uleb128_fixes, NULL); } struct trampoline_chain_entry @@ -8234,11 +8239,87 @@ xtensa_mark_zcl_first_insns (void) } } +/* A difference of two symbols encoded as a uleb128 value can be handed to + the linker as a BFD_RELOC_XTENSA_PDIFF_ULEB128 relocation: the difference + itself stays in the section contents and the relocation records the + address of the subtracted symbol, which is all the linker needs to + rewrite the value after relaxation. DWARF 5 location and range lists + are made almost entirely of such differences, so without this the code + covered by them could never be relaxed. + + This has to run before relaxation turns the rs_leb128 frags into plain + data. Differences that cannot be represented this way are left to + xtensa_mark_difference_of_two_symbols. + + Mark the expression symbol so xtensa_leb128_frag_size can reserve one + spare byte: Xtensa linker relaxation can grow code, which may push a + difference across a uleb128 byte boundary. */ + +static void +xtensa_insert_uleb128_fixes (bfd *abfd ATTRIBUTE_UNUSED, + asection *sec, + void *unused ATTRIBUTE_UNUSED) +{ + segment_info_type *seginfo = seg_info (sec); + fragS *fragP; + + if (seginfo == NULL || seginfo->frchainP == NULL) + return; + + subseg_set (sec, 0); + + for (fragP = seginfo->frchainP->frch_root; fragP; fragP = fragP->fr_next) + { + expressionS *exp; + symbolS *op_sym; + + /* Non-zero fr_subtype is sleb128 (no relocation). */ + if (fragP->fr_type != rs_leb128 || fragP->fr_subtype != 0 + || fragP->fr_symbol == NULL) + continue; + + exp = symbol_get_value_expression (fragP->fr_symbol); + if (exp->X_op != O_subtract) + continue; + + op_sym = exp->X_op_symbol; + + /* R_XTENSA_PDIFF_ULEB128 only encodes same-section differences. */ + if (S_GET_SEGMENT (exp->X_add_symbol) != S_GET_SEGMENT (op_sym)) + continue; + + /* Only code sections are relaxed. */ + if (!(bfd_section_flags (S_GET_SEGMENT (op_sym)) & SEC_CODE)) + continue; + + fix_new (fragP, fragP->fr_fix, 0, op_sym, 0, 0, + BFD_RELOC_XTENSA_PDIFF_ULEB128); + symbol_get_tc (fragP->fr_symbol)->has_leb128_diff_reloc = 1; + } +} + +/* For uleb128 diffs with R_XTENSA_PDIFF_ULEB128, reserve one spare byte so + linker relaxation can grow the value across one 7-bit boundary without + overflowing. Growth that needs more than one extra byte still errors in + the linker. frag_var already allocated sizeof_leb128 (~0, 0) bytes; + clamp if the value already needs that many. */ + +offsetT +xtensa_leb128_frag_size (const fragS *fragP, offsetT size) +{ + if (fragP->fr_symbol != NULL + && symbol_get_tc (fragP->fr_symbol)->has_leb128_diff_reloc + && size < (offsetT) sizeof_leb128 (~(valueT) 0, 0)) + size++; + return size; +} + /* When a difference-of-symbols expression is encoded as a uleb128 or - sleb128 value, the linker is unable to adjust that value to account for - link-time relaxation. Mark all the code between such symbols so that - its size cannot be changed by linker relaxation. */ + sleb128 value and no relocation can express it, the linker is unable to + adjust that value to account for link-time relaxation. Mark all the code + between such symbols so that its size cannot be changed by linker + relaxation. */ static void xtensa_mark_difference_of_two_symbols (void) @@ -8250,6 +8331,9 @@ xtensa_mark_difference_of_two_symbols (void) { expressionS *exp = symbol_get_value_expression (expr_sym); + if (symbol_get_tc (expr_sym)->has_leb128_diff_reloc) + continue; + if (exp->X_op == O_subtract) { symbolS *left = exp->X_add_symbol; diff --git a/gas/config/tc-xtensa.h b/gas/config/tc-xtensa.h index 64d735144d7..afd7478dd8d 100644 --- a/gas/config/tc-xtensa.h +++ b/gas/config/tc-xtensa.h @@ -297,6 +297,9 @@ typedef struct xtensa_symfield_type { unsigned int is_loop_target : 1; unsigned int is_branch_target : 1; + /* Set when a uleb128 difference got R_XTENSA_PDIFF_ULEB128, so code + between the symbols may still be relaxed. */ + unsigned int has_leb128_diff_reloc : 1; symbolS *next_expr_symbol; } xtensa_symfield_type; @@ -341,6 +344,7 @@ extern int xtensa_unrecognized_line (int); extern bool xtensa_check_inside_bundle (void); extern void xtensa_handle_align (fragS *); extern char *xtensa_section_rename (const char *); +extern offsetT xtensa_leb128_frag_size (const fragS *, offsetT); /* We need to set the target endianness in xtensa_init and not in md_begin. This is because xtensa_target_format is called before md_begin, and we @@ -374,6 +378,7 @@ extern void xtensa_init (int, char **); #define md_elf_section_change_hook xtensa_elf_section_change_hook #define md_finish xtensa_md_finish #define md_flush_pending_output() xtensa_flush_pending_output () +#define md_leb128_frag_size(frag, size) xtensa_leb128_frag_size (frag, size) #define md_operand(x) #define TEXT_SECTION_NAME xtensa_section_rename (".text") #define DATA_SECTION_NAME xtensa_section_rename (".data") diff --git a/gas/doc/internals.texi b/gas/doc/internals.texi index f840efa7a8c..d8345dd1283 100644 --- a/gas/doc/internals.texi +++ b/gas/doc/internals.texi @@ -613,8 +613,11 @@ have this type. This state is used to implement the DWARF ``little endian base 128'' variable length number format. The @code{fr_symbol} is always an expression symbol, as constant expressions are emitted directly. The @code{fr_offset} -field is used during relaxation to hold the previous size of the number so -that we can determine if the fragment changed size. +field is used during relaxation to hold the reserved size of the number so +that we can determine if the fragment changed size. If +@code{md_leb128_frag_size} is defined, that size may be larger than the +shortest encoding; converting the frag to fill then emits a padded LEB128 +of the reserved width. @item rs_machine_dependent Displacement relaxation is to be done on this frag. The target is indicated by @@ -1235,6 +1238,22 @@ the default implementation, to scan @code{TC_GENERIC_RELAX_TABLE}. If defined, it is a C statement that is invoked prior to scanning the relax table. +@item md_leb128_frag_size (@var{frag}, @var{size}) +@cindex md_leb128_frag_size +If defined, GAS will call this on each relaxation pass over an +@code{rs_leb128} frag. @var{frag} is the frag being relaxed. +@var{size} is the length of the shortest LEB128 encoding of the +current value (signedness is @code{fr_subtype}). The return value +becomes the reserved frag size. + +When the frag is converted to fill, GAS writes the shortest encoding and, +if the reserved width is larger, extends it with redundant high-order +bytes so the encoding still represents the same value. A target can +use this so a later linker rewrite can replace the encoding with a +larger value in place, up to the reserved width, without changing the +section size. If the macro is not defined, GAS keeps @var{size} +unchanged. + @item LINKER_RELAXING_SHRINKS_ONLY @cindex LINKER_RELAXING_SHRINKS_ONLY If you define this macro, and the global variable @samp{linkrelax} is set diff --git a/gas/testsuite/gas/xtensa/pdiff-uleb128-norelax.d b/gas/testsuite/gas/xtensa/pdiff-uleb128-norelax.d new file mode 100644 index 00000000000..dcd4641083f --- /dev/null +++ b/gas/testsuite/gas/xtensa/pdiff-uleb128-norelax.d @@ -0,0 +1,20 @@ +#as: --no-link-relax +#source: pdiff-uleb128.s +#objdump: -r -s -j .debug_info -j .xt.prop +#name: uleb128 difference without link-relax + +# Without link-relax, no R_XTENSA_PDIFF_ULEB128 is emitted. The assembler +# falls back to marking the covered code no_transform via .xt.prop, and the +# uleb128 uses the minimal encoding (no spare byte). + +.*: +file format .*xtensa.* + +RELOCATION RECORDS FOR \[.xt.prop\]: +OFFSET +TYPE +VALUE +0+ R_XTENSA_32 +.text.* + +Contents of section .debug_info: + 0000 c801.* +#... +Contents of section .xt.prop: + 0000 .* diff --git a/gas/testsuite/gas/xtensa/pdiff-uleb128.d b/gas/testsuite/gas/xtensa/pdiff-uleb128.d new file mode 100644 index 00000000000..5c38f56ca66 --- /dev/null +++ b/gas/testsuite/gas/xtensa/pdiff-uleb128.d @@ -0,0 +1,12 @@ +#objdump: -r -s -j .debug_info +#name: uleb128 difference reloc + +.*: +file format .*xtensa.* + +RELOCATION RECORDS FOR \[.debug_info\]: +OFFSET +TYPE +VALUE +0+ R_XTENSA_PDIFF_ULEB128 +.text.* + +# Difference is 200 (0xc8 0x01) plus one spare byte reserved for growth. +Contents of section .debug_info: + 0000 c88100.* diff --git a/gas/testsuite/gas/xtensa/pdiff-uleb128.s b/gas/testsuite/gas/xtensa/pdiff-uleb128.s new file mode 100644 index 00000000000..0b431a3ac26 --- /dev/null +++ b/gas/testsuite/gas/xtensa/pdiff-uleb128.s @@ -0,0 +1,7 @@ + .text +.L1: + .space 200 +.L2: + + .section .debug_info, "", @progbits + .uleb128 .L2 - .L1 diff --git a/gas/write.c b/gas/write.c index 4e898dd1021..ae7a532cea5 100644 --- a/gas/write.c +++ b/gas/write.c @@ -461,6 +461,23 @@ cvt_frag_to_fill (segT sec ATTRIBUTE_UNUSED, fragS *fragP) size = output_leb128 (fragP->fr_literal + fragP->fr_fix, value, fragP->fr_subtype); + /* Relaxation may have reserved more room than the shortest encoding + needs (see md_leb128_frag_size). Keep the reserved size by adding + redundant high-order continuation bytes; inert unless a target + hook grew fr_offset above the minimal encoding. */ + if ((offsetT) size < fragP->fr_offset) + { + char *p = fragP->fr_literal + fragP->fr_fix; + char pad = fragP->fr_subtype && (offsetT) value < 0 ? 0x7f : 0; + + do + { + p[size - 1] |= 0x80; + p[size++] = pad; + } + while ((offsetT) size < fragP->fr_offset); + } + fragP->fr_fix += size; fragP->fr_type = rs_fill; fragP->fr_var = 0; @@ -3124,6 +3141,9 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass) value = resolve_symbol_value (fragP->fr_symbol); size = sizeof_leb128 (value, fragP->fr_subtype); +#ifdef md_leb128_frag_size + size = md_leb128_frag_size (fragP, size); +#endif growth = size - fragP->fr_offset; fragP->fr_offset = size; } diff --git a/include/elf/xtensa.h b/include/elf/xtensa.h index 1a9bdf95cf7..032b94ef94b 100644 --- a/include/elf/xtensa.h +++ b/include/elf/xtensa.h @@ -93,6 +93,9 @@ START_RELOC_NUMBERS (elf_xtensa_reloc_type) RELOC_NUMBER (R_XTENSA_NDIFF8, 60) RELOC_NUMBER (R_XTENSA_NDIFF16, 61) RELOC_NUMBER (R_XTENSA_NDIFF32, 62) + /* Positive same-section difference encoded as uleb128; rewritten + in place after relaxation without changing the section size. */ + RELOC_NUMBER (R_XTENSA_PDIFF_ULEB128, 63) END_RELOC_NUMBERS (R_XTENSA_max) /* Processor-specific flags for the ELF header e_flags field. */ diff --git a/ld/testsuite/ld-xtensa/relax-uleb128-2.d b/ld/testsuite/ld-xtensa/relax-uleb128-2.d new file mode 100644 index 00000000000..c0f1c9ca378 --- /dev/null +++ b/ld/testsuite/ld-xtensa/relax-uleb128-2.d @@ -0,0 +1,12 @@ +#source: relax-uleb128-2.s +#source: lcall2.s +#ld: -T lcall.t +#objdump: -s -j .debug_info +#name: uleb128 difference shrinking below 128 + +# The difference is 156 before relaxation and 108 after it, so the value no +# longer needs two bytes. The assembler reserved one spare byte (three +# total), and the linker must keep that fixed width with uleb128 padding. +#... + 0000 ec8000.* +#... diff --git a/ld/testsuite/ld-xtensa/relax-uleb128-2.s b/ld/testsuite/ld-xtensa/relax-uleb128-2.s new file mode 100644 index 00000000000..c587ed8bb06 --- /dev/null +++ b/ld/testsuite/ld-xtensa/relax-uleb128-2.s @@ -0,0 +1,18 @@ + .globl _start + .globl foo + .text + .align 4 +_start: + .literal .Lunused, 0xffffffff + entry a5, 16 +.L1: + .space 60 + .begin longcalls + .rept 16 + call4 foo + .endr + .end longcalls +.L2: + + .section .debug_info, "", @progbits + .uleb128 .L2 - .L1 diff --git a/ld/testsuite/ld-xtensa/relax-uleb128.d b/ld/testsuite/ld-xtensa/relax-uleb128.d new file mode 100644 index 00000000000..08b017984d8 --- /dev/null +++ b/ld/testsuite/ld-xtensa/relax-uleb128.d @@ -0,0 +1,11 @@ +#source: relax-uleb128.s +#source: lcall2.s +#ld: -T lcall.t +#objdump: -s -j .debug_info +#name: uleb128 difference after longcall relax + +# Longcall relaxes from 6 bytes to 3; with the trailing nop the difference +# is 5. Assembler reserved one spare byte (two total). +#... + 0000 8500.* +#... diff --git a/ld/testsuite/ld-xtensa/relax-uleb128.s b/ld/testsuite/ld-xtensa/relax-uleb128.s new file mode 100644 index 00000000000..8e1c0058976 --- /dev/null +++ b/ld/testsuite/ld-xtensa/relax-uleb128.s @@ -0,0 +1,16 @@ + .globl _start + .globl foo + .text + .align 4 +_start: + .literal .Lunused, 0xffffffff + entry a5, 16 +.L1: + .begin longcalls + call4 foo + .end longcalls + nop +.L2: + + .section .debug_info, "", @progbits + .uleb128 .L2 - .L1 diff --git a/ld/testsuite/ld-xtensa/xtensa.exp b/ld/testsuite/ld-xtensa/xtensa.exp index 7173c6b21b2..66a6b7a60da 100644 --- a/ld/testsuite/ld-xtensa/xtensa.exp +++ b/ld/testsuite/ld-xtensa/xtensa.exp @@ -30,6 +30,8 @@ run_dump_test "lcall" run_dump_test "relax-diff1" run_dump_test "relax-loc" run_dump_test "relax-ndiff" +run_dump_test "relax-uleb128" +run_dump_test "relax-uleb128-2" run_dump_test "relax-static-pie" run_dump_test "relax-static-local-pie"