Home | History | Annotate | Download | only in ltrace
      1 /*
      2  * This file is part of ltrace.
      3  * Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc.
      4  * Copyright (C) 1998,2004,2007,2008,2009 Juan Cespedes
      5  * Copyright (C) 2006 Steve Fink
      6  * Copyright (C) 2006 Ian Wienand
      7  *
      8  * This program is free software; you can redistribute it and/or
      9  * modify it under the terms of the GNU General Public License as
     10  * published by the Free Software Foundation; either version 2 of the
     11  * License, or (at your option) any later version.
     12  *
     13  * This program is distributed in the hope that it will be useful, but
     14  * WITHOUT ANY WARRANTY; without even the implied warranty of
     15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     16  * General Public License for more details.
     17  *
     18  * You should have received a copy of the GNU General Public License
     19  * along with this program; if not, write to the Free Software
     20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
     21  * 02110-1301 USA
     22  */
     23 
     24 #include <assert.h>
     25 #include <stdint.h>
     26 #include <stdlib.h>
     27 #include <string.h>
     28 
     29 #include "printf.h"
     30 #include "type.h"
     31 #include "value.h"
     32 #include "expr.h"
     33 #include "zero.h"
     34 #include "param.h"
     35 #include "lens_default.h"
     36 
     37 struct param_enum {
     38 	struct value array;
     39 	int percent;
     40 	size_t *future_length;
     41 	char *format;
     42 	char const *ptr;
     43 	char const *end;
     44 	size_t width;
     45 };
     46 
     47 static struct param_enum *
     48 param_printf_init(struct value *cb_args, size_t nargs,
     49 		  struct value_dict *arguments)
     50 {
     51 	assert(nargs == 1);
     52 
     53 	struct process *proc = cb_args[0].inferior;
     54 	assert(proc != NULL);
     55 
     56 	/* We expect a pointer to array.  */
     57 	if (cb_args->type->type != ARGTYPE_POINTER
     58 	    || cb_args->type->u.ptr_info.info->type != ARGTYPE_ARRAY)
     59 		return NULL;
     60 
     61 	/* The element type should be either character (for narrow
     62 	 * strings) or an integral type (for wide strings).  */
     63 	struct arg_type_info *et
     64 		= cb_args->type->u.ptr_info.info->u.array_info.elt_type;
     65 	switch (et->type) {
     66 	case ARGTYPE_CHAR:
     67 	case ARGTYPE_SHORT:
     68 	case ARGTYPE_USHORT:
     69 	case ARGTYPE_INT:
     70 	case ARGTYPE_UINT:
     71 	case ARGTYPE_LONG:
     72 	case ARGTYPE_ULONG:
     73 		break;
     74 	default:
     75 		return NULL;
     76 	}
     77 
     78 	struct param_enum *self = malloc(sizeof(*self));
     79 	if (self == NULL) {
     80 	fail:
     81 		free(self);
     82 		return NULL;
     83 	}
     84 	self->width = type_sizeof(proc, et);
     85 	if (self->width == (size_t) -1)
     86 		goto fail;
     87 
     88 	if (value_init_deref(&self->array, cb_args) < 0)
     89 		goto fail;
     90 	assert(self->array.type->type == ARGTYPE_ARRAY);
     91 
     92 	self->format = (char *)value_get_data(&self->array, arguments);
     93 	if (self->format == NULL) {
     94 		value_destroy(&self->array);
     95 		goto fail;
     96 	}
     97 
     98 	size_t size = value_size(&self->array, arguments);
     99 	if (size == (size_t)-1) {
    100 		value_destroy(&self->array);
    101 		goto fail;
    102 	}
    103 
    104 	self->percent = 0;
    105 	self->ptr = self->format;
    106 	self->end = self->format + size;
    107 	self->future_length = NULL;
    108 	return self;
    109 }
    110 
    111 static void
    112 drop_future_length(struct param_enum *self)
    113 {
    114 	if (self->future_length != NULL) {
    115 		free(self->future_length);
    116 		self->future_length = NULL;
    117 	}
    118 }
    119 
    120 static int
    121 form_next_param(struct param_enum *self,
    122 		enum arg_type format_type, enum arg_type elt_type,
    123 		unsigned hlf, unsigned lng, char *len_buf, size_t len_buf_len,
    124 		struct arg_type_info *infop)
    125 {
    126 	/* XXX note: Some types are wrong because we lack
    127 	   ARGTYPE_LONGLONG, ARGTYPE_UCHAR and ARGTYPE_SCHAR.  */
    128 	assert(lng <= 2);
    129 	assert(hlf <= 2);
    130 	static enum arg_type ints[] =
    131 		{ ARGTYPE_CHAR, ARGTYPE_SHORT, ARGTYPE_INT,
    132 		  ARGTYPE_LONG, ARGTYPE_ULONG };
    133 	static enum arg_type uints[] =
    134 		{ ARGTYPE_CHAR, ARGTYPE_USHORT, ARGTYPE_UINT,
    135 		  ARGTYPE_ULONG, ARGTYPE_ULONG };
    136 
    137 	struct arg_type_info *elt_info = NULL;
    138 	if (format_type == ARGTYPE_ARRAY || format_type == ARGTYPE_POINTER)
    139 		elt_info = type_get_simple(elt_type);
    140 	else if (format_type == ARGTYPE_INT)
    141 		format_type = ints[2 + lng - hlf];
    142 	else if (format_type == ARGTYPE_UINT)
    143 		format_type = uints[2 + lng - hlf];
    144 
    145 
    146 	if (format_type == ARGTYPE_ARRAY) {
    147 		struct arg_type_info *array = malloc(sizeof(*array));
    148 		if (array == NULL)
    149 			return -1;
    150 
    151 		struct expr_node *node = NULL;
    152 		int own_node;
    153 		if (len_buf_len != 0
    154 		    || self->future_length != NULL) {
    155 			struct tmp {
    156 				struct expr_node node;
    157 				struct arg_type_info type;
    158 			};
    159 			struct tmp *len = malloc(sizeof(*len));
    160 			if (len == NULL) {
    161 			fail:
    162 				free(len);
    163 				free(array);
    164 				return -1;
    165 			}
    166 
    167 			len->type = *type_get_simple(ARGTYPE_LONG);
    168 
    169 			long l;
    170 			if (self->future_length != NULL) {
    171 				l = *self->future_length;
    172 				drop_future_length(self);
    173 			} else {
    174 				l = atol(len_buf);
    175 			}
    176 
    177 			expr_init_const_word(&len->node, l, &len->type, 0);
    178 
    179 			node = build_zero_w_arg(&len->node, 1);
    180 			if (node == NULL)
    181 				goto fail;
    182 			own_node = 1;
    183 
    184 		} else {
    185 			node = expr_node_zero();
    186 			own_node = 0;
    187 		}
    188 		assert(node != NULL);
    189 
    190 		type_init_array(array, elt_info, 0, node, own_node);
    191 		type_init_pointer(infop, array, 1);
    192 
    193 	} else if (format_type == ARGTYPE_POINTER) {
    194 		type_init_pointer(infop, elt_info, 0);
    195 
    196 	} else {
    197 		*infop = *type_get_simple(format_type);
    198 	}
    199 
    200 	return 0;
    201 }
    202 
    203 static int
    204 param_printf_next(struct param_enum *self, struct arg_type_info *infop,
    205 		  int *insert_stop)
    206 {
    207 	unsigned hlf = 0;
    208 	unsigned lng = 0;
    209 	enum arg_type format_type = ARGTYPE_VOID;
    210 	enum arg_type elt_type = ARGTYPE_VOID;
    211 	char len_buf[25] = {};
    212 	size_t len_buf_len = 0;
    213 	struct lens *lens = NULL;
    214 
    215 	for (; self->ptr < self->end; self->ptr += self->width) {
    216 		union {
    217 			uint8_t u8;
    218 			uint16_t u16;
    219 			uint32_t u32;
    220 			uint64_t u64;
    221 			char buf[0];
    222 		} u;
    223 		memcpy(u.buf, self->ptr, self->width);
    224 		switch (self->width) {
    225 		case 1: u.u64 = u.u8; break;
    226 		case 2: u.u64 = u.u16; break;
    227 		case 4: u.u64 = u.u32; break;
    228 		}
    229 		uint64_t c = u.u64;
    230 
    231 		if (!self->percent) {
    232 			if (c == '%')
    233 				self->percent = 1;
    234 			continue;
    235 		}
    236 
    237 		switch (c) {
    238 		case '#': case ' ': case '-':
    239 		case '+': case 'I': case '\'':
    240 			/* These are only important for formatting,
    241 			 * not for interpreting the type.  */
    242 			continue;
    243 
    244 		case '*':
    245 			/* Length parameter given in the next
    246 			 * argument.  */
    247 			if (self->future_length == NULL)
    248 				/* This should really be an assert,
    249 				 * but we can't just fail on invalid
    250 				 * format string.  */
    251 				self->future_length
    252 					= malloc(sizeof(*self->future_length));
    253 
    254 			if (self->future_length != NULL) {
    255 				self->ptr += self->width;
    256 				format_type = ARGTYPE_INT;
    257 				break;
    258 			}
    259 
    260 		case '0':
    261 		case '1': case '2': case '3':
    262 		case '4': case '5': case '6':
    263 		case '7': case '8': case '9':
    264 			/* Field length likewise, but we need to parse
    265 			 * this to attach the appropriate string
    266 			 * length expression.  */
    267 			if (len_buf_len < sizeof(len_buf) - 1)
    268 				len_buf[len_buf_len++] = c;
    269 			continue;
    270 
    271 		case 'h':
    272 			if (hlf < 2)
    273 				hlf++;
    274 			continue;
    275 
    276 		case 'l':
    277 			if (lng < 2)
    278 				lng++;
    279 			continue;
    280 
    281 		case 'q':
    282 			lng = 2;
    283 			continue;
    284 
    285 		case 'L': /* long double */
    286 			lng = 1;
    287 			continue;
    288 
    289 		case 'j': /* intmax_t */
    290 			/*   XXX ABI should know */
    291 			lng = 2;
    292 			continue;
    293 
    294 		case 't': /* ptrdiff_t */
    295 		case 'Z': case 'z': /* size_t */
    296 			lng = 1; /* XXX ABI should tell */
    297 			continue;
    298 
    299 		case 'd':
    300 		case 'i':
    301 			format_type = ARGTYPE_INT;
    302 			self->percent = 0;
    303 			break;
    304 
    305 		case 'o':
    306 			lens = &octal_lens;
    307 			goto uint;
    308 
    309 		case 'x': case 'X':
    310 			lens = &hex_lens;
    311 			/* Fall through.  */
    312 		case 'u':
    313 		uint:
    314 			format_type = ARGTYPE_UINT;
    315 			self->percent = 0;
    316 			break;
    317 
    318 		case 'e': case 'E':
    319 		case 'f': case 'F':
    320 		case 'g': case 'G':
    321 		case 'a': case 'A':
    322 			format_type = ARGTYPE_DOUBLE;
    323 			self->percent = 0;
    324 			break;
    325 
    326 		case 'C': /* like "lc" */
    327 			if (lng == 0)
    328 				lng++;
    329 		case 'c':
    330 			/* XXX "lc" means wchar_t string.  */
    331 			format_type = ARGTYPE_CHAR;
    332 			self->percent = 0;
    333 			break;
    334 
    335 		case 'S': /* like "ls" */
    336 			if (lng == 0)
    337 				lng++;
    338 		case 's':
    339 			format_type = ARGTYPE_ARRAY;
    340 			elt_type = lng == 0 ? ARGTYPE_CHAR : ARGTYPE_INT;
    341 			self->percent = 0;
    342 			lens = &string_lens;
    343 			break;
    344 
    345 		case 'p':
    346 		case 'n': /* int* where to store no. of printed chars.  */
    347 			format_type = ARGTYPE_POINTER;
    348 			elt_type = ARGTYPE_VOID;
    349 			self->percent = 0;
    350 			break;
    351 
    352 		case 'm': /* (glibc) print argument of errno */
    353 		case '%':
    354 			lng = 0;
    355 			hlf = 0;
    356 			self->percent = 0;
    357 			continue;
    358 
    359 		default:
    360 			continue;
    361 		}
    362 
    363 		/* If we got here, the type must have been set.  */
    364 		assert(format_type != ARGTYPE_VOID);
    365 
    366 		if (form_next_param(self, format_type, elt_type, hlf, lng,
    367 				    len_buf, len_buf_len, infop) < 0)
    368 			return -1;
    369 
    370 		infop->lens = lens;
    371 		infop->own_lens = 0;
    372 
    373 		return 0;
    374 	}
    375 
    376 	*infop = *type_get_simple(ARGTYPE_VOID);
    377 	return 0;
    378 }
    379 
    380 static enum param_status
    381 param_printf_stop(struct param_enum *self, struct value *value)
    382 {
    383 	if (self->future_length != NULL
    384 	    && value_extract_word(value, (long *)self->future_length, NULL) < 0)
    385 		drop_future_length(self);
    386 
    387 	return PPCB_CONT;
    388 }
    389 
    390 static void
    391 param_printf_done(struct param_enum *context)
    392 {
    393 	value_destroy(&context->array);
    394 	free(context);
    395 }
    396 
    397 void
    398 param_pack_init_printf(struct param *param, struct expr_node *arg, int own_arg)
    399 {
    400 	param_init_pack(param, PARAM_PACK_VARARGS, arg, 1, own_arg,
    401 			&param_printf_init, &param_printf_next,
    402 			&param_printf_stop, &param_printf_done);
    403 }
    404