mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Ahmad Fatoum <ahmad@a3f.at>
To: barebox@lists.infradead.org
Subject: [RFC PATCH 1/4] ARM: implement sjlj-based TRY/CATCH exception handling
Date: Wed,  1 Apr 2020 11:31:01 +0200	[thread overview]
Message-ID: <20200401093104.959691-2-ahmad@a3f.at> (raw)
In-Reply-To: <20200401093104.959691-1-ahmad@a3f.at>

Now with setjmp supported in barebox, we don't need to limit ourselves
to outdated of return-value-based error propagation.
Instead, embrace the state-of-the-art and port kazlib's ANSI C exceptions
to barebox.

Reviewed-by: Ahmad Fatoum <ahmad@a3f.at>
---
 common/Makefile      |   3 +
 common/except.c      | 282 +++++++++++++++++++++++++++++
 include/except.h     | 156 ++++++++++++++++
 include/exceptions.h | 419 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 860 insertions(+)
 create mode 100644 common/except.c
 create mode 100644 include/except.h
 create mode 100644 include/exceptions.h

diff --git a/common/Makefile b/common/Makefile
index 84463b4d485a..ee0d75ae86e2 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -69,6 +69,9 @@ obj-$(CONFIG_SERIAL_DEV_BUS)	+= serdev.o
 obj-$(CONFIG_USBGADGET_START)	+= usbgadget.o
 pbl-$(CONFIG_PBL_OPTEE)		+= optee.o
 obj-$(CONFIG_BOOTM_OPTEE)	+= optee.o
+obj-y				+= except.o
+
+UBSAN_SANITIZE_except.o := n
 
 ifdef CONFIG_PASSWORD
 
