Linux debugging

Check our new training course

Linux debugging, tracing, profiling & perf. analysis

Check our new training course
with Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

This is a "function" patch for msh which is in use by some busybox
users. Unfortunately it is far too buggy to be applied, but maybe
it's a useful starting point for future work.

Function-related code is delimited by comments of the form
	//funccode:start
	...
	//funccode:end
for ease of grepping

An example of buggy behavior:

#f() {
#    echo foo
#    echo test`echo bar >&2`
#    echo END f
#}

function g {
#    echo 2 foo
#    echo 2 test`echo 2 bar >&2`
#    f
    echo END g
#    echo "1:'$1' 2:'$2'"
}

# Even this first block fails - it does not even call functions!
# (replacing "echo END g" above with "echo END" makes it run ok)
echo DRY RUN
    echo 2 foo
    echo 2 test`echo 2 bar >&2`
    echo END g
    echo "1:'$1' 2:'$2'"
    echo foo
    echo test`echo bar >&2`
    echo END f
echo END DRY RUN

exit

# This would fail too
g "$1-one" "two$2"
echo DONE



diff -d -urpN busybox.7/shell/msh.c busybox.8/shell/msh.c
--- busybox.7/shell/msh.c	2008-06-09 09:34:45.000000000 +0200
+++ busybox.8/shell/msh.c	2008-06-09 09:38:17.000000000 +0200
@@ -89,6 +89,14 @@ static char *itoa(int n)
 
 //#define MSHDEBUG 4
 
+/* Used only in "function" support code */
+#ifdef KSDBG //funccode:start
+      #define KSDBG_PRINT_FUNCNAME fprintf(stderr, "in %s\n", __FUNCTION__)
+#else
+      #define KSDBG_PRINT_FUNCNAME ((void)0)
+#endif
+//funccode:end
+
 #ifdef MSHDEBUG
 static int mshdbg = MSHDEBUG;
 
@@ -220,6 +228,9 @@ struct op {
 #define TASYNC  16      /* c & */
 /* Added to support "." file expansion */
 #define TDOT    17
+#define TFUNC   18 //funccode:start
+#define TRETURN 19
+ //funccode:end
 
 /* Strings for names to make debug easier */
 #ifdef MSHDEBUG
@@ -319,6 +330,27 @@ struct region {
 	int area;
 };
 
+static int func_finished; //funccode:start
+struct func {
+	char* name;
+	int begin_addr; /* pos in buffer of function */
+	int end_addr;
+};
+#define MAX_FUNCS 100
+
+static struct func funcs[MAX_FUNCS];
+
+/* the max DEPTH of function call */
+#define MAX_DEPTH 100
+static struct _frame_s {
+	int argc;
+	char **argv;
+	int saved_return_addr;
+} frame[MAX_DEPTH];
+
+static void register_func(int begin, int end);
+static struct func* find_func(char* name);
+static void exec_func(struct func* f); //funccode:end
 
 /* -------- grammar stuff -------- */
 typedef union {
@@ -347,6 +379,8 @@ typedef union {
 #define IN      272
 /* Added for "." file expansion */
 #define DOT     273
+#define FUNC    274 //funccode:start
+#define RETURN  275 //funccode:end
 
 #define	YYERRCODE 300
 
@@ -1722,6 +1756,40 @@ static struct op *simple(void)
 			(void) synio(0);
 			break;
 
+		case FUNC: { //funccode:start
+			int stop_flag;
+			int number_brace;
+			int func_begin;
+			int func_end;
+			int c;
+			while ((c = my_getc(0)) == ' ' || c == '\t'|| c == '\n') /* skip whitespace */
+				continue;
+			stop_flag = 1;
+			number_brace = 0;
+			func_begin = global_env.iobase->argp->afpos;
+			while (stop_flag) {
+				if (c == '{')
+					number_brace++;
+				if (c == '}')
+					number_brace--;
+				if (!number_brace) /* if we reach the brace of most outsite */
+					stop_flag = 0;
+				c = my_getc(0);
+			}
+			unget(c);
+			unget(c);
+			func_end = global_env.iobase->argp->afpos;
+			register_func(func_begin, func_end);
+			peeksym = 0;
+			t = NULL;
+			return t;
+		}
+		case RETURN:
+			func_finished = 1;
+			peeksym = 0;
+			t = NULL;
+			return t; //funccode:end
+
 		case WORD:
 			if (t == NULL) {
 				t = newtp();
@@ -2265,6 +2333,13 @@ static int yylex(int cf)
 	case ')':
 		startl = 1;
 		return c;
+	case '{': //funccode:start
+		c = collect(c, '}');
+		if (c != '\0')
+			return c;
+		break;
+	case '}':
+		return RETURN; //funccode:end
 	}
 
 	unget(c);
@@ -2293,9 +2368,172 @@ static int yylex(int cf)
 	}
 
 	yylval.cp = strsave(line, areanum);
+	/* To identify a subroutine */ //funccode:start
+	c = my_getc(0);
+	if (c && any(c, "(")) {
+		c = my_getc(0);
+		if (c && any(c, ")"))
+			return FUNC;
+		zzerr();
+	} else
+		unget(c);
+	/* read the first char */
+	/* To identify a function */
+	if (strcmp(yylval.cp, "function") == 0) {
+		int ret = yylex(0);
+		/* read the function name after "function" */
+		if (ret == WORD)
+			return (FUNC);
+		zzerr();
+	}
+	{
+		struct func* f = find_func(yylval.cp);
+		if (f != NULL) {
+			exec_func(f);
+			return RETURN;
+		}
+	}
+	if (yylval.cp != NULL && strcmp(yylval.cp, "return") == 0) {
+		return RETURN;
+	} //funccode:end
 	return WORD;
 }
 
+static void register_func(int begin, int end) //funccode:start
+{
+	struct func *p;
+	int i;
+        for (i = 0; i < MAX_FUNCS; i++) {
+		if (funcs[i].name == NULL) {
+			p = &funcs[i];
+			break;
+		}
+	}
+	if (i == MAX_FUNCS) {
+		fprintf(stderr, "Too much functions beyond limit\n");
+		leave();
+	}
+	p->name = xstrdup(yylval.cp);
+	//fprintf(stderr, "register function,%d,%d,%s\n", begin, end, p->name);
+	KSDBG_PRINT_FUNCNAME;
+	/* io stream */
+	p->begin_addr = begin;
+	p->end_addr = end;
+}
+
+static struct func* find_func(char* name)
+{
+	int i;
+	for (i = 0; i < MAX_FUNCS; i++) {
+		if (funcs[i].name == NULL)
+			continue;
+		if (!strcmp(funcs[i].name, name))
+			return &funcs[i];
+	}
+	KSDBG_PRINT_FUNCNAME;
+	//fprintf(stderr, "not found the function %s\n", name);
+	return NULL;
+	//zzerr();
+}
+
+/* Begin to execute the function */
+static int cur_frame = 0;
+
+static void exec_func(struct func* f)
+{
+	int c;
+	int temp_argc;
+	char** temp_argv;
+	struct iobuf *bp;
+
+	/* create a new frame, save the argument and return address to this frame */
+	frame[cur_frame].argc = dolc;
+	frame[cur_frame].argv = dolv;
+
+	cur_frame++;
+	/* do some argument parse and set arguments */
+	temp_argv = xmalloc(sizeof(char *));
+	temp_argv[0] = xstrdup(f->name);
+	temp_argc = 0;
+	global_env.iop->argp->afpos--;
+	global_env.iop->argp->afbuf->bufp--;
+//	unget(c);
+	while (((c = yylex(0)) != '\n') && (yylval.cp != NULL)) {
+		temp_argc++;
+		temp_argv = xrealloc(temp_argv, sizeof(char *) * (temp_argc+1));
+		/* parse $ var if passed argument is a variable */
+		if (yylval.cp[0] == '$') {
+			struct var *arg = lookup(&yylval.cp[1]);
+			temp_argv[temp_argc] = xstrdup(arg->value);
+			//fprintf(stderr, "arg->value=%s\n", arg->value);
+		} else {
+			temp_argv[temp_argc] = xstrdup(yylval.cp);
+			//fprintf(stderr, "ARG:%s\n", yylval.cp);
+		}
+	}
+	/*
+	global_env.iop->argp->afpos--;
+	global_env.iop->argp->afbuf->bufp--;
+	*/
+	dolc = temp_argc;
+	dolv = temp_argv;
+	//unget(c);
+	//while ((c = my_getc(0)) == ' ' || c == '\t')  /* Skip whitespace */
+	//	continue;
+	//unget(c);
+	frame[cur_frame].saved_return_addr = global_env.iop->argp->afpos;
+
+	/* get function begin address and execute this function */
+
+	bp = global_env.iop->argp->afbuf;
+	bp->bufp = &(bp->buf[f->begin_addr]);
+	global_env.iop->argp->afpos = f->begin_addr;
+
+	/* func_finished=0 means we are in a function and func_finished=1 means we are executing a function */
+	func_finished = 0;
+
+	//fprintf(stderr, "exec function %s\n", f->name);
+	KSDBG_PRINT_FUNCNAME;
+	for (;;) {
+		//fprintf(stderr, "afpos=%d,%s\n", global_env.iop->argp->afpos, yylval.cp);
+		if (global_env.iop->argp->afpos == f->end_addr)
+			break;
+		onecommand();
+		/* we return from a function, when func_finished = 1 */
+		if (func_finished)
+			break;
+	}
+
+	{
+		//fprintf(stderr, "%s is finished @%d!\n", f->name, global_env.iop->argp->afpos);
+		int ret = frame[cur_frame].saved_return_addr;
+		/* workaround code for \n */
+		if (dolc)
+			ret--;
+		/* get return address from current frame and jump to */
+		global_env.iop->argp->afpos = ret;
+		global_env.iop->argp->afbuf->bufp = &(global_env.iop->argp->afbuf->buf[ret]);
+	}
+	/*
+	fprintf(stderr, "******** after execution ********************\n");
+	fprintf(stderr, " %s \n############# %d\n", global_env.iop->argp->afbuf->bufp, ret);
+	fprintf(stderr, "*******************************\n");
+	*/
+	/* we return to previous frame */
+	cur_frame--;
+	/* free some space occupied by argument */
+	while (dolc--)
+		free(dolv[dolc]);
+	free(dolv);
+
+	/* recover argument for last function */
+	dolv = frame[cur_frame].argv;
+	dolc = frame[cur_frame].argc;
+	/* If we are not in the outest frame, we should set
+	 * func_finished to 0 that means we still in some function */
+	if (cur_frame != 0)
+		func_finished = 0;
+} //funccode:end
 
 static int collect(int c, int c1)
 {
@@ -2601,6 +2839,10 @@ static int execute(struct op *t, int *pi
 				execute(t->right->right, pin, pout, /* no_fork: */ 0);
 		}
 		break;
+	case TFUNC: //funccode:start
+		break;
+	case TRETURN:
+		break; //funccode:end
 
 	case TCASE:
 		cp = evalstr(t->str, DOSUB | DOTRIM);