diff mbox series

[v2,15/39] gdbserver: put_mem handle arbitrary lengths

Message ID 20220420065013.222816-16-npiggin@gmail.com
State New
Headers show
Series gdbserver multi-threaded debugging and POWER9/10 support | expand

Commit Message

Nicholas Piggin April 20, 2022, 6:49 a.m. UTC
Allow put_mem to handle arbitrary lengths > 4 by allocating a buffer to
read the bytes into.

This also avoids endian complexities by not interpreting the byte stream
as a big-endian integer.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 src/gdb_parser.rl           |  31 +++-
 src/gdb_parser_precompile.c | 293 ++++++++++++++++++++----------------
 src/pdbgproxy.c             |  15 +-
 3 files changed, 203 insertions(+), 136 deletions(-)
diff mbox series

Patch

diff --git a/src/gdb_parser.rl b/src/gdb_parser.rl
index 72a907cf..020f10d7 100644
--- a/src/gdb_parser.rl
+++ b/src/gdb_parser.rl
@@ -1,5 +1,6 @@ 
 #include <stdbool.h>
 #include <stdint.h>
+#include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <assert.h>
@@ -39,6 +40,24 @@ 
 			*data += *p - 'A' + 10;
 	}
 
+	action mem_hex_digit {
+		uint8_t *d;
+		uint64_t idx = mem_data_i / 2; /* 2 chars per byte */
+
+		assert(idx < mem_data_length);
+		d = &mem_data[idx];
+
+		*d <<= 4;
+		if (*p >= '0' && *p <= '9')
+			*d += *p - '0';
+		else if (*p >= 'a' && *p <= 'f')
+			*d += *p - 'a' + 10;
+		else if (*p >= 'A' && *p <= 'F')
+			*d += *p - 'A' + 10;
+
+		mem_data_i++;
+	}
+
 	action end {
 		/* *data should point to the CRC */
 		if (crc != *data) {
@@ -69,7 +88,14 @@ 
 		   ','
 		   xdigit+ $hex_digit %push
 		   ':'
-		   xdigit+ $hex_digit %push);
+		   @{	mem_data_length = *(data - 1);
+			mem_data = calloc(1, mem_data_length); // handler frees
+			*data = (unsigned long)mem_data;
+			data++;
+			assert(data < &stack[10]);
+			mem_data_i = 0;
+		   }
+		   xdigit+ $mem_hex_digit);
 
 	get_gprs = ('g' @{cmd = GET_GPRS;});
 
@@ -114,6 +140,9 @@ 
 
 static enum gdb_command cmd = NONE;
 static uint64_t stack[10], *data = stack;
+static uint8_t *mem_data;
+static uint64_t mem_data_length;
+static uint64_t mem_data_i;
 static char *rsp;
 static uint8_t crc;
 static int cs;
diff --git a/src/gdb_parser_precompile.c b/src/gdb_parser_precompile.c
index d5372913..6b9b08e2 100644
--- a/src/gdb_parser_precompile.c
+++ b/src/gdb_parser_precompile.c
@@ -2,6 +2,7 @@ 
 #line 1 "src/gdb_parser.rl"
 #include <stdbool.h>
 #include <stdint.h>
+#include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <assert.h>
@@ -10,11 +11,14 @@ 
 #include "debug.h"
 
 
-#line 113 "src/gdb_parser.rl"
+#line 139 "src/gdb_parser.rl"
 
 
 static enum gdb_command cmd = NONE;
 static uint64_t stack[10], *data = stack;
+static uint8_t *mem_data;
+static uint64_t mem_data_length;
+static uint64_t mem_data_i;
 static char *rsp;
 static uint8_t crc;
 static int cs;
@@ -22,28 +26,29 @@  static int cs;
 static command_cb *command_callbacks;
 
 
