static int linker_append_btf()

in compile/remote-compile/lbc/libbpf/src/linker.c [2232:2411]


static int linker_append_btf(struct bpf_linker *linker, struct src_obj *obj)
{
	const struct btf_type *t;
	int i, j, n, start_id, id;
	const char *name;

	if (!obj->btf)
		return 0;

	start_id = btf__type_cnt(linker->btf);
	n = btf__type_cnt(obj->btf);

	obj->btf_type_map = calloc(n + 1, sizeof(int));
	if (!obj->btf_type_map)
		return -ENOMEM;

	for (i = 1; i < n; i++) {
		struct glob_sym *glob_sym = NULL;

		t = btf__type_by_id(obj->btf, i);

		/* DATASECs are handled specially below */
		if (btf_kind(t) == BTF_KIND_DATASEC)
			continue;

		if (btf_is_non_static(t)) {
			/* there should be glob_sym already */
			name = btf__str_by_offset(obj->btf, t->name_off);
			glob_sym = find_glob_sym(linker, name);

			/* VARs without corresponding glob_sym are those that
			 * belong to skipped/deduplicated sections (i.e.,
			 * license and version), so just skip them
			 */
			if (!glob_sym)
				continue;

			/* linker_append_elf_sym() might have requested
			 * updating underlying type ID, if extern was resolved
			 * to strong symbol or weak got upgraded to non-weak
			 */
			if (glob_sym->underlying_btf_id == 0)
				glob_sym->underlying_btf_id = -t->type;

			/* globals from previous object files that match our
			 * VAR/FUNC already have a corresponding associated
			 * BTF type, so just make sure to use it
			 */
			if (glob_sym->btf_id) {
				/* reuse existing BTF type for global var/func */
				obj->btf_type_map[i] = glob_sym->btf_id;
				continue;
			}
		}

		id = btf__add_type(linker->btf, obj->btf, t);
		if (id < 0) {
			pr_warn("failed to append BTF type #%d from file '%s'\n", i, obj->filename);
			return id;
		}

		obj->btf_type_map[i] = id;

		/* record just appended BTF type for var/func */
		if (glob_sym) {
			glob_sym->btf_id = id;
			glob_sym->underlying_btf_id = -t->type;
		}
	}

	/* remap all the types except DATASECs */
	n = btf__type_cnt(linker->btf);
	for (i = start_id; i < n; i++) {
		struct btf_type *dst_t = btf_type_by_id(linker->btf, i);

		if (btf_type_visit_type_ids(dst_t, remap_type_id, obj->btf_type_map))
			return -EINVAL;
	}

	/* Rewrite VAR/FUNC underlying types (i.e., FUNC's FUNC_PROTO and VAR's
	 * actual type), if necessary
	 */
	for (i = 0; i < linker->glob_sym_cnt; i++) {
		struct glob_sym *glob_sym = &linker->glob_syms[i];
		struct btf_type *glob_t;

		if (glob_sym->underlying_btf_id >= 0)
			continue;

		glob_sym->underlying_btf_id = obj->btf_type_map[-glob_sym->underlying_btf_id];

		glob_t = btf_type_by_id(linker->btf, glob_sym->btf_id);
		glob_t->type = glob_sym->underlying_btf_id;
	}

	/* append DATASEC info */
	for (i = 1; i < obj->sec_cnt; i++) {
		struct src_sec *src_sec;
		struct dst_sec *dst_sec;
		const struct btf_var_secinfo *src_var;
		struct btf_var_secinfo *dst_var;

		src_sec = &obj->secs[i];
		if (!src_sec->sec_type_id || src_sec->skipped)
			continue;
		dst_sec = &linker->secs[src_sec->dst_id];

		/* Mark section as having BTF regardless of the presence of
		 * variables. In some cases compiler might generate empty BTF
		 * with no variables information. E.g., when promoting local
		 * array/structure variable initial values and BPF object
		 * file otherwise has no read-only static variables in
		 * .rodata. We need to preserve such empty BTF and just set
		 * correct section size.
		 */
		dst_sec->has_btf = true;

		t = btf__type_by_id(obj->btf, src_sec->sec_type_id);
		src_var = btf_var_secinfos(t);
		n = btf_vlen(t);
		for (j = 0; j < n; j++, src_var++) {
			void *sec_vars = dst_sec->sec_vars;
			int new_id = obj->btf_type_map[src_var->type];
			struct glob_sym *glob_sym = NULL;

			t = btf_type_by_id(linker->btf, new_id);
			if (btf_is_non_static(t)) {
				name = btf__str_by_offset(linker->btf, t->name_off);
				glob_sym = find_glob_sym(linker, name);
				if (glob_sym->sec_id != dst_sec->id) {
					pr_warn("global '%s': section mismatch %d vs %d\n",
						name, glob_sym->sec_id, dst_sec->id);
					return -EINVAL;
				}
			}

			/* If there is already a member (VAR or FUNC) mapped
			 * to the same type, don't add a duplicate entry.
			 * This will happen when multiple object files define
			 * the same extern VARs/FUNCs.
			 */
			if (glob_sym && glob_sym->var_idx >= 0) {
				__s64 sz;

				dst_var = &dst_sec->sec_vars[glob_sym->var_idx];
				/* Because underlying BTF type might have
				 * changed, so might its size have changed, so
				 * re-calculate and update it in sec_var.
				 */
				sz = btf__resolve_size(linker->btf, glob_sym->underlying_btf_id);
				if (sz < 0) {
					pr_warn("global '%s': failed to resolve size of underlying type: %d\n",
						name, (int)sz);
					return -EINVAL;
				}
				dst_var->size = sz;
				continue;
			}

			sec_vars = libbpf_reallocarray(sec_vars,
						       dst_sec->sec_var_cnt + 1,
						       sizeof(*dst_sec->sec_vars));
			if (!sec_vars)
				return -ENOMEM;

			dst_sec->sec_vars = sec_vars;
			dst_sec->sec_var_cnt++;

			dst_var = &dst_sec->sec_vars[dst_sec->sec_var_cnt - 1];
			dst_var->type = obj->btf_type_map[src_var->type];
			dst_var->size = src_var->size;
			dst_var->offset = src_sec->dst_off + src_var->offset;

			if (glob_sym)
				glob_sym->var_idx = dst_sec->sec_var_cnt - 1;
		}
	}

	return 0;
}