Browse Source

Implements labels declaration & references

For the moment labels are only subsituted with relative notation
". +/- OFFSET"
Yann Weber 1 year ago
parent
commit
be9be061c9
17 changed files with 478 additions and 143 deletions
  1. 26
    0
      README.md
  2. 2
    2
      src/Makefile.am
  3. 8
    0
      src/asm_env.c
  4. 4
    0
      src/asm_env.h
  5. 50
    2
      src/asmsh.h
  6. 1
    1
      src/history.c
  7. 2
    0
      src/history.h
  8. 113
    2
      src/shell.c
  9. 10
    0
      src/shell.h
  10. 5
    120
      src/shell_cmd_breakpoint.c
  11. 76
    0
      src/shell_cmds.c
  12. 10
    3
      src/shell_cmds.h
  13. 106
    0
      src/shell_cmds_lib.c
  14. 43
    0
      src/shell_cmds_lib.h
  15. 6
    5
      src/shell_sym.c
  16. 9
    1
      src/shell_sym.h
  17. 7
    7
      tests/tests_shell_sym.c

+ 26
- 0
README.md View File

@@ -138,6 +138,32 @@ INFO: Breakpoint 00007f3020bec00b reached
138 138
 asmsh@0x7f3020bec00b > .regs %rbx
139 139
 rbx: 0000000000000042
