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;
}