-#line 26 "src/gdb_parser_precompile.c"
+#line 30 "src/gdb_parser_precompile.c"
 static const char _gdb_actions[] = {
 	0, 1, 0, 1, 1, 1, 2, 1, 
-	3, 1, 14, 1, 19, 1, 20, 1, 
-	21, 1, 22, 2, 0, 1, 2, 2, 
-	1, 2, 3, 1, 2, 3, 4, 2, 
-	10, 1, 2, 12, 1, 2, 13, 1, 
-	2, 14, 1, 2, 15, 1, 2, 16, 
-	1, 2, 17, 1, 2, 18, 1, 3, 
-	0, 5, 1, 3, 0, 6, 1, 3, 
-	0, 7, 1, 3, 0, 8, 1, 3, 
-	0, 9, 1, 3, 0, 11, 1
+	3, 1, 16, 1, 21, 1, 22, 1, 
+	23, 1, 24, 2, 0, 1, 2, 2, 
+	1, 2, 3, 1, 2, 3, 5, 2, 
+	4, 1, 2, 12, 1, 2, 14, 1, 
+	2, 15, 1, 2, 16, 1, 2, 17, 
+	1, 2, 18, 1, 2, 19, 1, 2, 
+	20, 1, 3, 0, 6, 1, 3, 0, 
+	7, 1, 3, 0, 9, 1, 3, 0, 
+	10, 1, 3, 0, 11, 1, 3, 0, 
+	13, 1, 3, 2, 8, 1
 };
 
 static const unsigned char _gdb_key_offsets[] = {
 	0, 0, 10, 11, 17, 23, 30, 37, 
-	38, 45, 53, 60, 68, 75, 83, 88, 
-	90, 92, 94, 96, 98, 100, 102, 104, 
-	111, 113, 115, 117, 119, 121, 123, 125, 
-	127, 129, 130, 132, 134, 136, 138, 140, 
-	142, 144, 146, 148, 150, 152, 154, 156, 
-	158, 161, 164, 165, 166
+	38, 45, 53, 60, 68, 75, 82, 90, 
+	95, 97, 99, 101, 103, 105, 107, 109, 
+	111, 118, 120, 122, 124, 126, 128, 130, 
+	132, 134, 136, 137, 139, 141, 143, 145, 
+	147, 149, 151, 153, 155, 157, 159, 161, 
+	163, 165, 168, 171, 172, 173
 };
 
 static const char _gdb_trans_keys[] = {
@@ -56,49 +61,50 @@  static const char _gdb_trans_keys[] = {
 	57, 65, 70, 97, 102, 35, 48, 57, 
 	65, 70, 97, 102, 35, 58, 48, 57, 
 	65, 70, 97, 102, 35, 48, 57, 65, 
-	70, 97, 102, 35, 44, 48, 57, 65, 
-	70, 97, 102, 35, 65, 67, 83, 102, 
-	35, 116, 35, 116, 35, 97, 35, 99, 
-	35, 104, 35, 101, 35, 100, 35, 58, 
-	35, 48, 57, 65, 70, 97, 102, 35, 
-	117, 35, 112, 35, 112, 35, 111, 35, 
-	114, 35, 116, 35, 101, 35, 100, 35, 
-	58, 35, 35, 84, 35, 104, 35, 114, 
-	35, 101, 35, 97, 35, 100, 35, 73, 
-	35, 110, 35, 102, 35, 111, 35, 67, 
-	35, 111, 35, 110, 35, 116, 35, 59, 
-	63, 35, 99, 115, 35, 35, 3, 36, 
-	43, 45, 0
+	70, 97, 102, 35, 48, 57, 65, 70, 
+	97, 102, 35, 44, 48, 57, 65, 70, 
+	97, 102, 35, 65, 67, 83, 102, 35, 
+	116, 35, 116, 35, 97, 35, 99, 35, 
+	104, 35, 101, 35, 100, 35, 58, 35, 
+	48, 57, 65, 70, 97, 102, 35, 117, 
+	35, 112, 35, 112, 35, 111, 35, 114, 
+	35, 116, 35, 101, 35, 100, 35, 58, 
+	35, 35, 84, 35, 104, 35, 114, 35, 
+	101, 35, 97, 35, 100, 35, 73, 35, 
+	110, 35, 102, 35, 111, 35, 67, 35, 
+	111, 35, 110, 35, 116, 35, 59, 63, 
+	35, 99, 115, 35, 35, 3, 36, 43, 
+	45, 0
 };
 
 static const char _gdb_single_lengths[] = {
 	0, 10, 1, 0, 0, 1, 1, 1, 
-	1, 2, 1, 2, 1, 2, 5, 2, 
-	2, 2, 2, 2, 2, 2, 2, 1, 
+	1, 2, 1, 2, 1, 1, 2, 5, 
 	2, 2, 2, 2, 2, 2, 2, 2, 
-	2, 1, 2, 2, 2, 2, 2, 2, 
+	1, 2, 2, 2, 2, 2, 2, 2, 
+	2, 2, 1, 2, 2, 2, 2, 2, 
 	2, 2, 2, 2, 2, 2, 2, 2, 
-	3, 3, 1, 1, 4
+	2, 3, 3, 1, 1, 4
 };
 
 static const char _gdb_range_lengths[] = {
 	0, 0, 0, 3, 3, 3, 3, 0, 
-	3, 3, 3, 3, 3, 3, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 3, 
+	3, 3, 3, 3, 3, 3, 3, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
+	3, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0
+	0, 0, 0, 0, 0, 0
 };
 
 static const unsigned char _gdb_index_offsets[] = {
 	0, 0, 11, 13, 17, 21, 26, 31, 
-	33, 38, 44, 49, 55, 60, 66, 72, 
-	75, 78, 81, 84, 87, 90, 93, 96, 
-	101, 104, 107, 110, 113, 116, 119, 122, 
-	125, 128, 130, 133, 136, 139, 142, 145, 
-	148, 151, 154, 157, 160, 163, 166, 169, 
-	172, 176, 180, 182, 184
+	33, 38, 44, 49, 55, 60, 65, 71, 
+	77, 80, 83, 86, 89, 92, 95, 98, 
+	101, 106, 109, 112, 115, 118, 121, 124, 
+	127, 130, 133, 135, 138, 141, 144, 147, 
+	150, 153, 156, 159, 162, 165, 168, 171, 
+	174, 177, 181, 185, 187, 189
 };
 
 static const char _gdb_indicies[] = {
@@ -109,67 +115,70 @@  static const char _gdb_indicies[] = {
 	18, 12, 19, 19, 19, 11, 12, 20, 
 	19, 19, 19, 11, 12, 21, 21, 21, 
 	11, 12, 22, 21, 21, 21, 11, 12, 
-	23, 23, 23, 11, 12, 22, 23, 23, 
-	23, 11, 12, 24, 25, 26, 27, 11, 
-	12, 28, 11, 12, 29, 11, 12, 30, 
-	11, 12, 31, 11, 12, 32, 11, 12, 
-	33, 11, 12, 34, 11, 12, 35, 11, 
-	12, 36, 36, 36, 11, 12, 37, 11, 
-	12, 38, 11, 12, 39, 11, 12, 40, 
-	11, 12, 41, 11, 12, 42, 11, 12, 
-	43, 11, 12, 44, 11, 12, 45, 11, 
-	47, 46, 12, 48, 11, 12, 49, 11, 
-	12, 50, 11, 12, 51, 11, 12, 52, 
-	11, 12, 53, 11, 12, 54, 11, 12, 
-	55, 11, 12, 56, 11, 12, 57, 11, 
-	12, 58, 11, 12, 59, 11, 12, 60, 
-	11, 12, 61, 11, 12, 62, 63, 11, 
-	12, 64, 65, 11, 12, 66, 12, 67, 
-	68, 69, 70, 71, 14, 0
+	23, 23, 23, 11, 12, 24, 24, 24, 
+	11, 12, 25, 24, 24, 24, 11, 12, 
+	26, 27, 28, 29, 11, 12, 30, 11, 
+	12, 31, 11, 12, 32, 11, 12, 33, 
+	11, 12, 34, 11, 12, 35, 11, 12, 
+	36, 11, 12, 37, 11, 12, 38, 38, 
+	38, 11, 12, 39, 11, 12, 40, 11, 
+	12, 41, 11, 12, 42, 11, 12, 43, 
+	11, 12, 44, 11, 12, 45, 11, 12, 
+	46, 11, 12, 47, 11, 49, 48, 12, 
+	50, 11, 12, 51, 11, 12, 52, 11, 
+	12, 53, 11, 12, 54, 11, 12, 55, 
+	11, 12, 56, 11, 12, 57, 11, 12, 
+	58, 11, 12, 59, 11, 12, 60, 11, 
+	12, 61, 11, 12, 62, 11, 12, 63, 
+	11, 12, 64, 65, 11, 12, 66, 67, 
+	11, 12, 68, 12, 69, 70, 71, 72, 
+	73, 14, 0
 };
 
 static const char _gdb_trans_targs[] = {
-	2, 3, 2, 5, 7, 8, 2, 12, 
-	5, 14, 44, 2, 3, 4, 0, 52, 
-	6, 3, 7, 9, 10, 11, 5, 13, 
-	15, 2, 24, 34, 16, 17, 18, 19, 
-	20, 21, 22, 23, 23, 25, 26, 27, 
-	28, 29, 30, 31, 32, 33, 2, 3, 
-	35, 36, 37, 38, 39, 40, 41, 42, 
-	43, 2, 45, 46, 47, 48, 49, 2, 
-	50, 51, 50, 51, 52, 1, 52, 52
+	2, 3, 2, 5, 7, 8, 2, 13, 
+	5, 15, 45, 2, 3, 4, 0, 53, 
+	6, 3, 7, 9, 10, 11, 12, 12, 
+	14, 5, 16, 2, 25, 35, 17, 18, 
+	19, 20, 21, 22, 23, 24, 24, 26, 
+	27, 28, 29, 30, 31, 32, 33, 34, 
+	2, 3, 36, 37, 38, 39, 40, 41, 
+	42, 43, 44, 2, 46, 47, 48, 49, 
+	50, 2, 51, 52, 51, 52, 53, 1, 
+	53, 53
 };
 
 static const char _gdb_trans_actions[] = {
-	19, 1, 71, 75, 19, 59, 63, 55, 
-	67, 19, 19, 3, 0, 7, 0, 28, 
-	25, 5, 31, 25, 22, 25, 22, 25, 
-	3, 37, 3, 3, 3, 3, 3, 3, 
-	3, 3, 3, 3, 34, 3, 3, 3, 
-	3, 3, 3, 3, 3, 3, 40, 9, 
+	19, 1, 74, 78, 19, 62, 66, 58, 
+	70, 19, 19, 3, 0, 7, 0, 28, 
+	25, 5, 34, 25, 22, 25, 82, 31, 
+	25, 22, 3, 40, 3, 3, 3, 3, 
+	3, 3, 3, 3, 3, 3, 37, 3, 
 	3, 3, 3, 3, 3, 3, 3, 3, 
-	3, 43, 3, 3, 3, 3, 3, 46, 
-	3, 3, 49, 52, 11, 13, 15, 17
+	43, 9, 3, 3, 3, 3, 3, 3, 
+	3, 3, 3, 46, 3, 3, 3, 3, 
+	3, 49, 3, 3, 52, 55, 11, 13, 
+	15, 17
 };
 
-static const int gdb_start = 52;
-static const int gdb_first_final = 52;
+static const int gdb_start = 53;
+static const int gdb_first_final = 53;
 static const int gdb_error = 0;
 
-static const int gdb_en_main = 52;
+static const int gdb_en_main = 53;
 
 
-#line 124 "src/gdb_parser.rl"
+#line 153 "src/gdb_parser.rl"
 
 void parser_init(command_cb *callbacks)
 {
 	
-#line 168 "src/gdb_parser_precompile.c"
+#line 177 "src/gdb_parser_precompile.c"
 	{
 	cs = gdb_start;
 	}
 
-#line 128 "src/gdb_parser.rl"
+#line 157 "src/gdb_parser.rl"
 
 	command_callbacks = callbacks;
 }
@@ -180,7 +189,7 @@  int parse_buffer(char *buf, size_t len, void *priv)
 	char *pe = p + len;
 
 	
-#line 184 "src/gdb_parser_precompile.c"
+#line 193 "src/gdb_parser_precompile.c"
 	{
 	int _klen;
 	unsigned int _trans;
@@ -255,7 +264,7 @@  _match:
 		switch ( *_acts++ )
 		{
 	case 0:
-#line 13 "src/gdb_parser.rl"
+#line 14 "src/gdb_parser.rl"
 	{
 		cmd = 0;
 		rsp = NULL;
@@ -266,20 +275,20 @@  _match:
 	}
 	break;
 	case 1:
-#line 22 "src/gdb_parser.rl"
+#line 23 "src/gdb_parser.rl"
 	{
 		crc += *p;
 	}
 	break;
 	case 2:
-#line 26 "src/gdb_parser.rl"
+#line 27 "src/gdb_parser.rl"
 	{
 		data++;
 		assert(data < &stack[10]);
 	}
 	break;
 	case 3:
-#line 31 "src/gdb_parser.rl"
+#line 32 "src/gdb_parser.rl"
 	{
 		*data *= 16;
 
@@ -292,7 +301,27 @@  _match:
 	}
 	break;
 	case 4:
-#line 42 "src/gdb_parser.rl"
+#line 43 "src/gdb_parser.rl"
+	{
+		uint8_t *d;
+		uint64_t idx = mem_data_i / 2; /* 2 chars per byte */
+
+		assert(idx < mem_data_length);
+		d = &mem_data[idx];
+
+		*d <<= 4;
+		if (*p >= '0' && *p <= '9')
+			*d += *p - '0';
+		else if (*p >= 'a' && *p <= 'f')
+			*d += *p - 'a' + 10;
+		else if (*p >= 'A' && *p <= 'F')
+			*d += *p - 'A' + 10;
+
+		mem_data_i++;
+	}
+	break;
+	case 5:
+#line 61 "src/gdb_parser.rl"
 	{
 		/* *data should point to the CRC */
 		if (crc != *data) {
@@ -313,79 +342,89 @@  _match:
 		}
 	}
 	break;
-	case 5:
-#line 62 "src/gdb_parser.rl"
+	case 6:
+#line 81 "src/gdb_parser.rl"
 	{cmd = GET_MEM;}
 	break;
-	case 6:
-#line 67 "src/gdb_parser.rl"
+	case 7:
+#line 86 "src/gdb_parser.rl"
 	{cmd = PUT_MEM;}
 	break;
-	case 7:
-#line 74 "src/gdb_parser.rl"
+	case 8:
+#line 91 "src/gdb_parser.rl"
+	{	mem_data_length = *(data - 1);
+			mem_data = calloc(1, mem_data_length); // handler frees
+			*data = (unsigned long)mem_data;
+			data++;
+			assert(data < &stack[10]);
+			mem_data_i = 0;
+		   }
+	break;
+	case 9:
+#line 100 "src/gdb_parser.rl"
 	{cmd = GET_GPRS;}
 	break;
-	case 8:
-#line 76 "src/gdb_parser.rl"
+	case 10:
+#line 102 "src/gdb_parser.rl"
 	{cmd = GET_SPR;}
 	break;
-	case 9:
-#line 79 "src/gdb_parser.rl"
+	case 11:
+#line 105 "src/gdb_parser.rl"
 	{cmd = STOP_REASON;}
 	break;
-	case 10:
-#line 81 "src/gdb_parser.rl"
+	case 12:
+#line 107 "src/gdb_parser.rl"
 	{cmd = SET_THREAD;}
 	break;
-	case 11:
-#line 83 "src/gdb_parser.rl"
+	case 13:
+#line 109 "src/gdb_parser.rl"
 	{cmd = DETACH;}
 	break;
-	case 12:
-#line 87 "src/gdb_parser.rl"
+	case 14:
+#line 113 "src/gdb_parser.rl"
 	{rsp = "1";}
 	break;
-	case 13:
-#line 88 "src/gdb_parser.rl"
+	case 15:
+#line 114 "src/gdb_parser.rl"
 	{rsp = "QC1";}
 	break;
-	case 14:
-#line 89 "src/gdb_parser.rl"
+	case 16:
+#line 115 "src/gdb_parser.rl"
 	{rsp = "multiprocess+;vContSupported+";}
 	break;
-	case 15:
-#line 90 "src/gdb_parser.rl"
+	case 17:
+#line 116 "src/gdb_parser.rl"
 	{rsp = "m1l";}
 	break;
-	case 16:
-#line 93 "src/gdb_parser.rl"
+	case 18:
+#line 119 "src/gdb_parser.rl"
 	{rsp = "vCont;c;C;s;S";}
 	break;
-	case 17:
-#line 94 "src/gdb_parser.rl"
+	case 19:
+#line 120 "src/gdb_parser.rl"
 	{cmd = V_CONTC;}
 	break;
-	case 18:
-#line 95 "src/gdb_parser.rl"
+	case 20:
+#line 121 "src/gdb_parser.rl"
 	{cmd = V_CONTS;}
 	break;
-	case 19:
-#line 98 "src/gdb_parser.rl"
+	case 21:
+#line 124 "src/gdb_parser.rl"
 	{ if (command_callbacks) command_callbacks[INTERRUPT](stack, priv); PR_INFO("RAGEL:interrupt\n");}
 	break;
-	case 20:
-#line 105 "src/gdb_parser.rl"
+	case 22:
+#line 131 "src/gdb_parser.rl"
 	{PR_INFO("RAGEL:cmd\n");}
 	break;
-	case 21:
-#line 108 "src/gdb_parser.rl"
+	case 23:
+#line 134 "src/gdb_parser.rl"
 	{PR_INFO("RAGEL:ack\n");}
 	break;
-	case 22:
-#line 109 "src/gdb_parser.rl"
+	case 24:
+#line 135 "src/gdb_parser.rl"
 	{PR_INFO("RAGEL:nack\n");}
 	break;
-#line 389 "src/gdb_parser_precompile.c"
+#line 428 "src/gdb_parser_precompile.c"
 		}
 	}
 
@@ -398,7 +437,7 @@  _again:
 	_out: {}
 	}
 
-#line 138 "src/gdb_parser.rl"
+#line 167 "src/gdb_parser.rl"
 
 	if (cs == gdb_error) {
 		printf("parse error\n");
diff --git a/src/pdbgproxy.c b/src/pdbgproxy.c
index 1668126f..0cc33fbf 100644
--- a/src/pdbgproxy.c
+++ b/src/pdbgproxy.c
@@ -253,6 +253,7 @@  static void put_mem(uint64_t *stack, void *priv)
 	uint64_t addr, len;
 	uint8_t *data;
 	uint8_t attn_opcode[] = {0x00, 0x00, 0x02, 0x00};
+	uint8_t gdb_break_opcode[] = {0x7d, 0x82, 0x10, 0x08};
 	int err = 0;
 	struct thread *thread = target_to_thread(thread_target);
 
@@ -263,7 +264,7 @@  static void put_mem(uint64_t *stack, void *priv)
 
 	addr = stack[0];
 	len = stack[1];
-	data = (uint8_t *) &stack[2];
+	data = (uint8_t *)(unsigned long)stack[2];
 
 	addr = get_real_addr(addr);
 	if (addr == -1UL) {
@@ -272,8 +273,7 @@  static void put_mem(uint64_t *stack, void *priv)
 		goto out;
 	}
 
-
-	if (len == 4 && stack[2] == 0x0810827d) {
+	if (len == 4 && !memcmp(data, gdb_break_opcode, 4)) {
 		/* According to linux-ppc-low.c gdb only uses this
 		 * op-code for sw break points so we replace it with
 		 * the correct attn opcode which is what we need for
@@ -282,15 +282,12 @@  static void put_mem(uint64_t *stack, void *priv)
 		 * TODO: Upstream a patch to gdb so that it uses the
 		 * right opcode for baremetal debug. */
 		PR_INFO("Breakpoint opcode detected, replacing with attn\n");
-		data = attn_opcode;
+		memcpy(data, attn_opcode, 4);
 
 		/* Need to enable the attn instruction in HID0 */
 		if (thread->enable_attn(thread))
 			goto out;
-	} else
-		stack[2] = __builtin_bswap64(stack[2]) >> 32;
-
-	PR_INFO("put_mem 0x%016" PRIx64 " = 0x%016" PRIx64 "\n", addr, stack[2]);
+	}
 
 	if (mem_write(adu_target, addr, data, len, 0, false)) {
 		PR_ERROR("Unable to write memory\n");
@@ -298,6 +295,8 @@  static void put_mem(uint64_t *stack, void *priv)
 	}
 
 out:
+	free(data); // allocated by gdb_parser.rl
+
 	if (err)
 		send_response(fd, ERROR(EPERM));
 	else