static int dwarf_parse_cie()

in kernel/dwarf.c [735:863]


static int dwarf_parse_cie(void *entry, void *p, unsigned long len,
			   unsigned char *end, struct module *mod)
{
	struct rb_node **rb_node = &cie_root.rb_node;
	struct rb_node *parent = *rb_node;
	struct dwarf_cie *cie;
	unsigned long flags;
	int count;

	cie = kzalloc(sizeof(*cie), GFP_KERNEL);
	if (!cie)
		return -ENOMEM;

	cie->length = len;

	/*
	 * Record the offset into the .eh_frame section
	 * for this CIE. It allows this CIE to be
	 * quickly and easily looked up from the
	 * corresponding FDE.
	 */
	cie->cie_pointer = (unsigned long)entry;

	cie->version = *(char *)p++;
	UNWINDER_BUG_ON(cie->version != 1);

	cie->augmentation = p;
	p += strlen(cie->augmentation) + 1;

	count = dwarf_read_uleb128(p, &cie->code_alignment_factor);
	p += count;

	count = dwarf_read_leb128(p, &cie->data_alignment_factor);
	p += count;

	/*
	 * Which column in the rule table contains the
	 * return address?
	 */
	if (cie->version == 1) {
		cie->return_address_reg = __raw_readb(p);
		p++;
	} else {
		count = dwarf_read_uleb128(p, &cie->return_address_reg);
		p += count;
	}

	if (cie->augmentation[0] == 'z') {
		unsigned int length, count;
		cie->flags |= DWARF_CIE_Z_AUGMENTATION;

		count = dwarf_read_uleb128(p, &length);
		p += count;

		UNWINDER_BUG_ON((unsigned char *)p > end);

		cie->initial_instructions = p + length;
		cie->augmentation++;
	}

	while (*cie->augmentation) {
		/*
		 * "L" indicates a byte showing how the
		 * LSDA pointer is encoded. Skip it.
		 */
		if (*cie->augmentation == 'L') {
			p++;
			cie->augmentation++;
		} else if (*cie->augmentation == 'R') {
			/*
			 * "R" indicates a byte showing
			 * how FDE addresses are
			 * encoded.
			 */
			cie->encoding = *(char *)p++;
			cie->augmentation++;
		} else if (*cie->augmentation == 'P') {
			/*
			 * "R" indicates a personality
			 * routine in the CIE
			 * augmentation.
			 */
			UNWINDER_BUG();
		} else if (*cie->augmentation == 'S') {
			UNWINDER_BUG();
		} else {
			/*
			 * Unknown augmentation. Assume
			 * 'z' augmentation.
			 */
			p = cie->initial_instructions;
			UNWINDER_BUG_ON(!p);
			break;
		}
	}

	cie->initial_instructions = p;
	cie->instructions_end = end;

	/* Add to list */
	spin_lock_irqsave(&dwarf_cie_lock, flags);

	while (*rb_node) {
		struct dwarf_cie *cie_tmp;

		cie_tmp = rb_entry(*rb_node, struct dwarf_cie, node);

		parent = *rb_node;

		if (cie->cie_pointer < cie_tmp->cie_pointer)
			rb_node = &parent->rb_left;
		else if (cie->cie_pointer >= cie_tmp->cie_pointer)
			rb_node = &parent->rb_right;
		else
			WARN_ON(1);
	}

	rb_link_node(&cie->node, parent, rb_node);
	rb_insert_color(&cie->node, &cie_root);

#ifdef CONFIG_MODULES
	if (mod != NULL)
		list_add_tail(&cie->link, &mod->arch.cie_list);
#endif

	spin_unlock_irqrestore(&dwarf_cie_lock, flags);

	return 0;
}