From patchwork Thu Mar 19 11:16:38 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Evgeny Karpov X-Patchwork-Id: 131967 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 2213B4B1968F for ; Thu, 19 Mar 2026 11:19:30 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 2213B4B1968F Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=arm.com header.i=@arm.com header.a=rsa-sha256 header.s=selector1 header.b=n3qYe8ON; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.a=rsa-sha256 header.s=selector1 header.b=n3qYe8ON X-Original-To: binutils@sourceware.org Delivered-To: binutils@sourceware.org Received: from AS8PR04CU009.outbound.protection.outlook.com (mail-westeuropeazlp170110003.outbound.protection.outlook.com [IPv6:2a01:111:f403:c201::3]) by sourceware.org (Postfix) with ESMTPS id 95A9D4B920AA for ; Thu, 19 Mar 2026 11:18:16 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 95A9D4B920AA Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=arm.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 95A9D4B920AA Authentication-Results: server2.sourceware.org; arc=pass smtp.remote-ip=2a01:111:f403:c201::3 ARC-Seal: i=3; a=rsa-sha256; d=sourceware.org; s=key; t=1773919096; cv=pass; b=Q7tVQZvyuf1M6mG1n+Vwo4UzqklvUj8zXDAaqmflFINAPUrpA20XMoWoh5792VduIKW9IU45IeqrUL2X3eFM3uVjphFfr3zwDRqDMcJG6icYUtE/vBOxen7Fg/6O6omYqKuAIFsK9rWnSzK8yH9n0nHu8g4hA9Sm5UNvEoRMfAg= ARC-Message-Signature: i=3; a=rsa-sha256; d=sourceware.org; s=key; t=1773919096; c=relaxed/simple; bh=0wkYAM97azJx6N2sMqhCMNBbPE+tJFUMHSNaqNH43jY=; h=DKIM-Signature:DKIM-Signature:From:To:Subject:Date:Message-ID: MIME-Version; b=LtzZMbyKX5oixBjNadHJaOBftD4lgugOqrIAO/cwt0Z780Zrhaox4f8/d4f7Dmy51dagMlCtYLnEfcBmdk8jsT8ztBffWaaghmq1/Guq1cH5lFsqBDhkcgQBHAp66IFRUci6netoN/PFgMdARMXvlXF/FVjEjVUxkqDG9Wn+aAA= ARC-Authentication-Results: i=3; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 95A9D4B920AA ARC-Seal: i=2; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=pass; b=vhjn4aW/1yn6vbtBhLeC8sVQcgPuF7/8k2QIkHtSepKARaDSTQKJtCGxJb3Z7OrP20DnpDzmuAMdNL24Q9JCwZlJFme9p1CC+SYsc08DGX/Dc35vFNJjvJE2dSmGV45Wy6Y2cY3Ssf8SumRT722TiEKxPREDPfpiM1GhaaOMZcAtPEnCk9uybJr7u5OeDX2UnIIDtPzZ86RsUOfTlznWUlr7VQuLvZ0uBMs8Lnk7icltfPZ3L4ewOREp4QqDjMyDXG/IaCSIq96l/+9C9Vfp3xndXKV6/xdMqwXCEYghBxORXEYqV6zpliAytvmzW8iQ81WBmzuys/CvbUvjz0vXhQ== ARC-Message-Signature: i=2; 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=/YLiF2gwm3VUJv8khQE4PLrBmoWUG+7DFdrhWALWfkA=; b=K897dwx40RMnRGwhWN4DBy4+rRe2UMCCKmnB5eGI39pAIcZ90C1rmNppqc9IH4Jqnf0OGyQDLHeIA8aUdCrJkS4bphqElq1m+4OOJW0hQCR4JE/dSD9ZopZ28D3Pg0KLQ8i7lAKZ9h1CzuLo484RdoRiCKbJr0bXtAcAevYQkm0RtzJpHZ8yCRxRPSv9PHoL39pA4BwDgdgly+NBca48E37OQlJqwiU8uMIb/0MVAGUPH7Ws2x2ziehX1sEgOL58DnxsIpB95fg4CnXx1W9YBWLR25y3N4YYq2oYg6XgOaG9nzQrPtDPjWos0h90S9Q1quRRkoXakEXcCKqfBHEueg== ARC-Authentication-Results: i=2; mx.microsoft.com 1; spf=pass (sender ip is 4.158.2.129) smtp.rcpttodomain=sourceware.org smtp.mailfrom=arm.com; dmarc=pass (p=none sp=none pct=100) action=none header.from=arm.com; dkim=pass (signature was verified) header.d=arm.com; arc=pass (0 oda=1 ltdi=1 spf=[1,1,smtp.mailfrom=arm.com] dmarc=[1,1,header.from=arm.com]) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arm.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=/YLiF2gwm3VUJv8khQE4PLrBmoWUG+7DFdrhWALWfkA=; b=n3qYe8ONs/Km4qkdVD4/TT363nnbHONk0M5KsUM2Xg8AZOmED7j/xA++ol+h8ffN9XvU3ncDsFrqdsvC4uOyc/XUTFUNKqxr/pxR2gRiLEmW+vkcUDOi+A3wn5nXCVODiVKpWo+dB25SgDq8FLAE+GuJ63Z1l5VNdGRCZJ2MlnQ= Received: from DUZPR01CA0107.eurprd01.prod.exchangelabs.com (2603:10a6:10:4bb::25) by DBAPR08MB5576.eurprd08.prod.outlook.com (2603:10a6:10:1ae::11) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9723.19; Thu, 19 Mar 2026 11:18:08 +0000 Received: from DU6PEPF00009527.eurprd02.prod.outlook.com (2603:10a6:10:4bb:cafe::2d) by DUZPR01CA0107.outlook.office365.com (2603:10a6:10:4bb::25) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.20.9700.27 via Frontend Transport; Thu, 19 Mar 2026 11:18:08 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 4.158.2.129) smtp.mailfrom=arm.com; dkim=pass (signature was verified) header.d=arm.com;dmarc=pass action=none header.from=arm.com; Received-SPF: Pass (protection.outlook.com: domain of arm.com designates 4.158.2.129 as permitted sender) receiver=protection.outlook.com; client-ip=4.158.2.129; helo=outbound-uk1.az.dlp.m.darktrace.com; pr=C Received: from outbound-uk1.az.dlp.m.darktrace.com (4.158.2.129) by DU6PEPF00009527.mail.protection.outlook.com (10.167.8.8) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.20.9723.19 via Frontend Transport; Thu, 19 Mar 2026 11:18:08 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=UfS8KnMo+gAE0gn1V3jjGsvZk0hRZeUlPX83ubJuNw3KApL7ciPBzXxH9YQt6REKt+iiwB4Xzgwwcbr57vkK+pVBrqvgH5G8Bepenba0AhK0eeUDAvjW21rx2fzF+8AnQ7j7lK1hY3IjP2Mmae4T/k+oHF8kF6EZBG5wyKV4IiNeNmg6Kljpsim2dEa3k64o+iG3SOhLHwV2VJpJGYRKp27KgYObMTl/U+P1SFUghn955/6fcQSyBV3B82p/BCtQAYKEgxOG7+yG0MB+h8ULKHxM94Wz5khCxQEOUk27PQm3bo08ZrZr1HpjEy2gzQMzhDiws6cN+xvKrhJp/a+rag== 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=/YLiF2gwm3VUJv8khQE4PLrBmoWUG+7DFdrhWALWfkA=; b=NdJncNaYaBY/+kGWCUckAnVAQsZ9XzY8RoIYozrDx9bwIkzGC708CVpU7pjqYRQ8viMfdLmnHhOsh9w8udFUZpI+pX+ZbZE26wucPCJRafXch0mZcempBA/g7S6Lx94efxN1FUsBiJdrbV5MEeh/7XNE65+QeK4yChay/mpmAbsw08LigowzvhM6dfe/vM+0/1OGFWiEjBV5wuWzA5LXIRfYNa/0b1V/wiU+otV8axMYcBf0116Rgwrs78JtEu/0WG9mQJ4jS1RXB2uVZXzRtlcLr5pFWkkReJ0In1/njvJl8ulXDdefjD7gDM+74vjnOf5echZzVmS28YlmqQ/Jwg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 172.205.89.229) smtp.rcpttodomain=sourceware.org smtp.mailfrom=arm.com; dmarc=pass (p=none sp=none pct=100) action=none header.from=arm.com; dkim=none (message not signed); arc=none (0) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arm.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=/YLiF2gwm3VUJv8khQE4PLrBmoWUG+7DFdrhWALWfkA=; b=n3qYe8ONs/Km4qkdVD4/TT363nnbHONk0M5KsUM2Xg8AZOmED7j/xA++ol+h8ffN9XvU3ncDsFrqdsvC4uOyc/XUTFUNKqxr/pxR2gRiLEmW+vkcUDOi+A3wn5nXCVODiVKpWo+dB25SgDq8FLAE+GuJ63Z1l5VNdGRCZJ2MlnQ= Received: from DU2PR04CA0053.eurprd04.prod.outlook.com (2603:10a6:10:234::28) by PR3PR08MB5852.eurprd08.prod.outlook.com (2603:10a6:102:8e::21) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9723.19; Thu, 19 Mar 2026 11:17:00 +0000 Received: from DU2PEPF00028D12.eurprd03.prod.outlook.com (2603:10a6:10:234:cafe::5d) by DU2PR04CA0053.outlook.office365.com (2603:10a6:10:234::28) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.20.9723.19 via Frontend Transport; Thu, 19 Mar 2026 11:17:00 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 172.205.89.229) smtp.mailfrom=arm.com; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=arm.com; Received-SPF: Pass (protection.outlook.com: domain of arm.com designates 172.205.89.229 as permitted sender) receiver=protection.outlook.com; client-ip=172.205.89.229; helo=nebula.arm.com; pr=C Received: from nebula.arm.com (172.205.89.229) by DU2PEPF00028D12.mail.protection.outlook.com (10.167.242.26) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9723.19 via Frontend Transport; Thu, 19 Mar 2026 11:17:00 +0000 Received: from AZ-NEU-EX03.Arm.com (10.240.25.137) by AZ-NEU-EX03.Arm.com (10.240.25.137) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.29; Thu, 19 Mar 2026 11:16:57 +0000 Received: from LD2C7QYYTH.arm.com (10.57.19.196) by mail.arm.com (10.240.25.137) with Microsoft SMTP Server id 15.2.2562.29 via Frontend Transport; Thu, 19 Mar 2026 11:16:57 +0000 From: Evgeny Karpov To: CC: , , , , , , , Subject: [PATCH v7 3/3] aarch64: Implement Structured Exception Handling (SEH) on AArch64 Date: Thu, 19 Mar 2026 12:16:38 +0100 Message-ID: <20260319111638.93074-4-evgeny.karpov@arm.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260319111638.93074-1-evgeny.karpov@arm.com> References: <20260319111638.93074-1-evgeny.karpov@arm.com> MIME-Version: 1.0 X-EOPAttributedMessage: 1 X-MS-TrafficTypeDiagnostic: DU2PEPF00028D12:EE_|PR3PR08MB5852:EE_|DU6PEPF00009527:EE_|DBAPR08MB5576:EE_ X-MS-Office365-Filtering-Correlation-Id: 9e066326-e4d8-4e45-cd59-08de85a932f1 x-checkrecipientrouted: true NoDisclaimer: true X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam-Untrusted: BCL:0; ARA:13230040|36860700016|376014|82310400026|1800799024|56012099003|22082099003|18002099003|13003099007; X-Microsoft-Antispam-Message-Info-Original: 93fCozGf/UyvpdysMRGSlgsQlk8kZ7Q9hXhe++E+Z08N+kRsE04S0lWH0Y56ZXc1/Cnyu3PAbPtfxcGrz3csPQZT98auB/XorCc2h/xHI0DE5qXtr6Amc5qsBbd9sgveWF+f57ThOHtt+bzfATRxSdHEh3m9vnYgwl1ZO0qs5deg5TUpDoqvowGSEOYE+40eFIMTG+W4QRs3RJO4ot5WCDgq0PZSWi49mIJ12kEfdpiYZ62jAMKsserFb3XqntNEor2JrjG7LUxQglDD1s60Xt5gi5lh1r992kaoxYjFjj1wfuROQ1BSe5SubFV8qKmF4O2gOWh1rdfp+bw/VxyQ0Tr6iUPD19xmWaITLOLUsE5XGNOQmRpCEu41ZoTKPcW1GHoeUqWHQCFMKiPw4pwCN/TmsLHNbu5Qb2cXW7Uk2u1SJFIAXGhDMyoFVaj/PKspOaXKhX9GNr87O9OgptohwAyS2vlTo0pOygeXxXlS4l2+0YCguzLB4rewfX2IlMH5qLI/1vDg/aQrWOrkk1TjvBrwxHjb5vuR5AI/PcXdRcY/vlXnwSmke5f+vjKjw4k2EgvZ2mbDwaG62Cx8TiWmz6zDkgOTtRBVTlVaevnKOo6X5ndwfcLjzkgGsVjSzjNm811+8y3R45n4SDBpKhehPJMjLr1IsVLJYZHrcEV0B6AZabH8WTRr7Ana06Cww9MUy4KkiUc6p9EhwN+/hmRXTKe4tPMYzCHsL6EIe+UeubumAkpvqhyIbuwjIO66oc+vNLlc6wVsizIPLO2WnDqNYlP1pJO0jqflMHZYH5jh3gU= X-Forefront-Antispam-Report-Untrusted: CIP:172.205.89.229; CTRY:IE; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:nebula.arm.com; PTR:InfoDomainNonexistent; CAT:NONE; SFS:(13230040)(36860700016)(376014)(82310400026)(1800799024)(56012099003)(22082099003)(18002099003)(13003099007); DIR:OUT; SFP:1101; X-Exchange-RoutingPolicyChecked: EuzWs8veut47HqYWIf3BdKjJCUFzNQS8wGn0Wn4y2lcQ/uTiKO3Tm7eSiBU9gXyLy3uoqSVqh3j6ieIjrjs1gfDaOjModhmWJOFrf/aHTXDr5lWkLhSbK+D2SSkzTS52IrKONbk/j3O/UM58DnILZ1mFb8gBC1K4WsWSmEe9xsrIiGCicEz8nSWHOUiXxPhqnKLKTAUowMh3w8HktYv4drbuAzCBXro0wf5JEOtYTxH79fSrOuSYDEXOJlZsZQ/QXeuWDBgsLjCvG6iTMhGcvM5/BejWeWGnyrWnCSV5usUcABNwHn78X+s5V/00HhhDdlHtxa6c4jG26id5yZ3lmA== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PR3PR08MB5852 X-MS-Exchange-Transport-CrossTenantHeadersStripped: DU6PEPF00009527.eurprd02.prod.outlook.com X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id-Prvs: 43bbfad8-e6fb-4e53-8fd0-08de85a90a76 X-Microsoft-Antispam: BCL:0; ARA:13230040|35042699022|1800799024|14060799003|82310400026|36860700016|376014|18002099003|22082099003|56012099003|13003099007; X-Microsoft-Antispam-Message-Info: hRrUYDA+Fd9XlIqf0jG+gH4Qi3OCOAdW7/qUu06ot2T/uttTvxwnDT71McI+9yX3aslUl6CswfTLEtjCGDb6TTJNoi7+ityXQvAp5qXNDcqj+DFLmyAszrWlTWSO03hGGaspxrRAvq8IRGqptXJnMmbVBvWYviPETGLckWi6FNzOlkiB685x0m9kF9HO+cMp6mkDBjxJQMScvlnIRIt554uhpH4Y5hYzdimg8r6RVS16W80zHm0LzIiTyDVQdv0owqUuu9f5dSejlItgUM6EM71TMYJ8dA4qi/KDNJ5wmpTxS+zZjd8LaxHhM0nb2Dp7gPxbAftxco2um//6X7603iJyZ0mqV6vp/Dj2occafClsyJvVgdCh99fMk2yuRbatmJwJ69UvYyR5HAjM7SC4rC96jGQ/xea53M1cjwamlhjWuf8/MfLDrYeRH3VRG2TjQ4yqt/Mw2ucji4EPqO2mht+I6zqPaAOdSlxj4YuQujyahgSXQ2/ETjoSjRYGI/OvGMnjpl1dADAsqDXTZ+OxQFjCNKsKZhlyBuItCsNXNS9MoVI7sDHp8qmi9uV8R4FpKufrBeZsPVmhMztvLdG50k5DEJGzoDwspbA/C5NLYgTwrySgtwrNjZv0i/YZmfB/IxQN3Za3f0phxuhBtvD+pEE0F/N7c8vJM6+1Xo32G95gW++2O5WHP3fkFA/QSdbtnnRCdLFAuouEoZKOV0W8EvOJnLB+K/hzk84RRVu7IJKB42LndcvHIntMR9qD+xS5NkWqe8krYDW5XEbAtw9r8jgO3nigYTxyCVibuYdyFXk= X-Forefront-Antispam-Report: CIP:4.158.2.129; CTRY:GB; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:outbound-uk1.az.dlp.m.darktrace.com; PTR:InfoDomainNonexistent; CAT:NONE; SFS:(13230040)(35042699022)(1800799024)(14060799003)(82310400026)(36860700016)(376014)(18002099003)(22082099003)(56012099003)(13003099007); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: it88S4XDB/P0ZG+xcxVPkHFymNYnzwp/dAYbqDCU5UoPmdn+bL6bdYmz6R2yN06/l86twv1rmdOzwmf+MWlaDNnvciqtdWUpYIzW5NM2IHLwpdHYv5yrN92ZqS4u6f6Hpb57EeUM6oEV7PwAYX7AvAXvgASb7T9M71Nip2aZBJw6qRdJZZcdlBhvDMS9I/dYYxIAfITzea2eWlRvMlOEGEDETUap3VHgihRYQrWEFGJ6jKoWnpJGVgo0JsGs8Hl9na+5ijjyym3w7kxtz2DKsBmA3Pm2YTqLYoiDLpOwd5BWcaBEIml2V8UP2C0WPNA5SP36P+FXqkO1qONO2fP9iAkq/3fn6pVC0r6rEIiMHYKb9eVQCvYGX0CqW6uKumTZMbSzNbUWcCEpob8rJ7bL00lihVdm8926TR0fzC+gjtLyjKydIP3WDdOeC3rZJ9Ui X-OriginatorOrg: arm.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 19 Mar 2026 11:18:08.2685 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 9e066326-e4d8-4e45-cd59-08de85a932f1 X-MS-Exchange-CrossTenant-Id: f34e5979-57d9-4aaa-ad4d-b122a662184d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=f34e5979-57d9-4aaa-ad4d-b122a662184d; Ip=[4.158.2.129]; Helo=[outbound-uk1.az.dlp.m.darktrace.com] X-MS-Exchange-CrossTenant-AuthSource: DU6PEPF00009527.eurprd02.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: DBAPR08MB5576 X-Spam-Status: No, score=-9.7 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FORGED_SPF_HELO, GIT_PATCH_0, RCVD_IN_DNSWL_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP, URIBL_BLOCKED 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 The patch reuses shared helpers for SEH and implements SEH on AArch64. The implementation is based on (https://learn.microsoft.com/en-us/cpp/build/arm64-exception-handling?view=msvc-170) and pdata/xdata SEH records are emitted from md_finish. When .pdata/.xdata is emitted, the function size is required. Function sizes are calculated as late as possible, and the code segment needs to be relaxed to be able to calculate the function sizes. Initially, obj_coff_generate_pdata was declared in write_object_file. Before the change, obj_coff_generate_pdata was used only to validate syntax, which was sufficient for that purpose. However, that location seems incorrect, as it is too late to emit .pdata/.xdata records in the AArch64 case. md_finish has been declared for AArch64 and extended with seh_aarch64_write_data to emit .pdata/.xdata records after all assembly has been completed. Signed-off-by: Evgeny Karpov gas/ChangeLog: * gas/config/obj-coff-seh-shared.c (defined): Update. * gas/config/obj-coff.c (defined): Update. * gas/config/tc-aarch64.c (defined): Add OBJ_COFF guard. (aarch64_md_finish): Add. * gas/config/tc-aarch64.h (defined): Add OBJ_COFF guard. (md_finish): Add. (aarch64_md_finish): Add. (seh_aarch64_write_data): Add. * gas/write.c: Update. * gas/write.h (subsegs_finish_section): Update. * gas/config/obj-coff-seh-aarch64.c: New file. * gas/config/obj-coff-seh-aarch64.h: New file. --- gas/config/obj-coff-seh-aarch64.c | 889 ++++++++++++++++++++++++++++++ gas/config/obj-coff-seh-aarch64.h | 265 +++++++++ gas/config/obj-coff-seh-shared.c | 5 + gas/config/obj-coff.c | 4 + gas/config/tc-aarch64.c | 10 + gas/config/tc-aarch64.h | 6 + gas/write.c | 2 +- gas/write.h | 1 + 8 files changed, 1181 insertions(+), 1 deletion(-) create mode 100644 gas/config/obj-coff-seh-aarch64.c create mode 100644 gas/config/obj-coff-seh-aarch64.h diff --git a/gas/config/obj-coff-seh-aarch64.c b/gas/config/obj-coff-seh-aarch64.c new file mode 100644 index 00000000000..6b5354f6544 --- /dev/null +++ b/gas/config/obj-coff-seh-aarch64.c @@ -0,0 +1,889 @@ +/* SEH .pdata/.xdata COFF object file format on AArch64 + Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of GAS. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GAS; see the file COPYING. If not, write to the Free + Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "obj-coff-seh-aarch64.h" + +static struct seh_aarch64_context *seh_ctx_root = NULL; +static bool in_seh_proc = false; + +struct aarch64_unwind_code_pack_info { + const char *directive; + unsigned offset_bits; + unsigned reg_bits; + unsigned code_bits; + unsigned code; + unsigned offset_right_shift; + unsigned offset; + unsigned reg_right_shift; + unsigned reg_offset; + unsigned size; +}; + +static const struct aarch64_unwind_code_pack_info +aarch64_unwind_code_pack_data[] = { +/* Unwind codes packing for AArch64 is described at + https://learn.microsoft.com/en-us/cpp/build/arm64-exception-handling?view=msvc-170#unwind-codes + and calculated in seh_aarch64_add_unwind_element function. */ + { + .directive = NULL, .offset_bits = 5, .reg_bits = 0, + .code_bits = 3, .code = AARCH64_UNOP_ALLOCS, .offset_right_shift = 4, + .offset = 0, .reg_right_shift = 0, .reg_offset = 0, .size = 1 + }, + { + .directive = NULL, .offset_bits = 11, .reg_bits = 0, + .code_bits = 5, .code = AARCH64_UNOP_ALLOCM, .offset_right_shift = 4, + .offset = 0, .reg_right_shift = 0, .reg_offset = 0, .size = 2 + }, + { + .directive = NULL, .offset_bits = 24, .reg_bits = 0, + .code_bits = 8, .code = AARCH64_UNOP_ALLOCL, .offset_right_shift = 4, + .offset = 0, .reg_right_shift = 0, .reg_offset = 0, .size = 4 + }, + { + .directive = ".seh_save_reg", .offset_bits = 6, .reg_bits = 4, + .code_bits = 6, .code = AARCH64_UNOP_SAVEREG, .offset_right_shift = 3, + .offset = 0, .reg_right_shift = 0, .reg_offset = 19, .size = 2 + }, + { + .directive = ".seh_save_reg_x", .offset_bits = 5, .reg_bits = 4, + .code_bits = 7, .code = AARCH64_UNOP_SAVEREGX, .offset_right_shift = 3, + .offset = 1, .reg_right_shift = 0, .reg_offset = 19, .size = 2 + }, + { + .directive = ".seh_save_regp", .offset_bits = 6, .reg_bits = 4, + .code_bits = 6, .code = AARCH64_UNOP_SAVEREGP, .offset_right_shift = 3, + .offset = 0, .reg_right_shift = 0, .reg_offset = 19, .size = 2 + }, + { + .directive = ".seh_save_regp_x", .offset_bits = 6, .reg_bits = 4, + .code_bits = 6, .code = AARCH64_UNOP_SAVEREGPX, .offset_right_shift = 3, + .offset = 1, .reg_right_shift = 0, .reg_offset = 19, .size = 2 + }, + { + .directive = ".seh_save_fregp", .offset_bits = 6, .reg_bits = 3, + .code_bits = 7, .code = AARCH64_UNOP_SAVEFREGP, .offset_right_shift = 3, + .offset = 0, .reg_right_shift = 0, .reg_offset = 8, .size = 2 + }, + { + .directive = ".seh_save_fregp_x", .offset_bits = 6, .reg_bits = 3, + .code_bits = 7, .code = AARCH64_UNOP_SAVEFREGPX, .offset_right_shift = 3, + .offset = 1, .reg_right_shift = 0, .reg_offset = 8, .size = 2 + }, + { + .directive = ".seh_save_freg", .offset_bits = 6, .reg_bits = 3, + .code_bits = 7, .code = AARCH64_UNOP_SAVEFREG, .offset_right_shift = 3, + .offset = 0, .reg_right_shift = 0, .reg_offset = 8, .size = 2 + }, + { + .directive = ".seh_save_freg_x", .offset_bits = 5, .reg_bits = 3, + .code_bits = 8, .code = AARCH64_UNOP_SAVEFREGX, .offset_right_shift = 3, + .offset = 1, .reg_right_shift = 0, .reg_offset = 8, .size = 2 + }, + { + .directive = ".seh_save_lrpair", .offset_bits = 6, .reg_bits = 3, + .code_bits = 7, .code = AARCH64_UNOP_SAVELRPAIR, .offset_right_shift = 3, + .offset = 0, .reg_right_shift = 1, .reg_offset = 19, .size = 2 + }, + { + .directive = ".seh_save_fplr", .offset_bits = 6, .reg_bits = 0, + .code_bits = 2, .code = AARCH64_UNOP_SAVEFPLR, .offset_right_shift = 3, + .offset = 0, .reg_right_shift = 0, .reg_offset = 0, .size = 1 + }, + { + .directive = ".seh_save_fplr_x", .offset_bits = 6, .reg_bits = 0, + .code_bits = 2, .code = AARCH64_UNOP_SAVEFPLRX, .offset_right_shift = 3, + .offset = 1, .reg_right_shift = 0, .reg_offset = 0, .size = 1 + }, + { + .directive = ".seh_save_r19r20_x", .offset_bits = 5, .reg_bits = 0, + .code_bits = 3, .code = AARCH64_UNOP_SAVER19R20X, .offset_right_shift = 3, + .offset = 0, .reg_right_shift = 0, .reg_offset = 0, .size = 1 + }, + { + .directive = ".seh_add_fp", .offset_bits = 8, .reg_bits = 0, + .code_bits = 8, .code = AARCH64_UNOP_ADDFP, .offset_right_shift = 0, + .offset = 0, .reg_right_shift = 0, .reg_offset = 0, .size = 2 + }, + { + .directive = ".seh_set_fp", .offset_bits = 0, .reg_bits = 0, + .code_bits = 8, .code = AARCH64_UNOP_SETFP, .offset_right_shift = 0, + .offset = 0, .reg_right_shift = 0, .reg_offset = 0, .size = 1 + }, + { + .directive = ".seh_save_next", .offset_bits = 0, .reg_bits = 0, + .code_bits = 8, .code = AARCH64_UNOP_SAVENEXT, .offset_right_shift = 0, + .offset = 0, .reg_right_shift = 0, .reg_offset = 0, .size = 1 + }, + { + .directive = ".seh_nop", .offset_bits = 0, .reg_bits = 0, + .code_bits = 8, .code = AARCH64_UNOP_NOP, .offset_right_shift = 0, + .offset = 0, .reg_right_shift = 0, .reg_offset = 0, .size = 1 + }, + { + .directive = ".seh_pac_sign_lr", .offset_bits = 0, .reg_bits = 0, + .code_bits = 8, .code = AARCH64_UNOP_PACSIGNLR, .offset_right_shift = 0, + .offset = 0, .reg_right_shift = 0, .reg_offset = 0, .size = 1 + }, + { + .directive = NULL, .offset_bits = 0, .reg_bits = 0, + .code_bits = 8, .code = AARCH64_UNOP_END, .offset_right_shift = 0, + .offset = 0, .reg_right_shift = 0, .reg_offset = 0, .size = 1 + }, +}; + +/* Set for current context the default handler. */ +static void +obj_coff_seh_handler (const int what ATTRIBUTE_UNUSED) +{ + char *symbol_name; + char name_end; + + if (!verify_context (".seh_handler")) + return; + + if (*input_line_pointer == 0 || *input_line_pointer == '\n') + as_bad (_(".seh_handler requires a handler")); + + SKIP_WHITESPACE (); + + if (*input_line_pointer == '@') + { + name_end = get_symbol_name (&symbol_name); + + seh_ctx_cur->handler.X_op = O_constant; + seh_ctx_cur->handler.X_add_number = 0; + + if (strcasecmp (symbol_name, "@0") == 0 + || strcasecmp (symbol_name, "@null") == 0) + ; + else if (strcasecmp (symbol_name, "@1") == 0) + seh_ctx_cur->handler.X_add_number = 1; + else + as_bad (_("unknown constant value '%s' for handler"), symbol_name); + + (void) restore_line_pointer (name_end); + } + else + expression (&seh_ctx_cur->handler); + + seh_ctx_cur->handler_data.X_op = O_constant; + seh_ctx_cur->handler_data.X_add_number = 0; + seh_ctx_cur->xdata_header.x = 1; + + while (skip_whitespace_and_comma (0)) + { + name_end = get_symbol_name (&symbol_name); + (void) restore_line_pointer (name_end); + } +} + +/* Switch to subsection for handler data for exception region. */ +static void +obj_coff_seh_handlerdata (const int what ATTRIBUTE_UNUSED) +{ + demand_empty_rest_of_line (); + + switch_xdata (seh_ctx_cur->subsection + 1, seh_ctx_cur->code_seg); +} + +/* Obtain available unwind element. */ +static void +seh_aarch64_add_unwind_element (const seh_aarch64_unwind_types unwind_type, + unsigned offset, unsigned reg) +{ + gas_assert (in_seh_proc); + + const struct aarch64_unwind_code_pack_info *unwind_code_pack_info + = aarch64_unwind_code_pack_data + unwind_type; + unsigned value_offset_bits = 0; + + if ((seh_ctx_cur->unwind_codes_byte_count + + unwind_code_pack_info->size) > AARCH64_MAX_UNWIND_CODES_SIZE) + as_bad (_("no unwind element available.")); + + seh_aarch64_unwind_code *aarch64_element; + aarch64_element = seh_ctx_cur->unwind_codes + + seh_ctx_cur->unwind_codes_count++; + aarch64_element->value = 0; + + if (unwind_code_pack_info->offset_bits) + { + offset = (offset >> unwind_code_pack_info->offset_right_shift) + - unwind_code_pack_info->offset; + offset &= (1 << unwind_code_pack_info->offset_bits) - 1; + aarch64_element->value |= offset << value_offset_bits; + value_offset_bits += unwind_code_pack_info->offset_bits; + } + + if (unwind_code_pack_info->reg_bits) + { + reg = (reg >> unwind_code_pack_info->reg_right_shift) + - unwind_code_pack_info->reg_offset; + reg &= (1 << unwind_code_pack_info->reg_bits) - 1; + aarch64_element->value |= reg << value_offset_bits; + value_offset_bits += unwind_code_pack_info->reg_bits; + } + + if (unwind_code_pack_info->code_bits) + { + unsigned code = unwind_code_pack_info->code; + code &= (1 << unwind_code_pack_info->code_bits) - 1; + aarch64_element->value |= code << value_offset_bits; + } + + aarch64_element->type = unwind_type; + seh_ctx_cur->unwind_codes_byte_count += unwind_code_pack_info->size; +} + +/* Mark begin of new context. */ +static void +obj_coff_seh_proc (const int what ATTRIBUTE_UNUSED) +{ + char *symbol_name; + char name_end; + + if (in_seh_proc) + as_bad (_("previous SEH entry not closed (missing .seh_endproc)")); + + if (*input_line_pointer == 0 || *input_line_pointer == '\n') + as_bad (_(".seh_proc requires function label name")); + + + if (!seh_ctx_root) + { + seh_ctx_root = XCNEW (seh_context); + seh_ctx_cur = seh_ctx_root; + } + else + { + seh_ctx_cur->next = XCNEW (seh_context); + seh_ctx_cur = seh_ctx_cur->next; + } + + seh_ctx_cur->next = NULL; + seh_ctx_cur->code_seg = now_seg; + + /* The current implementation always use a pair of .pdata and .xdata + records. */ + const bool use_xdata = true; + + if (use_xdata) + { + x_segcur = seh_hash_find_or_make (seh_ctx_cur->code_seg, ".xdata"); + seh_ctx_cur->subsection = x_segcur->subseg; + x_segcur->subseg += 2; + + /* Initialize an empty .xdata record. */ + seh_ctx_cur->unwind_codes_count = 0; + seh_ctx_cur->unwind_codes_byte_count = 0; + seh_ctx_cur->epilogue_scopes_count = 0; + seh_ctx_cur->epilogue_scopes_capacity = 0; + seh_ctx_cur->epilogue_scopes = NULL; + seh_ctx_cur->xdata_header.x = 0; + } + + SKIP_WHITESPACE (); + + name_end = get_symbol_name (&symbol_name); + seh_ctx_cur->func_name = xstrdup (symbol_name); + (void) restore_line_pointer (name_end); + + demand_empty_rest_of_line (); + + seh_ctx_cur->start_addr = symbol_temp_new_now (); + in_seh_proc = true; +} + +/* Mark end of prologue for current context. */ +static void +obj_coff_seh_endprologue (const int what ATTRIBUTE_UNUSED) +{ + if (!verify_context (".seh_endprologue") + || !seh_validate_seg (".seh_endprologue")) + return; + demand_empty_rest_of_line (); + + if (seh_ctx_cur->endprologue_addr != NULL) + as_warn (_("duplicate .seh_endprologue in .seh_proc block")); + else + seh_ctx_cur->endprologue_addr = symbol_temp_new_now (); + + /* Unwind codes need to be reversed. */ + for (unsigned i = 0, n = seh_ctx_cur->unwind_codes_count; i < n / 2; ++i) + { + seh_aarch64_unwind_code *unwind_codes = seh_ctx_cur->unwind_codes; + const seh_aarch64_unwind_code temp = unwind_codes[i]; + unwind_codes[i] = unwind_codes[n-i-1]; + unwind_codes[n-i-1] = temp; + } + + seh_aarch64_add_unwind_element (unwind_end, 0, 0); +} + +/* Mark end of current context. */ +static void +obj_coff_seh_endproc (const int what ATTRIBUTE_UNUSED) +{ + demand_empty_rest_of_line (); + if (!in_seh_proc) + as_bad (_(".seh_endproc used without .seh_proc")); + + seh_validate_seg (".seh_endproc"); + + seh_ctx_cur->end_addr = symbol_temp_new_now (); + in_seh_proc = false; +} + +static void +obj_coff_seh_startepilogue (const int what ATTRIBUTE_UNUSED) +{ + if (!verify_context (".seh_startepilogue") + || !seh_validate_seg (".seh_startepilogue")) + return; + demand_empty_rest_of_line (); + + const unsigned max_epilogue_scopes = AARCH64_MAX_EPILOGUE_SCOPES; + if (seh_ctx_cur->epilogue_scopes_count >= max_epilogue_scopes) + as_bad (_("no epilogue scopes available.")); + + symbolS *epilogue_start_addr = symbol_temp_new_now (); + expressionS exp; + exp.X_op = O_subtract; + exp.X_add_symbol = epilogue_start_addr; + exp.X_op_symbol = seh_ctx_cur->start_addr; + exp.X_add_number = 0; + + if (!resolve_expression (&exp) || exp.X_op != O_constant + || exp.X_add_number < 0) + as_bad (_(".seh_startepilog offset expression for %s " + "does not evaluate to a non-negative constant"), + S_GET_NAME (epilogue_start_addr)); + + if (seh_ctx_cur->epilogue_scopes_count + >= seh_ctx_cur->epilogue_scopes_capacity) + { + const unsigned initial_capacity = 32; + if (seh_ctx_cur->epilogue_scopes_capacity) + seh_ctx_cur->epilogue_scopes_capacity *= 2; + else + seh_ctx_cur->epilogue_scopes_capacity = initial_capacity; + + seh_ctx_cur->epilogue_scopes + = XRESIZEVEC (seh_aarch64_epilogue_scope, seh_ctx_cur->epilogue_scopes, + seh_ctx_cur->epilogue_scopes_capacity); + } + + seh_aarch64_epilogue_scope *epilogue_scope = seh_ctx_cur->epilogue_scopes + + seh_ctx_cur->epilogue_scopes_count; + epilogue_scope->epilogue_start_offset = exp.X_add_number / 4; + epilogue_scope->reserved = 0; + epilogue_scope->epilogue_start_index = seh_ctx_cur->unwind_codes_byte_count; + seh_ctx_cur->epilogue_scopes_count++; +} + +static void +obj_coff_seh_endepilogue (const int what ATTRIBUTE_UNUSED) +{ + if (!verify_context (".seh_endepilogue") + || !seh_validate_seg (".seh_endepilogue")) + return; + + demand_empty_rest_of_line (); + + expressionS exp; + symbolS *epilogue_end_addr = symbol_temp_new_now (); + exp.X_op = O_subtract; + exp.X_add_symbol = epilogue_end_addr; + exp.X_op_symbol = seh_ctx_cur->start_addr; + exp.X_add_number = 0; + + if (!resolve_expression (&exp) || exp.X_op != O_constant + || exp.X_add_number < 0) + as_bad (_(".seh_endepilogue offset expression for %s " + "does not evaluate to a non-negative constant"), + S_GET_NAME (epilogue_end_addr)); + + seh_aarch64_epilogue_scope *epilogue_scope = seh_ctx_cur->epilogue_scopes + + seh_ctx_cur->epilogue_scopes_count - 1; + + epilogue_scope->epilogue_end_offset = exp.X_add_number; + + /* End code. */ + seh_aarch64_add_unwind_element (unwind_end, 0, 0); +} + +/* End-of-file hook. */ +static void +free_seh_ctx (struct seh_aarch64_context *seh_ctx) +{ + free (seh_ctx->func_name); + const seh_aarch64_func_fragment *fragment = seh_ctx->func_fragment.next; + while (fragment) + { + const seh_aarch64_func_fragment *next = fragment->next; + XDELETE (fragment); + fragment = next; + } + XDELETEVEC (seh_ctx->epilogue_scopes); + free (seh_ctx); +} + +static void +obj_coff_seh_save_reg (const int type) +{ + gas_assert (type >= 0 && type <= unwind_last_type); + + const struct aarch64_unwind_code_pack_info *unwind_code_pack_info + = aarch64_unwind_code_pack_data + type; + + if (!unwind_code_pack_info->directive + || !seh_validate_seg (unwind_code_pack_info->directive)) + return; + + SKIP_WHITESPACE (); + + char *symbol_name = NULL; + unsigned reg = -1; + + if (unwind_code_pack_info->reg_bits) + { + char name_end = get_symbol_name (&symbol_name); + reg = atoi (symbol_name + 1); + (void) restore_line_pointer (name_end); + + if (!skip_whitespace_and_comma (1)) + return; + + if (reg > 30) + as_bad (_("register number is out of range")); + } + + offsetT off = -1; + if (unwind_code_pack_info->offset_bits) + { + off = get_absolute_expression (); + + if (off < 0) + as_bad (_("offset is negative")); + } + + demand_empty_rest_of_line (); + + seh_aarch64_add_unwind_element (type, off, reg); +} + +/* Add a stack-allocation token to current context. */ +static void +obj_coff_seh_stackalloc (const int what ATTRIBUTE_UNUSED) +{ + const offsetT off = get_absolute_expression (); + demand_empty_rest_of_line (); + + /* aarch64 offset should be encoded in multiples of sixteen. */ + if ((off & 0xf) != 0) + as_bad (_(".seh_stackalloc offset < 16-byte stack alignment")); + + if (off < 0x200) + seh_aarch64_add_unwind_element (unwind_alloc_s, off, 0); + else if (off < 0x8000) + seh_aarch64_add_unwind_element (unwind_alloc_m, off, 0); + else if (off < 0x10000000) + seh_aarch64_add_unwind_element (unwind_alloc_l, off, 0); + else + as_bad (_(".seh_stackalloc offset out of range")); +} + +/* Data writing routines. */ +static void +seh_aarch64_emit_epilog_scopes (const seh_context *seh_ctx, + const uint64_t fragment_offset, + const unsigned prolog_size, + const unsigned first_fragment_scope, + const unsigned last_fragment_scope, + const bool has_phantom_prolog) +{ + unsigned start_index_offset = 0; + const seh_aarch64_epilogue_scope *scopes = seh_ctx->epilogue_scopes; + if (first_fragment_scope < seh_ctx->epilogue_scopes_count) + start_index_offset = scopes[first_fragment_scope].epilogue_start_index + - prolog_size; + if (has_phantom_prolog) + { + if (start_index_offset == 0) + as_bad (_("start index offset for the epilogue cannot be 0 when " + "phantom prolog is used")); + --start_index_offset; + } + + for (unsigned i = first_fragment_scope; i < last_fragment_scope; ++i) + { + seh_aarch64_epilogue_scope scope = seh_ctx->epilogue_scopes[i]; + scope.epilogue_start_offset_reduced = (scope.epilogue_start_offset + - fragment_offset) >> 2; + scope.epilogue_start_index -= start_index_offset; + uint32_t scope_code; + memcpy (&scope_code, &scope, sizeof (scope_code)); + md_number_to_chars (frag_more (4), scope_code, 4); + } +} + +static void +seh_aarch64_emit_unwind_codes (const seh_context *seh_ctx, + const unsigned prolog_size, + const unsigned first_epilog_index, + const unsigned last_epilog_index, + const bool has_phantom_prolog) +{ + unsigned total_byte_count = 0; + + if (has_phantom_prolog) + { + ++total_byte_count; + md_number_to_chars (frag_more (1), AARCH64_UNOP_ENDC, 1); + } + + unsigned unwind_bytes_offset = 0; + for (unsigned i = 0; i < seh_ctx->unwind_codes_count; ++i) + { + const seh_aarch64_unwind_code *code = seh_ctx->unwind_codes + + i; + const unsigned byte_count + = aarch64_unwind_code_pack_data[code->type].size; + unwind_bytes_offset += byte_count; + + if (unwind_bytes_offset > last_epilog_index) + break; + + if (unwind_bytes_offset > prolog_size + && unwind_bytes_offset <= first_epilog_index) + continue; + + /* emit unwind code bytes in big endian. */ + number_to_chars_bigendian (frag_more (byte_count), code->value, + byte_count); + total_byte_count += byte_count; + } + + /* handle word alignment. */ + unsigned required_padding = (4 - total_byte_count % 4) % 4; + if (required_padding) + { + /* Use AARCH64_UNOP_NOP for alignment. */ + const uint32_t nop_chain = (AARCH64_UNOP_NOP << 24) + | (AARCH64_UNOP_NOP << 16) + | (AARCH64_UNOP_NOP << 8) + | AARCH64_UNOP_NOP; + + md_number_to_chars (frag_more (required_padding), nop_chain, + required_padding); + } +} + +static bool +seh_function_size (const struct seh_aarch64_context *seh_ctx, + uintptr_t *size) +{ + fragS *start_frag, *end_frag; + addressT start_offset, end_offset; + start_frag = symbol_get_frag_and_value (seh_ctx->start_addr, &start_offset); + end_frag = symbol_get_frag_and_value (seh_ctx->end_addr, &end_offset); + + intptr_t func_size = end_frag->fr_address + end_offset + - start_frag->fr_address - start_offset; + if (func_size < 0) + return false; + + *size = func_size; + return true; +} + +/* Write out the xdata information for one function. */ +static void +seh_aarch64_write_function_xdata (struct seh_aarch64_context *seh_ctx) +{ + if (!seh_ctx->unwind_codes_byte_count) + return; + + const segT save_seg = now_seg; + const subsegT save_subseg = now_subseg; + + switch_xdata (seh_ctx->subsection, seh_ctx->code_seg); + + /* Set 4-byte alignment. */ + frag_align (2, 0, 0); + + uintptr_t func_size = 0; + if (!seh_function_size (seh_ctx, &func_size)) + { + as_bad (_("the function size for %s has not been evaluated"), + seh_ctx->func_name); + return; + } + + /* The large functions should be split into fragments smaller than 1MB with + 4 bytes alignment. + https://learn.microsoft.com/en-us/cpp/build/arm64-exception-handling?view=msvc-170#large-functions. */ + const unsigned max_frag_size = (1 << 20) - 4; + const bool is_fragmented_function = func_size > max_frag_size; + + /* [first_fragment_scope, last_fragment_scope). */ + unsigned prolog_insn_count = 0; + for (unsigned i = 0; i < seh_ctx->unwind_codes_count; ++i) + { + if (seh_ctx->unwind_codes[i].type == unwind_end) + { + prolog_insn_count = i + 1; + break; + } + } + + unsigned prolog_size; + if (seh_ctx->epilogue_scopes_count) + prolog_size = seh_ctx->epilogue_scopes[0].epilogue_start_index; + else + prolog_size = seh_ctx->unwind_codes_byte_count; + + seh_aarch64_func_fragment *fragment; + fragment = &seh_ctx->func_fragment; + uintptr_t fragment_offset = 0; + unsigned first_fragment_scope = 0; + unsigned last_fragment_scope = 0; + while (true) + { + fragment->xdata_addr = symbol_temp_new_now (); + fragment->offset = fragment_offset; + fragment->next = NULL; + + uintptr_t frag_size = func_size - fragment_offset; + if (frag_size > max_frag_size) + frag_size = max_frag_size; + + const bool is_first_frag = fragment_offset == 0; + const bool is_last_frag = (fragment_offset + frag_size) == func_size; + + if (!is_fragmented_function) + last_fragment_scope = seh_ctx->epilogue_scopes_count; + else + { + first_fragment_scope = last_fragment_scope; + for (unsigned i = first_fragment_scope; + i < seh_ctx->epilogue_scopes_count; ++i) + { + const seh_aarch64_epilogue_scope *scope + = seh_ctx->epilogue_scopes; + scope += i; + if (scope->epilogue_start_offset >= (fragment_offset + frag_size)) + break; + + if (scope->epilogue_end_offset >= (fragment_offset + frag_size)) + { + frag_size = scope->epilogue_start_offset - fragment_offset; + break; + } + + if (scope->epilogue_start_offset >= fragment_offset) + last_fragment_scope = i + 1; + } + } + + seh_aarch64_xdata_header *header = &seh_ctx->xdata_header; + const seh_aarch64_epilogue_scope *scopes = seh_ctx->epilogue_scopes; + + const uint32_t func_length_encoded = frag_size >> 2; + header->func_length = func_length_encoded; + header->vers = 0; + header->e = 0; + header->code_words = 0; + header->epilogue_count = 0; + + header->ext_code_words = 0; + header->ext_epilogue_count = last_fragment_scope + - first_fragment_scope; + header->reserved = 0; + + unsigned first_epilog_index = 0; + unsigned last_epilog_index = 0; + if (!header->ext_epilogue_count) + { + first_epilog_index = prolog_size; + last_epilog_index = prolog_size; + } + else + { + const seh_aarch64_epilogue_scope *scope; + scope = scopes + first_fragment_scope; + first_epilog_index = scope->epilogue_start_index; + if (last_fragment_scope == seh_ctx->epilogue_scopes_count) + last_epilog_index = seh_ctx->unwind_codes_byte_count; + else + { + scope = scopes + last_fragment_scope; + last_epilog_index = scope->epilogue_start_index; + } + } + + unsigned unwind_bytes = 0; + if (is_first_frag || is_last_frag) + unwind_bytes += prolog_size; + + if (header->ext_epilogue_count) + unwind_bytes += last_epilog_index - first_epilog_index; + + const bool has_phantom_prolog = is_fragmented_function && is_last_frag; + if (has_phantom_prolog && unwind_bytes) + { + /* One more epilogue scope and unwind code are emitted with phantom + prolog. */ + unwind_bytes += 1; + ++header->ext_epilogue_count; + } + + /* Calculate the number of code words with 4-byte alignment. */ + header->ext_code_words = (unwind_bytes + 3) / 4; + + if ((header->ext_code_words == 0 && header->ext_epilogue_count == 0) + || header->ext_code_words > 31 + || header->ext_epilogue_count > 31) + md_number_to_chars (frag_more (8), + seh_ctx->xdata_header_value, 8); + else + { + header->code_words = header->ext_code_words; + header->epilogue_count = header->ext_epilogue_count; + if (header->epilogue_count == 1) + { + header->e = 1; + if (has_phantom_prolog) + header->ext_epilogue_count = 0; + else + { + const seh_aarch64_epilogue_scope *scope; + scope = scopes + first_fragment_scope; + header->ext_epilogue_count = scope->epilogue_start_index; + } + } + md_number_to_chars (frag_more (4), + seh_ctx->xdata_header_value, 4); + } + + if (header->ext_epilogue_count && !header->e) + { + seh_aarch64_emit_epilog_scopes (seh_ctx, + fragment_offset, prolog_size, + first_fragment_scope, + last_fragment_scope, + has_phantom_prolog); + if (has_phantom_prolog) + { + const uint32_t epilog_start_index_encoded = 1 << 22; + const uint32_t epilog_start_offset_encoded + = (frag_size - prolog_insn_count * 4) >> 2; + md_number_to_chars (frag_more (4), + epilog_start_index_encoded + | epilog_start_offset_encoded, 4); + } + } + + if (header->ext_code_words) + seh_aarch64_emit_unwind_codes (seh_ctx, prolog_size, first_epilog_index, + last_epilog_index, has_phantom_prolog); + + if (header->x == 1) + { + if (seh_ctx->handler.X_op == O_symbol) + seh_ctx->handler.X_op = O_symbol_rva; + + emit_expr (&seh_ctx->handler, 4); + } + + fragment_offset += frag_size; + if (fragment_offset == func_size) + break; + + fragment->next = XCNEW (seh_aarch64_func_fragment); + fragment = fragment->next; + } + + subseg_set (save_seg, save_subseg); +} + +/* Write out pdata for one function. */ +static void +seh_aarch64_write_function_pdata (const seh_context *seh_ctx) +{ + expressionS exp; + const segT save_seg = now_seg; + const subsegT save_subseg = now_subseg; + memset (&exp, 0, sizeof (expressionS)); + switch_pdata (seh_ctx->code_seg); + + if (seh_ctx->unwind_codes_byte_count) + { + const seh_aarch64_func_fragment *fragment = &seh_ctx->func_fragment; + while (fragment) + { + exp.X_op = O_symbol_rva; + exp.X_add_number = fragment->offset; + exp.X_add_symbol = seh_ctx->start_addr; + emit_expr (&exp, 4); + + exp.X_op = O_symbol_rva; + /* TODO: Implementing packed unwind data. */ + exp.X_add_number = 0; + exp.X_add_symbol = fragment->xdata_addr; + emit_expr (&exp, 4); + fragment = fragment->next; + } + } + + subseg_set (save_seg, save_subseg); +} + +void +seh_aarch64_write_data (void) +{ + if (in_seh_proc) + as_bad (_("open SEH entry at end of file (missing .seh_endproc)")); + + if (!seh_ctx_root) + return; + + struct seh_aarch64_context *seh_ctx = seh_ctx_root; + seh_ctx_root = NULL; + + /* Relax the segment to be able to calculate the function sizes. */ + subsegs_finish_section (seh_ctx->code_seg); + const segment_info_type *seginfo = seg_info (seh_ctx->code_seg); + relax_segment (seginfo->frchainP->frch_root, seh_ctx->code_seg, 0); + + while (seh_ctx) + { + seh_aarch64_write_function_xdata (seh_ctx); + seh_aarch64_write_function_pdata (seh_ctx); + struct seh_aarch64_context *next = seh_ctx->next; + free_seh_ctx (seh_ctx); + seh_ctx = next; + } +} + +void +obj_coff_seh_do_final (void) +{ +} diff --git a/gas/config/obj-coff-seh-aarch64.h b/gas/config/obj-coff-seh-aarch64.h new file mode 100644 index 00000000000..bda7e00adc5 --- /dev/null +++ b/gas/config/obj-coff-seh-aarch64.h @@ -0,0 +1,265 @@ +/* SEH .pdata/.xdata COFF object file format on AArch64 + Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of GAS. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GAS; see the file COPYING. If not, write to the Free + Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/* SEH COFF AArch64 implementation partially intersects with the x64 + version, however it has a different extension to the unwind codes. + It emits SEH data to pdata and xdata sections. In some cases SEH + data could be emitted to a packed record in the pdata section + without the need for data in the xdata section. However, the packed + pdata record is not implemented yet. */ + +#ifndef OBJ_COFF_SEH_AARCH64_H +#define OBJ_COFF_SEH_AARCH64_H + +typedef enum seh_aarch64_unwind_types +{ + unwind_alloc_s, + unwind_alloc_m, + unwind_alloc_l, + unwind_save_reg, + unwind_save_reg_x, + unwind_save_regp, + unwind_save_regp_x, + unwind_save_fregp, + unwind_save_fregp_x, + unwind_save_freg, + unwind_save_freg_x, + unwind_save_lrpair, + unwind_save_fplr, + unwind_save_fplr_x, + unwind_save_r19r20_x, + unwind_add_fp, + unwind_set_fp, + unwind_save_next, + unwind_nop, + unwind_pac_sign_lr, + unwind_end, + unwind_end_c, + unwind_last_type = unwind_end_c +} seh_aarch64_unwind_types; + +#define SEH_CMDS \ + /* Start a function that contains SEH. */ \ + {"seh_proc", obj_coff_seh_proc, 0}, \ + \ + /* End a function that contains SEH. */ \ + {"seh_endproc", obj_coff_seh_endproc, 0}, \ + \ + /* End a SEH prolog with unwinding codes. */ \ + {"seh_endprologue", obj_coff_seh_endprologue, 0}, \ + \ + /* Allocate stack. */ \ + {"seh_stackalloc", obj_coff_seh_stackalloc, 0}, \ + \ + /* Set a SEH handler. */ \ + {"seh_handler", obj_coff_seh_handler, 0}, \ + \ + /* Set a SEH handler data. */ \ + {"seh_handlerdata", obj_coff_seh_handlerdata, 0}, \ + \ + /* Start a SEH epilogue. */ \ + {"seh_startepilogue", obj_coff_seh_startepilogue, 0}, \ + \ + /* End a SEH epilogue. */ \ + {"seh_endepilogue", obj_coff_seh_endepilogue, 0}, \ + \ + /* Save an 'x' register. */ \ + {"seh_save_reg", obj_coff_seh_save_reg, unwind_save_reg}, \ + \ + /* Save an 'x' register with a pre-indexed offset. */ \ + {"seh_save_reg_x", obj_coff_seh_save_reg, unwind_save_reg_x}, \ + \ + /* Save an 'x' register pair. */ \ + {"seh_save_regp", obj_coff_seh_save_reg, unwind_save_regp}, \ + \ + /* Save an 'x' register pair with a pre-indexed offset. */ \ + {"seh_save_regp_x", obj_coff_seh_save_reg, unwind_save_regp_x}, \ + \ + /* Save an 'x' register and lr. */ \ + {"seh_save_lrpair", obj_coff_seh_save_reg, unwind_save_lrpair}, \ + \ + /* Save a 'd' register pair. */ \ + {"seh_save_fregp", obj_coff_seh_save_reg, unwind_save_fregp}, \ + \ + /* Save a 'd' register pair with a pre-indexed offset. */ \ + {"seh_save_fregp_x", obj_coff_seh_save_reg, unwind_save_fregp_x}, \ + \ + /* Save a 'd' register. */ \ + {"seh_save_freg", obj_coff_seh_save_reg, unwind_save_freg}, \ + \ + /* Save a 'd' register with a pre-indexed offset. */ \ + {"seh_save_freg_x", obj_coff_seh_save_reg, unwind_save_freg_x}, \ + \ + /* Save fp and lr registers. */ \ + {"seh_save_fplr", obj_coff_seh_save_reg, unwind_save_fplr}, \ + \ + /* Save fp and lr registers with a pre-indexed offset. */ \ + {"seh_save_fplr_x", obj_coff_seh_save_reg, unwind_save_fplr_x}, \ + \ + /* Save x19 and x20 registers with a pre-indexed offset. */ \ + {"seh_save_r19r20_x", obj_coff_seh_save_reg, unwind_save_r19r20_x}, \ + \ + /* Set fp by sp + offset. */ \ + {"seh_add_fp", obj_coff_seh_save_reg, unwind_add_fp}, \ + \ + /* Unwind operation is not required. */ \ + {"seh_nop", obj_coff_seh_save_reg, unwind_nop}, \ + \ + /* Sign the return address in lr with pacibsp. */ \ + {"seh_pac_sign_lr", obj_coff_seh_save_reg, unwind_pac_sign_lr}, \ + \ + /* Set fp by sp. */ \ + {"seh_set_fp", obj_coff_seh_save_reg, unwind_set_fp}, \ + \ + /* Save next register pair. */ \ + {"seh_save_next", obj_coff_seh_save_reg, unwind_save_next}, + +/* AArch64 exceptions handling and unwinding structures. + https://learn.microsoft.com/en-us/cpp/build/arm64-exception-handling#pdata-records. */ + +typedef struct seh_aarch64_unwind_code +{ + unsigned value; + seh_aarch64_unwind_types type; +} seh_aarch64_unwind_code; + +typedef struct seh_aarch64_packed_unwind_data +{ + uint32_t flag : 2; + uint32_t func_length : 11; + uint32_t frame_size : 9; + uint32_t cr : 2; + uint32_t h : 1; + uint32_t regI : 4; + uint32_t regF : 3; +} seh_aarch64_packed_unwind_data; + +typedef struct seh_aarch64_except_info +{ + uint32_t flag : 2; + uint32_t except_info_rva : 30; +} seh_aarch64_except_info; + +typedef union seh_aarch64_unwind_info +{ + seh_aarch64_except_info except_info; + seh_aarch64_packed_unwind_data packed_unwind_data; +} seh_aarch64_unwind_info; + +typedef struct seh_aarch64_xdata_header +{ + uint32_t func_length : 18; + uint32_t vers : 2; + uint32_t x : 1; + uint32_t e : 1; + uint32_t epilogue_count : 5; + uint32_t code_words : 5; + uint32_t ext_epilogue_count : 16; + uint32_t ext_code_words : 8; + uint32_t reserved : 8; +} seh_aarch64_xdata_header; + +typedef struct seh_aarch64_epilogue_scope +{ + uint32_t epilogue_start_offset_reduced : 18; + uint32_t reserved : 4; + uint32_t epilogue_start_index : 10; + bfd_vma epilogue_start_offset; + bfd_vma epilogue_end_offset; +} seh_aarch64_epilogue_scope; + +typedef struct seh_aarch64_func_fragment +{ + bfd_vma offset; + symbolS *xdata_addr; + struct seh_aarch64_func_fragment *next; +} seh_aarch64_func_fragment; + +/* AARCH64_MAX_UNWIND_CODES is limited by + seh_aarch64_xdata_header::ext_code_words. */ +#define AARCH64_MAX_UNWIND_CODES (255 * 4) +#define AARCH64_MAX_UNWIND_CODES_SIZE (255 * 4) +/* AARCH64_MAX_EPILOGUE_SCOPES is limited by + seh_aarch64_xdata_header::ext_epilogue_count. */ +#define AARCH64_MAX_EPILOGUE_SCOPES 65535 + +typedef struct seh_aarch64_context +{ + struct seh_aarch64_context *next; + + /* Initial code-segment. */ + segT code_seg; + /* Function name. */ + char *func_name; + /* BeginAddress. */ + symbolS *start_addr; + /* EndAddress. */ + symbolS *end_addr; + /* PrologueEnd. */ + symbolS *endprologue_addr; + /* ExceptionHandler. */ + expressionS handler; + /* ExceptionHandlerData. */ + expressionS handler_data; + + subsegT subsection; + + union { + seh_aarch64_xdata_header xdata_header; + valueT xdata_header_value; + }; + unsigned unwind_codes_count; + unsigned unwind_codes_byte_count; + seh_aarch64_unwind_code unwind_codes[AARCH64_MAX_UNWIND_CODES]; + unsigned epilogue_scopes_count; + unsigned epilogue_scopes_capacity; + seh_aarch64_epilogue_scope *epilogue_scopes; + expressionS except_handler; + expressionS except_handler_data; + /* The function fragments. */ + seh_aarch64_func_fragment func_fragment; +} seh_context; + +/* aarch64 unwind code prefixes. */ + +#define AARCH64_UNOP_ALLOCS 0b000U +#define AARCH64_UNOP_SAVER19R20X 0b001U +#define AARCH64_UNOP_SAVEFPLR 0b01U +#define AARCH64_UNOP_SAVEFPLRX 0b10U +#define AARCH64_UNOP_ALLOCM 0b11000U +#define AARCH64_UNOP_SAVEREGP 0b110010U +#define AARCH64_UNOP_SAVEREGPX 0b110011U +#define AARCH64_UNOP_SAVEREG 0b110100U +#define AARCH64_UNOP_SAVEREGX 0b1101010U +#define AARCH64_UNOP_SAVELRPAIR 0b1101011U +#define AARCH64_UNOP_SAVEFREGP 0b1101100U +#define AARCH64_UNOP_SAVEFREGPX 0b1101101U +#define AARCH64_UNOP_SAVEFREG 0b1101110U +#define AARCH64_UNOP_SAVEFREGX 0b11011110U +#define AARCH64_UNOP_ALLOCL 0b11100000U +#define AARCH64_UNOP_SETFP 0b11100001U +#define AARCH64_UNOP_ADDFP 0b11100010U +#define AARCH64_UNOP_NOP 0b11100011U +#define AARCH64_UNOP_END 0b11100100U +#define AARCH64_UNOP_ENDC 0b11100101U +#define AARCH64_UNOP_SAVENEXT 0b11100110U +#define AARCH64_UNOP_PACSIGNLR 0b11111100U + +#endif /* OBJ_COFF_SEH_AARCH64_H. */ diff --git a/gas/config/obj-coff-seh-shared.c b/gas/config/obj-coff-seh-shared.c index f3afd981aca..132470c70e6 100644 --- a/gas/config/obj-coff-seh-shared.c +++ b/gas/config/obj-coff-seh-shared.c @@ -19,8 +19,13 @@ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ +#if defined (COFFAARCH64) +#include "obj-coff-seh-aarch64.h" +typedef struct seh_aarch64_context seh_context_t; +#else #include "obj-coff-seh.h" typedef struct seh_context seh_context_t; +#endif /* Private segment collection list. */ struct seh_seg_list { diff --git a/gas/config/obj-coff.c b/gas/config/obj-coff.c index 7732c0af911..b08295019d8 100644 --- a/gas/config/obj-coff.c +++ b/gas/config/obj-coff.c @@ -55,7 +55,11 @@ static const char weak_altprefix[] = ".weak."; #endif /* TE_PE */ #include "obj-coff-seh-shared.c" +#if defined (COFFAARCH64) +#include "obj-coff-seh-aarch64.c" +#else #include "obj-coff-seh.c" +#endif typedef struct { diff --git a/gas/config/tc-aarch64.c b/gas/config/tc-aarch64.c index cd76163488c..d9d3296a458 100644 --- a/gas/config/tc-aarch64.c +++ b/gas/config/tc-aarch64.c @@ -10375,6 +10375,16 @@ aarch64_cleanup (void) } } +#if defined (OBJ_COFF) +/* Called after all assembly has been done. */ + +void +aarch64_md_finish (void) +{ + seh_aarch64_write_data(); +} +#endif /* OBJ_COFF. */ + #ifdef OBJ_ELF /* Remove any excess mapping symbols generated for alignment frags in SEC. We may have created a mapping symbol before a zero byte diff --git a/gas/config/tc-aarch64.h b/gas/config/tc-aarch64.h index d1fb4c9058b..67f476a3dcc 100644 --- a/gas/config/tc-aarch64.h +++ b/gas/config/tc-aarch64.h @@ -82,6 +82,11 @@ struct aarch64_fix #define tc_frob_section(S) aarch64_frob_section (S) +#if defined (OBJ_COFF) +#define md_finish aarch64_md_finish +extern void aarch64_md_finish (void); +#endif + /* The key used to sign a function's return address. */ enum pointer_auth_key { AARCH64_PAUTH_KEY_A, @@ -363,6 +368,7 @@ extern void aarch64_handle_align (struct frag *); extern int tc_aarch64_regname_to_dw2regnum (char *regname); extern void tc_aarch64_frame_initial_instructions (void); extern bool aarch64_fix_adjustable (struct fix *); +extern void seh_aarch64_write_data (void); #ifdef TE_PE diff --git a/gas/write.c b/gas/write.c index 9d0777051dd..903f858b4dc 100644 --- a/gas/write.c +++ b/gas/write.c @@ -1831,7 +1831,7 @@ set_symtab (void) #endif #endif -static void +void subsegs_finish_section (asection *s) { struct frchain *frchainP; diff --git a/gas/write.h b/gas/write.h index 7281930c2d6..0638e5958d6 100644 --- a/gas/write.h +++ b/gas/write.h @@ -188,5 +188,6 @@ extern fixS *fix_new_exp (fragS *, unsigned long, unsigned long, const expressionS *, int, bfd_reloc_code_real_type); extern void write_print_statistics (FILE *); extern void as_bad_subtract (fixS *); +extern void subsegs_finish_section (asection *); #endif /* __write_h__ */