From patchwork Thu Dec 3 12:41:08 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vineet Gupta X-Patchwork-Id: 552240 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2001:1868:205::9]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 11FD11402C0 for ; Thu, 3 Dec 2015 23:42:49 +1100 (AEDT) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1a4TDj-0003wJ-No; Thu, 03 Dec 2015 12:42:47 +0000 Received: from us01smtprelay-2.synopsys.com ([198.182.60.111] helo=smtprelay.synopsys.com) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1a4TDg-0003qI-A1 for linux-snps-arc@lists.infradead.org; Thu, 03 Dec 2015 12:42:46 +0000 Received: from dc8secmta1.synopsys.com (dc8secmta1.synopsys.com [10.13.218.200]) by smtprelay.synopsys.com (Postfix) with ESMTP id E76EE10C122E; Thu, 3 Dec 2015 04:42:25 -0800 (PST) Received: from dc8secmta1.internal.synopsys.com (dc8secmta1.internal.synopsys.com [127.0.0.1]) by dc8secmta1.internal.synopsys.com (Service) with ESMTP id E5CCD27116; Thu, 3 Dec 2015 04:42:25 -0800 (PST) Received: from mailhost.synopsys.com (mailhost1.synopsys.com [10.12.238.239]) by dc8secmta1.internal.synopsys.com (Service) with ESMTP id A421F27102; Thu, 3 Dec 2015 04:42:25 -0800 (PST) Received: from mailhost.synopsys.com (localhost [127.0.0.1]) by mailhost.synopsys.com (Postfix) with ESMTP id 8CB6CED1; Thu, 3 Dec 2015 04:42:25 -0800 (PST) Received: from US01WEHTC2.internal.synopsys.com (us01wehtc2-vip.internal.synopsys.com [10.12.239.238]) by mailhost.synopsys.com (Postfix) with ESMTP id 7F709ED0; Thu, 3 Dec 2015 04:42:25 -0800 (PST) Received: from IN01WEHTCB.internal.synopsys.com (10.144.199.106) by US01WEHTC2.internal.synopsys.com (10.12.239.237) with Microsoft SMTP Server (TLS) id 14.3.195.1; Thu, 3 Dec 2015 04:42:24 -0800 Received: from IN01WEHTCA.internal.synopsys.com (10.144.199.103) by IN01WEHTCB.internal.synopsys.com (10.144.199.105) with Microsoft SMTP Server (TLS) id 14.3.195.1; Thu, 3 Dec 2015 18:12:22 +0530 Received: from vineetg-E7440.internal.synopsys.com (10.12.197.182) by IN01WEHTCA.internal.synopsys.com (10.144.199.243) with Microsoft SMTP Server (TLS) id 14.3.195.1; Thu, 3 Dec 2015 18:12:20 +0530 From: Vineet Gupta To: Subject: [PATCH 10/17] ARC: dw2 unwind: CIE parsing/validation done only once at startup Date: Thu, 3 Dec 2015 18:11:08 +0530 Message-ID: <1449146475-15335-11-git-send-email-vgupta@synopsys.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1449146475-15335-1-git-send-email-vgupta@synopsys.com> References: <1449146475-15335-1-git-send-email-vgupta@synopsys.com> MIME-Version: 1.0 X-Originating-IP: [10.12.197.182] X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20151203_044244_471471_34D76E2A X-CRM114-Status: GOOD ( 21.09 ) X-Spam-Score: -2.6 (--) X-Spam-Report: SpamAssassin version 3.4.0 on bombadil.infradead.org summary: Content analysis details: (-2.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_MSPIKE_H3 RBL: Good reputation (+3) [198.182.60.111 listed in wl.mailspike.net] -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [198.182.60.111 listed in list.dnswl.org] -0.0 T_RP_MATCHES_RCVD Envelope sender domain matches handover relay domain -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] -0.0 RCVD_IN_MSPIKE_WL Mailspike good senders X-BeenThere: linux-snps-arc@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: Linux on Synopsys ARC Processors List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Vineet Gupta , Alexey.Brodkin@synopsys.com, linux-kernel@vger.kernel.org, JBeulich@suse.com Sender: "linux-snps-arc" Errors-To: linux-snps-arc-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org There is only 1 CIE per unwind table and applicable to all FDEs, so validate it only once. No need to do validate it when prcoessing the FDE itself. Signed-off-by: Vineet Gupta --- arch/arc/kernel/unwind.c | 336 +++++++++++++++++++++-------------------------- 1 file changed, 153 insertions(+), 183 deletions(-) diff --git a/arch/arc/kernel/unwind.c b/arch/arc/kernel/unwind.c index 6a09ffa0b697..f2a486d9dac2 100644 --- a/arch/arc/kernel/unwind.c +++ b/arch/arc/kernel/unwind.c @@ -122,6 +122,13 @@ static struct unwind_table { } core, init; const void *address; unsigned long size; + struct cie { + unsigned version:8, aug:8, pad:16; + uleb128_t codeAlign; + sleb128_t dataAlign; + int fde_pointer_type; + uleb128_t retAddrReg; + } cie; struct eh_frame_header *header; unsigned long hdrsz; struct unwind_table *link; @@ -140,20 +147,18 @@ struct unwind_item { struct unwind_state { uleb128_t loc, org; - const u8 *cieStart, *cieEnd; uleb128_t codeAlign; sleb128_t dataAlign; struct cfa { uleb128_t reg, offs; } cfa; struct unwind_item regs[ARRAY_SIZE(reg_info)]; - unsigned stackDepth:8; - unsigned version:8; + unsigned stackDepth; const u8 *label; const u8 *stack[MAX_STACK_DEPTH]; }; -static const struct cfa badCFA = { ARRAY_SIZE(reg_info), 1 }; +static struct cfa seed_CFA = { ARRAY_SIZE(reg_info), 1 }; static struct unwind_table *find_table(unsigned long pc) { @@ -214,7 +219,7 @@ void __init arc_unwind_init(void) static const u32 bad_cie, not_fde; static const u32 *cie_for_fde(const u32 *fde, const struct unwind_table *); -static signed fde_pointer_type(const u32 *cie); +static int cie_validate(const u32 *cie, struct cie *t_cie); static int cmp_eh_frame_hdr_table_entries(const void *p1, const void *p2) { @@ -245,7 +250,10 @@ static void __init setup_unwind_table(struct unwind_table *table, unsigned long tableSize = table->size, hdrSize; unsigned n; const u32 *fde; + int ptrType; struct eh_frame_header *header; + int n_cie = 0, len = 0; + char cie_orig[64]; if (table->header) return; @@ -261,19 +269,26 @@ static void __init setup_unwind_table(struct unwind_table *table, tableSize > sizeof(*fde) && tableSize - sizeof(*fde) >= *fde; tableSize -= sizeof(*fde) + *fde, fde += 1 + *fde / sizeof(*fde)) { const u32 *cie = cie_for_fde(fde, table); - signed ptrType; - if (cie == ¬_fde) + if (cie == ¬_fde) { + if (n_cie++ == 0) { + len = cie_validate(&fde[0], &table->cie); + if (!len) + panic("Invalid CIE\n"); + + memcpy(&cie_orig[0], &fde[0], len); + } else { + if (memcmp(&fde[0], &cie_orig[0], len) != 0) + panic("Multiple CIEs not same\n"); + } continue; + } if (cie == NULL || cie == &bad_cie) return; - ptrType = fde_pointer_type(cie); - if (ptrType < 0) - return; ptr = (const u8 *)(fde + 2); if (!read_pointer(&ptr, (const u8 *)(fde + 1) + *fde, - ptrType)) { + table->cie.fde_pointer_type)) { /* FIXME_Rajesh We have 4 instances of null addresses * instead of the initial loc addr * return; @@ -303,19 +318,17 @@ static void __init setup_unwind_table(struct unwind_table *table, BUILD_BUG_ON(offsetof(typeof(*header), table) % __alignof(typeof(*header->table))); + + ptrType = table->cie.fde_pointer_type; for (fde = table->address, tableSize = table->size, n = 0; tableSize; tableSize -= sizeof(*fde) + *fde, fde += 1 + *fde / sizeof(*fde)) { - /* const u32 *cie = fde + 1 - fde[1] / sizeof(*fde); */ - const u32 *cie = (const u32 *)(fde[1]); - if (fde[1] == 0xffffffff) continue; /* this is a CIE */ ptr = (const u8 *)(fde + 2); header->table[n].start = read_pointer(&ptr, - (const u8 *)(fde + 1) + - *fde, - fde_pointer_type(cie)); + (const u8 *)(fde + 1) + *fde, + ptrType); header->table[n].fde = (unsigned long)fde; ++n; } @@ -572,65 +585,6 @@ static unsigned long read_pointer(const u8 **pLoc, const void *end, return value; } -static signed fde_pointer_type(const u32 *cie) -{ - const u8 *ptr = (const u8 *)(cie + 2); - unsigned version = *ptr; - - if (version != 1) - return -1; /* unsupported */ - - if (*++ptr) { - const char *aug; - const u8 *end = (const u8 *)(cie + 1) + *cie; - uleb128_t len; - - /* check if augmentation size is first (and thus present) */ - if (*ptr != 'z') - return -1; - - /* check if augmentation string is nul-terminated */ - aug = (const void *)ptr; - ptr = memchr(aug, 0, end - ptr); - if (ptr == NULL) - return -1; - - ++ptr; /* skip terminator */ - get_uleb128(&ptr, end); /* skip code alignment */ - get_sleb128(&ptr, end); /* skip data alignment */ - /* skip return address column */ - version <= 1 ? (void) ++ptr : (void)get_uleb128(&ptr, end); - len = get_uleb128(&ptr, end); /* augmentation length */ - - if (ptr + len < ptr || ptr + len > end) - return -1; - - end = ptr + len; - while (*++aug) { - if (ptr >= end) - return -1; - switch (*aug) { - case 'L': - ++ptr; - break; - case 'P':{ - signed ptrType = *ptr++; - - if (!read_pointer(&ptr, end, ptrType) - || ptr > end) - return -1; - } - break; - case 'R': - return *ptr; - default: - return -1; - } - } - } - return DW_EH_PE_native | DW_EH_PE_abs; -} - static int advance_loc(unsigned long delta, struct unwind_state *state, char *str) { state->loc += delta * state->codeAlign; @@ -682,14 +636,6 @@ static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc, int result = 1; u8 opcode; - if (start != state->cieStart) { - state->loc = state->org; - result = - processCFI(state->cieStart, state->cieEnd, 0, ptrType, - state); - if (targetLoc == 0 && state->label == NULL) - return result; - } for (ptr.p8 = start; result && ptr.p8 < end;) { char *str = NULL; switch (*ptr.p8 >> 6) { @@ -775,7 +721,7 @@ static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc, state->label = state->stack[state->stackDepth - 1]; - memcpy(&state->cfa, &badCFA, + memcpy(&state->cfa, &seed_CFA, sizeof(state->cfa)); memset(state->regs, 0, sizeof(state->regs)); @@ -857,12 +803,102 @@ static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc, targetLoc < state->loc && */ state->label == NULL)); } +/* + * Returns length of CIE (0 if invalid) + */ +static int cie_validate(const u32 *cie, struct cie *t_cie) +{ + const u8 *ptr = NULL, *end = NULL; + u8 version; + uleb128_t retAddrReg = 0; + int ptrType = DW_EH_PE_native | DW_EH_PE_abs; + struct unwind_state state; + + if (cie == NULL) + return 0; + + ptr = (const u8 *)(cie + 2); + end = (const u8 *)(cie + 1) + *cie; + if ((version = *ptr) != 1) + return 0; /* unsupported version */ + + t_cie->version = version; + + if (*++ptr) { + /* check if augmentation size is first (thus present) */ + if (*ptr == 'z') { + t_cie->aug = 1; + while (++ptr < end && *ptr) { + switch (*ptr) { + /* chk for ignorable or already handled + * nul-terminated augmentation string */ + case 'L': + case 'P': + case 'R': + continue; + case 'S': /* signal frame */ + continue; + default: + break; + } + break; + } + } + if (ptr >= end || *ptr) + return 0; + } + ++ptr; /* skip terminator */ + + t_cie->codeAlign = get_uleb128(&ptr, end); + t_cie->dataAlign = get_sleb128(&ptr, end); + + if (t_cie->codeAlign == 0 || t_cie->dataAlign == 0 || ptr >= end) + return 0; + + retAddrReg = version <= 1 ? *ptr++ : get_uleb128(&ptr, end); + t_cie->retAddrReg = retAddrReg; + + /* skip augmentation data */ + if (((const char *)(cie + 2))[1] == 'z') { + get_uleb128(&ptr, end); /* augSize */ + + if (*ptr++ == 'R') /* FDE pointer encoding type */ + ptrType = *ptr; + } + t_cie->fde_pointer_type = ptrType; + + if (ptr > end + || retAddrReg >= ARRAY_SIZE(reg_info) + || REG_INVALID(retAddrReg) + || reg_info[retAddrReg].width != sizeof(unsigned long)) + return 0; + + unw_debug("\nDwarf Unwinder setup: CIE Info:\n"); + unw_debug("code Align: %lu\n", t_cie->codeAlign); + unw_debug("data Align: %ld\n", t_cie->dataAlign); + unw_debug("Return Address register r%d\n", (int)t_cie->retAddrReg); + unw_debug("FDE pointer type %d\n", ptrType); + unw_debug("CFI Instructions for CIE:\n"); + + memset(&state, sizeof(state), 0); + + /* CIE has rules for Default CFA */ + if (!processCFI(ptr, end, 0, ptrType, &state) + || state.cfa.reg >= ARRAY_SIZE(reg_info) + || state.cfa.offs % sizeof(unsigned long)) + return 0; + + memcpy(&seed_CFA, &state.cfa, sizeof(state.cfa)); + + return end - (const u8 *)cie; +} + /* Unwind to previous to frame. Returns 0 if successful, negative * number in case of an error. */ int arc_unwind(struct unwind_frame_info *frame) { #define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs]) - const u32 *fde = NULL, *cie = NULL; + const u32 *fde = NULL; const u8 *ptr = NULL, *end = NULL; unsigned long pc = UNW_PC(frame); unsigned long startLoc = 0, endLoc = 0, cfa; @@ -920,107 +956,41 @@ int arc_unwind(struct unwind_frame_info *frame) else return -EINVAL; - if (fde != NULL) { - cie = cie_for_fde(fde, table); - ptr = (const u8 *)(fde + 2); - if (cie != NULL - && cie != &bad_cie - && cie != ¬_fde - && (ptrType = fde_pointer_type(cie)) >= 0 - && read_pointer(&ptr, (const u8 *)(fde + 1) + *fde, ptrType) == startLoc) { - if (!(ptrType & DW_EH_PE_indirect)) - ptrType &= DW_EH_PE_FORM | DW_EH_PE_signed; - endLoc = startLoc + read_pointer(&ptr, (const u8 *)(fde + 1) + *fde, ptrType); - if (pc >= endLoc) { - fde = NULL; - cie = NULL; - } - } else { - fde = NULL; - cie = NULL; - } - } - if (cie != NULL) { - memset(&state, 0, sizeof(state)); - state.cieEnd = ptr; /* keep here temporarily */ - ptr = (const u8 *)(cie + 2); - end = (const u8 *)(cie + 1) + *cie; - if ((state.version = *ptr) != 1) - cie = NULL; /* unsupported version */ - else if (*++ptr) { - /* check if augmentation size is first (thus present) */ - if (*ptr == 'z') { - while (++ptr < end && *ptr) { - switch (*ptr) { - /* chk for ignorable or already handled - * nul-terminated augmentation string */ - case 'L': - case 'P': - case 'R': - continue; - case 'S': - /* signal frame not handled */ - continue; - default: - break; - } - break; - } - } - if (ptr >= end || *ptr) - cie = NULL; - } - ++ptr; - } - if (cie != NULL) { - /* get code aligment factor */ - state.codeAlign = get_uleb128(&ptr, end); - /* get data aligment factor */ - state.dataAlign = get_sleb128(&ptr, end); - if (state.codeAlign == 0 || state.dataAlign == 0 || ptr >= end) - cie = NULL; - else { - retAddrReg = - state.version <= 1 ? *ptr++ : get_uleb128(&ptr, - end); - unw_debug("CIE Frame Info:\n"); - unw_debug("return Address register 0x%lx\n", - retAddrReg); - unw_debug("data Align: %ld\n", state.dataAlign); - unw_debug("code Align: %lu\n", state.codeAlign); - /* skip augmentation */ - if (((const char *)(cie + 2))[1] == 'z') { - uleb128_t augSize = get_uleb128(&ptr, end); - - ptr += augSize; - } - if (ptr > end || retAddrReg >= ARRAY_SIZE(reg_info) - || REG_INVALID(retAddrReg) - || reg_info[retAddrReg].width != - sizeof(unsigned long)) - cie = NULL; - } + memset(&state, 0, sizeof(state)); + ptr = (const u8 *)(fde + 2); + end = (const u8 *)(fde + 1) + *fde; + + ptrType = table->cie.fde_pointer_type; + if (read_pointer(&ptr, end, ptrType) != startLoc) + return -EINVAL; + + if (!(ptrType & DW_EH_PE_indirect)) + ptrType &= DW_EH_PE_FORM | DW_EH_PE_signed; + + endLoc = startLoc + read_pointer(&ptr, end, ptrType); + + /* For symbols not present, this is mostly hit (not startLoc check above) */ + if (pc >= endLoc) { + unw_debug("Unwindo info missing for PC %lx: {%lx,%lx}\n", + pc, startLoc, endLoc); + return -EINVAL; } - if (cie != NULL) { - state.cieStart = ptr; - ptr = state.cieEnd; - state.cieEnd = end; - end = (const u8 *)(fde + 1) + *fde; - /* skip augmentation */ - if (((const char *)(cie + 2))[1] == 'z') { - uleb128_t augSize = get_uleb128(&ptr, end); - - if ((ptr += augSize) > end) - fde = NULL; - } + + if (table->cie.aug) { + uleb128_t augSize = get_uleb128(&ptr, end); + + if ((ptr += augSize) > end) + return -EINVAL; } - if (cie == NULL || fde == NULL) - return -ENXIO; - state.org = startLoc; - memcpy(&state.cfa, &badCFA, sizeof(state.cfa)); + state.org = state.loc = startLoc; + memcpy(&state.cfa, &seed_CFA, sizeof(state.cfa)); + state.codeAlign = table->cie.codeAlign; + state.dataAlign = table->cie.dataAlign; + + retAddrReg = table->cie.retAddrReg; - unw_debug("\nProcess CFA\n"); + unw_debug("\nProcess FDE:\n"); /* process instructions * For ARC, we optimize by having blink(retAddrReg) with