140 140
 ```
141
+
142
+**Make a loop with a label**
143
+
144
+```text
145
+asmsh@0x7f0fadb73000 > mov $27, %rcx
146
+asmsh@0x7f0fadb73005 > .label print
147
+INFO: Label '@print' 00007f0fadb73005 added
148
+asmsh@0x7f0fadb73005 > push %rcx
149
+asmsh@0x7f0fadb73006 > mov $27,%rbx
150
+asmsh@0x7f0fadb7300b > sub %rcx, %rbx
151
+asmsh@0x7f0fadb7300e > add $0x0a40, %rbx
152
+asmsh@0x7f0fadb73015 > push %rbx
153
+asmsh@0x7f0fadb73016 > mov $1, %rax
154
+asmsh@0x7f0fadb7301b > mov %rax, %rdi
155
+asmsh@0x7f0fadb7301e > mov %rsp, %rsi
156
+asmsh@0x7f0fadb73021 > mov $2, %rdx
157
+asmsh@0x7f0fadb73026 > syscall
158
+@
159
+asmsh@0x7f0fadb73028 > pop %rbx
160
+asmsh@0x7f0fadb73029 > pop %rcx
161
+asmsh@0x7f0fadb7302a > .breakpoint after loop @print
162
+INFO: Set breakpoint @ 00007F0FADB7302C
163
+asmsh@0x7f0fadb73005 > .run
164
+[...]
165
+```
166
+
141 167
 ## Tests & docs
142 168
 
143 169
 **Run tests and coverage**

+ 2
- 2
src/Makefile.am View File

@@ -5,12 +5,12 @@ noinst_LIBRARIES= libcheck_asmsh.a
5 5
 
6 6
 libcheck_asmsh_a_SOURCES = mmap_parse.c asm_env.c breakpoints.c compile.c logger.c \
7 7
 			   completion.c history.c \
8
-			   shell.c shell_cmds.c shell_sym.c \
8
+			   shell.c shell_cmds.c shell_sym.c shell_cmds_lib.c \
9 9
 			   shell_cmd_breakpoint.c
10 10
 
11 11
 noinst_HEADERS = asmsh.h mmap_parse.h asm_env.h breakpoints.h compile.h logger.h \
12 12
 		completion.h history.h \
13
-		shell.h shell_cmds.h shell_sym.h syscalls.h
13
+		shell.h shell_cmds.h shell_sym.h shell_cmds_lib.h syscalls.h
14 14
 
15 15
 
16 16
 asmsh_SOURCES = asmsh.c $(libcheck_asmsh_a_SOURCES)

+ 8
- 0
src/asm_env.c View File

@@ -37,10 +37,17 @@ asmsh_env_t* asmsh_env(const char *childpath)
37 37
 		errno = err;
38 38
 		return NULL;
39 39
 	}
40
+	/** @todo better error checks & clean */
40 41
 	child_mmap_init(&(res->mmap));
41 42
 
42 43
 	asmsh_brk_init(&res->brks);
43 44
 
45
+	if(asmsh_symtable_init(&res->labels) < 0)
46
+	{
47
+		asmsh_log_perror("Unable to initialize labels symtable");
48
+		return NULL;
49
+	}
50
+
44 51
 	res->childpath = NULL;
45 52
 	if(childpath && (res->childpath = strdup(childpath)) == NULL)
46 53
 	{
@@ -89,6 +96,7 @@ void asmsh_env_free(asmsh_env_t *asmenv)
89 96
 	}
90 97
 	kill(asmenv->pid, SIGKILL);
91 98
 	asmsh_brk_free(&asmenv->brks);
99
+	asmsh_symtable_clean(&asmenv->labels);
92 100
 	free(asmenv->childpath);
93 101
 	free(asmenv);
94 102
 }

+ 4
- 0
src/asm_env.h View File

@@ -33,6 +33,7 @@
33 33
 #define ASMSH_CHILD_TEXT_MAP_SZ 0x1000 // defined in child.s
34 34
 
35 35
 typedef struct asmsh_env_s asmsh_env_t;
36
+#include "shell_sym.h"
36 37
 
37 38
 struct asmsh_env_s
38 39
 {
@@ -60,6 +61,9 @@ struct asmsh_env_s
60 61
 	/** List of breakpoints addresses in child's memory */
61 62
 	asmsh_brk_t brks;
62 63
 
64
+	/** List of labels addresses in child's memory */
65
+	asmsh_symtable_t labels;
66
+
63 67
 	/** @todo check & delete useless properties */
64 68
 	/** Pointer on the next addr where we should write some
65 69
 	 *  compiled bytecode */

+ 50
- 2
src/asmsh.h View File

@@ -63,14 +63,32 @@ jnz . + 32
63 63
 loop . - 4
64 64
 </pre>
65 65
 
66
+@par Labels
67
+Labels can be defined to ease relative jumps using the ".label" command.
68
+Labels are used using "@NAME" notation, they are substituted with
69
+". SIGN OFFSET" with SIGN and OFFSET calculated from RIP value.
70
+
71
+@par Example
72
+The instruction "loop @lbl" could be transformed in "loop . - 8"
73
+
66 74
 @section shell_cmds COMMANDS
67 75
 
76
+@par .breakpoint [CMD] [OPTS...]
77
+Handle breakpoint with different subcommands :
78
+- add (implicit) add a breakpoint at given address (or RIP value if no address given)
79
+- del remove a breakpoint at given address (or RIP)
80
+- list list all breakpoints
68 81
 @par .bytecode
69 82
 Compile an instruction and display it's bytecode
70 83
 @par .flags
71 84
 Display the CPU flags
72 85
 @par .help [COMMAND]
73 86
 Display the builtin help or the help of the command gioven as argument
87
+@par .label [NAME] [ADDR]
88
+Handle labels :
89
+- if no name given all labels are displayed.
90
+- if a name is given without address, a label is set at RIP value
91
+- if address is 0, the label is deleted
74 92
 @par .maps
75 93
 Display process memory maps
76 94
 @par .quit
@@ -82,6 +100,11 @@ Print syscalls names and numbers
82 100
 @par .reset
83 101
 Reset the shell (spawn a new process)
84 102
 
103
+@section Files
104
+
105
+@par ~/.local/share/asmsh/asmsh.history
106
+The command history file
107
+
85 108
 @section EXAMPLES
86 109
 
87 110
 @subsection example_exit Exit with a specific status
@@ -125,17 +148,42 @@ asmsh@0x7f3020bec00b > .regs \%rbx
125 148
 rbx: 0000000000000042
126 149
 </pre>
127 150
 
151
+@subsection label_loop Make a loop using a label
152
+
153
+<pre>
154
+asmsh@0x7f0fadb73000 > mov $27, \%rcx
155
+asmsh@0x7f0fadb73005 > .label print
156
+INFO: Label '@print' 00007f0fadb73005 added
157
+asmsh@0x7f0fadb73005 > push \%rcx
158
+asmsh@0x7f0fadb73006 > mov $27,\%rbx
159
+asmsh@0x7f0fadb7300b > sub \%rcx, \%rbx
160
+asmsh@0x7f0fadb7300e > add $0x0a40, \%rbx
161
+asmsh@0x7f0fadb73015 > push \%rbx
162
+asmsh@0x7f0fadb73016 > mov $1, \%rax
163
+asmsh@0x7f0fadb7301b > mov \%rax, \%rdi
164
+asmsh@0x7f0fadb7301e > mov \%rsp, \%rsi
165
+asmsh@0x7f0fadb73021 > mov $2, \%rdx
166
+asmsh@0x7f0fadb73026 > syscall
167
+@
168
+asmsh@0x7f0fadb73028 > pop \%rbx
169
+asmsh@0x7f0fadb73029 > pop \%rcx
170
+asmsh@0x7f0fadb7302a > .breakpoint after loop @print
171
+INFO: Set breakpoint @ 00007F0FADB7302C
172
+asmsh@0x7f0fadb73005 > .run
173
+[...]
174
+</pre>
175
+
128 176
 @section TODO TODOLIST
129 177
 
130 178
 @todo Implement command for memory read/dump
131
-@todo Implement symbols for jumps
132
-@todo Add support for label declarations & references
133 179
 @todo Implement write without exec
134 180
 @todo Implement function declaration
135 181
 @todo Add switch between intel's & AT&T's syntaxes.
136 182
 @todo Implement a command to run until syscall only (and another for run until
137 183
 		breakpoint or syscall ?)
138 184
 @todo Rationalise commands argument parsing (at the moment .breakpoints allready uses special stuff :/)
185
+@todo Enhance support for labels & far/abs jump
186
+@todo Implement a script run mode without the prompt & without readline
139 187
 
140 188
 @section AUTHOR
141 189
 Written by Yann Weber &lt;yann.weber@members.fsf.org&gt;

+ 1
- 1
src/history.c View File

@@ -2,7 +2,7 @@
2 2
 
3 3
 char * history_filename_init(const char *homedir)
4 4
 {
5
-	const char fmt[] = "%s/.local/share/asmsh";
5
+	const char fmt[] = ASMSH_HISTORY_PATH_FMT;
6 6
 	int sz;
7 7
 	const char *home = homedir?homedir:getenv("HOME");
8 8
 	if(!home)

+ 2
- 0
src/history.h View File

@@ -25,6 +25,8 @@
25 25
 #include <sys/types.h>
26 26
 #include <sys/stat.h>
27 27
 
28
+#define ASMSH_HISTORY_PATH_FMT "%s/.local/share/asmsh"
29
+
28 30
 static const char ASMSH_HISTORY_FILE[] = "asmsh.history";
29 31
 
30 32
 typedef void(add_history_f)(const char*);

+ 113
- 2
src/shell.c View File

@@ -80,14 +80,37 @@ void asmsh_cleanup(asmsh_t *sh)
80 80
 static int _compile_step(asmsh_t *sh, const char *cmd);
81 81
 /** Attempt to handle given shell internal command (starting with  '.') */
82 82
 static int _handle_command(asmsh_t *sh, const char *cmd);
83
-int asmsh_exec(asmsh_t *sh, const char *cmd)
83
+int asmsh_exec(asmsh_t *sh, const char *_cmd)
84 84
 {
85
-	if(!cmd)
85
+	if(!_cmd)
86 86
 	{
87 87
 		errno=EINVAL;
88 88
 		return -1;
89 89
 	}
90 90
 
91
+	char *cmd;
92
+	char prealloc_cmd[4096];
93
+	char *m_cmd = NULL;
94
+	cmd = prealloc_cmd;
95
+
96
+	size_t need_sz = asmsh_parse_labels(sh,
97
+			ASMSH_LABEL_SYM, _cmd, prealloc_cmd, 4096);
98
+	if(need_sz == 0)
99
+	{
100
+		return -1;
101
+	}
102
+
103
+	if(need_sz > 4096)
104
+	{
105
+		if(!(m_cmd = malloc(need_sz)))
106
+		{
107
+			asmsh_log_perror("Unable to allocate memory for parsed commandline");
108
+			return -1;
109
+		}
110
+		asmsh_parse_labels(sh, ASMSH_LABEL_SYM, _cmd, m_cmd, need_sz);
111
+		cmd = m_cmd;
112
+	}
113
+
91 114
 	int ret;
92 115
 	//lstrip whitespace
93 116
 	for(; *cmd && (*cmd == ' ' || *cmd == '\t'); cmd++);
@@ -102,6 +125,7 @@ int asmsh_exec(asmsh_t *sh, const char *cmd)
102 125
 			ret = _compile_step(sh, cmd);
103 126
 			break;
104 127
 	}
128
+	if(m_cmd) { free(m_cmd); }
105 129
 	if(!ret)
106 130
 	{
107 131
 		asmsh_env_update_regs(sh->env);
@@ -187,3 +211,90 @@ static int _handle_command(asmsh_t *sh, const char *cmd)
187 211
 	asmsh_cmd_args_free(args);
188 212
 	return ret;
189 213
 }
214
+
215
+
216
+size_t asmsh_parse_labels(asmsh_t *sh, char preffix, const char *cmd,
217
+		char *buf, size_t buf_sz)
218
+{
219
+	asmsh_symtable_t *table = &sh->env->labels;
220
+	const char *cmdptr, *prevptr;
221
+	size_t res, prev_sz;
222
+	
223
+	cmdptr = prevptr = cmd;
224
+
225
+	res = 0;
226
+	prev_sz = 0;
227
+	while(*cmdptr)
228
+	{
229
+		if(*cmdptr != preffix)
230
+		{
231
+			cmdptr++;
232
+			prev_sz++;
233
+			continue;
234
+		}
235
+		if(res + prev_sz < buf_sz)
236
+		{
237
+			// enough buffer to write
238
+			memcpy(buf+res, prevptr, prev_sz);
239
+		}
240
+		res += prev_sz;
241
+		cmdptr++;
242
+		prevptr = cmdptr;
243
+		prev_sz = 0;
244
+		// cmdptr is on start of the symbol, looking for end
245
+		while(*cmdptr && ( (*cmdptr >= 'a' && *cmdptr <= 'z') ||\
246
+					(*cmdptr >= 'A' && *cmdptr <= 'Z') ||\
247
+					(*cmdptr >= '0' && *cmdptr <= '9') ||\
248
+					*cmdptr == '-' || *cmdptr == '_'))
249
+		{
250
+			cmdptr++;
251
+		}
252
+		size_t symlen=cmdptr-prevptr;
253
+		char symname[symlen+1];
254
+		memcpy(symname, prevptr, symlen);
255
+		symname[symlen] = '\0';
256
+
257
+		const asmsh_sym_t *sym = asmsh_symtable_get(table, symname);
258
+		if(!sym)
259
+		{
260
+			asmsh_log_error("Symbol '%c%s' not found",
261
+					preffix, symname);
262
+			return 0;
263
+		}
264
+
265
+		const char fmt_addr[] = ". %c 0x%lx";
266
+		char sign = sym->addr > sh->env->regs.rip ? '+': '-';
267
+		size_t offset = sym->addr > sh->env->regs.rip ?  \
268
+				sym->addr - sh->env->regs.rip:\
269
+				sh->env->regs.rip - sym->addr;
270
+		int sz_print = snprintf(NULL, 0, fmt_addr, sign, offset);
271
+		if(res + sz_print + 1 < buf_sz)
272
+		{
273
+			res += snprintf(&buf[res], sz_print+1, fmt_addr, sign, offset);
274
+		}
275
+		else
276
+		{
277
+			res += sz_print;
278
+		}
279
+		prevptr=cmdptr;
280
+		prev_sz=0;
281
+	}
282
+	if(res + prev_sz < buf_sz)
283
+	{
284
+		// enough buffer to writea
285
+		memcpy(buf+res, prevptr, prev_sz);
286
+	}
287
+	res += prev_sz;
288
+	if(res < buf_sz)
289
+	{
290
+		buf[res] = '\0';
291
+		res++;
292
+	}
293
+	else if(buf_sz)
294
+	{
295
+		// ensure \0 is at end of string
296
+		buf[buf_sz-1] = '\0';
297
+	}
298
+	return res;
299
+}
300
+

+ 10
- 0
src/shell.h View File

@@ -44,6 +44,16 @@ void asmsh_cleanup(asmsh_t *sh);
44 44
 /** @return <0 on error 0 on ok 1 on exit */
45 45
 int asmsh_exec(asmsh_t *sh, const char *cmd);
46 46
 
47
+/** Parse all symbols found in given command
48
+ * @param asmsh_t* The shell
49
+ * @param char The prefix character before a symbol name
50
+ * @param const char* The string we want to parse
51
+ * @param char* A pointer on a buffer large enough to store the parsed string
52
+ * @param size_t The buffer size
53
+ * @return 0 on error else return the needed buffer size
54
+ */
55
+size_t asmsh_parse_labels(asmsh_t *sh, char preffix, const char *cmd,
56
+		char *buf, size_t buf_sz);
47 57
 
48 58
 #include "shell_cmds.h" // declares the static commands list
49 59
 

+ 5
- 120
src/shell_cmd_breakpoint.c View File

@@ -48,31 +48,6 @@ static int brk_after(asmsh_t *sh, asmsh_cmd_args_t *args, int first);
48 48
  */
49 49
 static int brk_ls(asmsh_t *sh, asmsh_cmd_args_t *args, int expr_first);
50 50
 
51
-/** Parse a breakpoint address expression
52
- *
53
- * An expression must be of the form A [OP B] with A and B
54
- * an integer or '.' for current RIP value and OP one of + or -
55
- *
56
- * @param asmsh_cmd_args_t* The breakpoint command argument list
57
- * @param int The first argument of the address expression
58
- * @param const unsigned long RIP value
59
- * @param unsigned long* A pointer on the result address
60
- *
61
- * @returns -1 on error and 0 if ok
62
- */
63
-static int parse_addr_expr(asmsh_cmd_args_t *args, int first,
64
-		const unsigned long rip,  unsigned long *addr);
65
-
66
-/** Utility function for @ref parse_addr_expr() that parse an expression value.
67
- *
68
- * An expression can be . for rip value or an unsigned long, possibly in hex, etc
69
- * @param const char* The value
70
- * @param unsigned long The value of RIP
71
- * @param unsigned long* A pointer on the result
72
- * @return -1 on error else 0
73
- */
74
-static int addr_expr_val(const char *val, unsigned long rip, unsigned long *res);
75
-
76 51
 /** Add a breakpoint and display a message about it
77 52
  * @param asmsh_t The shell
78 53
  * @param unsigned long The breakpoint address
@@ -81,11 +56,6 @@ static int addr_expr_val(const char *val, unsigned long rip, unsigned long *res)
81 56
 static int brk_add_or_err(asmsh_t *sh, unsigned long addr);
82 57
 
83 58
 
84
-void log_expr_usage()
85
-{
86
-	asmsh_log_info("Expected an address expression of the form A + B or A - B with A and B an integer or '.' (for current RIP value)");
87
-}
88
-
89 59
 void log_brk_usage()
90 60
 {
91 61
 	asmsh_log_info("Available commands are :");
@@ -97,7 +67,7 @@ void log_brk_usage()
97 67
 	asmsh_log_info("\t\tAdd a new breakpoint after an instruction");
98 68
 	asmsh_log_info("\tlist|ls|l");
99 69
 	asmsh_log_info("\t\tList existing breakpoints");
100
-	log_expr_usage();
70
+	asmsh_log_addr_expr_usage();
101 71
 }
102 72
 
103 73
 
@@ -110,7 +80,7 @@ int asmsh_cmd_breakpoint(asmsh_t *sh, asmsh_cmd_args_t *args)
110 80
 	{
111 81
 		if((ret = brk_add(sh, args, 0)) < 0)
112 82
 		{
113
-			log_expr_usage();
83
+			asmsh_log_addr_expr_usage();
114 84
 		}
115 85
 		return ret;
116 86
 	}
@@ -125,7 +95,7 @@ int asmsh_cmd_breakpoint(asmsh_t *sh, asmsh_cmd_args_t *args)
125 95
 	{
126 96
 		if((ret = brk_add(sh, args, 1)) < 0)
127 97
 		{
128
-			log_expr_usage();
98
+			asmsh_log_addr_expr_usage();
129 99
 		}
130 100
 		return ret;
131 101
 	}
@@ -191,7 +161,7 @@ static int brk_add(asmsh_t *sh, asmsh_cmd_args_t *args, int expr_first)
191 161
 
192 162
 	unsigned long addr;
193 163
 
194
-	if(parse_addr_expr(args, expr_first, rip, &addr) < 0)
164
+	if(asmsh_cmd_parse_addr_expr(args, expr_first, rip, &addr) < 0)
195 165
 	{
196 166
 		return -1;
197 167
 	}
@@ -224,7 +194,7 @@ static int brk_del(asmsh_t *sh, asmsh_cmd_args_t *args, int expr_first)
224 194
 
225 195
 	unsigned long addr;
226 196
 
227
-	if(parse_addr_expr(args, expr_first, rip, &addr) < 0)
197
+	if(asmsh_cmd_parse_addr_expr(args, expr_first, rip, &addr) < 0)
228 198
 	{
229 199
 		return -1;
230 200
 	}
@@ -258,88 +228,3 @@ static int brk_ls(asmsh_t *sh, asmsh_cmd_args_t *args, int expr_first)
258 228
 	return 0;
259 229
 }
260 230
 
261
-static int addr_expr_val(const char *val, unsigned long rip, unsigned long *res)
262
-{
263
-	if(strcmp(".", val) == 0)
264
-	{
265
-		*res = rip;
266
-		return 0;
267
-	}
268
-
269
-	char *endptr;
270
-	errno = 0;
271
-	*res = strtoul(val, &endptr, 0);
272
-	if(errno != 0)
273
-	{
274
-		return -1;
275
-	}
276
-	else if(*endptr != '\0')
277
-	{
278
-		errno = EINVAL;
279
-		return -1;
280
-	}
281
-	return 0;
282
-}
283
-
284
-static int parse_addr_expr(asmsh_cmd_args_t *args, int first,
285
-		const unsigned long rip, unsigned long *addr)
286
-{
287
-	char * const * argv = args->args + first;
288
-	const int argc = args->argc - first;
289
-
290
-	unsigned long val[2];
291
-
292
-	switch(argc)
293
-	{
294
-		case 0:
295
-			*addr = rip;
296
-			break;
297
-		case 1:
298
-			if(addr_expr_val(argv[0], rip, addr) < 0)
299
-			{
300
-				int err = errno;
301
-				asmsh_log_error("Invalid expression value '%s': %s",
302
-						argv[0], strerror(errno));
303
-				errno = err;
304
-				return -1;
305
-			}
306
-			break;
307
-		case 3:
308
-
309
-			for(int i=0; i<2; i++)
310
-			{
311
-				const char *arg = i ? argv[2]:argv[0];
312
-				if(addr_expr_val(arg, rip, &val[i]) < 0)
313
-				{
314
-
315
-					asmsh_log_error("Invalid value '%s' in expression '%s %s %s' : %s",
316
-							arg,
317
-							argv[0], argv[1], argv[2],
318
-							strerror(errno));
319
-					return -1;
320
-				}
321
-			}
322
-
323
-			const char operator = strlen(argv[1]) == 1 ? argv[1][0]: '\0';
324
-			switch(operator)
325
-			{
326
-				case '+':
327
-					*addr = val[0] + val[1];
328
-					break;
329
-				case '-':
330
-					*addr = val[0] - val[1];
331
-					break;
332
-				default:
333
-					asmsh_log_error("Invalid operator '%s' in expression '%s %s %s'",
334
-							argv[1],
335
-							argv[0], argv[1], argv[2]);
336
-					return -1;
337
-			}
338
-			break;
339
-		default:
340
-			//USAGE !
341
-			asmsh_log_error("Unexexpected argument count for an expression. Expecting one of 0, 1 or 3 but got %d", argc);
342
-			return -1;
343
-	}
344
-	return 0;
345
-}

+ 76
- 0
src/shell_cmds.c View File

@@ -224,6 +224,82 @@ int asmsh_cmd_bcode(asmsh_t *sh, asmsh_cmd_args_t *args)
224 224
 	return 0;
225 225
 }
226 226
 
227
+
228
+int asmsh_cmd_label(asmsh_t *sh, asmsh_cmd_args_t *args)
229
+{
230
+	const unsigned long rip = sh->env->regs.rip;
231
+
232
+	unsigned long addr;
233
+
234
+	if(args->argc == 0)
235
+	{
236
+		if(sh->env->labels.syms_sz == 0)
237
+		{
238
+			printf("No label defined...\n");
239
+			asmsh_log_info("Use '.label NAME [addr]' to define a label");
240
+			return 0;
241
+		}
242
+		printf("%3ld labels :\n", sh->env->labels.syms_sz);
243
+		for(size_t i=0; i<sh->env->labels.syms_sz; i++)
244
+		{
245
+			printf("\t%c%s\t %016lx\n",
246
+					ASMSH_LABEL_SYM,
247
+					sh->env->labels.syms[i].name,
248
+					sh->env->labels.syms[i].addr);
249
+		}
250
+		return 0;
251
+	}
252
+	if(args->argc == 1)
253
+	{
254
+		addr = rip;
255
+	}
256
+	else
257
+	{
258
+		if(asmsh_cmd_parse_addr_expr(args, 1, rip, &addr) < 0)
259
+		{
260
+			asmsh_log_addr_expr_usage();
261
+			return -1;
262
+		}
263
+	}
264
+
265
+	int ret;
266
+	const char *label = args->args[0];
267
+	if(addr == 0)
268
+	{
269
+		ret = asmsh_symtable_del(&sh->env->labels, label);
270
+		if(ret < 0)
271
+		{
272
+			asmsh_log_perror("Unable to delete label");
273
+			return -1;
274
+		}
275
+		else if(ret > 0)
276
+		{
277
+			asmsh_log_warning("No label '%c%s'",
278
+					ASMSH_LABEL_SYM, label);
279
+			return -1;
280
+		}
281
+		asmsh_log_info("Label '%c%s' deleted", 
282
+				ASMSH_LABEL_SYM, label);
283
+		return 0;
284
+	}
285
+	ret = asmsh_symtable_add(&sh->env->labels, label, addr);
286
+	if(ret < 0)
287
+	{
288
+		asmsh_log_perror("Unable to add label");
289
+		return -1;
290
+	}
291
+	else if(ret > 0)
292
+	{
293
+		asmsh_log_warning("Label '%c%s' updated to %016x",
294
+				ASMSH_LABEL_SYM, label, addr);
295
+		return 0;
296
+	}
297
+	asmsh_log_info("Label '%c%s' %016lx added",
298
+			ASMSH_LABEL_SYM, label, addr);
299
+	return ret;
300
+}
301
+
302
+
227 303
 int asmsh_cmd_maps(asmsh_t *sh, asmsh_cmd_args_t *args)
228 304
 {
229 305
 	if(asmsh_env_update_maps(sh->env) < 0)

+ 10
- 3
src/shell_cmds.h View File

@@ -34,6 +34,8 @@ typedef struct asmsh_cmd_s asmsh_cmd_t;
34 34
 typedef struct asmsh_cmd_args_s asmsh_cmd_args_t;
35 35
 #include "shell.h"
36 36
 #include "asm_env.h"
37
+#include "shell_cmds_lib.h"
38
+#include "shell_sym.h"
37 39
 
38 40
 /** @return <0 on recoverable error 0 on ok, 1+status on exit */
39 41
 typedef int (asmsh_cmd_f)(asmsh_t*, asmsh_cmd_args_t*);
@@ -82,7 +84,6 @@ void asmsh_cmd_args_free(asmsh_cmd_args_t *args);
82 84
 
83 85
 const char *asmsh_cmd_help(asmsh_t *sh);
84 86
 
85
-
86 87
 /*
87 88
  * Commands declaration
88 89
  *
@@ -93,13 +94,16 @@ const char *asmsh_cmd_help(asmsh_t *sh);
93 94
 // Quit the shell
94 95
 int asmsh_cmd_quit(asmsh_t *sh, asmsh_cmd_args_t *args);
95 96
 
96
-// Defined in @file shell_cmd_breakpoint.c
97
+// Defined in @ref shell_cmd_breakpoint.c
97 98
 int asmsh_cmd_breakpoint(asmsh_t *sh, asmsh_cmd_args_t *args);
98 99
 
99 100
 // Print an instruction bytecode 
100 101
 int asmsh_cmd_bcode_(asmsh_t *sh, char *buf, int bufsz, int argc, char **args);
101 102
 int asmsh_cmd_bcode(asmsh_t *sh, asmsh_cmd_args_t *args);
102 103
 
104
+// Defined in @ref shell_cmd_label.c
105
+int asmsh_cmd_label(asmsh_t* sh, asmsh_cmd_args_t *args);
106
+
103 107
 int asmsh_cmd_maps(asmsh_t *sh, asmsh_cmd_args_t *args);
104 108
 
105 109
 int asmsh_cmd_print_regs(asmsh_t *sh, asmsh_cmd_args_t *args);
@@ -124,7 +128,7 @@ int asmsh_cmd_help_(asmsh_t *sh, asmsh_cmd_args_t *args);
124 128
  */
125 129
 static const asmsh_cmd_t asmsh_CMDS[] = {
126 130
 	{".breakpoint", asmsh_cmd_breakpoint, 3,
127
-	 ".br(eakpoint)", "[addr]",
131
+	 ".br(eakpoint)", "[add|del|list] [addr]",
128 132
 	 "Set a breakpoint"},
129 133
 	{".bytecode", asmsh_cmd_bcode, 2,
130 134
 	 ".b(ytecode)", "",
@@ -135,6 +139,9 @@ static const asmsh_cmd_t asmsh_CMDS[] = {
135 139
 	{".help", asmsh_cmd_help_, 2,
136 140
 	 ".h(elp)","[cmd]",
137 141
 	 "display this help or the help of specified command"},
142
+	{".label", asmsh_cmd_label, 2,
143
+	 ".l(abel)", "label_name [addr]",
144
+	 "Set a label at given address (or . if none given)"},
138 145
 	{".maps", asmsh_cmd_maps, 2,
139 146
 	 ".m(aps)", "",
140 147
 	 "display memory maps"},

+ 106
- 0
src/shell_cmds_lib.c View File

@@ -0,0 +1,106 @@
1
+#include "shell_cmds_lib.h"
2
+
3
+/** Utility function for @ref asmsh_cmd_parse_addr_expr() that parse an expression value.
4
+ *
5
+ * An expression can be . for rip value or an unsigned long, possibly in hex, etc
6
+ * @param const char* The value
7
+ * @param unsigned long The value of RIP
8
+ * @param unsigned long* A pointer on the result
9
+ * @return -1 on error else 0
10
+ */
11
+static int addr_expr_val(const char *val, unsigned long rip, unsigned long *res);
12
+
13
+
14
+void asmsh_log_addr_expr_usage()
15
+{
16
+	asmsh_log_info("Expected an address expression of the form A + B or A - B with A and B an integer or '.' (for current RIP value)");
17
+}
18
+
19
+
20
+int asmsh_cmd_parse_addr_expr(asmsh_cmd_args_t *args, int first,
21
+		const unsigned long rip, unsigned long *addr)
22
+{
23
+	char * const * argv = args->args + first;
24
+	const int argc = args->argc - first;
25
+
26
+	unsigned long val[2];
27
+
28
+	switch(argc)
29
+	{
30
+		case 0:
31
+			*addr = rip;
32
+			break;
33
+		case 1:
34
+			if(addr_expr_val(argv[0], rip, addr) < 0)
35
+			{
36
+				int err = errno;
37
+				asmsh_log_error("Invalid expression value '%s': %s",
38
+						argv[0], strerror(errno));
39
+				errno = err;
40
+				return -1;
41
+			}
42
+			break;
43
+		case 3:
44
+
45
+			for(int i=0; i<2; i++)
46
+			{
47
+				const char *arg = i ? argv[2]:argv[0];
48
+				if(addr_expr_val(arg, rip, &val[i]) < 0)
49
+				{
50
+
51
+					asmsh_log_error("Invalid value '%s' in expression '%s %s %s' : %s",
52
+							arg,
53
+							argv[0], argv[1], argv[2],
54
+							strerror(errno));
55
+					return -1;
56
+				}
57
+			}
58
+
59
+			const char operator = strlen(argv[1]) == 1 ? argv[1][0]: '\0';
60
+			switch(operator)
61
+			{
62
+				case '+':
63
+					*addr = val[0] + val[1];
64
+					break;
65
+				case '-':
66
+					*addr = val[0] - val[1];
67
+					break;
68
+				default:
69
+					asmsh_log_error("Invalid operator '%s' in expression '%s %s %s'",
70
+							argv[1],
71
+							argv[0], argv[1], argv[2]);
72
+					return -1;
73
+			}
74
+			break;
75
+		default:
76
+			//USAGE !
77
+			asmsh_log_error("Unexexpected argument count for an expression. Expecting one of 0, 1 or 3 but got %d", argc);
78
+			return -1;
79
+	}
80
+	return 0;
81
+}
82
+
83
+
84
+static int addr_expr_val(const char *val, unsigned long rip, unsigned long *res)
85
+{
86
+	if(strcmp(".", val) == 0)
87
+	{
88
+		*res = rip;
89
+		return 0;
90
+	}
91
+
92
+	char *endptr;
93
+	errno = 0;
94
+	*res = strtoul(val, &endptr, 0);
95
+	if(errno != 0)
96
+	{
97
+		return -1;
98
+	}
99
+	else if(*endptr != '\0')
100
+	{
101
+		errno = EINVAL;
102
+		return -1;
103
+	}
104
+	return 0;
105
+}
106
+

+ 43
- 0
src/shell_cmds_lib.h View File

@@ -0,0 +1,43 @@
1
+/* Copyright Yann Weber <asmsh@yannweb.net>
2
+   This file is part of asmsh.
3
+
4
+   asmsh is free software: you can redistribute it and/or modify it under the
5
+   terms of the GNU General Public License as published by the Free Software
6
+   Foundation, either version 3 of the License, or any later version.
7
+   
8
+   asmsh is distributed in the hope that it will be useful, but WITHOUT ANY
9
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10
+   FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
11
+   details.
12
+   
13
+   You should have received a copy of the GNU General Public License along
14
+   with asmsh. If not, see <https://www.gnu.org/licenses/>.
15
+*/
16
+#ifndef ASMSH_SHELL_CMDS_LIB_H
17
+#define ASMSH_SHELL_CMDS_LIB_H
18
+#include "config.h"
19
+
20
+#include "logger.h"
21
+#include "shell_cmds.h"
22
+
23
+/** Logs address expression usage */
24
+void asmsh_log_addr_expr_usage();
25
+
26
+
27
+/** Parse an address expression
28
+ *
29
+ * An expression must be of the form A [OP B] with A and B
30
+ * an integer or '.' for current RIP value and OP one of + or -
31
+ *
32
+ * @param asmsh_cmd_args_t* The breakpoint command argument list
33
+ * @param int The first argument of the address expression
34
+ * @param const unsigned long RIP value
35
+ * @param unsigned long* A pointer on the result address
36
+ *
37
+ * @returns -1 on error and 0 if ok
38
+ */
39
+int asmsh_cmd_parse_addr_expr(asmsh_cmd_args_t *args, int first,
40
+		const unsigned long rip,  unsigned long *addr);
41
+
42
+#endif
43
+

+ 6
- 5
src/shell_sym.c View File

@@ -84,7 +84,7 @@ int asmsh_symtable_add(asmsh_symtable_t *table, const char *name, unsigned long
84 84
 			}
85 85
 			*/
86 86
 			table->syms[i].addr = val;
87
-			return 0;
87
+			return 1;
88 88
 		}
89 89
 		else if(cmp > 0)
90 90
 		{
@@ -109,7 +109,7 @@ int asmsh_symtable_del(asmsh_symtable_t *table, const char *name)
109 109
 {
110 110
 	if(strlen(name) > ASMSH_VARNAME_MAX)
111 111
 	{
112
-		return 0;
112
+		return -1;
113 113
 	}
114 114
 	int i=0;
115 115
 	for(i=0; i<table->syms_sz;  i++)
@@ -119,7 +119,7 @@ int asmsh_symtable_del(asmsh_symtable_t *table, const char *name)
119 119
 	}
120 120
 	if(i==table->syms_sz)
121 121
 	{
122
-		return 0; // not found
122
+		return 1; // not found
123 123
 	}
124 124
 	//if(table->freeval) { free(table->syms[i].val); }
125 125
 	if(table->syms_sz > i+1)
@@ -133,13 +133,13 @@ int asmsh_symtable_del(asmsh_symtable_t *table, const char *name)
133 133
 					sizeof(*table->syms)*table->alloc);
134 134
 			if(!tmp)
135 135
 			{
136
-				return 0; // :/
136
+				return -1; // :/
137 137
 			}
138 138
 			table->syms = tmp;
139 139
 		}
140 140
 	}
141 141
 	table->syms_sz--;
142
-	return 1; // 1 deleted
142
+	return 0;
143 143
 }
144 144
 
145 145
 
@@ -161,3 +161,4 @@ const asmsh_sym_t *asmsh_symtable_get(asmsh_symtable_t *table, const char *name)
161 161
 	return NULL;
162 162
 }
163 163
 
164
+

+ 9
- 1
src/shell_sym.h View File

@@ -24,6 +24,9 @@
24 24
 #define ASMSH_VARNAME_MAX 256
25 25
 #define ASMSH_SYMALLOC 64
26 26
 
27
+/** The symbol prefixing a label */
28
+#define ASMSH_LABEL_SYM '@'
29
+
27 30
 typedef struct asmsh_sym_s asmsh_sym_t;
28 31
 typedef struct asmsh_symtable_s asmsh_symtable_t;
29 32
 
@@ -48,11 +51,16 @@ struct asmsh_symtable_s
48 51
 int asmsh_symtable_init(asmsh_symtable_t *table);
49 52
 void asmsh_symtable_clean(asmsh_symtable_t *table);
50 53
 
54
+/**
55
+ * @return 1 on update 0 on added -1 on error */
51 56
 int asmsh_symtable_add(asmsh_symtable_t *table, const char *name, unsigned long val);
57
+/**
58
+ * @return 0 on deleted, 1 on not found, -1 on error */
52 59
 int asmsh_symtable_del(asmsh_symtable_t *table, const char *name);
60
+/**
61
+ * @return NULL on error or not found else a ref */
53 62
 const asmsh_sym_t *asmsh_symtable_get(asmsh_symtable_t *table, const char *name);
54 63
 
55
-
56 64
 #include "shell.h"
57 65
 
58 66
 #endif

+ 7
- 7
tests/tests_shell_sym.c View File

@@ -24,7 +24,7 @@ START_TEST(add)
24 24
 	ck_assert_int_eq(st.syms_sz, 1);
25 25
 	ck_assert_int_eq(asmsh_symtable_add(&st, "a", 0), 0);
26 26
 	ck_assert_int_eq(st.syms_sz, 2);
27
-	ck_assert_int_eq(asmsh_symtable_add(&st, "a", 0), 0);
27
+	ck_assert_int_eq(asmsh_symtable_add(&st, "a", 0), 1);
28 28
 	ck_assert_int_eq(st.syms_sz, 2);
29 29
 	ck_assert_int_eq(asmsh_symtable_add(&st, "ab", 0), 0);
30 30
 	ck_assert_int_eq(st.syms_sz, 3);
@@ -37,19 +37,19 @@ START_TEST(del)
37 37
 
38 38
 	asmsh_symtable_t st;
39 39
 	ck_assert_int_eq(asmsh_symtable_init(&st), 0);
40
-	ck_assert_int_eq(asmsh_symtable_del(&st, "a"), 0);
41
-	ck_assert_int_eq(asmsh_symtable_add(&st, "a", 0), 0);
42 40
 	ck_assert_int_eq(asmsh_symtable_del(&st, "a"), 1);
41
+	ck_assert_int_eq(asmsh_symtable_add(&st, "a", 0), 0);
43 42
 	ck_assert_int_eq(asmsh_symtable_del(&st, "a"), 0);
43
+	ck_assert_int_eq(asmsh_symtable_del(&st, "a"), 1);
44 44
 	ck_assert_int_eq(asmsh_symtable_add(&st, "a", 0), 0);
45 45
 	ck_assert_int_eq(asmsh_symtable_add(&st, "b", 0), 0);
46 46
 	ck_assert_int_eq(asmsh_symtable_add(&st, "c", 0), 0);
47
-	ck_assert_int_eq(asmsh_symtable_del(&st, "d"), 0);
48
-	ck_assert_int_eq(asmsh_symtable_del(&st, "b"), 1);
47
+	ck_assert_int_eq(asmsh_symtable_del(&st, "d"), 1);
49 48
 	ck_assert_int_eq(asmsh_symtable_del(&st, "b"), 0);
49
+	ck_assert_int_eq(asmsh_symtable_del(&st, "b"), 1);
50 50
 	ck_assert_int_eq(asmsh_symtable_add(&st, "b", 0), 0);
51
-	ck_assert_int_eq(asmsh_symtable_del(&st, "a"), 1);
52 51
 	ck_assert_int_eq(asmsh_symtable_del(&st, "a"), 0);
52
+	ck_assert_int_eq(asmsh_symtable_del(&st, "a"), 1);
53 53
 	asmsh_symtable_clean(&st);
54 54
 }
55 55
 END_TEST
@@ -65,7 +65,7 @@ START_TEST(get)
65 65
 	s = asmsh_symtable_get(&st, "a");
66 66
 	ck_assert_ptr_nonnull(s);
67 67
 	ck_assert_int_eq(s->addr, 0);
68
-	ck_assert_int_eq(asmsh_symtable_add(&st, "a", 42), 0);
68
+	ck_assert_int_eq(asmsh_symtable_add(&st, "a", 42), 1);
69 69
 	s = asmsh_symtable_get(&st, "a");
70 70
 	ck_assert_ptr_nonnull(s);
71 71
 	ck_assert_int_eq(s->addr, 42);

Loading…
Cancel
Save