in src/common/dwarf/dwarf2reader.cc [1011:1195]
bool LineInfo::ProcessOneOpcode(ByteReader* reader,
LineInfoHandler* handler,
const struct LineInfoHeader &header,
const uint8_t *start,
struct LineStateMachine* lsm,
size_t* len,
uintptr pc,
bool *lsm_passes_pc) {
size_t oplen = 0;
size_t templen;
uint8 opcode = reader->ReadOneByte(start);
oplen++;
start++;
// If the opcode is great than the opcode_base, it is a special
// opcode. Most line programs consist mainly of special opcodes.
if (opcode >= header.opcode_base) {
opcode -= header.opcode_base;
const int64 advance_address = (opcode / header.line_range)
* header.min_insn_length;
const int32 advance_line = (opcode % header.line_range)
+ header.line_base;
// Check if the lsm passes "pc". If so, mark it as passed.
if (lsm_passes_pc &&
lsm->address <= pc && pc < lsm->address + advance_address) {
*lsm_passes_pc = true;
}
lsm->address += advance_address;
lsm->line_num += advance_line;
lsm->basic_block = true;
*len = oplen;
return true;
}
// Otherwise, we have the regular opcodes
switch (opcode) {
case DW_LNS_copy: {
lsm->basic_block = false;
*len = oplen;
return true;
}
case DW_LNS_advance_pc: {
uint64 advance_address = reader->ReadUnsignedLEB128(start, &templen);
oplen += templen;
// Check if the lsm passes "pc". If so, mark it as passed.
if (lsm_passes_pc && lsm->address <= pc &&
pc < lsm->address + header.min_insn_length * advance_address) {
*lsm_passes_pc = true;
}
lsm->address += header.min_insn_length * advance_address;
}
break;
case DW_LNS_advance_line: {
const int64 advance_line = reader->ReadSignedLEB128(start, &templen);
oplen += templen;
lsm->line_num += static_cast<int32>(advance_line);
// With gcc 4.2.1, we can get the line_no here for the first time
// since DW_LNS_advance_line is called after DW_LNE_set_address is
// called. So we check if the lsm passes "pc" here, not in
// DW_LNE_set_address.
if (lsm_passes_pc && lsm->address == pc) {
*lsm_passes_pc = true;
}
}
break;
case DW_LNS_set_file: {
const uint64 fileno = reader->ReadUnsignedLEB128(start, &templen);
oplen += templen;
lsm->file_num = static_cast<uint32>(fileno);
}
break;
case DW_LNS_set_column: {
const uint64 colno = reader->ReadUnsignedLEB128(start, &templen);
oplen += templen;
lsm->column_num = static_cast<uint32>(colno);
}
break;
case DW_LNS_negate_stmt: {
lsm->is_stmt = !lsm->is_stmt;
}
break;
case DW_LNS_set_basic_block: {
lsm->basic_block = true;
}
break;
case DW_LNS_fixed_advance_pc: {
const uint16 advance_address = reader->ReadTwoBytes(start);
oplen += 2;
// Check if the lsm passes "pc". If so, mark it as passed.
if (lsm_passes_pc &&
lsm->address <= pc && pc < lsm->address + advance_address) {
*lsm_passes_pc = true;
}
lsm->address += advance_address;
}
break;
case DW_LNS_const_add_pc: {
const int64 advance_address = header.min_insn_length
* ((255 - header.opcode_base)
/ header.line_range);
// Check if the lsm passes "pc". If so, mark it as passed.
if (lsm_passes_pc &&
lsm->address <= pc && pc < lsm->address + advance_address) {
*lsm_passes_pc = true;
}
lsm->address += advance_address;
}
break;
case DW_LNS_extended_op: {
const uint64 extended_op_len = reader->ReadUnsignedLEB128(start,
&templen);
start += templen;
oplen += templen + extended_op_len;
const uint64 extended_op = reader->ReadOneByte(start);
start++;
switch (extended_op) {
case DW_LNE_end_sequence: {
lsm->end_sequence = true;
*len = oplen;
return true;
}
break;
case DW_LNE_set_address: {
// With gcc 4.2.1, we cannot tell the line_no here since
// DW_LNE_set_address is called before DW_LNS_advance_line is
// called. So we do not check if the lsm passes "pc" here. See
// also the comment in DW_LNS_advance_line.
uint64 address = reader->ReadAddress(start);
lsm->address = address;
}
break;
case DW_LNE_define_file: {
const char *filename = reinterpret_cast<const char *>(start);
templen = strlen(filename) + 1;
start += templen;
uint64 dirindex = reader->ReadUnsignedLEB128(start, &templen);
oplen += templen;
const uint64 mod_time = reader->ReadUnsignedLEB128(start,
&templen);
oplen += templen;
const uint64 filelength = reader->ReadUnsignedLEB128(start,
&templen);
oplen += templen;
if (handler) {
handler->DefineFile(filename, -1, static_cast<uint32>(dirindex),
mod_time, filelength);
}
}
break;
}
}
break;
default: {
// Ignore unknown opcode silently
if (header.std_opcode_lengths) {
for (int i = 0; i < (*header.std_opcode_lengths)[opcode]; i++) {
reader->ReadUnsignedLEB128(start, &templen);
start += templen;
oplen += templen;
}
}
}
break;
}
*len = oplen;
return false;
}