diff --git a/common/except.c b/common/except.c
new file mode 100644
index 000000000000..0e408c669ee0
--- /dev/null
+++ b/common/except.c
@@ -0,0 +1,282 @@
+/*
+ * Portable Exception Handling for ANSI C.
+ * Copyright (C) 1999 Kaz Kylheku <kaz@ashi.footprints.net>
+ *
+ * modified for barebox:
+ * Copyright (C) 2019 Ahmad Fatoum
+ *
+ * Free Software License:
+ *
+ * All rights are reserved by the author, with the following exceptions:
+ * Permission is granted to freely reproduce and distribute this software,
+ * possibly in exchange for a fee, provided that this copyright notice appears
+ * intact. Permission is also granted to adapt this software to produce
+ * derivative works, as long as the modified versions carry this copyright
+ * notice and additional notices stating that the work has been modified.
+ * This source code may be translated into executable form and incorporated
+ * into proprietary software; there is no requirement for such software to
+ * contain a copyright notice related to this source.
+ * $Id: except.c,v 1.27.2.2 2001/07/27 01:20:34 kaz Exp $
+ * $Name: kazlib_1_20 $
+ */
+
+#include <stdlib.h>
+#include <common.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <linux/kernel.h>
+#include "except.h"
+
+#define XCEPT_BUFFER_SIZE	1024
+
+#define group except_group
+#define code except_code
+#define id except_id
+#define message except_message
+#define dyndata except_dyndata
+#define func except_func
+#define context except_context
+#define id except_id
+#define size except_size
+#define obj except_obj
+#define jmp except_jmp
+#define down except_down
+#define type except_type
+#define catcher except_catcher
+#define cleanup except_cleanup
+#define info except_info
+
+static int init_counter;
+static void unhandled_catcher(except_t *);
+static void (*uh_catcher_ptr)(except_t *) = unhandled_catcher;
+static void *(*allocator)(size_t) = malloc;
+static void (*deallocator)(void *) = free;
+static struct except_stacknode *stack_top;
+
+#define get_top() (stack_top)
+#define set_top(T) (stack_top = (T))
+#define get_catcher() (uh_catcher_ptr)
+#define set_catcher(C) (uh_catcher_ptr = (C))
+#define get_alloc() (allocator)
+#define set_alloc(A) (allocator = (A))
+#define get_dealloc() (deallocator)
+#define set_dealloc(D) (deallocator = (D))
+
+int except_init(void)
+{
+    WARN_ON(init_counter == INT_MAX);
+    init_counter++;
+    return 1;
+}
+
+void except_deinit(void)
+{
+    WARN_ON(init_counter <= 0);
+    init_counter--;
+}
+
+static int match(const volatile except_id_t *thrown, const except_id_t *caught)
+{
+    int group_match = (caught->group == XCEPT_GROUP_ANY || caught->group == thrown->group);
+    int code_match = (caught->code == XCEPT_CODE_ANY || caught->code == thrown->code);
+
+    return group_match && code_match;
+}
+
+static __noreturn void do_throw(except_t *except)
+{
+    struct except_stacknode *top;
+
+    WARN_ON(except->id.group == 0 || except->id.code == 0);
+
+    for (top = get_top(); top != 0; top = top->down) {
+	if (top->type == XCEPT_CLEANUP) {
+	    top->info.cleanup->func(top->info.cleanup->context);
+	} else {
+	    struct except_catch *catcher = top->info.catcher;
+	    const except_id_t *pi = catcher->id;
+	    size_t i;
+
+	    WARN_ON(top->type != XCEPT_CATCHER);
+	    except_free(catcher->obj.dyndata);
+
+	    for (i = 0; i < catcher->size; pi++, i++) {
+		if (match(&except->id, pi)) {
+		    catcher->obj = *except;
+		    set_top(top);
+		    longjmp(catcher->jmp, 1);
+		}
+	    }
+	}
+    }
+
+    set_top(top);
+    get_catcher()(except);	/* unhandled exception */
+    panic("");
+}
+
+static const char * const exception_strs[] = {
+	[LogicError]			= "LogicError",
+	[RuntimeError]			= "RuntimeError",
+	[OutOfMemoryError]		= "OutOfMemoryError",
+	[UndefinedInstructionException]	= "UndefinedInstructionException",
+	[SoftwareInterruptException]	= "SoftwareInterruptException",
+	[PrefetchAbortException]	= "PrefetchAbortException",
+	[DataAbortException]		= "DataAbortExceotion",
+	[FiqException]			= "FiqException",
+	[IrqException]			= "IrqException",
+};
+
+static void unhandled_catcher(except_t *except)
+{
+	printk("Unhandled exception (\"%s\", msg=%p, group=%ld, code=%ld)\n",
+	       exception_strs[except->id.code],
+	       except->message, except->id.group, except->id.code);
+	dump_stack();
+	panic("");
+}
+
+static void stack_push(struct except_stacknode *node)
+{
+    node->down = get_top();
+    set_top(node);
+}
+
+void except_setup_clean(struct except_stacknode *esn,
+	struct except_cleanup *ecl, void (*cleanf)(void *), void *context)
+{
+    esn->type = XCEPT_CLEANUP;
+    ecl->func = cleanf;
+    ecl->context = context;
+    esn->info.cleanup = ecl;
+    stack_push(esn);
+}
+
+void except_setup_try(struct except_stacknode *esn,
+	struct except_catch *ech, const except_id_t id[], size_t size)
+{
+   ech->id = id;
+   ech->size = size;
+   ech->obj.dyndata = 0;
+   esn->type = XCEPT_CATCHER;
+   esn->info.catcher = ech;
+   stack_push(esn);
+}
+
+struct except_stacknode *except_pop(void)
+{
+    struct except_stacknode *top = get_top();
+    set_top(top->down);
+    return top;
+}
+
+__noreturn void except_rethrow(except_t *except)
+{
+    struct except_stacknode *top = get_top();
+    WARN_ON(top == 0);
+    WARN_ON(top->type != XCEPT_CATCHER);
+    WARN_ON(&top->info.catcher->obj != except);
+    set_top(top->down);
+    do_throw(except);
+}
+
+__noreturn void except_throw(long group, long code, const char *msg)
+{
+    except_t except;
+
+    except.id.group = group;
+    except.id.code = code;
+    except.message = msg;
+    except.dyndata = 0;
+
+    do_throw(&except);
+}
+
+__noreturn void except_throwd(long group, long code, const char *msg, void *data)
+{
+    except_t except;
+
+    except.id.group = group;
+    except.id.code = code;
+    except.message = msg;
+    except.dyndata = data;
+
+    do_throw(&except);
+}
+
+__noreturn void except_vthrowf(long group, long code, const char *fmt,
+			       va_list vl)
+{
+    char *buf = except_alloc(XCEPT_BUFFER_SIZE);
+
+    vsnprintf(buf, XCEPT_BUFFER_SIZE, fmt, vl);
+    except_throwd(group, code, buf, buf);
+}
+
+__noreturn void except_throwf(long group, long code, const char *fmt, ...)
+{
+    va_list vl;
+
+    va_start (vl, fmt);
+    except_vthrowf(group, code, fmt, vl);
+    va_end (vl);
+}
+
+void (*except_unhandled_catcher(void (*new_catcher)(except_t *)))(except_t *)
+{
+    void (*old_catcher)(except_t *) = get_catcher();
+    set_catcher(new_catcher);
+    return old_catcher;
+}
+
+#undef except_code
+#undef except_group
+#undef except_message
+#undef except_data
+
+unsigned long except_code(except_t *ex)
+{
+    return ex->id.code;
+}
+
+unsigned long except_group(except_t *ex)
+{
+    return ex->id.group;
+}
+
+const char *except_message(except_t *ex)
+{
+    return ex->message;
+}
+
+void *except_data(except_t *ex)
+{
+    return ex->dyndata;
+}
+
+void *except_take_data(except_t *ex)
+{
+    void *data = ex->dyndata;
+    ex->dyndata = 0;
+    return data;
+}
+
+void except_set_allocator(void *(*alloc)(size_t), void (*dealloc)(void *))
+{
+    set_alloc(alloc);
+    set_dealloc(dealloc);
+}
+
+void *except_alloc(size_t size)
+{
+    void *ptr = get_alloc()(size);
+
+    if (ptr == 0)
+	except_throw(XCEPT_BAD_ALLOC, 0, "out of memory");
+    return ptr;
+}
+
+void except_free(void *ptr)
+{
+    get_dealloc()(ptr);
+}
diff --git a/include/except.h b/include/except.h
new file mode 100644
index 000000000000..ccf99d5d03d3
--- /dev/null
+++ b/include/except.h
@@ -0,0 +1,156 @@
+/*
+ * Portable Exception Handling for ANSI C.
+ * Copyright (C) 1999 Kaz Kylheku <kaz@ashi.footprints.net>
+ *
+ * modified for barebox:
+ * Copyright (C) 2019 Ahmad Fatoum
+ *
+ * Free Software License:
+ *
+ * All rights are reserved by the author, with the following exceptions:
+ * Permission is granted to freely reproduce and distribute this software,
+ * possibly in exchange for a fee, provided that this copyright notice appears
+ * intact. Permission is also granted to adapt this software to produce
+ * derivative works, as long as the modified versions carry this copyright
+ * notice and additional notices stating that the work has been modified.
+ * This source code may be translated into executable form and incorporated
+ * into proprietary software; there is no requirement for such software to
+ * contain a copyright notice related to this source.
+ *
+ * $Id: except.h,v 1.18.2.1 2001/07/25 03:37:06 kaz Exp $
+ * $Name: kazlib_1_20 $
+ */
+
+#ifndef XCEPT_H
+#define XCEPT_H
+
+#include <asm/setjmp.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <linux/bug.h>
+
+#define LogicError			0
+#define RuntimeError			1
+#define OutOfMemoryError		2
+#define UndefinedInstructionException	3
+#define	SoftwareInterruptException	4
+#define	PrefetchAbortException		5
+#define	DataAbortException		6
+#define	FiqException			7
+#define	IrqException			8
+
+#define XCEPT_GROUP_ANY	0
+#define XCEPT_CODE_ANY	0
+#define XCEPT_BAD_ALLOC	1
+
+#define XCEPT_DATA_NONDYN(x) ((void *volatile)((unsigned long)x | 1))
+
+enum { except_no_call, except_call };
+
+typedef struct {
+	unsigned long except_group;
+	unsigned long except_code;
+} except_id_t;
+
+typedef struct {
+	except_id_t volatile except_id;
+	const char *volatile except_message;
+	void *volatile except_dyndata;
+} except_t;
+
+struct except_cleanup {
+	void (*except_func)(void *);
+	void *except_context;
+};
+
+struct except_catch {
+	const except_id_t *except_id;
+	size_t except_size;
+	except_t except_obj;
+	jmp_buf except_jmp;
+};
+
+enum except_stacktype {
+	XCEPT_CLEANUP, XCEPT_CATCHER
+};
+
+struct except_stacknode {
+	struct except_stacknode *except_down;
+	enum except_stacktype except_type;
+	union {
+		struct except_catch *except_catcher;	
+		struct except_cleanup *except_cleanup;
+	} except_info;
+};
+
+/* private functions made external so they can be used in macros */
+void except_setup_clean(struct except_stacknode *,
+			struct except_cleanup *, void (*)(void *), void *);
+void except_setup_try(struct except_stacknode *,
+		      struct except_catch *, const except_id_t [], size_t);
+struct except_stacknode *except_pop(void);
+
+/* public interface functions */
+int except_init(void);
+void except_deinit(void);
+__noreturn void except_rethrow(except_t *);
+__noreturn void except_throw(long, long, const char *);
+__noreturn void except_throwd(long, long, const char *, void *);
+__noreturn void except_vthrowf(long group, long code, const char *fmt, va_list vl);
+__noreturn void except_throwf(long, long, const char *, ...) __printf(3, 4);
+void (*except_unhandled_catcher(void (*)(except_t *)))(except_t *);
+unsigned long except_code(except_t *);
+unsigned long except_group(except_t *);
+const char *except_message(except_t *);
+void *except_data(except_t *);
+void *except_take_data(except_t *);
+void except_set_allocator(void *(*)(size_t), void (*)(void *));
+void *except_alloc(size_t);
+void except_free(void *);
+
+#define except_code(E) ((E)->except_id.except_code)
+#define except_group(E) ((E)->except_id.except_group)
+#define except_message(E) ((E)->except_message)
+#define except_data(E) ((E)->except_dyndata)
+
+/* void except_cleanup_push(void (*)(void *), void *); */
+#define except_cleanup_push(F, C) 				\
+{								\
+	struct except_stacknode except_sn;			\
+	struct except_cleanup except_cl;			\
+	except_setup_clean(&except_sn, &except_cl, F, C)
+
+/* void except_cleanup_pop(int); */
+#define except_cleanup_pop(E)					\
+	except_pop();						\
+	if (E)							\
+	except_cl.except_func(except_cl.except_context);	\
+}
+
+/* void except_checked_cleanup_pop(void (*)(void *), int); */
+#define except_checked_cleanup_pop(F, E)			\
+	except_pop();						\
+	WARN_ON(except_cl.except_func != (F));			\
+	if (E)							\
+	except_cl.except_func(except_cl.except_context);	\
+	}
+
+/* void except_try_push(const except_id_t [], size_t, except_t **); */
+#define except_try_push(ID, NUM, PPE)				\
+{								\
+	struct except_stacknode except_sn;			\
+	struct except_catch except_ch;				\
+	except_setup_try(&except_sn, &except_ch, ID, NUM);	\
+	if (setjmp(except_ch.except_jmp))			\
+	*(PPE) = &except_ch.except_obj;				\
+	else							\
+	*(PPE) = 0
+
+/* void except_try_pop(void); */
+#define except_try_pop()							\
+	if ((((unsigned long)except_ch.except_obj.except_dyndata) & 1) == 0)	\
+		except_free(except_ch.except_obj.except_dyndata);		\
+	except_pop();								\
+}
+
+#endif
diff --git a/include/exceptions.h b/include/exceptions.h
new file mode 100644
index 000000000000..8183b7f86fbb
--- /dev/null
+++ b/include/exceptions.h
@@ -0,0 +1,419 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 1998 Gerald Combs, original Wireshark code
+ * Copyright 2019 Ahmad Fatoum, barebox port
+ *
+ */
+
+#ifndef __EXCEPTIONS_H__
+#define __EXCEPTIONS_H__
+
+#include "except.h"
+
+/* We've only one exception group, to make these macros simple */
+#define XCEPT_GROUP_BAREBOX 1
+
+#define CATCH_ERRORS \
+	CATCH3(LogicError, RuntimeError, OutOfMemoryError)
+
+#define CATCH_EXCEPTIONS \
+	CATCH6(UndefinedInstructionException, \
+		SoftwareInterruptException, \
+		PrefetchAbortException, \
+		DataAbortException, \
+		FiqException, \
+		IrqException)
+
+/* Usage:
+ *
+ * TRY {
+ * 	code;
+ * }
+ *
+ * CATCH(exception) {
+ * 	code;
+ * }
+ *
+ * CATCH2(exception1, exception2) {
+ * 	code;
+ * }
+ *
+ * CATCH3(exception1, exception2, exception3) {
+ * 	code;
+ * }
+ *
+ * CATCH4(exception1, exception2, exception3, exception4) {
+ * 	code;
+ * }
+ *
+ * CATCH5(exception1, exception2, exception3, exception4, exception5) {
+ * 	code;
+ * }
+ *
+ * CATCH6(exception1, exception2, exception3, exception4, exception5, exception6) {
+ * 	code;
+ * }
+ *
+ * CATCH7(exception1, exception2, exception3, exception4, exception5, exception6, exception7) {
+ * 	code;
+ * }
+ *
+ * CATCH8(exception1, exception2, exception3, exception4, exception5, exception6, exception7, exception8) {
+ * 	code;
+ * }
+ *
+ * CATCH9(exception1, exception2, exception3, exception4, exception5, exception6, exception7, exception8, exception9) {
+ * 	code;
+ * }
+ *
+ * CATCH_NONFATAL_ERRORS {
+ *	code;
+ * }
+ *
+ * CATCH_BOUNDS_ERRORS {
+ *	code;
+ * }
+ *
+ * CATCH_ALL {
+ * 	code;
+ * }
+ *
+ * FINALLY {
+ * 	code;
+ * }
+ *
+ * ENDTRY;
+ *
+ * ********* Never use 'goto' or 'return' inside the TRY, CATCH*, or
+ * ********* FINALLY blocks. Execution must proceed through ENDTRY before
+ * ********* branching out.
+ *
+ * This is really something like:
+ *
+ * {
+ * 	caught = FALSE:
+ * 	x = setjmp();
+ * 	if (x == 0) {
+ * 		<TRY code>
+ * 	}
+ * 	if (!caught && x == 1) {
+ * 		caught = TRUE;
+ * 		<CATCH(1) code>
+ * 	}
+ * 	if (!caught && x == 2) {
+ * 		caught = TRUE;
+ * 		<CATCH(2) code>
+ * 	}
+ * 	if (!caught && (x == 3 || x == 4)) {
+ * 		caught = TRUE;
+ * 		<CATCH2(3,4) code>
+ * 	}
+ * 	if (!caught && (x == 5 || x == 6 || x == 7)) {
+ * 		caught = TRUE;
+ * 		<CATCH3(5,6,7) code>
+ * 	}
+ * 	if (!caught && x != 0) {
+ *		caught = TRUE;
+ * 		<CATCH_ALL code>
+ * 	}
+ * 	<FINALLY code>
+ * 	if(!caught) {
+ *      	RETHROW(x)
+ * 	}
+ * }<ENDTRY tag>
+ *
+ * All CATCH's must precede a CATCH_ALL.
+ * FINALLY must occur after any CATCH or CATCH_ALL.
+ * ENDTRY marks the end of the TRY code.
+ * TRY and ENDTRY are the mandatory parts of a TRY block.
+ * CATCH, CATCH_ALL, and FINALLY are all optional (although
+ * you'll probably use at least one, otherwise why "TRY"?)
+ *
+ * GET_MESSAGE	returns string ptr to exception message
+ * 		when exception is thrown via THROW_MESSAGE()
+ *
+ * To throw/raise an exception.
+ *
+ * THROW(exception)
+ * RETHROW				rethrow the caught exception
+ *
+ * A cleanup callback is a function called in case an exception occurs
+ * and is not caught. It should be used to free any dynamically-allocated data.
+ * A pop or call_and_pop should occur at the same statement-nesting level
+ * as the push.
+ *
+ * CLEANUP_CB_PUSH(func, data)
+ * CLEANUP_CB_POP
+ * CLEANUP_CB_CALL_AND_POP
+ */
+
+/* we do up to three passes through the bit of code after except_try_push(),
+ * and except_state is used to keep track of where we are.
+ */
+#define EXCEPT_CAUGHT   1 /* exception has been caught, no need to rethrow at
+                           * ENDTRY */
+
+#define EXCEPT_RETHROWN 2 /* the exception was rethrown from a CATCH
+                           * block. Don't reenter the CATCH blocks, but do
+                           * execute FINALLY and rethrow at ENDTRY */
+
+#define EXCEPT_FINALLY  4 /* we've entered the FINALLY block - don't allow
+                           * RETHROW, and don't reenter FINALLY if a
+                           * different exception is thrown */
+
+#define TRY \
+{\
+	except_t *volatile exc; \
+	volatile int except_state = 0; \
+	static const except_id_t catch_spec[] = { \
+		{ XCEPT_GROUP_BAREBOX, XCEPT_CODE_ANY } }; \
+	except_try_push(catch_spec, 1, &exc); \
+	                                               \
+    	if(except_state & EXCEPT_CAUGHT)               \
+            except_state |= EXCEPT_RETHROWN;           \
+	except_state &= ~EXCEPT_CAUGHT;                \
+	                                               \
+	if (except_state == 0 && exc == 0)             \
+		/* user's code goes here */
+
+#define ENDTRY \
+	/* rethrow the exception if necessary */ \
+	if(!(except_state&EXCEPT_CAUGHT) && exc != 0)  \
+	    except_rethrow(exc);                 \
+	except_try_pop();\
+}
+
+/* the (except_state |= EXCEPT_CAUGHT) in the below is a way of setting
+ * except_state before the user's code, without disrupting the user's code if
+ * it's a one-liner.
+ */
+#define CATCH(x) \
+	if (except_state == 0 && exc != 0 && \
+	    exc->except_id.except_code == (x) && \
+	    (except_state |= EXCEPT_CAUGHT)) \
+		/* user's code goes here */
+
+#define CATCH2(x,y) \
+	if (except_state == 0 && exc != 0 && \
+	    (exc->except_id.except_code == (x) || \
+	     exc->except_id.except_code == (y)) && \
+	    (except_state|=EXCEPT_CAUGHT)) \
+		/* user's code goes here */
+
+#define CATCH3(x,y,z) \
+	if (except_state == 0 && exc != 0 && \
+	    (exc->except_id.except_code == (x) || \
+	     exc->except_id.except_code == (y) || \
+	     exc->except_id.except_code == (z)) && \
+	    (except_state|=EXCEPT_CAUGHT)) \
+		/* user's code goes here */
+
+#define CATCH4(w,x,y,z) \
+	if (except_state == 0 && exc != 0 && \
+	    (exc->except_id.except_code == (w) || \
+	     exc->except_id.except_code == (x) || \
+	     exc->except_id.except_code == (y) || \
+	     exc->except_id.except_code == (z)) && \
+	    (except_state|=EXCEPT_CAUGHT)) \
+		/* user's code goes here */
+
+#define CATCH5(v,w,x,y,z) \
+	if (except_state == 0 && exc != 0 && \
+	    (exc->except_id.except_code == (v) || \
+	     exc->except_id.except_code == (w) || \
+	     exc->except_id.except_code == (x) || \
+	     exc->except_id.except_code == (y) || \
+	     exc->except_id.except_code == (z)) && \
+	    (except_state|=EXCEPT_CAUGHT)) \
+		/* user's code goes here */
+
+#define CATCH6(u,v,w,x,y,z) \
+	if (except_state == 0 && exc != 0 && \
+	    (exc->except_id.except_code == (u) || \
+	     exc->except_id.except_code == (v) || \
+	     exc->except_id.except_code == (w) || \
+	     exc->except_id.except_code == (x) || \
+	     exc->except_id.except_code == (y) || \
+	     exc->except_id.except_code == (z)) && \
+	    (except_state|=EXCEPT_CAUGHT)) \
+		/* user's code goes here */
+
+#define CATCH7(t,u,v,w,x,y,z) \
+	if (except_state == 0 && exc != 0 && \
+	    (exc->except_id.except_code == (t) || \
+	     exc->except_id.except_code == (u) || \
+	     exc->except_id.except_code == (v) || \
+	     exc->except_id.except_code == (w) || \
+	     exc->except_id.except_code == (x) || \
+	     exc->except_id.except_code == (y) || \
+	     exc->except_id.except_code == (z)) && \
+	    (except_state|=EXCEPT_CAUGHT)) \
+		/* user's code goes here */
+
+#define CATCH8(s,t,u,v,w,x,y,z) \
+	if (except_state == 0 && exc != 0 && \
+	    (exc->except_id.except_code == (s) || \
+	    (exc->except_id.except_code == (t) || \
+	     exc->except_id.except_code == (u) || \
+	     exc->except_id.except_code == (v) || \
+	     exc->except_id.except_code == (w) || \
+	     exc->except_id.except_code == (x) || \
+	     exc->except_id.except_code == (y) || \
+	     exc->except_id.except_code == (z)) && \
+	    (except_state|=EXCEPT_CAUGHT)) \
+		/* user's code goes here */
+
+#define CATCH9(r,s,t,u,v,w,x,y,z) \
+	if (except_state == 0 && exc != 0 && \
+	    (exc->except_id.except_code == (r) || \
+	    (exc->except_id.except_code == (s) || \
+	    (exc->except_id.except_code == (t) || \
+	     exc->except_id.except_code == (u) || \
+	     exc->except_id.except_code == (v) || \
+	     exc->except_id.except_code == (w) || \
+	     exc->except_id.except_code == (x) || \
+	     exc->except_id.except_code == (y) || \
+	     exc->except_id.except_code == (z)) && \
+	    (except_state|=EXCEPT_CAUGHT)) \
+		/* user's code goes here */
+
+#define CATCH_ALL \
+	if (except_state == 0 && exc != 0 && \
+	    (except_state|=EXCEPT_CAUGHT)) \
+		/* user's code goes here */
+
+#define FINALLY \
+	if( !(except_state & EXCEPT_FINALLY) && (except_state|=EXCEPT_FINALLY)) \
+		/* user's code goes here */
+
+#define THROW(x) \
+	except_throw(XCEPT_GROUP_BAREBOX, (x), NULL)
+
+#define THROW_ON(cond, x) do { \
+	if ((cond)) \
+		except_throw(XCEPT_GROUP_BAREBOX, (x), NULL); \
+} while (0)
+
+#define THROW_MESSAGE(x, y) \
+	except_throw(XCEPT_GROUP_BAREBOX, (x), (y))
+
+#define THROW_MESSAGE_ON(cond, x, y) do { \
+	if ((cond)) \
+		except_throw(XCEPT_GROUP_BAREBOX, (x), (y)); \
+} while (0)
+
+#define THROW_DATA(x, s, d) \
+	except_throwd(XCEPT_GROUP_BAREBOX, (x), (s), (d))
+
+#define THROW_DATA_ON(cond, x, s, d) do { \
+	if ((cond)) \
+		except_throwd(XCEPT_GROUP_BAREBOX, (x), (s), (d)); \
+} while (0)
+
+/* Throws a formatted message, its memory is cleared after catching it. */
+#define THROW_FORMATTED(x, ...) \
+	except_throwf(XCEPT_GROUP_BAREBOX, (x), __VA_ARGS__)
+
+/* Like THROW_FORMATTED, but takes a va_list as an argument */
+#define VTHROW_FORMATTED(x, format, args) \
+	except_vthrowf(XCEPT_GROUP_BAREBOX, (x), format, args)
+
+#define GET_MESSAGE			except_message(exc)
+#define GET_DATA			except_data(exc)
+
+#define RETHROW                                     \
+    {                                               \
+        /* check we're in a catch block */          \
+        WARN_ON(except_state != EXCEPT_CAUGHT);     \
+	/* we can't use except_rethrow here, as that pops a catch block \
+	 * off the stack, and we don't want to do that, because we want to \
+	 * excecute the FINALLY {} block first.     \
+	 * except_throw doesn't provide an interface to rethrow an existing \
+	 * exception; however, longjmping back to except_try_push() has the \
+	 * desired effect.			    \
+	 *					    \
+	 * Note also that THROW and RETHROW should provide much the same \
+	 * functionality in terms of which blocks to enter, so any messing \
+	 * about with except_state in here would indicate that THROW is \
+	 * doing the wrong thing.                   \
+	 */					    \
+        longjmp(except_ch.except_jmp,1);            \
+    }
+
+#define EXCEPT_CODE			except_code(exc)
+
+/* Register cleanup functions in case an exception is thrown and not caught.
+ * From the Kazlib documentation, with modifications for use with the
+ * Wireshark-specific macros:
+ *
+ * CLEANUP_PUSH(func, arg)
+ *
+ *  The call to CLEANUP_PUSH shall be matched with a call to
+ *  CLEANUP_CALL_AND_POP or CLEANUP_POP which must occur in the same
+ *  statement block at the same level of nesting. This requirement allows
+ *  an implementation to provide a CLEANUP_PUSH macro which opens up a
+ *  statement block and a CLEANUP_POP which closes the statement block.
+ *  The space for the registered pointers can then be efficiently
+ *  allocated from automatic storage.
+ *
+ *  The CLEANUP_PUSH macro registers a cleanup handler that will be
+ *  called if an exception subsequently occurs before the matching
+ *  CLEANUP_[CALL_AND_]POP is executed, and is not intercepted and
+ *  handled by a try-catch region that is nested between the two.
+ *
+ *  The first argument to CLEANUP_PUSH is a pointer to the cleanup
+ *  handler, a function that returns nothing and takes a single
+ *  argument of type void*. The second argument is a void* value that
+ *  is registered along with the handler.  This value is what is passed
+ *  to the registered handler, should it be called.
+ *
+ *  Cleanup handlers are called in the reverse order of their nesting:
+ *  inner handlers are called before outer handlers.
+ *
+ *  The program shall not leave the cleanup region between
+ *  the call to the macro CLEANUP_PUSH and the matching call to
+ *  CLEANUP_[CALL_AND_]POP by means other than throwing an exception,
+ *  or calling CLEANUP_[CALL_AND_]POP.
+ *
+ *  Within the call to the cleanup handler, it is possible that new
+ *  exceptions may happen.  Such exceptions must be handled before the
+ *  cleanup handler terminates. If the call to the cleanup handler is
+ *  terminated by an exception, the behavior is undefined. The exception
+ *  which triggered the cleanup is not yet caught; thus the program
+ *  would be effectively trying to replace an exception with one that
+ *  isn't in a well-defined state.
+ *
+ *
+ * CLEANUP_POP and CLEANUP_CALL_AND_POP
+ *
+ *  A call to the CLEANUP_POP or CLEANUP_CALL_AND_POP macro shall match
+ *  each call to CLEANUP_PUSH which shall be in the same statement block
+ *  at the same nesting level.  It shall match the most recent such a
+ *  call that is not matched by a previous CLEANUP_[CALL_AND_]POP at
+ *  the same level.
+ *
+ *  These macros causes the registered cleanup handler to be removed. If
+ *  CLEANUP_CALL_AND_POP is called, the cleanup handler is called.
+ *  In that case, the registered context pointer is passed to the cleanup
+ *  handler. If CLEANUP_POP is called, the cleanup handler is not called.
+ *
+ *  The program shall not leave the region between the call to the
+ *  macro CLEANUP_PUSH and the matching call to CLEANUP_[CALL_AND_]POP
+ *  other than by throwing an exception, or by executing the
+ *  CLEANUP_CALL_AND_POP.
+ *
+ */
+
+
+#define CLEANUP_PUSH(f,a)		except_cleanup_push((f),(a))
+#define CLEANUP_POP			except_cleanup_pop(0)
+#define CLEANUP_CALL_AND_POP		except_cleanup_pop(1)
+
+/* Variants to allow nesting of except_cleanup_push w/o "shadowing" variables */
+#define CLEANUP_PUSH_PFX(pfx,f,a)	except_cleanup_push_pfx(pfx,(f),(a))
+#define CLEANUP_POP_PFX(pfx)		except_cleanup_pop_pfx(pfx,0)
+#define CLEANUP_CALL_AND_POP_PFX(pfx)	except_cleanup_pop_pfx(pfx,1)
+
+
+
+#endif /* __EXCEPTIONS_H__ */
-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

  reply	other threads:[~2020-04-01  9:31 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-04-01  9:31 [RFC PATCH 0/4] ARM: introduce sjlj structured " Ahmad Fatoum
2020-04-01  9:31 ` Ahmad Fatoum [this message]
2020-04-02 19:51   ` [RFC PATCH 1/4] ARM: implement sjlj-based TRY/CATCH " Roland Hieber
2020-04-03  6:09     ` Ahmad Fatoum
2020-04-01  9:31 ` [RFC PATCH 2/4] startup: wrap barebox startup in TRY/CATCH Ahmad Fatoum
2020-04-01  9:31 ` [RFC PATCH 3/4] ARM: rethrow CPU exceptions as sjlj-exceptions Ahmad Fatoum
2020-04-01  9:31 ` [RFC PATCH 4/4] commands: implement except test command Ahmad Fatoum

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200401093104.959691-2-ahmad@a3f.at \
    --to=ahmad@a3f.at \
    --cc=barebox@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox