Zephyr API Documentation  2.7.0-rc2
A Scalable Open Source RTOS
cbprintf_internal.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2020 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7#ifndef ZEPHYR_INCLUDE_SYS_CBPRINTF_INTERNAL_H_
8#define ZEPHYR_INCLUDE_SYS_CBPRINTF_INTERNAL_H_
9
10#include <errno.h>
11#include <stdarg.h>
12#include <stddef.h>
13#include <stdint.h>
14#include <toolchain.h>
15#include <sys/util.h>
16
17#ifdef CONFIG_CBPRINTF_STATIC_PACKAGE_CHECK_ALIGNMENT
18#include <sys/__assert.h>
19#endif
20
21/*
22 * Special alignment cases
23 */
24
25#if defined(__i386__)
26/* there are no gaps on the stack */
27#define VA_STACK_ALIGN(type) 1
28#elif defined(__sparc__)
29/* there are no gaps on the stack */
30#define VA_STACK_ALIGN(type) 1
31#elif defined(__x86_64__)
32#define VA_STACK_MIN_ALIGN 8
33#elif defined(__aarch64__)
34#define VA_STACK_MIN_ALIGN 8
35#elif defined(__riscv)
36#define VA_STACK_MIN_ALIGN (__riscv_xlen / 8)
37#endif
38
39/*
40 * Default alignment values if not specified by architecture config
41 */
42
43#ifndef VA_STACK_MIN_ALIGN
44#define VA_STACK_MIN_ALIGN 1
45#endif
46
47#ifndef VA_STACK_ALIGN
48#define VA_STACK_ALIGN(type) MAX(VA_STACK_MIN_ALIGN, __alignof__(type))
49#endif
50
51static inline void z_cbprintf_wcpy(int *dst, int *src, size_t len)
52{
53 for (size_t i = 0; i < len; i++) {
54 dst[i] = src[i];
55 }
56}
57
58#include <sys/cbprintf_cxx.h>
59
60#ifdef __cplusplus
61extern "C" {
62#endif
63
64
65#if defined(__sparc__)
66/* The SPARC V8 ABI guarantees that the arguments of a variable argument
67 * list function are stored on the stack at addresses which are 32-bit
68 * aligned. It means that variables of type unit64_t and double may not
69 * be properly aligned on the stack.
70 *
71 * The compiler is aware of the ABI and takes care of this. However,
72 * as we are directly accessing the variable argument list here, we need
73 * to take the alignment into consideration and copy 64-bit arguments
74 * as 32-bit words.
75 */
76#define Z_CBPRINTF_VA_STACK_LL_DBL_MEMCPY 1
77#else
78#define Z_CBPRINTF_VA_STACK_LL_DBL_MEMCPY 0
79#endif
80
87#ifdef __cplusplus
88#define Z_CBPRINTF_IS_PCHAR(x) z_cbprintf_cxx_is_pchar(x)
89#else
90#define Z_CBPRINTF_IS_PCHAR(x) \
91 _Generic((x) + 0, \
92 char * : 1, \
93 const char * : 1, \
94 volatile char * : 1, \
95 const volatile char * : 1, \
96 wchar_t * : 1, \
97 const wchar_t * : 1, \
98 volatile wchar_t * : 1, \
99 const volatile wchar_t * : 1, \
100 default : \
101 0)
102#endif
103
112#define Z_CBPRINTF_HAS_PCHAR_ARGS(fmt, ...) \
113 (FOR_EACH(Z_CBPRINTF_IS_PCHAR, (+), __VA_ARGS__))
114
126#if Z_C_GENERIC
127#define Z_CBPRINTF_MUST_RUNTIME_PACKAGE(skip, ...) ({\
128 _Pragma("GCC diagnostic push") \
129 _Pragma("GCC diagnostic ignored \"-Wpointer-arith\"") \
130 int _rv = COND_CODE_0(NUM_VA_ARGS_LESS_1(__VA_ARGS__), \
131 (0), \
132 (((Z_CBPRINTF_HAS_PCHAR_ARGS(__VA_ARGS__) - skip) > 0))); \
133 _Pragma("GCC diagnostic pop")\
134 _rv; \
135})
136#else
137#define Z_CBPRINTF_MUST_RUNTIME_PACKAGE(skip, ...) 1
138#endif
139
149#ifdef __cplusplus
150#define Z_CBPRINTF_ARG_SIZE(v) z_cbprintf_cxx_arg_size(v)
151#else
152#define Z_CBPRINTF_ARG_SIZE(v) ({\
153 __auto_type _v = (v) + 0; \
154 size_t _arg_size = _Generic((v), \
155 float : sizeof(double), \
156 default : \
157 sizeof((_v)) \
158 ); \
159 _arg_size; \
160})
161#endif
162
169#ifdef __cplusplus
170#define Z_CBPRINTF_STORE_ARG(buf, arg) z_cbprintf_cxx_store_arg(buf, arg)
171#else
172#define Z_CBPRINTF_STORE_ARG(buf, arg) do { \
173 if (Z_CBPRINTF_VA_STACK_LL_DBL_MEMCPY) { \
174 /* If required, copy arguments by word to avoid unaligned access.*/ \
175 __auto_type _v = (arg) + 0; \
176 double _d = _Generic((arg) + 0, \
177 float : (arg) + 0, \
178 default : \
179 0.0); \
180 size_t arg_size = Z_CBPRINTF_ARG_SIZE(arg); \
181 size_t _wsize = arg_size / sizeof(int); \
182 z_cbprintf_wcpy((int *)buf, \
183 (int *) _Generic((arg) + 0, float : &_d, default : &_v), \
184 _wsize); \
185 } else { \
186 *_Generic((arg) + 0, \
187 char : (int *)buf, \
188 unsigned char: (int *)buf, \
189 short : (int *)buf, \
190 unsigned short : (int *)buf, \
191 int : (int *)buf, \
192 unsigned int : (unsigned int *)buf, \
193 long : (long *)buf, \
194 unsigned long : (unsigned long *)buf, \
195 long long : (long long *)buf, \
196 unsigned long long : (unsigned long long *)buf, \
197 float : (double *)buf, \
198 double : (double *)buf, \
199 long double : (long double *)buf, \
200 default : \
201 (const void **)buf) = arg; \
202 } \
203} while (0)
204#endif
205
212#ifdef __cplusplus
213#define Z_CBPRINTF_ALIGNMENT(_arg) z_cbprintf_cxx_alignment(_arg)
214#else
215#define Z_CBPRINTF_ALIGNMENT(_arg) \
216 MAX(_Generic((_arg) + 0, \
217 float : VA_STACK_ALIGN(double), \
218 double : VA_STACK_ALIGN(double), \
219 long double : VA_STACK_ALIGN(long double), \
220 long long : VA_STACK_ALIGN(long long), \
221 unsigned long long : VA_STACK_ALIGN(long long), \
222 default : \
223 __alignof__((_arg) + 0)), VA_STACK_MIN_ALIGN)
224#endif
225
236#ifdef __cplusplus
237#if defined(__x86_64__) || defined(__riscv) || defined(__aarch64__)
238#define Z_CBPRINTF_IS_LONGDOUBLE(x) 0
239#else
240#define Z_CBPRINTF_IS_LONGDOUBLE(x) z_cbprintf_cxx_is_longdouble(x)
241#endif
242#else
243#define Z_CBPRINTF_IS_LONGDOUBLE(x) \
244 _Generic((x) + 0, long double : 1, default : 0)
245#endif
246
262#define Z_CBPRINTF_PACK_ARG2(_buf, _idx, _align_offset, _max, \
263 _cfg_flags, _s_idx, _s_buf, _arg) \
264do { \
265 BUILD_ASSERT(!((sizeof(double) < VA_STACK_ALIGN(long double)) && \
266 Z_CBPRINTF_IS_LONGDOUBLE(_arg) && \
267 !IS_ENABLED(CONFIG_CBPRINTF_PACKAGE_LONGDOUBLE)),\
268 "Packaging of long double not enabled in Kconfig."); \
269 while (_align_offset % Z_CBPRINTF_ALIGNMENT(_arg)) { \
270 _idx += sizeof(int); \
271 _align_offset += sizeof(int); \
272 } \
273 uint32_t _arg_size = Z_CBPRINTF_ARG_SIZE(_arg); \
274 if (Z_CBPRINTF_IS_PCHAR(_arg)) { \
275 _s_buf[_s_idx++] = _idx / sizeof(int); \
276 } \
277 if (_buf && _idx < (int)_max) { \
278 Z_CBPRINTF_STORE_ARG(&_buf[_idx], _arg); \
279 } \
280 _idx += _arg_size; \
281 _align_offset += _arg_size; \
282} while (0)
283
290#define Z_CBPRINTF_PACK_ARG(arg) \
291 Z_CBPRINTF_PACK_ARG2(_pbuf, _pkg_len, _pkg_offset, _pmax, _flags, \
292 _s_cnt, _s_buffer, arg)
293
300struct z_cbprintf_desc {
301 uint8_t len;
302 uint8_t str_cnt;
303 uint8_t ro_str_cnt;
304};
305
307union z_cbprintf_hdr {
308 struct z_cbprintf_desc desc;
309 void *raw;
310};
311
312/* When using clang additional warning needs to be suppressed since each
313 * argument of fmt string is used for sizeof() which results in the warning
314 * if argument is a stirng literal. Suppression is added here instead of
315 * the macro which generates the warning to not slow down the compiler.
316 */
317#if __clang__ == 1
318#define Z_CBPRINTF_SUPPRESS_SIZEOF_ARRAY_DECAY \
319 _Pragma("GCC diagnostic ignored \"-Wsizeof-array-decay\"")
320#else
321#define Z_CBPRINTF_SUPPRESS_SIZEOF_ARRAY_DECAY
322#endif
323
339#define Z_CBPRINTF_STATIC_PACKAGE_GENERIC(buf, _inlen, _outlen, _align_offset, \
340 _flags, ... /* fmt, ... */) \
341do { \
342 _Pragma("GCC diagnostic push") \
343 _Pragma("GCC diagnostic ignored \"-Wpointer-arith\"") \
344 Z_CBPRINTF_SUPPRESS_SIZEOF_ARRAY_DECAY \
345 BUILD_ASSERT(!IS_ENABLED(CONFIG_XTENSA) || \
346 (IS_ENABLED(CONFIG_XTENSA) && \
347 !(_align_offset % CBPRINTF_PACKAGE_ALIGNMENT)), \
348 "Xtensa requires aligned package."); \
349 BUILD_ASSERT((_align_offset % sizeof(int)) == 0, \
350 "Alignment offset must be multiply of a word."); \
351 IF_ENABLED(CONFIG_CBPRINTF_STATIC_PACKAGE_CHECK_ALIGNMENT, \
352 (__ASSERT(!((uintptr_t)buf & (CBPRINTF_PACKAGE_ALIGNMENT - 1)), \
353 "Buffer must be aligned.");)) \
354 bool str_idxs = _flags & CBPRINTF_PACKAGE_ADD_STRING_IDXS; \
355 uint8_t *_pbuf = buf; \
356 uint8_t _s_cnt = 0; \
357 uint16_t _s_buffer[16]; \
358 size_t _pmax = (buf != NULL) ? _inlen : INT32_MAX; \
359 int _pkg_len = 0; \
360 int _total_len = 0; \
361 int _pkg_offset = _align_offset; \
362 union z_cbprintf_hdr *_len_loc; \
363 /* package starts with string address and field with length */ \
364 if (_pmax < sizeof(union z_cbprintf_hdr)) { \
365 _outlen = -ENOSPC; \
366 break; \
367 } \
368 _len_loc = (union z_cbprintf_hdr *)_pbuf; \
369 _pkg_len += sizeof(union z_cbprintf_hdr); \
370 _pkg_offset += sizeof(union z_cbprintf_hdr); \
371 /* Pack remaining arguments */\
372 FOR_EACH(Z_CBPRINTF_PACK_ARG, (;), __VA_ARGS__);\
373 _total_len = _pkg_len; \
374 if (str_idxs) {\
375 _total_len += _s_cnt; \
376 if (_pbuf) { \
377 for (int i = 0; i < _s_cnt; i++) { \
378 _pbuf[_pkg_len + i] = _s_buffer[i]; \
379 } \
380 } \
381 } \
382 /* Store length */ \
383 _outlen = (_total_len > (int)_pmax) ? -ENOSPC : _total_len; \
384 /* Store length in the header, set number of dumped strings to 0 */ \
385 if (_pbuf) { \
386 union z_cbprintf_hdr hdr = { \
387 .desc = { \
388 .len = (uint8_t)(_pkg_len / sizeof(int)), \
389 .str_cnt = 0, \
390 .ro_str_cnt = str_idxs ? _s_cnt : (uint8_t)0, \
391 } \
392 }; \
393 *_len_loc = hdr; \
394 } \
395 _Pragma("GCC diagnostic pop") \
396} while (0)
397
398#if Z_C_GENERIC
399#define Z_CBPRINTF_STATIC_PACKAGE(packaged, inlen, outlen, align_offset, flags, \
400 ... /* fmt, ... */) \
401 Z_CBPRINTF_STATIC_PACKAGE_GENERIC(packaged, inlen, outlen, \
402 align_offset, flags, __VA_ARGS__)
403#else
404#define Z_CBPRINTF_STATIC_PACKAGE(packaged, inlen, outlen, align_offset, flags, \
405 ... /* fmt, ... */) \
406do { \
407 /* Small trick needed to avoid warning on always true */ \
408 if (((uintptr_t)packaged + 1) != 1) { \
409 outlen = cbprintf_package(packaged, inlen, flags, __VA_ARGS__); \
410 } else { \
411 outlen = cbprintf_package(NULL, align_offset, flags, __VA_ARGS__); \
412 } \
413} while (0)
414#endif /* Z_C_GENERIC */
415
416#ifdef __cplusplus
417}
418#endif
419
420
421#endif /* ZEPHYR_INCLUDE_SYS_CBPRINTF_INTERNAL_H_ */
System error numbers.
__UINT8_TYPE__ uint8_t
Definition: stdint.h:58
Macros to abstract toolchain specific capabilities.
Misc utilities.