/* per-type auxiliary state */ struct btf_dump_type_aux_state { /* topological sorting state */ enum btf_dump_type_order_state order_state: 2; /* emitting state used to determine the need for forward declaration */ enum btf_dump_type_emit_state emit_state: 2; /* whether forward declaration was already emitted */
__u8 fwd_emitted: 1; /* whether unique non-duplicate name was already assigned */
__u8 name_resolved: 1; /* whether type is referenced from any other type */
__u8 referenced: 1;
};
/* indent string length; one indent string is added for each indent level */ #define BTF_DATA_INDENT_STR_LEN 32
/* *CommoninternaldataforBTFtypedatadumpoperations.
*/ struct btf_dump_data { constvoid *data_end; /* end of valid data to show */ bool compact; bool skip_names; bool emit_zeroes; bool emit_strings;
__u8 indent_lvl; /* base indent level */ char indent_str[BTF_DATA_INDENT_STR_LEN]; /* below are used during iteration */ int depth; bool is_array_member; bool is_array_terminated; bool is_array_char;
};
struct btf_dump { conststruct btf *btf;
btf_dump_printf_fn_t printf_fn; void *cb_ctx; int ptr_sz; bool strip_mods; bool skip_anon_defs; int last_id;
/* per-type auxiliary state */ struct btf_dump_type_aux_state *type_states;
size_t type_states_cap; /* per-type optional cached unique name, must be freed, if present */ constchar **cached_names;
size_t cached_names_cap;
/* topo-sorted list of dependent type definitions */
__u32 *emit_queue; int emit_queue_cap; int emit_queue_cnt;
/* *stackoftypedeclarations(e.g.,chainofmodifiers,arrays, *funcs,etc)
*/
__u32 *decl_stack; int decl_stack_cap; int decl_stack_cnt;
/* maps struct/union/enum name to a number of name occurrences */ struct hashmap *type_names; /* *mapstypedefidentifiersandenumvaluenamestoanumberofsuch *nameoccurrences
*/ struct hashmap *ident_names; /* *datafortypeddisplay;allocatedifneeded.
*/ struct btf_dump_data *typed_dump;
};
free(d->type_states); if (d->cached_names) { /* any set cached name is owned by us and should be freed */ for (i = 0; i <= d->last_id; i++) { if (d->cached_names[i])
free((void *)d->cached_names[i]);
}
}
free(d->cached_names);
free(d->emit_queue);
free(d->decl_stack);
btf_dump_free_names(d->type_names);
btf_dump_free_names(d->ident_names);
for (i = 0; i < d->emit_queue_cnt; i++)
btf_dump_emit_type(d, d->emit_queue[i], 0/*top-level*/);
return0;
}
/* *Markalltypesthatarereferencedfromanyothertype.Thisisusedto *determinetop-levelanonymousenumsthatneedtobeemittedasan *independenttypedeclarations. *Anonymousenumscomeintwoflavors:eitherembeddedinastruct'sfield *definition,inwhichcasetheyhavetobedeclaredinlineaspartoffield *typedeclaration;orasatop-levelanonymousenum,typicallyusedfor *declaringglobalconstants.It'simpossibletodistinguishbetweentwo *withoutknowingwhethergivenenumtypewasreferencedfromothertype: *top-levelanonymousenumwon'tbereferencedbyanything,whileembedded *onewill.
*/ staticint btf_dump_mark_referenced(struct btf_dump *d)
{ int i, j, n = btf__type_cnt(d->btf); conststruct btf_type *t;
__u16 vlen;
for (i = d->last_id + 1; i < n; i++) {
t = btf__type_by_id(d->btf, i);
vlen = btf_vlen(t);
switch (btf_kind(t)) { case BTF_KIND_INT: case BTF_KIND_ENUM: case BTF_KIND_ENUM64: case BTF_KIND_FWD: case BTF_KIND_FLOAT: break;
case BTF_KIND_VOLATILE: case BTF_KIND_CONST: case BTF_KIND_RESTRICT: case BTF_KIND_PTR: case BTF_KIND_TYPEDEF: case BTF_KIND_FUNC: case BTF_KIND_VAR: case BTF_KIND_DECL_TAG: case BTF_KIND_TYPE_TAG:
d->type_states[t->type].referenced = 1; break;
case BTF_KIND_ARRAY: { conststruct btf_array *a = btf_array(t);
d->type_states[a->index_type].referenced = 1;
d->type_states[a->type].referenced = 1; break;
} case BTF_KIND_STRUCT: case BTF_KIND_UNION: { conststruct btf_member *m = btf_members(t);
/* typedef is similar to struct/union w.r.t. fwd-decls */ if (through_ptr && !is_strong) return0;
/* typedef is always a named definition */
err = btf_dump_add_emit_queue_id(d, id); if (err) return err;
d->type_states[id].order_state = ORDERED; return1;
} case BTF_KIND_VOLATILE: case BTF_KIND_CONST: case BTF_KIND_RESTRICT: case BTF_KIND_TYPE_TAG: return btf_dump_order_type(d, t->type, through_ptr);
case BTF_KIND_FUNC_PROTO: { conststruct btf_param *p = btf_params(t); bool is_strong;
m = btf_members(t);
vlen = btf_vlen(t); /* all non-bitfield fields have to be naturally aligned */ for (i = 0; i < vlen; i++, m++) {
align = btf__align_of(btf, m->type);
bit_sz = btf_member_bitfield_size(t, i); if (align && bit_sz == 0 && m->offset % (8 * align) != 0) returntrue;
max_align = max(align, max_align);
} /* size of a non-packed struct has to be a multiple of its alignment */ if (t->size % max_align != 0) returntrue; /* *iforiginalstructwasmarkedaspacked,butitslayoutis *naturallyaligned,we'lldetectthatit'snotpacked
*/ returnfalse;
}
staticvoid btf_dump_emit_bit_padding(conststruct btf_dump *d, int cur_off, int next_off, int next_align, bool in_bitfield, int lvl)
{ conststruct { constchar *name; int bits;
} pads[] = {
{"long", d->ptr_sz * 8}, {"int", 32}, {"short", 16}, {"char", 8}
}; int new_off = 0, pad_bits = 0, bits, i; constchar *pad_type = NULL;
if (cur_off >= next_off) return; /* no gap */
/* For filling out padding we want to take advantage of *naturalalignmentrulestominimizeunnecessaryexplicit *padding.First,wefindthelargesttype(amonglong,int, *short,orchar)thatcanbeusedtoforcenaturallyaligned *boundary.Oncedetermined,we'llusesuchtypetofillin *theremainingpaddinggap.Insomecaseswecanrelyon *compilerfillingsomegaps,butsometimesweneedtoforce *alignmenttoclosenaturalalignmentwithmarkerslike *`long:0`(thisisalwaysthecaseforbitfields).Note *thatevenifstructitselfhas,let'ssay4-bytealignment *(i.e.,itonlyusesuptoint-alignedtypes),using`long: *X;`explicitpaddingdoesn'tactuallychangestruct's *overallalignmentrequirements,butcompilerdoestakeinto *accountthattype's(long,inthisexample)natural *alignmentrequirementswhenaddingimplicitpadding.Weuse *thisfactheavilyanddon'tworryaboutruiningcorrect *structalignmentrequirement.
*/ for (i = 0; i < ARRAY_SIZE(pads); i++) {
pad_bits = pads[i].bits;
pad_type = pads[i].name;
new_off = roundup(cur_off, pad_bits); if (new_off <= next_off) break;
}
if (new_off > cur_off && new_off <= next_off) { /* We need explicit `<type>: 0` aligning mark if next *fieldisrightonalignmentoffsetandits *alignmentrequirementislessstrictthan<type>'s *alignment(socompilerwon'tnaturallyaligntothe *offsetweexpect),orifsubsequent`<type>:X`, *willactuallycompletelyfitintheremaininghole, *makingcompilerbasicallyignore`<type>:X` *completely.
*/ if (in_bitfield ||
(new_off == next_off && roundup(cur_off, next_align * 8) != new_off) ||
(new_off != next_off && next_off - new_off <= new_off - cur_off)) /* but for bitfields we'll emit explicit bit count */
btf_dump_printf(d, "\n%s%s: %d;", pfx(lvl), pad_type,
in_bitfield ? new_off - cur_off : 0);
cur_off = new_off;
}
/* Now we know we start at naturally aligned offset for a chosen *paddingtype(long,int,short,orchar),andsotherestisjust *astraightforwardfillingofremainingpaddinggapwithfull *`<type>:sizeof(<type>);`markers,exceptforthelastone,which *mightneedsmallerthansizeof(<type>)padding.
*/ while (cur_off != next_off) {
bits = min(next_off - cur_off, pad_bits); if (bits == pad_bits) {
btf_dump_printf(d, "\n%s%s: %d;", pfx(lvl), pad_type, pad_bits);
cur_off += bits; continue;
} /* For the remainder padding that doesn't cover entire *pad_typebitlength,wepickthesmallestnecessarytype. *Thisispureaesthetics,wecouldhavejustused`long`, *buthavingsmallestnecessaryonecommunicatesbetterthe *scaleofthepaddinggap.
*/ for (i = ARRAY_SIZE(pads) - 1; i >= 0; i--) {
pad_type = pads[i].name;
pad_bits = pads[i].bits; if (pad_bits < bits) continue;
/* special case enums with special sizes */ if (t->size == 1) { /* one-byte enums can be forced with mode(byte) attribute */
btf_dump_printf(d, " __attribute__((mode(byte)))");
} elseif (t->size == 8 && d->ptr_sz == 8) { /* enum can be 8-byte sized if one of the enumerator values *doesn'tfitin32-bitinteger,orbyaddingmode(word) *attribute(butprobablyonlyon64-bitarchitectures);do *ourbestheretotrytosatisfythecontractwithoutadding *unnecessaryattributes
*/ bool needs_word_mode;
if (btf_is_enum(t)) { /* enum can't represent 64-bit values, so we need word mode */
needs_word_mode = true;
} else { /* enum64 needs mode(word) if none of its values has *non-zeroupper32-bits(whichmeansthatallvalues *fitin32-bitintegersandwon'tcausecompilerto *bumpenumtobe64-bitnaturally
*/ int i;
needs_word_mode = true; for (i = 0; i < vlen; i++) { if (btf_enum64(t)[i].val_hi32 != 0) {
needs_word_mode = false; break;
}
}
} if (needs_word_mode)
btf_dump_printf(d, " __attribute__((mode(word)))");
}
staticvoid btf_dump_emit_type_decl(struct btf_dump *d, __u32 id, constchar *fname, int lvl)
{ struct id_stack decl_stack; conststruct btf_type *t; int err, stack_start;
stack_start = d->decl_stack_cnt; for (;;) {
t = btf__type_by_id(d->btf, id); if (d->strip_mods && btf_is_mod(t)) goto skip_mod;
err = btf_dump_push_decl_stack_id(d, id); if (err < 0) { /* *ifwedon'thaveenoughmemoryforentiretypedecl *chain,restorestack,emitwarning,andtryto *proceednevertheless
*/
pr_warn("not enough memory for decl stack: %s\n", errstr(err));
d->decl_stack_cnt = stack_start; return;
}
skip_mod: /* VOID */ if (id == 0) break;
switch (btf_kind(t)) { case BTF_KIND_PTR: case BTF_KIND_VOLATILE: case BTF_KIND_CONST: case BTF_KIND_RESTRICT: case BTF_KIND_FUNC_PROTO: case BTF_KIND_TYPE_TAG:
id = t->type; break; case BTF_KIND_ARRAY:
id = btf_array(t)->type; break; case BTF_KIND_INT: case BTF_KIND_ENUM: case BTF_KIND_ENUM64: case BTF_KIND_FWD: case BTF_KIND_STRUCT: case BTF_KIND_UNION: case BTF_KIND_TYPEDEF: case BTF_KIND_FLOAT: goto done; default:
pr_warn("unexpected type in decl chain, kind:%u, id:[%u]\n",
btf_kind(t), id); goto done;
}
}
done: /* *Wemightbeinsideachainofdeclarations(e.g.,arrayoffunction *pointersreturninganonymous(soinlined)structs,havinganother *arrayfield).Eachofthoseneedsitsown"stackframe"tohandle *emittingofdeclarations.Thosestackframesarenon-overlapping *portionsofsharedbtf_dump->decl_stack.Tomakeitabitnicerto *handlethissetofnestedstacks,wecreateaviewcorrespondingto *ourown"stackframe"andworkwithitasanindependentstack. *We'llneedtocleanupafteremit_type_chain()returns,though.
*/
decl_stack.ids = d->decl_stack + stack_start;
decl_stack.cnt = d->decl_stack_cnt - stack_start;
btf_dump_emit_type_chain(d, &decl_stack, fname, lvl); /* *emit_type_chain()guaranteesthatitwillpopitsentiredecl_stack *framebeforereturning.Butitworkswitharead-onlyviewinto *decl_stack,soitdoesn'tactuallypopanythingfromthe *perspectiveofsharedbtf_dump->decl_stack,perse.Weneedto *resetdecl_stackstatetohowitwasbeforeustoavoiditgrowing *allthetime.
*/
d->decl_stack_cnt = stack_start;
}
while (decl_stack->cnt) {
id = decl_stack->ids[decl_stack->cnt - 1];
t = btf__type_by_id(d->btf, id); if (!btf_is_mod(t)) return;
decl_stack->cnt--;
}
}
while (decls->cnt) {
id = decls->ids[--decls->cnt]; if (id == 0) { /* VOID is a special snowflake */
btf_dump_emit_mods(d, decls);
btf_dump_printf(d, "void");
last_was_ptr = false; continue;
}
t = btf__type_by_id(d->btf, id);
kind = btf_kind(t);
switch (kind) { case BTF_KIND_INT: case BTF_KIND_FLOAT:
btf_dump_emit_mods(d, decls);
name = btf_name_of(d, t->name_off);
btf_dump_printf(d, "%s", name); break; case BTF_KIND_STRUCT: case BTF_KIND_UNION:
btf_dump_emit_mods(d, decls); /* inline anonymous struct/union */ if (t->name_off == 0 && !d->skip_anon_defs)
btf_dump_emit_struct_def(d, id, t, lvl); else
btf_dump_emit_struct_fwd(d, id, t); break; case BTF_KIND_ENUM: case BTF_KIND_ENUM64:
btf_dump_emit_mods(d, decls); /* inline anonymous enum */ if (t->name_off == 0 && !d->skip_anon_defs)
btf_dump_emit_enum_def(d, id, t, lvl); else
btf_dump_emit_enum_fwd(d, id, t); break; case BTF_KIND_FWD:
btf_dump_emit_mods(d, decls);
btf_dump_emit_fwd_def(d, id, t); break; case BTF_KIND_TYPEDEF:
btf_dump_emit_mods(d, decls);
btf_dump_printf(d, "%s", btf_dump_ident_name(d, id)); break; case BTF_KIND_PTR:
btf_dump_printf(d, "%s", last_was_ptr ? "*" : " *"); break; case BTF_KIND_VOLATILE:
btf_dump_printf(d, " volatile"); break; case BTF_KIND_CONST:
btf_dump_printf(d, " const"); break; case BTF_KIND_RESTRICT:
btf_dump_printf(d, " restrict"); break; case BTF_KIND_TYPE_TAG:
btf_dump_emit_mods(d, decls);
name = btf_name_of(d, t->name_off); if (btf_kflag(t))
btf_dump_printf(d, " __attribute__((%s))", name); else
btf_dump_printf(d, " __attribute__((btf_type_tag(\"%s\")))", name); break; case BTF_KIND_ARRAY: { conststruct btf_array *a = btf_array(t); conststruct btf_type *next_t;
__u32 next_id; bool multidim; /* *GCChasabug *(https://gcc.gnu.org/bugzilla/show_bug.cgi?id=8354) *whichcausesittoemitextraconst/volatile *modifiersforanarray,ifarray'selementtypehas *const/volatilemodifiers.Clangdoesn'tdothat. *Ingeneral,itdoesn'tseemverymeaningfultohave *aconst/volatilemodifierforarray,soweare *goingtosilentlyskipthemhere.
*/
btf_dump_drop_mods(d, decls);
next_id = decls->ids[decls->cnt - 1];
next_t = btf__type_by_id(d->btf, next_id);
multidim = btf_is_array(next_t); /* we need space if we have named non-pointer */ if (fname[0] && !last_was_ptr)
btf_dump_printf(d, " "); /* no parentheses for multi-dimensional array */ if (!multidim)
btf_dump_printf(d, "(");
btf_dump_emit_type_chain(d, decls, fname, lvl); if (!multidim)
btf_dump_printf(d, ")");
btf_dump_printf(d, "[%u]", a->nelems); return;
} case BTF_KIND_FUNC_PROTO: { conststruct btf_param *p = btf_params(t);
__u16 vlen = btf_vlen(t); int i;
for (i = 0; i < vlen; i++, p++) { if (i > 0)
btf_dump_printf(d, ", ");
/* last arg of type void is vararg */ if (i == vlen - 1 && p->type == 0) {
btf_dump_printf(d, "..."); break;
}
name = btf_name_of(d, p->name_off);
btf_dump_emit_type_decl(d, p->type, name, lvl);
}
btf_dump_printf(d, ")"); return;
} default:
pr_warn("unexpected type in decl chain, kind:%u, id:[%u]\n",
kind, id); return;
}
last_was_ptr = kind == BTF_KIND_PTR;
}
btf_dump_emit_name(d, fname, last_was_ptr);
}
/* show type name as (type_name) */ staticvoid btf_dump_emit_type_cast(struct btf_dump *d, __u32 id, bool top_level)
{ conststruct btf_type *t;
/* for array members, we don't bother emitting type name for each *membertoavoidtheredundancyof *.name=(char[4])[(char)'f',(char)'o',(char)'o',]
*/ if (d->typed_dump->is_array_member) return;
/* avoid type name specification for variable/section; it will be done *fortheassociatedvariablevalue(s).
*/
t = btf__type_by_id(d->btf, id); if (btf_is_var(t) || btf_is_datasec(t)) return;
/* return number of duplicates (occurrences) of a given name */ static size_t btf_dump_name_dups(struct btf_dump *d, struct hashmap *name_map, constchar *orig_name)
{ char *old_name, *new_name;
size_t dup_cnt = 0; int err;
new_name = strdup(orig_name); if (!new_name) return1;
staticvoid btf_dump_data_pfx(struct btf_dump *d)
{ int i, lvl = d->typed_dump->indent_lvl + d->typed_dump->depth;
if (d->typed_dump->compact) return;
for (i = 0; i < lvl; i++)
btf_dump_printf(d, "%s", d->typed_dump->indent_str);
}
/* A macro is used here as btf_type_value[s]() appends format specifiers *totheformatspecifierpassedin;thesedotheworkofappending *delimitersetcwhilethecallersimplyhastospecifythetypevalues *intheformatspecifier+value(s).
*/ #define btf_dump_type_values(d, fmt, ...) \
btf_dump_printf(d, fmt "%s%s", \ ##__VA_ARGS__, \
btf_dump_data_delim(d), \
btf_dump_data_newline(d))
/* For pointer types, pointer size is not defined on a per-type basis. *Ondumpcreationhowever,westorethepointersize.
*/ if (btf_kind(t) == BTF_KIND_PTR)
nr_bytes = d->ptr_sz; else
nr_bytes = t->size;
if (nr_bytes < 1 || nr_bytes > 16) {
pr_warn("unexpected size %d for id [%u]\n", nr_bytes, id); return -EINVAL;
}
if (memcmp(data, bytecmp, nr_bytes) == 0) return -ENODATA; return0;
}
if (sz == 0 || sz > sizeof(buf)) {
pr_warn("unexpected size %d for id [%u]\n", sz, type_id); return -EINVAL;
}
/* handle packed int data - accesses of integers not aligned on *intboundariescancauseproblemsonsomeplatforms.
*/ if (!ptr_is_aligned(d->btf, type_id, data)) {
memcpy(buf, data, sz);
data = buf;
}
switch (linkage) { case BTF_FUNC_STATIC:
l = "static "; break; case BTF_FUNC_EXTERN:
l = "extern "; break; case BTF_FUNC_GLOBAL: default:
l = ""; break;
}
/* format of output here is [linkage] [type] [varname] = (type)value, *forexample"staticintcpu_profile_flip=(int)1"
*/
btf_dump_printf(d, "%s", l);
type_id = v->type;
t = btf__type_by_id(d->btf, type_id);
btf_dump_emit_type_cast(d, type_id, false);
btf_dump_printf(d, " %s = ", btf_name_of(d, v->name_off)); return btf_dump_dump_type_data(d, NULL, t, type_id, data, 0, 0);
}
/* Make sure it is a NUL-terminated string. */ for (i = 0; i < array->nelems; i++) { if ((void *)(chars + i) >= d->typed_dump->data_end) return -E2BIG; if (chars[i] == '\0') break;
} if (i == array->nelems) { /* The caller will print this as a regular array. */ return -EINVAL;
}
btf_dump_data_pfx(d);
btf_dump_printf(d, "\"");
for (i = 0; i < array->nelems; i++) { char c = chars[i];
is_signed = btf_kflag(t); if (btf_is_enum(t)) { conststruct btf_enum *e;
for (i = 0, e = btf_enum(t); i < btf_vlen(t); i++, e++) { if (value != e->val) continue;
btf_dump_type_values(d, "%s", btf_name_of(d, e->name_off)); return0;
}
for (i = 0, e = btf_enum64(t); i < btf_vlen(t); i++, e++) { if (value != btf_enum64_value(e)) continue;
btf_dump_type_values(d, "%s", btf_name_of(d, e->name_off)); return0;
}
for (i = 0, vsi = btf_var_secinfos(t); i < btf_vlen(t); i++, vsi++) {
var = btf__type_by_id(d->btf, vsi->type);
err = btf_dump_dump_type_data(d, NULL, var, vsi->type, data + vsi->offset, 0, 0); if (err < 0) return err;
btf_dump_printf(d, ";");
} return0;
}
/* return size of type, or if base type overflows, return -E2BIG. */ staticint btf_dump_type_data_check_overflow(struct btf_dump *d, conststruct btf_type *t,
__u32 id, constvoid *data,
__u8 bits_offset,
__u8 bit_sz)
{
__s64 size;
if (bit_sz) { /* bits_offset is at most 7. bit_sz is at most 128. */
__u8 nr_bytes = (bits_offset + bit_sz + 7) / 8;
/* When bit_sz is non zero, it is called from *btf_dump_struct_data()whereitonlycaresabout *negativeerrorvalue. *Returnnr_bytesinsuccesscasetomakeit *consistentastheregularintegercasebelow.
*/ return data + nr_bytes > d->typed_dump->data_end ? -E2BIG : nr_bytes;
}
size = btf__resolve_size(d->btf, id);
if (size < 0 || size >= INT_MAX) {
pr_warn("unexpected size [%zu] for id [%u]\n",
(size_t)size, id); return -EINVAL;
}
/* Only do overflow checking for base types; we do not want to *avoidshowingpartofastruct,unionorarray,evenifwe *donothaveenoughdatatoshowthefullobject.By *restrictingoverflowcheckingtobasetypeswecanensure *thatpartialdisplaysucceeds,whileavoidingoverflowing *andusingbogusdatafordisplay.
*/
t = skip_mods_and_typedefs(d->btf, id, NULL); if (!t) {
pr_warn("unexpected error skipping mods/typedefs for id [%u]\n",
id); return -EINVAL;
}
switch (btf_kind(t)) { case BTF_KIND_INT: case BTF_KIND_FLOAT: case BTF_KIND_PTR: case BTF_KIND_ENUM: case BTF_KIND_ENUM64: if (data + bits_offset / 8 + size > d->typed_dump->data_end) return -E2BIG; break; default: break;
} return (int)size;
}
/* check all elements; if _any_ element is nonzero, all *ofarrayisdisplayed.Wemakeanexceptionhowever *forchararrayswherethefirstelementis0;these *areconsideredzeroedalso,eveniflaterelementsare *non-zerobecausethestringisterminated.
*/ for (i = 0; i < array->nelems; i++) { if (i == 0 && ischar && *(char *)data == 0) return -ENODATA;
err = btf_dump_type_data_check_zero(d, elem_type,
elem_type_id,
data +
(i * elem_size),
bits_offset, 0); if (err != -ENODATA) return err;
} return -ENODATA;
} case BTF_KIND_STRUCT: case BTF_KIND_UNION: { conststruct btf_member *m = btf_members(t);
__u16 n = btf_vlen(t);
/* if any struct/union member is non-zero, the struct/union *isconsiderednon-zeroanddumped.
*/ for (i = 0; i < n; i++, m++) { conststruct btf_type *mtype;
__u32 moffset;
ret = btf_dump_dump_type_data(d, NULL, t, id, data, 0, 0);
d->typed_dump = NULL;
return libbpf_err(ret);
}
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.71Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.