Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2021 BayLibre, SAS
3 : : *
4 : : * SPDX-License-Identifier: Apache-2.0
5 : : */
6 : :
7 : : #include <errno.h>
8 : : #include <stdarg.h>
9 : : #include <stdint.h>
10 : : #include <string.h>
11 : : #include <zephyr/toolchain.h>
12 : : #include <zephyr/linker/utils.h>
13 : : #include <zephyr/sys/cbprintf.h>
14 : : #include <sys/types.h>
15 : : #include <zephyr/sys/util.h>
16 : : #include <zephyr/sys/__assert.h>
17 : : #include <zephyr/logging/log.h>
18 : : LOG_MODULE_REGISTER(cbprintf_package, CONFIG_CBPRINTF_PACKAGE_LOG_LEVEL);
19 : :
20 : : #if defined(CONFIG_CBPRINTF_PACKAGE_SUPPORT_TAGGED_ARGUMENTS) && \
21 : : !Z_C_GENERIC
22 : : #error "CONFIG_CBPRINTF_PACKAGE_SUPPORT_TAGGED_ARGUMENTS " \
23 : : "requires toolchain to support _Generic!"
24 : : #endif
25 : :
26 : : /**
27 : : * @brief Check if address is in read only section.
28 : : *
29 : : * @param addr Address.
30 : : *
31 : : * @return True if address identified within read only section.
32 : : */
33 : 514 : static inline bool ptr_in_rodata(const char *addr)
34 : : {
35 : : #if defined(CBPRINTF_VIA_UNIT_TEST)
36 : : /* Unit test is X86 (or other host) but not using Zephyr
37 : : * linker scripts.
38 : : */
39 : : return false;
40 : : #else
41 : 514 : return linker_is_in_rodata(addr);
42 : : #endif
43 : : }
44 : :
45 : : /*
46 : : * va_list creation
47 : : */
48 : :
49 : : #if defined(__CHECKER__)
50 : : static int cbprintf_via_va_list(cbprintf_cb out,
51 : : cbvprintf_external_formatter_func formatter,
52 : : void *ctx,
53 : : const char *fmt, void *buf)
54 : : {
55 : : return 0;
56 : : }
57 : : #elif defined(__aarch64__)
58 : : /*
59 : : * Reference:
60 : : *
61 : : * Procedure Call Standard for the ARM 64-bit Architecture
62 : : */
63 : :
64 : : struct __va_list {
65 : : void *__stack;
66 : : void *__gr_top;
67 : : void *__vr_top;
68 : : int __gr_offs;
69 : : int __vr_offs;
70 : : };
71 : :
72 : : BUILD_ASSERT(sizeof(va_list) == sizeof(struct __va_list),
73 : : "architecture specific support is wrong");
74 : :
75 : : static int cbprintf_via_va_list(cbprintf_cb out,
76 : : cbvprintf_external_formatter_func formatter,
77 : : void *ctx,
78 : : const char *fmt, void *buf)
79 : : {
80 : : union {
81 : : va_list ap;
82 : : struct __va_list __ap;
83 : : } u;
84 : :
85 : : /* create a valid va_list with our buffer */
86 : : u.__ap.__stack = buf;
87 : : u.__ap.__gr_top = NULL;
88 : : u.__ap.__vr_top = NULL;
89 : : u.__ap.__gr_offs = 0;
90 : : u.__ap.__vr_offs = 0;
91 : :
92 : : return formatter(out, ctx, fmt, u.ap);
93 : : }
94 : :
95 : : #elif defined(__x86_64__)
96 : : /*
97 : : * Reference:
98 : : *
99 : : * System V Application Binary Interface
100 : : * AMD64 Architecture Processor Supplement
101 : : */
102 : :
103 : : struct __va_list {
104 : : unsigned int gp_offset;
105 : : unsigned int fp_offset;
106 : : void *overflow_arg_area;
107 : : void *reg_save_area;
108 : : };
109 : :
110 : : BUILD_ASSERT(sizeof(va_list) == sizeof(struct __va_list),
111 : : "architecture specific support is wrong");
112 : :
113 : : static int cbprintf_via_va_list(cbprintf_cb out,
114 : : cbvprintf_external_formatter_func formatter,
115 : : void *ctx,
116 : : const char *fmt, void *buf)
117 : : {
118 : : union {
119 : : va_list ap;
120 : : struct __va_list __ap;
121 : : } u;
122 : :
123 : : /* create a valid va_list with our buffer */
124 : : u.__ap.overflow_arg_area = buf;
125 : : u.__ap.reg_save_area = NULL;
126 : : u.__ap.gp_offset = (6 * 8);
127 : : u.__ap.fp_offset = (6 * 8 + 16 * 16);
128 : :
129 : : return formatter(out, ctx, fmt, u.ap);
130 : : }
131 : :
132 : : #elif defined(__xtensa__)
133 : : /*
134 : : * Reference:
135 : : *
136 : : * gcc source code (gcc/config/xtensa/xtensa.c)
137 : : * xtensa_build_builtin_va_list(), xtensa_va_start(),
138 : : * xtensa_gimplify_va_arg_expr()
139 : : */
140 : :
141 : : struct __va_list {
142 : : void *__va_stk;
143 : : void *__va_reg;
144 : : int __va_ndx;
145 : : };
146 : :
147 : : BUILD_ASSERT(sizeof(va_list) == sizeof(struct __va_list),
148 : : "architecture specific support is wrong");
149 : :
150 : : static int cbprintf_via_va_list(cbprintf_cb out,
151 : : cbvprintf_external_formatter_func formatter,
152 : : void *ctx,
153 : : const char *fmt, void *buf)
154 : : {
155 : : union {
156 : : va_list ap;
157 : : struct __va_list __ap;
158 : : } u;
159 : :
160 : : /* create a valid va_list with our buffer */
161 : : u.__ap.__va_stk = (char *)buf - 32;
162 : : u.__ap.__va_reg = NULL;
163 : : u.__ap.__va_ndx = (6 + 2) * 4;
164 : :
165 : : return formatter(out, ctx, fmt, u.ap);
166 : : }
167 : :
168 : : #else
169 : : /*
170 : : * Default implementation shared by many architectures like
171 : : * 32-bit ARM and Intel.
172 : : *
173 : : * We assume va_list is a simple pointer.
174 : : */
175 : :
176 : : BUILD_ASSERT(sizeof(va_list) == sizeof(void *),
177 : : "architecture specific support is needed");
178 : :
179 : 177 : static int cbprintf_via_va_list(cbprintf_cb out,
180 : : cbvprintf_external_formatter_func formatter,
181 : : void *ctx,
182 : : const char *fmt, void *buf)
183 : : {
184 : 177 : union {
185 : : va_list ap;
186 : : void *ptr;
187 : : } u;
188 : :
189 : 177 : u.ptr = buf;
190 : :
191 : 177 : return formatter(out, ctx, fmt, u.ap);
192 : : }
193 : :
194 : : #endif
195 : :
196 : 0 : static size_t get_package_len(void *packaged)
197 : : {
198 [ # # ]: 0 : __ASSERT_NO_MSG(packaged != NULL);
199 : :
200 : 0 : uint8_t *buf = packaged;
201 : 0 : uint8_t *start = buf;
202 : 0 : unsigned int args_size, s_nbr, ros_nbr;
203 : :
204 : 0 : args_size = buf[0] * sizeof(int);
205 : 0 : s_nbr = buf[1];
206 : 0 : ros_nbr = buf[2];
207 : :
208 : : /* Move beyond args. */
209 : 0 : buf += args_size;
210 : :
211 : : /* Move beyond read-only string indexes array. */
212 : 0 : buf += ros_nbr;
213 : :
214 : : /* Move beyond strings appended to the package. */
215 [ # # ]: 0 : for (unsigned int i = 0; i < s_nbr; i++) {
216 : 0 : buf++;
217 : 0 : buf += strlen((const char *)buf) + 1;
218 : : }
219 : :
220 : 0 : return (size_t)(uintptr_t)(buf - start);
221 : : }
222 : :
223 : 0 : static int append_string(cbprintf_convert_cb cb, void *ctx, const char *str, uint16_t strl)
224 : : {
225 [ # # ]: 0 : if (cb == NULL) {
226 : 0 : return 1 + strlen(str);
227 : : }
228 : :
229 [ # # ]: 0 : strl = strl > 0 ? strl : strlen(str) + 1;
230 : 0 : return cb(str, strl, ctx);
231 : : }
232 : :
233 : 354 : int cbvprintf_package(void *packaged, size_t len, uint32_t flags,
234 : : const char *fmt, va_list ap)
235 : : {
236 : : /*
237 : : * Internally, a byte is used to store location of a string argument within a
238 : : * package. MSB bit is set if string is read-only so effectively 7 bits are
239 : : * used for index, which should be enough.
240 : : */
241 : : #define STR_POS_RO_FLAG BIT(7)
242 : : #define STR_POS_MASK BIT_MASK(7)
243 : :
244 : : /* Buffer offset abstraction for better code clarity. */
245 : : #define BUF_OFFSET ((uintptr_t)buf - (uintptr_t)buf0)
246 : :
247 : 354 : uint8_t *buf0 = packaged; /* buffer start (may be NULL) */
248 : 354 : uint8_t *buf = buf0; /* current buffer position */
249 : 354 : unsigned int size; /* current argument's size */
250 : 354 : unsigned int align; /* current argument's required alignment */
251 : 354 : uint8_t str_ptr_pos[16]; /* string pointer positions */
252 : 354 : uint8_t str_ptr_arg[16]; /* string pointer argument index */
253 : 354 : unsigned int s_idx = 0; /* index into str_ptr_pos[] */
254 : 354 : unsigned int s_rw_cnt = 0; /* number of rw strings */
255 : 354 : unsigned int s_ro_cnt = 0; /* number of ro strings */
256 : 354 : int arg_idx = -1; /* Argument index. Preincremented thus starting from -1.*/
257 : 354 : unsigned int i;
258 : 354 : const char *s;
259 : 354 : bool parsing = false;
260 : : /* Flag indicates that rw strings are stored as array with positions,
261 : : * instead of appending them to the package.
262 : : */
263 : 354 : bool rws_pos_en = !!(flags & CBPRINTF_PACKAGE_ADD_RW_STR_POS);
264 : : /* Get number of first read only strings present in the string.
265 : : * There is always at least 1 (fmt) but flags can indicate more, e.g
266 : : * fixed prefix appended to all strings.
267 : : */
268 : 354 : int fros_cnt = 1 + Z_CBPRINTF_PACKAGE_FIRST_RO_STR_CNT_GET(flags);
269 : 354 : bool is_str_arg = false;
270 : 354 : union cbprintf_package_hdr *pkg_hdr = packaged;
271 : :
272 : : /* Buffer must be aligned at least to size of a pointer. */
273 [ + - ]: 354 : if ((uintptr_t)packaged % sizeof(void *)) {
274 : : return -EFAULT;
275 : : }
276 : :
277 : : #if defined(__xtensa__)
278 : : /* Xtensa requires package to be 16 bytes aligned. */
279 : : if ((uintptr_t)packaged % CBPRINTF_PACKAGE_ALIGNMENT) {
280 : : return -EFAULT;
281 : : }
282 : : #endif
283 : :
284 : : /*
285 : : * Make room to store the arg list size, the number of
286 : : * appended writable strings and the number of appended
287 : : * read-only strings. They both occupy 1 byte each.
288 : : * Skip a byte. Then a uint32_t to store flags used to
289 : : * create the package.
290 : : *
291 : : * Given the next value to store is the format string pointer
292 : : * which is guaranteed to be at least 4 bytes, we just reserve
293 : : * multiple of pointer size for the above to preserve alignment.
294 : : *
295 : : * Refer to union cbprintf_package_hdr for more details.
296 : : */
297 : 354 : buf += sizeof(*pkg_hdr);
298 : :
299 : : /*
300 : : * When buf0 is NULL we don't store anything.
301 : : * Instead we count the needed space to store the data.
302 : : * In this case, incoming len argument indicates the anticipated
303 : : * buffer "misalignment" offset.
304 : : */
305 [ + + ]: 354 : if (buf0 == NULL) {
306 : 177 : buf += len % CBPRINTF_PACKAGE_ALIGNMENT;
307 : : /*
308 : : * The space to store the data is represented by both the
309 : : * buffer offset as well as the extra string data to be
310 : : * appended. When only figuring out the needed space, we
311 : : * don't append anything. Instead, we reuse the len variable
312 : : * to sum the size of that data.
313 : : *
314 : : * Also, we subtract any initial misalignment offset from
315 : : * the total as this won't be part of the buffer. To avoid
316 : : * going negative with an unsigned variable, we add an offset
317 : : * (CBPRINTF_PACKAGE_ALIGNMENT) that will be removed before
318 : : * returning.
319 : : */
320 : 177 : len = CBPRINTF_PACKAGE_ALIGNMENT - (len % CBPRINTF_PACKAGE_ALIGNMENT);
321 : : }
322 : :
323 : : /*
324 : : * Otherwise we must ensure we can store at least
325 : : * the pointer to the format string itself.
326 : : */
327 [ + + + - ]: 354 : if ((buf0 != NULL) && (BUF_OFFSET + sizeof(char *)) > len) {
328 : : return -ENOSPC;
329 : : }
330 : :
331 : : /*
332 : : * Then process the format string itself.
333 : : * Here we branch directly into the code processing strings
334 : : * which is in the middle of the following while() loop. That's the
335 : : * reason for the post-decrement on fmt as it will be incremented
336 : : * prior to the next (actually first) round of that loop.
337 : : */
338 : 354 : s = fmt;
339 : 354 : --fmt;
340 : 354 : align = VA_STACK_ALIGN(char *);
341 : 354 : size = sizeof(char *);
342 : 354 : goto process_string;
343 : :
344 : 13710 : while (true) {
345 : :
346 : : #if defined(CONFIG_CBPRINTF_PACKAGE_SUPPORT_TAGGED_ARGUMENTS)
347 : : if ((flags & CBPRINTF_PACKAGE_ARGS_ARE_TAGGED)
348 : : == CBPRINTF_PACKAGE_ARGS_ARE_TAGGED) {
349 : : int arg_tag = va_arg(ap, int);
350 : :
351 : : /*
352 : : * Here we copy the tag over to the package.
353 : : */
354 : : align = VA_STACK_ALIGN(int);
355 : : size = sizeof(int);
356 : :
357 : : /* align destination buffer location */
358 : : buf = (void *)ROUND_UP(buf, align);
359 : :
360 : : /* make sure the data fits */
361 : : if (buf0 != NULL && BUF_OFFSET + size > len) {
362 : : return -ENOSPC;
363 : : }
364 : :
365 : : if (buf0 != NULL) {
366 : : *(int *)buf = arg_tag;
367 : : }
368 : :
369 : : buf += sizeof(int);
370 : :
371 : : if (arg_tag == CBPRINTF_PACKAGE_ARG_TYPE_END) {
372 : : /* End of arguments */
373 : : break;
374 : : }
375 : :
376 : : /*
377 : : * There are lots of __fallthrough here since
378 : : * quite a few of the data types have the same
379 : : * storage size.
380 : : */
381 : : switch (arg_tag) {
382 : : case CBPRINTF_PACKAGE_ARG_TYPE_CHAR:
383 : : __fallthrough;
384 : : case CBPRINTF_PACKAGE_ARG_TYPE_UNSIGNED_CHAR:
385 : : __fallthrough;
386 : : case CBPRINTF_PACKAGE_ARG_TYPE_SHORT:
387 : : __fallthrough;
388 : : case CBPRINTF_PACKAGE_ARG_TYPE_UNSIGNED_SHORT:
389 : : __fallthrough;
390 : : case CBPRINTF_PACKAGE_ARG_TYPE_INT:
391 : : __fallthrough;
392 : : case CBPRINTF_PACKAGE_ARG_TYPE_UNSIGNED_INT:
393 : : align = VA_STACK_ALIGN(int);
394 : : size = sizeof(int);
395 : : break;
396 : :
397 : : case CBPRINTF_PACKAGE_ARG_TYPE_LONG:
398 : : __fallthrough;
399 : : case CBPRINTF_PACKAGE_ARG_TYPE_UNSIGNED_LONG:
400 : : align = VA_STACK_ALIGN(long);
401 : : size = sizeof(long);
402 : : break;
403 : :
404 : : case CBPRINTF_PACKAGE_ARG_TYPE_LONG_LONG:
405 : : __fallthrough;
406 : : case CBPRINTF_PACKAGE_ARG_TYPE_UNSIGNED_LONG_LONG:
407 : : align = VA_STACK_ALIGN(long long);
408 : : size = sizeof(long long);
409 : : break;
410 : :
411 : : case CBPRINTF_PACKAGE_ARG_TYPE_FLOAT:
412 : : __fallthrough;
413 : : case CBPRINTF_PACKAGE_ARG_TYPE_DOUBLE:
414 : : __fallthrough;
415 : : case CBPRINTF_PACKAGE_ARG_TYPE_LONG_DOUBLE: {
416 : : /*
417 : : * Handle floats separately as they may be
418 : : * held in a different register set.
419 : : */
420 : : union { double d; long double ld; } v;
421 : :
422 : : if (arg_tag == CBPRINTF_PACKAGE_ARG_TYPE_LONG_DOUBLE) {
423 : : v.ld = va_arg(ap, long double);
424 : : align = VA_STACK_ALIGN(long double);
425 : : size = sizeof(long double);
426 : : } else {
427 : : v.d = va_arg(ap, double);
428 : : align = VA_STACK_ALIGN(double);
429 : : size = sizeof(double);
430 : : }
431 : :
432 : : /* align destination buffer location */
433 : : buf = (void *) ROUND_UP(buf, align);
434 : : if (buf0 != NULL) {
435 : : /* make sure it fits */
436 : : if ((BUF_OFFSET + size) > len) {
437 : : return -ENOSPC;
438 : : }
439 : : if (Z_CBPRINTF_VA_STACK_LL_DBL_MEMCPY) {
440 : : memcpy(buf, (uint8_t *)&v, size);
441 : : } else if (fmt[-1] == 'L') {
442 : : *(long double *)buf = v.ld;
443 : : } else {
444 : : *(double *)buf = v.d;
445 : : }
446 : : }
447 : : buf += size;
448 : : parsing = false;
449 : : continue;
450 : : }
451 : :
452 : : case CBPRINTF_PACKAGE_ARG_TYPE_PTR_CHAR:
453 : : is_str_arg = true;
454 : :
455 : : __fallthrough;
456 : : case CBPRINTF_PACKAGE_ARG_TYPE_PTR_VOID:
457 : : align = VA_STACK_ALIGN(void *);
458 : : size = sizeof(void *);
459 : : break;
460 : :
461 : : default:
462 : : return -EINVAL;
463 : : }
464 : :
465 : : } else
466 : : #endif /* CONFIG_CBPRINTF_PACKAGE_SUPPORT_TAGGED_ARGUMENTS */
467 : : {
468 : : /* Scan the format string */
469 [ + + ]: 13710 : if (*++fmt == '\0') {
470 : : break;
471 : : }
472 : :
473 [ + + ]: 13356 : if (!parsing) {
474 [ + + ]: 12142 : if (*fmt == '%') {
475 : 868 : parsing = true;
476 : 868 : arg_idx++;
477 : 868 : align = VA_STACK_ALIGN(int);
478 : 868 : size = sizeof(int);
479 : : }
480 : 12142 : continue;
481 : : }
482 [ - + + - : 1214 : switch (*fmt) {
- - + + -
- - ]
483 : 2 : case '%':
484 : 2 : parsing = false;
485 : 2 : arg_idx--;
486 : 2 : continue;
487 : :
488 : 346 : case '#':
489 : : case '-':
490 : : case '+':
491 : : case ' ':
492 : : case '0':
493 : : case '1':
494 : : case '2':
495 : : case '3':
496 : : case '4':
497 : : case '5':
498 : : case '6':
499 : : case '7':
500 : : case '8':
501 : : case '9':
502 : : case '.':
503 : : case 'h':
504 : : case 'l':
505 : : case 'L':
506 : 346 : continue;
507 : :
508 : : case '*':
509 : : break;
510 : :
511 : 0 : case 'j':
512 : 0 : align = VA_STACK_ALIGN(intmax_t);
513 : 0 : size = sizeof(intmax_t);
514 : 0 : continue;
515 : :
516 : 0 : case 'z':
517 : 0 : align = VA_STACK_ALIGN(size_t);
518 : 0 : size = sizeof(size_t);
519 : 0 : continue;
520 : :
521 : 0 : case 't':
522 : 0 : align = VA_STACK_ALIGN(ptrdiff_t);
523 : 0 : size = sizeof(ptrdiff_t);
524 : 0 : continue;
525 : :
526 : 352 : case 'c':
527 : : case 'd':
528 : : case 'i':
529 : : case 'o':
530 : : case 'u':
531 : : case 'x':
532 : : case 'X':
533 [ - + ]: 352 : if (fmt[-1] == 'l') {
534 [ # # ]: 0 : if (fmt[-2] == 'l') {
535 : : align = VA_STACK_ALIGN(long long);
536 : : size = sizeof(long long);
537 : : } else {
538 : 0 : align = VA_STACK_ALIGN(long);
539 : 0 : size = sizeof(long);
540 : : }
541 : : }
542 : : parsing = false;
543 : : break;
544 : :
545 : 514 : case 's':
546 : 514 : is_str_arg = true;
547 : :
548 : : __fallthrough;
549 : : case 'p':
550 : : case 'n':
551 : : align = VA_STACK_ALIGN(void *);
552 : : size = sizeof(void *);
553 : : parsing = false;
554 : : break;
555 : :
556 : 0 : case 'a':
557 : : case 'A':
558 : : case 'e':
559 : : case 'E':
560 : : case 'f':
561 : : case 'F':
562 : : case 'g':
563 : 0 : case 'G': {
564 : : /*
565 : : * Handle floats separately as they may be
566 : : * held in a different register set.
567 : : */
568 : 0 : union { double d; long double ld; } v;
569 : :
570 [ # # ]: 0 : if (fmt[-1] == 'L') {
571 : 0 : v.ld = va_arg(ap, long double);
572 : 0 : align = VA_STACK_ALIGN(long double);
573 : 0 : size = sizeof(long double);
574 : : } else {
575 : 0 : v.d = va_arg(ap, double);
576 : 0 : align = VA_STACK_ALIGN(double);
577 : 0 : size = sizeof(double);
578 : : }
579 : : /* align destination buffer location */
580 : 0 : buf = (void *) ROUND_UP(buf, align);
581 [ # # ]: 0 : if (buf0 != NULL) {
582 : : /* make sure it fits */
583 [ # # ]: 0 : if (BUF_OFFSET + size > len) {
584 : 0 : return -ENOSPC;
585 : : }
586 : 0 : if (Z_CBPRINTF_VA_STACK_LL_DBL_MEMCPY) {
587 : : memcpy(buf, (uint8_t *)&v, size);
588 [ # # ]: 0 : } else if (fmt[-1] == 'L') {
589 : 0 : *(long double *)buf = v.ld;
590 : : } else {
591 : 0 : *(double *)buf = v.d;
592 : : }
593 : : }
594 : 0 : buf += size;
595 : 0 : parsing = false;
596 : 0 : continue;
597 : : }
598 : :
599 : 0 : default:
600 : 0 : parsing = false;
601 : 0 : continue;
602 : : }
603 : : }
604 : :
605 : : /* align destination buffer location */
606 : 866 : buf = (void *) ROUND_UP(buf, align);
607 : :
608 : : /* make sure the data fits */
609 [ + + + - ]: 866 : if ((buf0 != NULL) && (BUF_OFFSET + size) > len) {
610 : : return -ENOSPC;
611 : : }
612 : :
613 : : /* copy va_list data over to our buffer */
614 [ + + ]: 866 : if (is_str_arg) {
615 : 514 : s = va_arg(ap, char *);
616 : 868 : process_string:
617 [ + + ]: 868 : if (buf0 != NULL) {
618 : 434 : *(const char **)buf = s;
619 : : }
620 : :
621 [ + + - + ]: 868 : bool is_ro = (fros_cnt-- > 0) ? true : ptr_in_rodata(s);
622 : 868 : bool do_ro = !!(flags & CBPRINTF_PACKAGE_ADD_RO_STR_POS);
623 : :
624 [ + + ]: 868 : if (is_ro && !do_ro) {
625 : : /* nothing to do */
626 : : } else {
627 : 514 : uint32_t s_ptr_idx = BUF_OFFSET / sizeof(int);
628 : :
629 : : /*
630 : : * In the do_ro case we must consider
631 : : * room for possible STR_POS_RO_FLAG.
632 : : * Otherwise the index range is 8 bits
633 : : * and any overflow is caught later.
634 : : */
635 [ - + ]: 514 : if (do_ro && s_ptr_idx > STR_POS_MASK) {
636 : 0 : __ASSERT(false, "String with too many arguments");
637 : : return -EINVAL;
638 : : }
639 : :
640 [ - + ]: 514 : if (s_idx >= ARRAY_SIZE(str_ptr_pos)) {
641 : 0 : __ASSERT(false, "str_ptr_pos[] too small");
642 : : return -EINVAL;
643 : : }
644 : :
645 [ + + ]: 514 : if (buf0 != NULL) {
646 : : /*
647 : : * Remember string pointer location.
648 : : * We will append non-ro strings later.
649 : : */
650 : 257 : str_ptr_pos[s_idx] = s_ptr_idx;
651 : 257 : str_ptr_arg[s_idx] = arg_idx;
652 [ - + ]: 257 : if (is_ro) {
653 : : /* flag read-only string. */
654 : 0 : str_ptr_pos[s_idx] |= STR_POS_RO_FLAG;
655 : 0 : s_ro_cnt++;
656 : : } else {
657 : 257 : s_rw_cnt++;
658 : : }
659 [ - + ]: 257 : } else if (is_ro) {
660 : : /*
661 : : * Add only pointer position prefix
662 : : * when counting strings.
663 : : */
664 : 0 : len += 1;
665 [ - + ]: 257 : } else if (rws_pos_en) {
666 : : /*
667 : : * Add only pointer position prefix and
668 : : * argument index when counting strings.
669 : : */
670 : 0 : len += 2;
671 : : } else {
672 : : /*
673 : : * Add the string length, the final '\0'
674 : : * and size of the pointer position prefix.
675 : : */
676 : 257 : len += strlen(s) + 1 + 1;
677 : : }
678 : :
679 : 514 : s_idx++;
680 : : }
681 : 868 : buf += sizeof(char *);
682 : :
683 : 868 : is_str_arg = false;
684 [ + - ]: 352 : } else if (size == sizeof(int)) {
685 : 352 : int v = va_arg(ap, int);
686 : :
687 [ + + ]: 352 : if (buf0 != NULL) {
688 : 176 : *(int *)buf = v;
689 : : }
690 : 352 : buf += sizeof(int);
691 : 0 : } else if (size == sizeof(long)) {
692 : : long v = va_arg(ap, long);
693 : :
694 : : if (buf0 != NULL) {
695 : : *(long *)buf = v;
696 : : }
697 : : buf += sizeof(long);
698 [ # # ]: 0 : } else if (size == sizeof(long long)) {
699 : 0 : long long v = va_arg(ap, long long);
700 : :
701 [ # # ]: 0 : if (buf0 != NULL) {
702 : 0 : if (Z_CBPRINTF_VA_STACK_LL_DBL_MEMCPY) {
703 : : memcpy(buf, (uint8_t *)&v, sizeof(long long));
704 : : } else {
705 : 0 : *(long long *)buf = v;
706 : : }
707 : : }
708 : 0 : buf += sizeof(long long);
709 : : } else {
710 : 0 : __ASSERT(false, "unexpected size %u", size);
711 : : return -EINVAL;
712 : : }
713 : : }
714 : :
715 : : /*
716 : : * We remember the size of the argument list as a multiple of
717 : : * sizeof(int) and limit it to a 8-bit field. That means 1020 bytes
718 : : * worth of va_list, or about 127 arguments on a 64-bit system
719 : : * (twice that on 32-bit systems). That ought to be good enough.
720 : : */
721 [ - + ]: 354 : if ((BUF_OFFSET / sizeof(int)) > 255) {
722 : 0 : __ASSERT(false, "too many format args");
723 : : return -EINVAL;
724 : : }
725 : :
726 : : /*
727 : : * If all we wanted was to count required buffer size
728 : : * then we have it now.
729 : : */
730 [ + + ]: 354 : if (buf0 == NULL) {
731 : 177 : return BUF_OFFSET + len - CBPRINTF_PACKAGE_ALIGNMENT;
732 : : }
733 : :
734 : : /* Clear our buffer header. We made room for it initially. */
735 : 177 : *(char **)buf0 = NULL;
736 : :
737 : : /* Record end of argument list. */
738 : 177 : pkg_hdr->desc.len = BUF_OFFSET / sizeof(int);
739 : :
740 [ - + ]: 177 : if (rws_pos_en) {
741 : : /* Strings are appended, update location counter. */
742 : 0 : pkg_hdr->desc.str_cnt = 0;
743 : 0 : pkg_hdr->desc.rw_str_cnt = s_rw_cnt;
744 : : } else {
745 : : /* Strings are appended, update append counter. */
746 : 177 : pkg_hdr->desc.str_cnt = s_rw_cnt;
747 : 177 : pkg_hdr->desc.rw_str_cnt = 0;
748 : : }
749 : :
750 : 177 : pkg_hdr->desc.ro_str_cnt = s_ro_cnt;
751 : :
752 : : #ifdef CONFIG_CBPRINTF_PACKAGE_HEADER_STORE_CREATION_FLAGS
753 : : pkg_hdr->desc.pkg_flags = flags;
754 : : #endif
755 : :
756 : : /* Store strings pointer locations of read only strings. */
757 [ - + ]: 177 : if (s_ro_cnt != 0U) {
758 [ # # ]: 0 : for (i = 0; i < s_idx; i++) {
759 [ # # ]: 0 : if (!(str_ptr_pos[i] & STR_POS_RO_FLAG)) {
760 : 0 : continue;
761 : : }
762 : :
763 : 0 : uint8_t pos = str_ptr_pos[i] & STR_POS_MASK;
764 : :
765 : : /* make sure it fits */
766 [ # # ]: 0 : if ((BUF_OFFSET + 1) > len) {
767 : : return -ENOSPC;
768 : : }
769 : : /* store the pointer position prefix */
770 : 0 : *buf = pos;
771 : 0 : ++buf;
772 : : }
773 : : }
774 : :
775 : : /* Store strings prefixed by their pointer location. */
776 [ + + ]: 434 : for (i = 0; i < s_idx; i++) {
777 : : /* Process only RW strings. */
778 [ - + - - ]: 257 : if (s_ro_cnt && str_ptr_pos[i] & STR_POS_RO_FLAG) {
779 : 0 : continue;
780 : : }
781 : :
782 [ - + ]: 257 : if (rws_pos_en) {
783 : 0 : size = 0;
784 : 0 : *buf++ = str_ptr_arg[i];
785 : : } else {
786 : : /* retrieve the string pointer */
787 : 257 : s = *(char **)(buf0 + str_ptr_pos[i] * sizeof(int));
788 : : /* clear the in-buffer pointer (less entropy if compressed) */
789 : 257 : *(char **)(buf0 + str_ptr_pos[i] * sizeof(int)) = NULL;
790 : : /* find the string length including terminating '\0' */
791 : 257 : size = strlen(s) + 1;
792 : : }
793 : :
794 : : /* make sure it fits */
795 [ + - ]: 257 : if ((BUF_OFFSET + 1 + size) > len) {
796 : : return -ENOSPC;
797 : : }
798 : : /* store the pointer position prefix */
799 : 257 : *buf = str_ptr_pos[i];
800 : 257 : ++buf;
801 : : /* copy the string with its terminating '\0' */
802 : 257 : memcpy(buf, (uint8_t *)s, size);
803 : 257 : buf += size;
804 : : }
805 : :
806 : : /*
807 : : * TODO: remove pointers for appended strings since they're useless.
808 : : * TODO: explore leveraging same mechanism to remove alignment padding
809 : : */
810 : :
811 : 177 : return BUF_OFFSET;
812 : :
813 : : #undef BUF_OFFSET
814 : : #undef STR_POS_RO_FLAG
815 : : #undef STR_POS_MASK
816 : : }
817 : :
818 : 0 : int cbprintf_package(void *packaged, size_t len, uint32_t flags,
819 : : const char *format, ...)
820 : : {
821 : 0 : va_list ap;
822 : 0 : int ret;
823 : :
824 : 0 : va_start(ap, format);
825 : 0 : ret = cbvprintf_package(packaged, len, flags, format, ap);
826 : 0 : va_end(ap);
827 : 0 : return ret;
828 : : }
829 : :
830 : 177 : int cbpprintf_external(cbprintf_cb out,
831 : : cbvprintf_external_formatter_func formatter,
832 : : void *ctx, void *packaged)
833 : : {
834 : 177 : uint8_t *buf = packaged;
835 : 177 : struct cbprintf_package_hdr_ext *hdr = packaged;
836 : 177 : char *s, **ps;
837 : 177 : unsigned int i, args_size, s_nbr, ros_nbr, rws_nbr, s_idx;
838 : :
839 [ + - ]: 177 : if (buf == NULL) {
840 : : return -EINVAL;
841 : : }
842 : :
843 : : /* Retrieve the size of the arg list and number of strings. */
844 : 177 : args_size = hdr->hdr.desc.len * sizeof(int);
845 : 177 : s_nbr = hdr->hdr.desc.str_cnt;
846 : 177 : ros_nbr = hdr->hdr.desc.ro_str_cnt;
847 : 177 : rws_nbr = hdr->hdr.desc.rw_str_cnt;
848 : :
849 : : /* Locate the string table */
850 : 177 : s = (char *)(buf + args_size + ros_nbr + 2 * rws_nbr);
851 : :
852 : : /*
853 : : * Patch in string pointers.
854 : : */
855 [ + + ]: 434 : for (i = 0; i < s_nbr; i++) {
856 : : /* Locate pointer location for this string */
857 : 257 : s_idx = *(uint8_t *)s;
858 : 257 : ++s;
859 : 257 : ps = (char **)(buf + s_idx * sizeof(int));
860 : : /* update the pointer with current string location */
861 : 257 : *ps = s;
862 : : /* move to next string */
863 : 257 : s += strlen(s) + 1;
864 : : }
865 : :
866 : : /* Skip past the header */
867 : 177 : buf += sizeof(*hdr);
868 : :
869 : : /* Turn this into a va_list and print it */
870 : 177 : return cbprintf_via_va_list(out, formatter, ctx, hdr->fmt, buf);
871 : : }
872 : :
873 : : /* Function checks if character might be format specifier. Check is relaxed since
874 : : * compiler ensures that correct format specifier is used so it is enough to check
875 : : * that character is not one of potential modifier (e.g. number, dot, etc.).
876 : : */
877 : 0 : static bool is_fmt_spec(char c)
878 : : {
879 : 0 : return (c >= 64) && (c <= 122);
880 : : }
881 : :
882 : : /* Function checks if nth argument is a pointer (%p). Returns true is yes. Returns
883 : : * false if not or if string does not have nth argument.
884 : : */
885 : 0 : bool is_ptr(const char *fmt, int n)
886 : : {
887 : 0 : char c;
888 : 0 : bool mod = false;
889 : 0 : int cnt = 0;
890 : :
891 [ # # ]: 0 : while ((c = *fmt++) != '\0') {
892 [ # # ]: 0 : if (mod) {
893 [ # # ]: 0 : if (cnt == n) {
894 [ # # ]: 0 : if (c == 'p') {
895 : : return true;
896 [ # # ]: 0 : } else if (is_fmt_spec(c)) {
897 : : return false;
898 : : }
899 [ # # ]: 0 : } else if (is_fmt_spec(c)) {
900 : 0 : cnt++;
901 : 0 : mod = false;
902 : : }
903 : : }
904 [ # # ]: 0 : if (c == '%') {
905 : 0 : mod = !mod;
906 : : }
907 : : }
908 : :
909 : : return false;
910 : : }
911 : :
912 : 0 : int cbprintf_package_convert(void *in_packaged,
913 : : size_t in_len,
914 : : cbprintf_convert_cb cb,
915 : : void *ctx,
916 : : uint32_t flags,
917 : : uint16_t *strl,
918 : : size_t strl_len)
919 : : {
920 [ # # ]: 0 : __ASSERT_NO_MSG(in_packaged != NULL);
921 : :
922 : 0 : uint8_t *buf = in_packaged;
923 : 0 : uint32_t *buf32 = in_packaged;
924 : 0 : unsigned int args_size, ros_nbr, rws_nbr;
925 : 0 : bool fmt_present = flags & CBPRINTF_PACKAGE_CONVERT_PTR_CHECK ? true : false;
926 : 0 : bool rw_cpy;
927 : 0 : bool ro_cpy;
928 : 0 : struct cbprintf_package_desc *in_desc = in_packaged;
929 : :
930 [ # # ]: 0 : in_len = in_len != 0 ? in_len : get_package_len(in_packaged);
931 : :
932 : : /* Get number of RO string indexes in the package and check if copying
933 : : * includes appending those strings.
934 : : */
935 : 0 : ros_nbr = in_desc->ro_str_cnt;
936 [ # # ]: 0 : ro_cpy = ros_nbr &&
937 [ # # ]: 0 : (flags & CBPRINTF_PACKAGE_CONVERT_RO_STR) == CBPRINTF_PACKAGE_CONVERT_RO_STR;
938 : :
939 : : /* Get number of RW string indexes in the package and check if copying
940 : : * includes appending those strings.
941 : : */
942 : 0 : rws_nbr = in_desc->rw_str_cnt;
943 [ # # ]: 0 : rw_cpy = rws_nbr > 0 &&
944 [ # # ]: 0 : (flags & CBPRINTF_PACKAGE_CONVERT_RW_STR) == CBPRINTF_PACKAGE_CONVERT_RW_STR;
945 : :
946 : : /* If flags are not set or appending request without rw string indexes
947 : : * present is chosen, just do a simple copy (or length calculation).
948 : : * Assuming that it is the most common case.
949 : : */
950 [ # # ]: 0 : if (!rw_cpy && !ro_cpy) {
951 [ # # ]: 0 : if (cb) {
952 : 0 : cb(in_packaged, in_len, ctx);
953 : : }
954 : :
955 : 0 : return in_len;
956 : : }
957 : :
958 : : /* If we got here, it means that coping will be more complex and will be
959 : : * done with strings appending.
960 : : * Retrieve the size of the arg list.
961 : : */
962 : 0 : args_size = in_desc->len * sizeof(int);
963 : :
964 : 0 : int out_len;
965 : :
966 : : /* Pointer to array with string locations. Array starts with read-only
967 : : * string locations.
968 : : */
969 : 0 : const char *fmt = *(const char **)(buf + sizeof(void *));
970 : 0 : uint8_t *str_pos = &buf[args_size];
971 : 0 : size_t strl_cnt = 0;
972 : :
973 : : /* If null destination, just calculate output length. */
974 [ # # ]: 0 : if (cb == NULL) {
975 : 0 : out_len = (int)in_len;
976 [ # # ]: 0 : if (ro_cpy) {
977 [ # # ]: 0 : for (unsigned int i = 0; i < ros_nbr; i++) {
978 : 0 : const char *str = *(const char **)&buf32[*str_pos];
979 : 0 : int len = append_string(cb, NULL, str, 0);
980 : :
981 : : /* If possible store calculated string length. */
982 [ # # ]: 0 : if (strl && strl_cnt < strl_len) {
983 : 0 : strl[strl_cnt++] = (uint16_t)len;
984 : : }
985 : 0 : out_len += len;
986 : 0 : str_pos++;
987 : : }
988 : : } else {
989 : 0 : str_pos += ros_nbr;
990 : : }
991 : :
992 : 0 : bool drop_ro_str_pos = !(flags &
993 : : (CBPRINTF_PACKAGE_CONVERT_KEEP_RO_STR |
994 : : CBPRINTF_PACKAGE_CONVERT_RO_STR));
995 : :
996 : : /* Handle RW strings. */
997 [ # # ]: 0 : for (unsigned int i = 0; i < rws_nbr; i++) {
998 : 0 : uint8_t arg_idx = *str_pos++;
999 : 0 : uint8_t arg_pos = *str_pos++;
1000 : 0 : const char *str = *(const char **)&buf32[arg_pos];
1001 : 0 : bool is_ro = ptr_in_rodata(str);
1002 : 0 : int len;
1003 : :
1004 [ # # ]: 0 : if (IS_ENABLED(CONFIG_CBPRINTF_CONVERT_CHECK_PTR) &&
1005 [ # # ]: 0 : fmt_present && is_ptr(fmt, arg_idx)) {
1006 : 0 : LOG_WRN("(unsigned) char * used for %%p argument. "
1007 : : "It's recommended to cast it to void * because "
1008 : : "it may cause misbehavior in certain "
1009 : : "configurations. String:\"%s\" argument:%d", fmt, arg_idx);
1010 : : /* Since location is being dropped, decrement
1011 : : * output length by 2 (argument index + position)
1012 : : */
1013 : 0 : out_len -= 2;
1014 : 0 : continue;
1015 : : }
1016 : :
1017 [ # # ]: 0 : if (is_ro) {
1018 [ # # ]: 0 : if (flags & CBPRINTF_PACKAGE_CONVERT_RO_STR) {
1019 : 0 : goto calculate_string_length;
1020 : : } else {
1021 [ # # ]: 0 : out_len -= drop_ro_str_pos ? 2 : 1;
1022 : : }
1023 [ # # ]: 0 : } else if (flags & CBPRINTF_PACKAGE_CONVERT_RW_STR) {
1024 : 0 : calculate_string_length:
1025 : 0 : len = append_string(cb, NULL, str, 0);
1026 : :
1027 : : /* If possible store calculated string length. */
1028 [ # # ]: 0 : if (strl && strl_cnt < strl_len) {
1029 : 0 : strl[strl_cnt++] = (uint16_t)len;
1030 : : }
1031 : : /* string length decremented by 1 because argument
1032 : : * index is dropped.
1033 : : */
1034 : 0 : out_len += (len - 1);
1035 : : }
1036 : : }
1037 : :
1038 : 0 : return out_len;
1039 : : }
1040 : :
1041 : 0 : struct cbprintf_package_desc out_desc;
1042 : : /* At least one is copied in. */
1043 : 0 : uint8_t cpy_str_pos[16];
1044 : : /* Up to one will be kept since if both types are kept it returns earlier. */
1045 : 0 : uint8_t keep_str_pos[16];
1046 : 0 : uint8_t scpy_cnt;
1047 : 0 : uint8_t keep_cnt;
1048 : 0 : uint8_t *dst;
1049 : 0 : int rv;
1050 : :
1051 : : /* If read-only strings shall be appended to the output package copy
1052 : : * their indexes to the local array, otherwise indicate that indexes
1053 : : * shall remain in the output package.
1054 : : */
1055 [ # # ]: 0 : if (ro_cpy) {
1056 : : scpy_cnt = ros_nbr;
1057 : : keep_cnt = 0;
1058 : : dst = cpy_str_pos;
1059 [ # # # # ]: 0 : } else if (ros_nbr && flags & CBPRINTF_PACKAGE_CONVERT_KEEP_RO_STR) {
1060 : : scpy_cnt = 0;
1061 : : keep_cnt = ros_nbr;
1062 : : dst = keep_str_pos;
1063 : : } else {
1064 : 0 : scpy_cnt = 0;
1065 : 0 : keep_cnt = 0;
1066 : 0 : dst = NULL;
1067 : : }
1068 [ # # ]: 0 : if (dst) {
1069 : 0 : memcpy(dst, str_pos, ros_nbr);
1070 : : }
1071 : 0 : str_pos += ros_nbr;
1072 : :
1073 : : /* Go through read-write strings and identify which shall be appended.
1074 : : * Note that there may be read-only strings there. Use address evaluation
1075 : : * to determine if strings is read-only.
1076 : : */
1077 [ # # ]: 0 : for (unsigned int i = 0; i < rws_nbr; i++) {
1078 : 0 : uint8_t arg_idx = *str_pos++;
1079 : 0 : uint8_t arg_pos = *str_pos++;
1080 : 0 : const char *str = *(const char **)&buf32[arg_pos];
1081 : 0 : bool is_ro = ptr_in_rodata(str);
1082 : :
1083 [ # # ]: 0 : if (IS_ENABLED(CONFIG_CBPRINTF_CONVERT_CHECK_PTR) &&
1084 [ # # ]: 0 : fmt_present && is_ptr(fmt, arg_idx)) {
1085 : 0 : continue;
1086 : : }
1087 : :
1088 [ # # ]: 0 : if (is_ro) {
1089 [ # # ]: 0 : if (flags & CBPRINTF_PACKAGE_CONVERT_RO_STR) {
1090 [ # # ]: 0 : __ASSERT_NO_MSG(scpy_cnt < sizeof(cpy_str_pos));
1091 : 0 : cpy_str_pos[scpy_cnt++] = arg_pos;
1092 [ # # ]: 0 : } else if (flags & CBPRINTF_PACKAGE_CONVERT_KEEP_RO_STR) {
1093 [ # # ]: 0 : __ASSERT_NO_MSG(keep_cnt < sizeof(keep_str_pos));
1094 : 0 : keep_str_pos[keep_cnt++] = arg_pos;
1095 : : } else {
1096 : : /* Drop information about ro_str location. */
1097 : : }
1098 : : } else {
1099 [ # # ]: 0 : if (flags & CBPRINTF_PACKAGE_CONVERT_RW_STR) {
1100 [ # # ]: 0 : __ASSERT_NO_MSG(scpy_cnt < sizeof(cpy_str_pos));
1101 : 0 : cpy_str_pos[scpy_cnt++] = arg_pos;
1102 : : } else {
1103 [ # # ]: 0 : __ASSERT_NO_MSG(keep_cnt < sizeof(keep_str_pos));
1104 : 0 : keep_str_pos[keep_cnt++] = arg_idx;
1105 : 0 : keep_str_pos[keep_cnt++] = arg_pos;
1106 : : }
1107 : : }
1108 : : }
1109 : :
1110 : : /* Set amount of strings appended to the package. */
1111 : 0 : out_desc.len = in_desc->len;
1112 : 0 : out_desc.str_cnt = in_desc->str_cnt + scpy_cnt;
1113 [ # # ]: 0 : out_desc.rw_str_cnt = (flags & CBPRINTF_PACKAGE_CONVERT_RW_STR) ? 0 : (keep_cnt / 2);
1114 [ # # ]: 0 : out_desc.ro_str_cnt = (flags & CBPRINTF_PACKAGE_CONVERT_RO_STR) ? 0 :
1115 [ # # ]: 0 : ((flags & CBPRINTF_PACKAGE_CONVERT_KEEP_RO_STR) ? keep_cnt : 0);
1116 : :
1117 : : /* Temporary overwrite input descriptor to allow bulk transfer */
1118 : 0 : struct cbprintf_package_desc in_desc_backup = *in_desc;
1119 : 0 : *in_desc = out_desc;
1120 : :
1121 : : /* Copy package header and arguments. */
1122 : 0 : rv = cb(in_packaged, args_size, ctx);
1123 [ # # ]: 0 : if (rv < 0) {
1124 : : return rv;
1125 : : }
1126 : 0 : out_len = rv;
1127 : : /* Restore input descriptor. */
1128 : 0 : *in_desc = in_desc_backup;
1129 : :
1130 : : /* Copy string positions which are kept. */
1131 : 0 : rv = cb(keep_str_pos, keep_cnt, ctx);
1132 [ # # ]: 0 : if (rv < 0) {
1133 : : return rv;
1134 : : }
1135 : 0 : out_len += rv;
1136 : :
1137 : : /* Copy appended strings from source package to destination. */
1138 : 0 : size_t strs_len = in_len - (args_size + ros_nbr + 2 * rws_nbr);
1139 : :
1140 : 0 : rv = cb(str_pos, strs_len, ctx);
1141 [ # # ]: 0 : if (rv < 0) {
1142 : : return rv;
1143 : : }
1144 : 0 : out_len += rv;
1145 : :
1146 : : /* Append strings */
1147 [ # # ]: 0 : for (unsigned int i = 0; i < scpy_cnt; i++) {
1148 : 0 : uint8_t loc = cpy_str_pos[i];
1149 : 0 : const char *str = *(const char **)&buf32[loc];
1150 [ # # ]: 0 : uint16_t str_len = strl ? strl[i] : 0;
1151 : :
1152 : 0 : rv = cb(&loc, 1, ctx);
1153 [ # # ]: 0 : if (rv < 0) {
1154 : 0 : return rv;
1155 : : }
1156 : 0 : out_len += rv;
1157 : :
1158 : 0 : rv = append_string(cb, ctx, str, str_len);
1159 [ # # ]: 0 : if (rv < 0) {
1160 : 0 : return rv;
1161 : : }
1162 : 0 : out_len += rv;
1163 : : }
1164 : :
1165 : : /* Empty call (can be interpreted as flushing) */
1166 : 0 : (void)cb(NULL, 0, ctx);
1167 : :
1168 : 0 : return out_len;
1169 : : }
|