From patchwork Fri Jun 27 13:38:12 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jens Remus X-Patchwork-Id: 115199 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 2737F3857BB6 for ; Fri, 27 Jun 2025 13:43:46 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 2737F3857BB6 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=ibm.com header.i=@ibm.com header.a=rsa-sha256 header.s=pp1 header.b=HG6WRII8 X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mx0a-001b2d01.pphosted.com (mx0a-001b2d01.pphosted.com [148.163.156.1]) by sourceware.org (Postfix) with ESMTPS id 828763857015 for ; Fri, 27 Jun 2025 13:39:17 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 828763857015 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=linux.ibm.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=linux.ibm.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 828763857015 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=148.163.156.1 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1751031557; cv=none; b=VaFJxJ7NNCuD1bzmY/nw1f/nD/WE04JpiW0/rl92hH8mZ6uuA6BlGOIhRbAohJiYhNwtWRpivp0uPU1TWaJsJf2JZf+G8bllU548z4C7kcXbHvnaCMyRtsXsqboYOC3U46CaoJu9A8ywh9DeGgzK13N+3P8YCa3LAE1OoSpyysU= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1751031557; c=relaxed/simple; bh=vtPV9jxc1lisfWXGz2BevKL2kKn9uSChTgW0tDWAVHM=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=aq1xPfqDOPgpTs/zK57UszAy0rOd7712nTl7LSlya1V3knqBumouB8EOeXkjGY3D9WthjSlIfhGG41RbbZfV97s2Y2GEb26G3oReQ2RWOwqfyaHFfjEv6yqoH5oHVCKf4l1qNJV0P/dIPAQ7bAa7FKACsTiANS+78EReQ99an+c= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 828763857015 Received: from pps.filterd (m0360083.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 55RBGoxR015880; Fri, 27 Jun 2025 13:38:30 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ibm.com; h=cc :content-transfer-encoding:date:from:in-reply-to:message-id :mime-version:references:subject:to; s=pp1; bh=C+CwQqdvAPoO9ZNcG 66cC6UXZeunfNlkRSZ4F0fyNEw=; b=HG6WRII8TBJp4NQNAqzUmHMtylAA4iMGr KB0/Q38OgYx+VPAJrGLtmOEQwUxGw6f9UhWJbabrcY58+IEzBlsbjhf4jBzuYqmU qSY68Adw7ZUh4V9+0siZU26lo5EeKJYiVpwp0OHsyAb7qzzJPM0mw6Lr7XYSV/vB D54SRsFtiLJ9R17W4W7mVt/Mw1Sw03YmASGVJQ+pR9P8W0Cc0CfU2AtWPbtVg25b IGNWNKV6kRMTMNnLun9P1/Ul6IlBPeonTE6guaaaHOcVLNwigbVN/eIVS0mtZioO e675+ECF2aoO5J0ZPzgm8HnL0zBc+7a8FyDve/AKILErNMUKIPdwA== Received: from ppma22.wdc07v.mail.ibm.com (5c.69.3da9.ip4.static.sl-reverse.com [169.61.105.92]) by mx0a-001b2d01.pphosted.com (PPS) with ESMTPS id 47dm8jwpwf-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 27 Jun 2025 13:38:30 +0000 (GMT) Received: from pps.filterd (ppma22.wdc07v.mail.ibm.com [127.0.0.1]) by ppma22.wdc07v.mail.ibm.com (8.18.1.2/8.18.1.2) with ESMTP id 55RCVkbL031277; Fri, 27 Jun 2025 13:38:29 GMT Received: from smtprelay07.fra02v.mail.ibm.com ([9.218.2.229]) by ppma22.wdc07v.mail.ibm.com (PPS) with ESMTPS id 47e7f0ceny-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 27 Jun 2025 13:38:28 +0000 Received: from smtpav04.fra02v.mail.ibm.com (smtpav04.fra02v.mail.ibm.com [10.20.54.103]) by smtprelay07.fra02v.mail.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id 55RDcPuP47448530 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Fri, 27 Jun 2025 13:38:25 GMT Received: from smtpav04.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 02F8720040; Fri, 27 Jun 2025 13:38:25 +0000 (GMT) Received: from smtpav04.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id CB6CA2004D; Fri, 27 Jun 2025 13:38:24 +0000 (GMT) Received: from tuxmaker.lnxne.boe (unknown [9.152.85.9]) by smtpav04.fra02v.mail.ibm.com (Postfix) with ESMTP; Fri, 27 Jun 2025 13:38:24 +0000 (GMT) From: Jens Remus To: libc-alpha@sourceware.org, Stefan Liebler , Claudiu Zissulescu-Ianculescu , Florian Weimer Cc: Jens Remus , Ulrich Weigand , Indu Bhagat , Elena Zannoni Subject: [RFC PATCH v2 1/2] elf: Start SFrame stack trace from PC and CFA Date: Fri, 27 Jun 2025 15:38:12 +0200 Message-ID: <20250627133813.2198784-2-jremus@linux.ibm.com> X-Mailer: git-send-email 2.48.1 In-Reply-To: <20250627133813.2198784-1-jremus@linux.ibm.com> References: <20250627133813.2198784-1-jremus@linux.ibm.com> MIME-Version: 1.0 X-TM-AS-GCONF: 00 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjUwNjI3MDExMCBTYWx0ZWRfX6KltGYSP6tPo H4BEGTj3o6PlpA81BLYbeqO1XQqxPFxm8M4lYdbpvWM19Mf/H2shW0heyDS9/A4vYpHAShOzSsB rkKF2r6ueUh2wKlHANTrrL9vus3Zb7l/uFsBqqkM1BjVOjeY1EZ2/1iB5/44YFfkQ2qMp5tIxb5 Wt8rvRtkaj+qq1UmNd5PuZd4+LDFCphBr7e+n9bgaD6hDZfXphbHk7zSf7Cpbn+HAk66ZGrBcOG n5e+5niUhd1azZcQrH2ezt+epRSn64xGlc/Wg8R25tLeGCiUA0THl/v8zIlUqYT0J0HKrvllimx 2wzHXsFQjCKPxzkPQg+Der2PaqItqHDEgItKLrlmin+LXrV/7JEqlpOTB+2GOXPt4zJmWhXzhFC Bb3hitjoAXG6zsJsOACwvIGWvO0h7nWbtYL6l3XWihuvJLftXr0kUVss4kT2rYJweVTZ2p0t X-Proofpoint-GUID: _ssZjVp-MtRLHbAoy4uM41XCq8sQ6hAf X-Proofpoint-ORIG-GUID: _ssZjVp-MtRLHbAoy4uM41XCq8sQ6hAf X-Authority-Analysis: v=2.4 cv=combk04i c=1 sm=1 tr=0 ts=685e9ed6 cx=c_pps a=5BHTudwdYE3Te8bg5FgnPg==:117 a=5BHTudwdYE3Te8bg5FgnPg==:17 a=6IFa9wvqVegA:10 a=VnNF1IyMAAAA:8 a=sHT5ADAqCMadcyeYHTcA:9 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1099,Hydra:6.1.7,FMLib:17.12.80.40 definitions=2025-06-27_04,2025-06-26_05,2025-03-28_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 malwarescore=0 spamscore=0 adultscore=0 mlxlogscore=999 clxscore=1011 impostorscore=0 suspectscore=0 mlxscore=0 phishscore=0 lowpriorityscore=0 bulkscore=0 classifier=spam authscore=0 authtc=n/a authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.19.0-2505280000 definitions=main-2506270110 X-Spam-Status: No, score=-12.4 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org Stack tracing in backtrace using SFrame stack trace information starts with a PC (program counter) in backtrace and the related SP (stack pointer) and FP (frame pointer) values. The following are then derived using SFrame information: - CFA (Canonical Frame Address): Using the tracked CFA base register (SP or FP) and tracked CFA offset from CFA base register - SP: Using the CFA (the CFA is defined as SP at call site on most architectures) - RA (return address): Using the CFA and the tracked RA stack slot offset from CFA - FP: Using CFA and the tracked FP stack slot offset from CFA, otherwise it remains unchanged If the CFA is based on the FP or if the FP is not saved then the initial value of the FP is important. Either because it is required to determine the CFA (and from that the SP, FP, and RA) or because is not restored from its stack slot and thus needs to be known. On x86-64 and Aarch64 the initial FP (in backtrace) can be obtained using __builtin_frame_address (0). On s390-64 (s390x) this does not work as __builtin_frame_address (0) does not return the FP register value, even if a FP register is used (e.g. due to dynamic stack allocation). Instead __builtin_frame_address (0) always returns the SP register value at function entry. This is because GCC and Clang on s390x setup a FP register only as late as possible, for instance after static stack allocation, which makes the FP register value meaningless for FP-based stack tracing purposes. Stack tracing in backtrace always starts in backtrace itself. Therefore use __builtin_unwind_init to enforce saving of FP and RA, obtain the CFA using __builtin_dwarf_cfa, and determine the caller's SP, FP, and RA using that CFA and the SFrame FP and RA stack slot offsets, as those are now guaranteed to be saved. This avoids the need to obtain the initial SP and FP values at all. debug/ * backtrace.c (DO_SFRAME_BACKTRACE): Call __builtin_unwind_init, obtain the CFA using __builtin_dwarf_cfa, and initialize SP and FP with NULL. sysdeps/generic/ * sframe.h (frame): Add field cfa, used for startup of SFrame stack trace. (getSP): Remove helper. * sframe.c (stacktrace_sframe): Use CFA from frame in topmost frame to startup SFrame stack trace. Treat FP not saved in topmost frame as error. Update CFA in frame. (getSP): Remove helper. Suggested-by: Ulrich Weigand Signed-off-by: Jens Remus --- Notes: Following are two options to resolve the SFrame strack trace startup issue on s390 64-bit (s390x): Option A: Introduce a helper macro that returns the FP value. The generic implementation (used on e.g. x86-64 and AArch64) would resolve to __builtin_frame_address (0). This is why is needs to be a macro. The s390 implementation would resolve to a new inline helper function s390_getFP (), that uses inline assembly to return the preferred FP register r11 value. Option B: Use __builtin_unwind_init to enforce saving of SP and FP at function entry and use __builtin_dwarf_cfa instead of SFrame information to obtain the initial CFA in the topmost frame (i.e. backtrace). This no longer requires the initial SP and FP values. The SP, FP, and RA of the caller are then obtained using the CFA (SP) and SFrame information from the stack register save slots (FP and RA). This patch implements Option B. debug/backtrace.c | 6 ++++-- sysdeps/generic/sframe.c | 46 ++++++++++++++++++++++------------------ sysdeps/generic/sframe.h | 5 +---- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/debug/backtrace.c b/debug/backtrace.c index aca0b5ade9c2..d42b1cc6bbcb 100644 --- a/debug/backtrace.c +++ b/debug/backtrace.c @@ -45,9 +45,11 @@ struct trace_arg { \ int cnt; \ frame frame; \ + __builtin_unwind_init (); \ frame.pc = getPC(); \ - frame.sp = getSP(); \ - frame.fp = (_Unwind_Ptr) __builtin_frame_address (0); \ + frame.cfa = (_Unwind_Ptr) __builtin_dwarf_cfa (); \ + frame.sp = (_Unwind_Ptr) NULL; \ + frame.fp = (_Unwind_Ptr) NULL; \ cnt = stacktrace_sframe (ARRAY, SIZE, &frame); \ if (cnt > 1) \ return cnt; \ diff --git a/sysdeps/generic/sframe.c b/sysdeps/generic/sframe.c index abdda07defd4..e3d0cee27d6b 100644 --- a/sysdeps/generic/sframe.c +++ b/sysdeps/generic/sframe.c @@ -117,16 +117,24 @@ stacktrace_sframe (void **ra_lst, int count, frame *frame) return 0; } - /* Get the CFA offset from the FRE. If offset is unavailable, - sets err. */ - cfa_offset = sframe_fre_get_cfa_offset (dctx, frep, &err); - if (err != _URC_NO_REASON) - /* Force fallback to DWARF stacktracer. */ - return 0; - - /* Get CFA using base reg id from the FRE info. */ - cfa = ((sframe_fre_get_base_reg_id (frep) - == SFRAME_BASE_REG_SP) ? frame->sp : frame->fp) + cfa_offset; + if (i == 0) + /* The caller (i.e. backtrace) provided its CFA value and ensured + to save its FP and RA at entry, so that the caller's SP, FP, and + RA at entry can be determined using the CFA. */ + cfa = frame->cfa; + else + { + /* Get the CFA offset from the FRE. If offset is unavailable, + sets err. */ + cfa_offset = sframe_fre_get_cfa_offset (dctx, frep, &err); + if (err != _URC_NO_REASON) + /* Force fallback to DWARF stacktracer. */ + return 0; + + /* Get CFA using base reg id from the FRE info. */ + cfa = ((sframe_fre_get_base_reg_id (frep) == SFRAME_BASE_REG_SP) + ? frame->sp : frame->fp) + cfa_offset; + } /* Get the RA offset from the FRE. If the offset is unavailable, sets err. */ @@ -146,7 +154,12 @@ stacktrace_sframe (void **ra_lst, int count, frame *frame) unavailable, sets err. */ fp_offset = sframe_fre_get_fp_offset (dctx, frep, &err); frame_ptr = frame->fp; - if (err == _URC_NO_REASON) + /* The FP must be saved in the topmost frame, as backtrace called + __builtin_unwind_init, which guarantees this. */ + if (i == 0 && err != _URC_NO_REASON) + /* Force fallback to DWARF stacktracer. */ + return 0; + else if (err == _URC_NO_REASON) { /* FP offset is available, get the value stored in the stack location. */ @@ -155,6 +168,7 @@ stacktrace_sframe (void **ra_lst, int count, frame *frame) } /* Set up for the next frame. */ + frame->cfa = cfa; frame->fp = frame_ptr; frame->sp = cfa + SFRAME_SP_VAL_OFFSET; frame->pc = return_addr; @@ -175,13 +189,3 @@ getPC (void) libc_hidden_def (getPC); -/* A noinline helper used to obtain the caller's current SP. It - mimics gcc14's __builtin_stack_address() functionality. */ - -_Unwind_Ptr __attribute__ ((noinline)) -getSP (void) -{ - return (_Unwind_Ptr) __builtin_dwarf_cfa() + SFRAME_SP_VAL_OFFSET; -} - -libc_hidden_def (getSP); diff --git a/sysdeps/generic/sframe.h b/sysdeps/generic/sframe.h index ce8f6cdf7f49..198045539542 100644 --- a/sysdeps/generic/sframe.h +++ b/sysdeps/generic/sframe.h @@ -344,6 +344,7 @@ typedef struct sframe_frame_row_entry_addr4 typedef struct cframe { _Unwind_Ptr pc; + _Unwind_Ptr cfa; _Unwind_Ptr sp; _Unwind_Ptr fp; } frame; @@ -356,10 +357,6 @@ libc_hidden_proto (stacktrace_sframe); _Unwind_Ptr getPC (void); libc_hidden_proto (getPC); -/* Helper used by SFrame tracing algorithm. */ -_Unwind_Ptr getSP (void); -libc_hidden_proto (getSP); - #ifdef __cplusplus } #endif From patchwork Fri Jun 27 13:38:13 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jens Remus X-Patchwork-Id: 115200 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id E46B2385AC3B for ; Fri, 27 Jun 2025 13:45:41 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org E46B2385AC3B Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=ibm.com header.i=@ibm.com header.a=rsa-sha256 header.s=pp1 header.b=Yy/Tj4W0 X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mx0b-001b2d01.pphosted.com (mx0b-001b2d01.pphosted.com [148.163.158.5]) by sourceware.org (Postfix) with ESMTPS id EFA983856DFD for ; Fri, 27 Jun 2025 13:38:34 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org EFA983856DFD Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=linux.ibm.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=linux.ibm.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org EFA983856DFD Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=148.163.158.5 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1751031515; cv=none; b=txJwNHHAjhOdWt6OJzMcytS2PNC25vQEdyoYanZ6b/fCM2o+CvQ36c5EjMjLPioy0Wf8vRTD0PJ5YxeJ2vq6Q/QRWl2Ip3FarIxJQLa6u0mDMPXh3HFmROx4tyHaeM2rKtEoNarRCpOMZ8YO6CFyxm8AAIhM6d7/CpKMafchKEs= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1751031515; c=relaxed/simple; bh=uZtzDpdVaBHrBPlr6uYcACWvkMz/wT4xMiiterY+ejw=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=iBZEwfSGjrx/xKI3ZQ6jEa/X1aN7cJhq0H+t+1Ky7/7YJ1Iwf6bKpBkpyRc004pGlJACFMfzguVr1aX73whbP/hCS1rnnBpQqPgYiR5N3EgXWHuAMA4it08SYmsUub28q/yAS8U5XWqB/HxFGs5oM7iFupDQIIU+ZWNTvK0UNyI= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org EFA983856DFD Received: from pps.filterd (m0353725.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 55RCZdM5030891; Fri, 27 Jun 2025 13:38:30 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ibm.com; h=cc :content-transfer-encoding:date:from:in-reply-to:message-id :mime-version:references:subject:to; s=pp1; bh=y8ic+nOPATV3FdtBr WRzydV0KN8scAIk2TFsC37OHLA=; b=Yy/Tj4W0wMLxGLZikGYBiq816gky5GMYs lh/WFXq6/sKDHpfD4MPqAqdfG5ekO3zQ0xvx4pIhalmamMT3HFJF/OmWeGupCYg9 o/0KXrGjx7lwV6ju3YBfshAFipf0pfVduEI7seo1jcvZs/alqdQWeGr2r5OHnv9P DWsZBSPL+gbGaYloklPk2ZRtBz3WzqSJO2TwR1Kj4/gNrr0zywK70qj89WN5aCOQ QmDtuRJxXoNqaCVh9m2TsAXkHc5xi8zGozMTo5W9UFIrbET8cSHvaO3jP8z/gDsm +dkC34iv3dRBafXyZeDIakb0eGeaHdfLvuszBJ5DGYn9A7xgoBeTg== Received: from ppma12.dal12v.mail.ibm.com (dc.9e.1632.ip4.static.sl-reverse.com [50.22.158.220]) by mx0a-001b2d01.pphosted.com (PPS) with ESMTPS id 47dk64d76t-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 27 Jun 2025 13:38:29 +0000 (GMT) Received: from pps.filterd (ppma12.dal12v.mail.ibm.com [127.0.0.1]) by ppma12.dal12v.mail.ibm.com (8.18.1.2/8.18.1.2) with ESMTP id 55RBYJkc014988; Fri, 27 Jun 2025 13:38:29 GMT Received: from smtprelay07.fra02v.mail.ibm.com ([9.218.2.229]) by ppma12.dal12v.mail.ibm.com (PPS) with ESMTPS id 47e72u4hx5-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 27 Jun 2025 13:38:28 +0000 Received: from smtpav04.fra02v.mail.ibm.com (smtpav04.fra02v.mail.ibm.com [10.20.54.103]) by smtprelay07.fra02v.mail.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id 55RDcPln47448534 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Fri, 27 Jun 2025 13:38:25 GMT Received: from smtpav04.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 3A62B20040; Fri, 27 Jun 2025 13:38:25 +0000 (GMT) Received: from smtpav04.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 07AE320043; Fri, 27 Jun 2025 13:38:25 +0000 (GMT) Received: from tuxmaker.lnxne.boe (unknown [9.152.85.9]) by smtpav04.fra02v.mail.ibm.com (Postfix) with ESMTP; Fri, 27 Jun 2025 13:38:24 +0000 (GMT) From: Jens Remus To: libc-alpha@sourceware.org, Stefan Liebler , Claudiu Zissulescu-Ianculescu , Florian Weimer Cc: Jens Remus , Ulrich Weigand , Indu Bhagat , Elena Zannoni Subject: [RFC PATCH v2 2/2] s390: Add SFrame support for s390 64-bit (s390x) architecture Date: Fri, 27 Jun 2025 15:38:13 +0200 Message-ID: <20250627133813.2198784-3-jremus@linux.ibm.com> X-Mailer: git-send-email 2.48.1 In-Reply-To: <20250627133813.2198784-1-jremus@linux.ibm.com> References: <20250627133813.2198784-1-jremus@linux.ibm.com> MIME-Version: 1.0 X-TM-AS-GCONF: 00 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjUwNjI3MDExMCBTYWx0ZWRfX87mcKzhWOJkV PDkrm2vSQILV2C5WLp5+NylV6YE7T9nQ6yUkVNU9Z2ni+mPVpqy8EfoaOvkXod2Y1mJX1LMP6jH xb2f7As8IwznNrhEP3dzj6fQHpSZwOrBGLgftR/dm9PjfjE2XHhhUNEmuDYOlMPa2P5efAV1mnX 92fPjHpR6LE3ABmgvTrgPvLgzG3QMncW2hOaDHH5OJq6WpWk2oZbbWVduoPbaelQvdylRdujNqc hk2PmjcY5tphhRhmyAPllOPBs+61PptcxQRgLwVAonS9/yySr+IAQ4aBZwzWKFUkGUlwYyTPbaK qUWmm93Kh2FAh1sAKzjgxSI+94pTi3YjAN8rO0T9ITNWqff5n0p129UNxV36YzPp5Sg0D56T2f5 hyVql2qIt/qUWo9F8rFY5p2vjhumWA3u8omuSVbRSTK+XDaLpwYR3QVSsg0OfGwWk/ivR/Ve X-Proofpoint-ORIG-GUID: m9vDPHAiNHQh1AtyZqISdMbsIpH41gHl X-Proofpoint-GUID: m9vDPHAiNHQh1AtyZqISdMbsIpH41gHl X-Authority-Analysis: v=2.4 cv=BfvY0qt2 c=1 sm=1 tr=0 ts=685e9ed6 cx=c_pps a=bLidbwmWQ0KltjZqbj+ezA==:117 a=bLidbwmWQ0KltjZqbj+ezA==:17 a=6IFa9wvqVegA:10 a=CCpqsmhAAAAA:8 a=VnNF1IyMAAAA:8 a=mDV3o1hIAAAA:8 a=gGaoscKgT_vU5drUmSEA:9 a=ul9cdbp4aOFLsgKbc677:22 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1099,Hydra:6.1.7,FMLib:17.12.80.40 definitions=2025-06-27_04,2025-06-26_05,2025-03-28_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 mlxscore=0 priorityscore=1501 lowpriorityscore=0 clxscore=1011 suspectscore=0 adultscore=0 spamscore=0 impostorscore=0 mlxlogscore=999 malwarescore=0 phishscore=0 bulkscore=0 classifier=spam authscore=0 authtc=n/a authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.19.0-2505280000 definitions=main-2506270110 X-Spam-Status: No, score=-12.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_EF, GIT_PATCH_0, KAM_ASCII_DIVIDERS, KAM_SHORT, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org Generation of SFrame stack trace information is supported on s390 64-bit (s390x) architecture. Enable stack tracing using SFrame on s390 64-bit too. Based on Claudiu Zissulescu's AArch64 patch "aarch64: Add SFrame support for aarch64 architecture". Add support for s390 64-bit (s390x) specific SFrame stack trace format extensions: 1. The CFA offset is stored adjusted by an offset originating from the definition of the CFA and scaled down by an alignment factor to enable and improve the use of signed 8-bit SFrame offsets. 2. FP without RA saved (on stack or in a register) is represented using a padding RA offset value of zero (SFRAME_FRE_RA_OFFSET_INVALID). This may not occur in backtrace, as it is not a leaf function. Treat as error and fallback to DWARF stack tracer. 3. FP and RA may be saved in registers instead of on the stack in the topmost frame when in a leaf function. This may not occur in backtrace, as it is not a leaf function. Treat as error and fallback to DWARF stack tracer. Add support to unwind SP, FP, and RA from (RT) signal frames on s390 64-bit (s390x). Add support to detect outermost frame on s390 64-bit (s390x). sysdeps/generic/ * sframe.h (SFRAME_ABI_S390X_ENDIAN_BIG, SFRAME_FRE_RA_OFFSET_INVALID, SFRAME_S390X_CFA_OFFSET_ADJUSTMENT, SFRAME_S390X_CFA_OFFSET_ALIGNMENT_FACTOR, SFRAME_V2_FRE_S390X_CFA_OFFSET_ENCODE, SFRAME_V2_FRE_S390X_CFA_OFFSET_DECODE, SFRAME_S390X_SP_VAL_OFFSET, SFRAME_S390X_OFFSET_IS_REGNUM, SFRAME_S390X_OFFSET_ENCODE_REGNUM, SFRAME_S390X_OFFSET_DECODE_REGNUM): Define. Based on Binutils include/sframe.h). * sframe.c (SFRAME_CFA_OFFSET_DECODE, SFRAME_OFFSET_IS_REGNUM): New defines that architectures such as s390 64-bit (s390x) may overwrite. (stacktrace_sframe): Add support for s390 64-bit (s390x) specific SFrame extensions. sysdeps/s390/ * s390-64/sframe.h (SFRAME_SP_VAL_OFFSET): Define s390 64-bit (s390x) specific SP value offset from CFA. (SFRAME_CFA_OFFSET_DECODE): Define s390 64-bit (s390x) specific decoding of SFrame CFA offset. (SFRAME_OFFSET_IS_REGNUM): Define s390 64-bit (s390x) specific test whether SFrame FP/RA offset is a register number. sysdeps/unix/sysv/linux/s390/ * s390-64/uw-sigframe.h (s390_decode_signal_frame): New function. Unwind SP, FP, and RA if in a (RT) signal frame. (MD_DECODE_SIGNAL_FRAME): Wire up s390_decode_signal_frame. (s390_detect_outermost_frame): New function. Detect outermost frame on s390 64-bit (s390x). (MD_DETECT_OUTERMOST_FRAME): Wire up s390_detect_outermost_frame. Signed-off-by: Jens Remus --- Notes: Changes in v2: - Sync sframe.h to latest Binutils s390 SFrame support patch series. - Use macros SFRAME_CFA_OFFSET_DECODE and SFRAME_OFFSET_IS_REGNUM that s390 64-bit (s390x) overwrites instead of ifdefs. - Use s390x/S390X, as done in Binutils. (Indu) - When in doubt fallback to DWARF stack tracer instead of stopping. - Implement MD_DETECT_OUTERMOST_FRAME for s390 64-bit (s390x). The s390 64-bit (s390x) support to generate SFrame in GNU assembler is still under review: Binutils patch series "[PATCH v3 00/11] s390: Support to generate .sframe in assembler and linker", https://inbox.sourceware.org/binutils/20250627110849.1198336-1-jremus@linux.ibm.com/ Note that the definitions in sysdeps/generic/sframe.h originate from Binutils include/sframe.h and should therefore only be modified if absolutely necessary. @Stefan: struct kernel_sigframe and struct kernel_rt_sigframe are based on Kernel arch/s390/kernel/signal.c. From the Glibc test suite I can see that the tests debug/tst-backtrace4 and debug/tst-backtrace5 use sigreturn while debug/tst-backtrace6 uses rt_sigreturn. AArch64 and MIPS both have a separate Glibc header file for struct kernel_rt_sigframe: - sysdeps/unix/sysv/linux/mips/kernel_rt_sigframe.h - sysdeps/unix/sysv/linux/mips/kernel_rt_sigframe.h 1. Should we do the same and introduce sysdeps/unix/sysv/linux/s390/s390-64/kernel_rt_sigframe.h for struct kernel_rt_sigframe? If we do, should we then also introduce sysdeps/unix/sysv/linux/s390/s390-64/kernel_sigframe.h for struct kernel_sigframe? 2. The Kernel struct rt_sigframe contains a struct ucontext_extended instead of a struct ucontext, which currently only differs in an added _sigregs_ext uc_mcontext_ext field. Should we introduce struct ucontext_extended (and ucontext_ext[ended]_t?) in sysdeps/unix/sysv/linux/s390/sys/ucontext.h to align more closely to the Kernel structures? sysdeps/generic/sframe.c | 39 +++++++ sysdeps/generic/sframe.h | 59 +++++++++- sysdeps/s390/s390-64/sframe.h | 50 +++++++++ .../sysv/linux/s390/s390-64/uw-sigframe.h | 101 ++++++++++++++++++ 4 files changed, 246 insertions(+), 3 deletions(-) create mode 100644 sysdeps/s390/s390-64/sframe.h create mode 100644 sysdeps/unix/sysv/linux/s390/s390-64/uw-sigframe.h diff --git a/sysdeps/generic/sframe.c b/sysdeps/generic/sframe.c index e3d0cee27d6b..dc64d9fe357f 100644 --- a/sysdeps/generic/sframe.c +++ b/sysdeps/generic/sframe.c @@ -27,6 +27,18 @@ #define SFRAME_SP_VAL_OFFSET 0 #endif +/* Some arches like s390x store the CFA offset encoded, such as + adjusted and/or scaled down. */ +#ifndef SFRAME_CFA_OFFSET_DECODE +#define SFRAME_CFA_OFFSET_DECODE(offset) (offset) +#endif + +/* Some arches like s390x may save FP/RA in another register. In + SFrame this is only supported for the topmost frame. */ +#ifndef SFRAME_OFFSET_IS_REGNUM +#define SFRAME_OFFSET_IS_REGNUM(offset) false +#endif + static inline _Unwind_Ptr read_stack_value (_Unwind_Ptr loc) { @@ -130,6 +142,9 @@ stacktrace_sframe (void **ra_lst, int count, frame *frame) if (err != _URC_NO_REASON) /* Force fallback to DWARF stacktracer. */ return 0; + /* Arches such as s390x may store the CFA offset encoded, + for example adjusted and/or scaled down. */ + cfa_offset = SFRAME_CFA_OFFSET_DECODE (cfa_offset); /* Get CFA using base reg id from the FRE info. */ cfa = ((sframe_fre_get_base_reg_id (frep) == SFRAME_BASE_REG_SP) @@ -143,6 +158,22 @@ stacktrace_sframe (void **ra_lst, int count, frame *frame) /* Force fallback to DWARF stacktracer. */ return 0; + /* Arches such as s390x may represent FP without RA saved using + an invalid RA offset value as padding. This may not occur in + backtrace, as the RA must have been saved prior to calling + getRA. */ + if (ra_offset == SFRAME_FRE_RA_OFFSET_INVALID) + /* Force fallback to DWARF stacktracer. */ + return 0; + + /* Arches such as s390x may save RA in another register instead + of on the stack. In SFrame this is only supported for the + topmost frame. This may not occur in backtrace, as its caller + is not topmost. */ + if (SFRAME_OFFSET_IS_REGNUM (ra_offset)) + /* Force fallback to DWARF stacktracer. */ + return 0; + /* RA offset is available, get the value stored in the stack location. */ ra_stack_loc = cfa + ra_offset; @@ -161,6 +192,14 @@ stacktrace_sframe (void **ra_lst, int count, frame *frame) return 0; else if (err == _URC_NO_REASON) { + /* Arches such as s390x may save FP in another register instead + of on the stack. In SFrame this is only supported for the + topmost frame. This may not occur in backtrace, as its caller + is not topmost. */ + if (SFRAME_OFFSET_IS_REGNUM (fp_offset)) + /* Force fallback to DWARF stacktracer. */ + return 0; + /* FP offset is available, get the value stored in the stack location. */ fp_stack_loc = cfa + fp_offset; diff --git a/sysdeps/generic/sframe.h b/sysdeps/generic/sframe.h index 198045539542..89bb20eea3a0 100644 --- a/sysdeps/generic/sframe.h +++ b/sysdeps/generic/sframe.h @@ -91,6 +91,7 @@ extern "C" #define SFRAME_ABI_AARCH64_ENDIAN_BIG 1 /* AARCH64 big endian. */ #define SFRAME_ABI_AARCH64_ENDIAN_LITTLE 2 /* AARCH64 little endian. */ #define SFRAME_ABI_AMD64_ENDIAN_LITTLE 3 /* AMD64 little endian. */ +#define SFRAME_ABI_S390X_ENDIAN_BIG 4 /* s390x big endian. */ /* SFrame FRE types. */ #define SFRAME_FRE_TYPE_ADDR1 0 @@ -188,7 +189,7 @@ typedef struct sframe_func_desc_entry - 2-bits: Unused. ------------------------------------------------------------------------ | Unused | PAC auth A/B key (aarch64) | FDE type | FRE type | - | | Unused (amd64) | | | + | | Unused (amd64, s390x) | | | ------------------------------------------------------------------------ 8 6 5 4 0 */ uint8_t sfde_func_info; @@ -234,6 +235,10 @@ typedef struct sframe_func_desc_entry may or may not be tracked. */ #define SFRAME_FRE_FP_OFFSET_IDX 2 +/* Invalid RA offset. Currently used for s390x as padding to represent FP + without RA saved. */ +#define SFRAME_FRE_RA_OFFSET_INVALID 0 + typedef struct sframe_fre_info { /* Information about @@ -246,7 +251,7 @@ typedef struct sframe_fre_info - 1 bit: Mangled RA state bit (aarch64 only). ---------------------------------------------------------------------------------- | Mangled-RA (aarch64) | Size of offsets | Number of offsets | base_reg | - | Unused (amd64) | | | | + | Unused (amd64, s390x)| | | | ---------------------------------------------------------------------------------- 8 7 5 1 0 @@ -272,7 +277,7 @@ typedef struct sframe_fre_info /* SFrame Frame Row Entry definitions. - Used for both AMD64 and AARCH64. + Used for AMD64, AARCH64, and s390x. An SFrame Frame Row Entry is a self-sufficient record which contains information on how to generate the stack trace for the specified range of @@ -296,6 +301,24 @@ typedef struct sframe_fre_info fi Note that in AAPCS64, a frame record, if created, will save both FP and LR on stack. + + s390x: + offset1 (interpreted as CFA = BASE_REG + offset1) + if RA is being tracked + offset2 (interpreted as RA = CFA + offset2; an offset value of + SFRAME_FRE_RA_OFFSET_INVALID indicates a dummy padding RA offset + to represent FP without RA saved on stack) + if FP is being tracked + offset3 (intrepreted as FP = CFA + offset3) + fi + else + if FP is being tracked + offset2 (intrepreted as FP = CFA + offset2) + fi + fi + Note that in s390x, if a FP/RA offset2/offset3 value has the least- + significant bit set it represents a DWARF register number shifted to the + left by 1 to restore the FP/RA value from. */ /* Used when SFRAME_FRE_TYPE_ADDR1 is specified as FRE type. */ @@ -340,6 +363,36 @@ typedef struct sframe_frame_row_entry_addr4 #define SFRAME_FRE_TYPE_ADDR4_LIMIT \ (1ULL << ((SFRAME_FRE_TYPE_ADDR4 * 2) * 8)) +/* On s390x, the CFA offset from CFA base register is by definition a minimum + of 160. Store it adjusted by -160 to enable use of 8-bit SFrame offsets. + Additionally scale by an alignment factor of 8, as the SP and thus CFA + offset on s390x is always 8-byte aligned. */ +#define SFRAME_S390X_CFA_OFFSET_ADJUSTMENT SFRAME_S390X_SP_VAL_OFFSET +#define SFRAME_S390X_CFA_OFFSET_ALIGNMENT_FACTOR 8 +#define SFRAME_V2_FRE_S390X_CFA_OFFSET_ENCODE(offset) \ + (((offset) + SFRAME_S390X_CFA_OFFSET_ADJUSTMENT) \ + / SFRAME_S390X_CFA_OFFSET_ALIGNMENT_FACTOR) +#define SFRAME_V2_FRE_S390X_CFA_OFFSET_DECODE(offset) \ + (((offset) * SFRAME_S390X_CFA_OFFSET_ALIGNMENT_FACTOR) \ + - SFRAME_S390X_CFA_OFFSET_ADJUSTMENT) + +/* On s390x, the CFA is defined as SP at call site + 160. Therefore the + SP value offset from CFA is -160. */ +#define SFRAME_S390X_SP_VAL_OFFSET (-160) + +/* On s390x, the FP and RA registers can be saved either on the stack or, + in case of leaf functions, in registers. Store DWARF register numbers + encoded as offset by using the least-significant bit (LSB) as indicator: + - LSB=0: Stack offset. The s390x ELF ABI mandates that stack register + slots must be 8-byte aligned. + - LSB=1: DWARF register number shifted to the left by one. */ +#define SFRAME_S390X_OFFSET_IS_REGNUM(offset) \ + ((offset) & 1) +#define SFRAME_S390X_OFFSET_ENCODE_REGNUM(regnum) \ + (((regnum) << 1) | 1) +#define SFRAME_S390X_OFFSET_DECODE_REGNUM(offset) \ + ((offset) >> 1) + /* Used to pass frame information to stack trace routine. */ typedef struct cframe { diff --git a/sysdeps/s390/s390-64/sframe.h b/sysdeps/s390/s390-64/sframe.h new file mode 100644 index 000000000000..fbeac5217baa --- /dev/null +++ b/sysdeps/s390/s390-64/sframe.h @@ -0,0 +1,50 @@ +/* SFrame format description. + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not see + . */ + +#ifndef _S390_SFRAME_H +#define _S390_SFRAME_H + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* SP value offset from CFA. On s390 64-bit (s390x) the CFA is defined + as SP at call site + 160. */ +#define SFRAME_SP_VAL_OFFSET SFRAME_S390X_SP_VAL_OFFSET + +/* Decode SFrame CFA offset. On s390 64-bit (s390x) the CFA offset + is stored adjusted and scaled down to enable use of 8-bit offsets. */ +#define SFRAME_CFA_OFFSET_DECODE(offset) \ + SFRAME_V2_FRE_S390X_CFA_OFFSET_DECODE (offset) + +/* Test whether SFrame FP/RA offset is a register number. On s390 + 64-bit (s390x) FP/RA may be saved in a register, but only when + in a leaf function. In SFrame this is only supported for the + topmost frame. */ +#define SFRAME_OFFSET_IS_REGNUM(offset) \ + SFRAME_S390X_OFFSET_IS_REGNUM (offset) + +#ifdef __cplusplus +} +#endif + +#endif /* _S390_SFRAME_H */ + diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/uw-sigframe.h b/sysdeps/unix/sysv/linux/s390/s390-64/uw-sigframe.h new file mode 100644 index 000000000000..57d363502c60 --- /dev/null +++ b/sysdeps/unix/sysv/linux/s390/s390-64/uw-sigframe.h @@ -0,0 +1,101 @@ +/* Signal frame backtracing support for SFrame on s390-64. + Copyright (C) 2025 Free Software Foundation, Inc. + + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied warranty + of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +/* This code is inspired from libgcc's MD_FALLBACK_FRAME_STATE_FOR + implementation. See libgcc/config/s390/linux-unwind.h */ + +#include +#include +#include + +#define MD_DECODE_SIGNAL_FRAME s390_decode_signal_frame + +static _Unwind_Reason_Code +s390_decode_signal_frame (frame *frame) +{ + /* Linux kernel's sigframe structure. */ + struct kernel_sigframe + { + unsigned char callee_used_stack[__SIGNAL_FRAMESIZE]; + struct sigcontext sc; + _sigregs sregs; + int signo; + _sigregs_ext sregs_ext; + unsigned short svc_insn; /* Offset of svc_insn is NOT fixed! */ + }; + + /* Linux kernel's rt_sigframe structure. */ + struct kernel_rt_sigframe + { + unsigned char callee_used_stack[__SIGNAL_FRAMESIZE]; + unsigned short svc_insn; + siginfo_t info; + ucontext_t uc; + }; + + unsigned char *pc = (unsigned char *) frame->pc; + _sigregs *sr; + +#define SVC_OPCODE 0x0a + + /* A signal frame will have a return address pointing to: + svc $__NR_sigreturn or svc $__NR_rt_sigreturn */ + if (pc[0] != SVC_OPCODE + || (pc[1] != __NR_sigreturn && pc[1] != __NR_rt_sigreturn)) + return _URC_END_OF_STACK; + + if (pc[1] == __NR_rt_sigreturn) + { + /* New-style RT frame. */ + struct kernel_rt_sigframe *rt_; + rt_ = (struct kernel_rt_sigframe *) frame->sp; + sr = (_sigregs *) &rt_->uc.uc_mcontext; + } + else + { + /* New-style non-RT frame. */ + struct kernel_sigframe *sf; + sf = (struct kernel_sigframe *) frame->sp; + sr = sf->sc.sregs; + } + +#define SP_REGNUM 15 +#define FP_REGNUM 11 + + frame->pc = (_Unwind_Ptr) sr->regs.psw.addr; + frame->sp = (_Unwind_Ptr) sr->regs.gprs[SP_REGNUM]; + frame->fp = (_Unwind_Ptr) sr->regs.gprs[FP_REGNUM]; + + return _URC_NO_REASON; +} + +#define MD_DETECT_OUTERMOST_FRAME s390_detect_outermost_frame + +static _Unwind_Reason_Code +s390_detect_outermost_frame (frame *frame) +{ + const char *pc = (const char *) frame->pc; + + /* Outermost frame's PC points to the unreachable invalid instruction + 0x0000 in _start. */ + if (pc[0] == 0x00 && pc[1] == 0x00) + return _URC_END_OF_STACK; + + return _URC_NO_REASON; +}