in src/common/dwarf/dwarf2reader.cc [1857:2137]
bool CallFrameInfo::State::DoInstruction() {
CIE *cie = entry_->cie;
Operands ops;
// Our entry's kind should have been set by now.
assert(entry_->kind != kUnknown);
// We shouldn't have been invoked unless there were more
// instructions to parse.
assert(cursor_ < entry_->end);
unsigned opcode = *cursor_++;
if ((opcode & 0xc0) != 0) {
switch (opcode & 0xc0) {
// Advance the address.
case DW_CFA_advance_loc: {
size_t code_offset = opcode & 0x3f;
address_ += code_offset * cie->code_alignment_factor;
break;
}
// Find a register at an offset from the CFA.
case DW_CFA_offset:
if (!ParseOperands("o", &ops) ||
!DoOffset(opcode & 0x3f, ops.offset * cie->data_alignment_factor))
return false;
break;
// Restore the rule established for a register by the CIE.
case DW_CFA_restore:
if (!DoRestore(opcode & 0x3f)) return false;
break;
// The 'if' above should have excluded this possibility.
default:
assert(0);
}
// Return here, so the big switch below won't be indented.
return true;
}
switch (opcode) {
// Set the address.
case DW_CFA_set_loc:
if (!ParseOperands("a", &ops)) return false;
address_ = ops.offset;
break;
// Advance the address.
case DW_CFA_advance_loc1:
if (!ParseOperands("1", &ops)) return false;
address_ += ops.offset * cie->code_alignment_factor;
break;
// Advance the address.
case DW_CFA_advance_loc2:
if (!ParseOperands("2", &ops)) return false;
address_ += ops.offset * cie->code_alignment_factor;
break;
// Advance the address.
case DW_CFA_advance_loc4:
if (!ParseOperands("4", &ops)) return false;
address_ += ops.offset * cie->code_alignment_factor;
break;
// Advance the address.
case DW_CFA_MIPS_advance_loc8:
if (!ParseOperands("8", &ops)) return false;
address_ += ops.offset * cie->code_alignment_factor;
break;
// Compute the CFA by adding an offset to a register.
case DW_CFA_def_cfa:
if (!ParseOperands("ro", &ops) ||
!DoDefCFA(ops.register_number, ops.offset))
return false;
break;
// Compute the CFA by adding an offset to a register.
case DW_CFA_def_cfa_sf:
if (!ParseOperands("rs", &ops) ||
!DoDefCFA(ops.register_number,
ops.signed_offset * cie->data_alignment_factor))
return false;
break;
// Change the base register used to compute the CFA.
case DW_CFA_def_cfa_register: {
if (!ParseOperands("r", &ops)) return false;
Rule *cfa_rule = rules_.CFARule();
if (!cfa_rule) {
if (!DoDefCFA(ops.register_number, ops.offset)) {
reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset());
return false;
}
} else {
cfa_rule->SetBaseRegister(ops.register_number);
if (!cfa_rule->Handle(handler_, address_,
Handler::kCFARegister))
return false;
}
break;
}
// Change the offset used to compute the CFA.
case DW_CFA_def_cfa_offset:
if (!ParseOperands("o", &ops) ||
!DoDefCFAOffset(ops.offset))
return false;
break;
// Change the offset used to compute the CFA.
case DW_CFA_def_cfa_offset_sf:
if (!ParseOperands("s", &ops) ||
!DoDefCFAOffset(ops.signed_offset * cie->data_alignment_factor))
return false;
break;
// Specify an expression whose value is the CFA.
case DW_CFA_def_cfa_expression: {
if (!ParseOperands("e", &ops))
return false;
Rule *rule = new ValExpressionRule(ops.expression);
rules_.SetCFARule(rule);
if (!rule->Handle(handler_, address_,
Handler::kCFARegister))
return false;
break;
}
// The register's value cannot be recovered.
case DW_CFA_undefined: {
if (!ParseOperands("r", &ops) ||
!DoRule(ops.register_number, new UndefinedRule()))
return false;
break;
}
// The register's value is unchanged from its value in the caller.
case DW_CFA_same_value: {
if (!ParseOperands("r", &ops) ||
!DoRule(ops.register_number, new SameValueRule()))
return false;
break;
}
// Find a register at an offset from the CFA.
case DW_CFA_offset_extended:
if (!ParseOperands("ro", &ops) ||
!DoOffset(ops.register_number,
ops.offset * cie->data_alignment_factor))
return false;
break;
// The register is saved at an offset from the CFA.
case DW_CFA_offset_extended_sf:
if (!ParseOperands("rs", &ops) ||
!DoOffset(ops.register_number,
ops.signed_offset * cie->data_alignment_factor))
return false;
break;
// The register is saved at an offset from the CFA.
case DW_CFA_GNU_negative_offset_extended:
if (!ParseOperands("ro", &ops) ||
!DoOffset(ops.register_number,
-ops.offset * cie->data_alignment_factor))
return false;
break;
// The register's value is the sum of the CFA plus an offset.
case DW_CFA_val_offset:
if (!ParseOperands("ro", &ops) ||
!DoValOffset(ops.register_number,
ops.offset * cie->data_alignment_factor))
return false;
break;
// The register's value is the sum of the CFA plus an offset.
case DW_CFA_val_offset_sf:
if (!ParseOperands("rs", &ops) ||
!DoValOffset(ops.register_number,
ops.signed_offset * cie->data_alignment_factor))
return false;
break;
// The register has been saved in another register.
case DW_CFA_register: {
if (!ParseOperands("ro", &ops) ||
!DoRule(ops.register_number, new RegisterRule(ops.offset)))
return false;
break;
}
// An expression yields the address at which the register is saved.
case DW_CFA_expression: {
if (!ParseOperands("re", &ops) ||
!DoRule(ops.register_number, new ExpressionRule(ops.expression)))
return false;
break;
}
// An expression yields the caller's value for the register.
case DW_CFA_val_expression: {
if (!ParseOperands("re", &ops) ||
!DoRule(ops.register_number, new ValExpressionRule(ops.expression)))
return false;
break;
}
// Restore the rule established for a register by the CIE.
case DW_CFA_restore_extended:
if (!ParseOperands("r", &ops) ||
!DoRestore( ops.register_number))
return false;
break;
// Save the current set of rules on a stack.
case DW_CFA_remember_state:
saved_rules_.push(rules_);
break;
// Pop the current set of rules off the stack.
case DW_CFA_restore_state: {
if (saved_rules_.empty()) {
reporter_->EmptyStateStack(entry_->offset, entry_->kind,
CursorOffset());
return false;
}
const RuleMap &new_rules = saved_rules_.top();
if (rules_.CFARule() && !new_rules.CFARule()) {
reporter_->ClearingCFARule(entry_->offset, entry_->kind,
CursorOffset());
return false;
}
rules_.HandleTransitionTo(handler_, address_, new_rules);
rules_ = new_rules;
saved_rules_.pop();
break;
}
// No operation. (Padding instruction.)
case DW_CFA_nop:
break;
// A SPARC register window save: Registers 8 through 15 (%o0-%o7)
// are saved in registers 24 through 31 (%i0-%i7), and registers
// 16 through 31 (%l0-%l7 and %i0-%i7) are saved at CFA offsets
// (0-15 * the register size). The register numbers must be
// hard-coded. A GNU extension, and not a pretty one.
case DW_CFA_GNU_window_save: {
// Save %o0-%o7 in %i0-%i7.
for (int i = 8; i < 16; i++)
if (!DoRule(i, new RegisterRule(i + 16)))
return false;
// Save %l0-%l7 and %i0-%i7 at the CFA.
for (int i = 16; i < 32; i++)
// Assume that the byte reader's address size is the same as
// the architecture's register size. !@#%*^ hilarious.
if (!DoRule(i, new OffsetRule(Handler::kCFARegister,
(i - 16) * reader_->AddressSize())))
return false;
break;
}
// I'm not sure what this is. GDB doesn't use it for unwinding.
case DW_CFA_GNU_args_size:
if (!ParseOperands("o", &ops)) return false;
break;
// An opcode we don't recognize.
default: {
reporter_->BadInstruction(entry_->offset, entry_->kind, CursorOffset());
return false;
}
}
return true;
}