Received: by 2002:a25:8b12:0:0:0:0:0 with SMTP id i18csp329319ybl; Mon, 12 Aug 2019 17:21:34 -0700 (PDT) X-Google-Smtp-Source: APXvYqyQG7YSrjsa1LFTKEr3IheDI4GLcZtcpmmdd6S/7WWcX1y5/FhiG/j3MKAZYevOZtD4mea0 X-Received: by 2002:a65:60d3:: with SMTP id r19mr32571346pgv.91.1565655693939; Mon, 12 Aug 2019 17:21:33 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1565655693; cv=none; d=google.com; s=arc-20160816; b=O5uDdMk0QZhKJgOiPJkApwWdQGar8XVl2ndqijrJfKnwHC2czHOzheWNWcFLNbD6sw 5KER+aPlYLyZUOkwu3QNTmjKXxY5VWEhzy6W1QzlcWCwGSiu4iFlvW8aJEH0uiv1pAVH shrLPXiWGJ5tRn46m6LVgHUnakzxlgM1C2AoSfLYFaGIUSW0h8voXYVST5YHJ8W0+xfE 4xajhSsbZnw1QNWmrc08KF70kMg+kLMbUpwYAlnJbwG4/IGus5vtxBn7fQEH/q14KCC1 nIV5LBGY0HcWJW6/1kDYiNXV4Kd4Ke0IHXbkmnAFxAawwYMXI70+LZpusI6zYHl5c5JN jF1g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:dkim-signature; bh=XOzXkxuzDaSaCrNnzQfIfBKq5vFfoJ+Ut/PxPNA6VMg=; b=JnWuUXeji1mBCScLNqIkdpFlvSSsmKD0jzceV2yCJxspuCvnbAGpGKtls9NOR7MDdH kOfiFGothhBXuZlGKy1EHNMP285VChkl5OuVU2I9cLPjLyLAVIO1gT0mbjy02H2VEVAd ttu9WueqyR/JGgn2abC+gu536vjOJp57zE++BJcUqc4XLmMVocTvNbS6TksYX9jANNi2 CRoASD7zcIhuhy1x/NNreT0Pmvl7oA+r4UXdgrNYpdIJDDcMM9QC/irFYmfKTzt84VJE 4MrMgboaBeQb9cTvxoiK3yX2bTDPNN7QNs7gPEydfeJl512kZinmdwzS+M9nLX5HA1iT TwgA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b="R/zhnL0M"; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id i69si22372454pgd.184.2019.08.12.17.21.17; Mon, 12 Aug 2019 17:21:33 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b="R/zhnL0M"; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726964AbfHLXuH (ORCPT + 99 others); Mon, 12 Aug 2019 19:50:07 -0400 Received: from mail-pl1-f194.google.com ([209.85.214.194]:43227 "EHLO mail-pl1-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726296AbfHLXuH (ORCPT ); Mon, 12 Aug 2019 19:50:07 -0400 Received: by mail-pl1-f194.google.com with SMTP id 4so41492467pld.10 for ; Mon, 12 Aug 2019 16:50:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=XOzXkxuzDaSaCrNnzQfIfBKq5vFfoJ+Ut/PxPNA6VMg=; b=R/zhnL0M6dBOq/DnCdstND60EDNy5mjSDrbwBKVQ31apNFU5aKroWFSwYDhbzpmV/Y 5ARtumowsAOU9rYkaWwJU86HX8FKGJSpkmb8TWJBV1M8CUKfHm58VlG6GVl5D7P9c/Ud xn5gC5ziAPDM8gGWU3jz4iz3pNNP8zUJq72thMy+oHaAu7jtGAE6Yo38bCQrBspPq1XG gVw/uRMAfrFgteDX+oP/1DxSaD9RcUQBLd9GkE8viyulqpnXheWLi12YhuJ6xbp6EBu6 pGJH49PyDKakALsJhE5mPaDARfpsDLUUL3zpQwvHS7dy4p+05C3GlM3tVPRn3Wa38H8v HB7g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=XOzXkxuzDaSaCrNnzQfIfBKq5vFfoJ+Ut/PxPNA6VMg=; b=SXUB+Pqbg5HAUlug2/TZ+FspfKSzXPm1qYfpZuwROKpyyA/oKAJWSqYcGo2WBcufh/ f4yM1NNkJt+NI3lwqHcnCAYWeAt3MTMvtafs1D29btHME9bQkEq062ymEVYlo9GAax03 +pxKe/aXkbyDlXfk0M6pAd10dS8xmt8mAj2VsIUkcdno7/2O2/KjRKHf6HP+gILJIpSp VuJczoEvK5Ov2QgddsZrNZfP6+ESRbW8U3tyQc+34OOhFJwZacOHqtqhIMhvle7SW4hZ 2cpVMNVtXGrjMvi8y86L05kyZoVh2ZjHdTdkwDjf4wXe8US+3F/iNa4yp9CLmSPdqb2U jQjg== X-Gm-Message-State: APjAAAWEOtXMVKD7pc/Kce37qOfG+1Y3dXcpbeBcD+IXhU3vrigavgg3 vdeb4+siGyfFfiRZk1pF7Mw80NGzUdY6X3spRv7Zyw== X-Received: by 2002:a17:902:a9c3:: with SMTP id b3mr4162381plr.179.1565653806199; Mon, 12 Aug 2019 16:50:06 -0700 (PDT) MIME-Version: 1.0 References: <20190801231046.105022-1-nhuck@google.com> In-Reply-To: <20190801231046.105022-1-nhuck@google.com> From: Nick Desaulniers Date: Mon, 12 Aug 2019 16:49:54 -0700 Message-ID: Subject: Re: [RFC PATCH] ARM: UNWINDER_FRAME_POINTER implementation for Clang To: Nathan Huckleberry Cc: Russell King , Linux ARM , LKML , clang-built-linux , Tri Vo Content-Type: text/plain; charset="UTF-8" Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Thu, Aug 1, 2019 at 4:10 PM 'Nathan Huckleberry' via Clang Built Linux wrote: > > The stackframe setup when compiled with clang is different. > Since the stack unwinder expects the gcc stackframe setup it > fails to print backtraces. This patch adds support for the > clang stackframe setup. > > Cc: clang-built-linux@googlegroups.com > Suggested-by: Tri Vo > Signed-off-by: Nathan Huckleberry Thanks for the patch! This is something definitely useful to have implemented with Clang. Some initial thoughts below: > --- > arch/arm/Kconfig.debug | 4 +- > arch/arm/Makefile | 2 +- > arch/arm/lib/backtrace.S | 134 ++++++++++++++++++++++++++++++++++++--- > 3 files changed, 128 insertions(+), 12 deletions(-) > > diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug > index 85710e078afb..92fca7463e21 100644 > --- a/arch/arm/Kconfig.debug > +++ b/arch/arm/Kconfig.debug > @@ -56,7 +56,7 @@ choice > > config UNWINDER_FRAME_POINTER > bool "Frame pointer unwinder" > - depends on !THUMB2_KERNEL && !CC_IS_CLANG > + depends on !THUMB2_KERNEL > select ARCH_WANT_FRAME_POINTERS > select FRAME_POINTER > help > @@ -1872,7 +1872,7 @@ config DEBUG_UNCOMPRESS > When this option is set, the selected DEBUG_LL output method > will be re-used for normal decompressor output on multiplatform > kernels. > - > + > > config UNCOMPRESS_INCLUDE > string > diff --git a/arch/arm/Makefile b/arch/arm/Makefile > index c3624ca6c0bc..a593d9c4e18a 100644 > --- a/arch/arm/Makefile > +++ b/arch/arm/Makefile > @@ -36,7 +36,7 @@ KBUILD_CFLAGS += $(call cc-option,-mno-unaligned-access) > endif > > ifeq ($(CONFIG_FRAME_POINTER),y) > -KBUILD_CFLAGS +=-fno-omit-frame-pointer -mapcs -mno-sched-prolog > +KBUILD_CFLAGS +=-fno-omit-frame-pointer $(call cc-option,-mapcs,) $(call cc-option,-mno-sched-prolog,) > endif > > ifeq ($(CONFIG_CPU_BIG_ENDIAN),y) > diff --git a/arch/arm/lib/backtrace.S b/arch/arm/lib/backtrace.S > index 1d5210eb4776..fd64eec9f6ae 100644 > --- a/arch/arm/lib/backtrace.S > +++ b/arch/arm/lib/backtrace.S > @@ -14,10 +14,7 @@ > @ fp is 0 or stack frame > > #define frame r4 > -#define sv_fp r5 > -#define sv_pc r6 It looks like sv_fp and sv_pc have the same values for both GCC and Clang, maybe they don't need to be moved here? > #define mask r7 > -#define offset r8 So GCC has an offset while Clang has sv_lr. > > ENTRY(c_backtrace) > > @@ -25,7 +22,8 @@ ENTRY(c_backtrace) > ret lr > ENDPROC(c_backtrace) > #else > - stmfd sp!, {r4 - r8, lr} @ Save an extra register so we have a location... > + stmfd sp!, {r4 - r8, fp, lr} @ Save an extra register Not having a preprocessor guard here makes it seem like GCC will always have to move the additional register, even if it's not using it? > + @ so we have a location.. > movs frame, r0 @ if frame pointer is zero > beq no_frame @ we have no stack frames > > @@ -35,11 +33,119 @@ ENDPROC(c_backtrace) > THUMB( orreq mask, #0x03 ) > movne mask, #0 @ mask for 32-bit > > -1: stmfd sp!, {pc} @ calculate offset of PC stored > - ldr r0, [sp], #4 @ by stmfd for this CPU > - adr r1, 1b > - sub offset, r0, r1 > > +#if defined(CONFIG_CC_IS_CLANG) #ifdef CONFIG_CC_IS_CLANG I'd only use `#if defined(foo)` if there were 2 or more things I needed to check: `#if defined(foo) || defined(bar)`. > +/* > + * Clang does not store pc or sp in function prologues > + * so we don't know exactly where the function > + * starts. > + * We can treat the current frame's lr as the saved pc and the > + * preceding frame's lr as the lr, but we can't > + * trace the most recent call. > + * Inserting a false stack frame allows us to reference the > + * function called last in the stacktrace. > + * If the call instruction was a bl we can look at the callers > + * branch instruction to calculate the saved pc. > + * We can recover the pc in most cases, but in cases such as > + * calling function pointers we cannot. In this > + * case, default to using the lr. This will be > + * some address in the function, but will not > + * be the function start. > + * Unfortunately due to the stack frame layout we can't dump > + * r0 - r3, but these are less frequently saved. > + * > + * Stack frame layout: > + * > + * saved lr > + * frame => saved fp > + * optionally saved caller registers (r4 - r10) > + * optionally saved arguments (r0 - r3) > + * > + * s/addressses/addresses/ > + * > + * Functions start with the following code sequence: > + * corrected pc => stmfd sp!, {..., fp, lr} > + * add fp, sp, #x > + * stmfd sp!, {r0 - r3} (optional) > + */ > +#define sv_fp r5 > +#define sv_pc r6 > +#define sv_lr r8 > + add frame, sp, #20 @ switch to false frame > +for_each_frame: tst frame, mask @ Check for address exceptions > + bne no_frame > + > +1001: ldr sv_pc, [frame, #4] @ get saved 'pc' > +1002: ldr sv_fp, [frame, #0] @ get saved fp These two sections seem to differ between GCC and Clang only for the offsets. Could these be made into preprocessor defines and more code shared? > + > + teq sv_fp, #0 @ make sure next frame exists > + beq no_frame > + > +1003: ldr sv_lr, [sv_fp, #4] @ get saved lr from next frame > + > + //try to find function start Use either /* c89 comments */ or @arm assembler comments. > + ldr r0, [sv_lr, #-4] @ get call instruction > + ldr r3, .Ldsi+8 > + and r2, r3, r0 @ is this a bl call > + teq r2, r3 > + bne finished_setup @ give up if it's not > + and r0, #0xffffff @ get call offset 24-bit int > + lsl r0, r0, #8 @ sign extend offset > + asr r0, r0, #8 > + ldr sv_pc, [sv_fp, #4] @ get lr address > + add sv_pc, sv_pc, #-4 @ get call instruction address > + add sv_pc, sv_pc, #8 @ take care of prefetch > + add sv_pc, sv_pc, r0, lsl #2 @ find function start > + b finished_setup Do we need an explicit branch to this label? Wouldn't we just fall through to it?j > + > +finished_setup: > + > + bic sv_pc, sv_pc, mask @ mask PC/LR for the mode > + > +1004: mov r0, sv_pc > + > + mov r1, sv_lr > + mov r2, frame > + bic r1, r1, mask @ mask PC/LR for the mode > + bl dump_backtrace_entry > + > +1005: ldr r1, [sv_pc, #0] @ if stmfd sp!, {..., fp, lr} > + ldr r3, .Ldsi @ instruction exists, > + teq r3, r1, lsr #11 > + ldr r0, [frame] @ locals are stored in > + @ the preceding frame > + subeq r0, r0, #4 > + bleq dump_backtrace_stm @ dump saved registers > + > + teq sv_fp, #0 @ zero saved fp means > + beq no_frame @ no further frames > + > + cmp sv_fp, frame @ next frame must be > + mov frame, sv_fp @ above the current frame > + bhi for_each_frame > + > +1006: adr r0, .Lbad > + mov r1, frame > + bl printk > +no_frame: ldmfd sp!, {r4 - r8, fp, pc} > +ENDPROC(c_backtrace) > + .pushsection __ex_table,"a" > + .align 3 > + .long 1001b, 1006b > + .long 1002b, 1006b > + .long 1003b, 1006b > + .long 1004b, 1006b > + .popsection > + > +.Lbad: .asciz "Backtrace aborted due to bad frame pointer <%p>\n" > + .align Probably don't need to duplicate the above (up to ENDPROC), but the below hunk looks compiler specific. > +.Ldsi: .word 0xe92d4800 >> 11 @ stmfd sp!, {... fp, lr} > + .word 0xe92d0000 >> 11 @ stmfd sp!, {} > + .word 0x0b000000 @ bl if these bits are set > + > +ENDPROC(c_backtrace) Duplicate ENDPROC? > + > +#else > /* > * Stack frame layout: > * optionally saved caller registers (r4 - r10) > @@ -55,6 +161,15 @@ ENDPROC(c_backtrace) > * stmfd sp!, {r0 - r3} (optional) > * corrected pc => stmfd sp!, {..., fp, ip, lr, pc} > */ > +#define sv_fp r5 > +#define sv_pc r6 > +#define offset r8 > + > +1: stmfd sp!, {pc} @ calculate offset of PC stored > + ldr r0, [sp], #4 @ by stmfd for this CPU > + adr r1, 1b > + sub offset, r0, r1 > + > for_each_frame: tst frame, mask @ Check for address exceptions > bne no_frame > > @@ -98,7 +213,7 @@ for_each_frame: tst frame, mask @ Check for address exceptions > 1006: adr r0, .Lbad > mov r1, frame > bl printk > -no_frame: ldmfd sp!, {r4 - r8, pc} > +no_frame: ldmfd sp!, {r4 - r8, fp, pc} More work for GCC... > ENDPROC(c_backtrace) > > .pushsection __ex_table,"a" > @@ -115,3 +230,4 @@ ENDPROC(c_backtrace) > .word 0xe92d0000 >> 11 @ stmfd sp!, {} > > #endif > +#endif It would be nice to put comments on the end of these #endif's what condition they're terminating: #endif /* CONFIG_CC_IS_CLANG #endif /* !defined(CONFIG_FRAME_POINTER) || !defined(CONFIG_PRINTK) */ Maybe also the #else's above. Will send more thoughts tomorrow/throughout the week. -- Thanks, ~Nick Desaulniers