package dwarf import ( "bytes" "encoding/binary" log "github.com/sirupsen/logrus" . "ios-wrapper/pkg/leb128" ) type header struct { unit_length uint32 version uint16 header_length uint32 minimum_instruction_length uint8 maximum_operations_per_instruction uint8 default_is_stmt uint8 line_base int8 line_range uint8 opcode_base uint8 } type register struct { address uint64 op_index uint32 file uint32 line uint32 column uint32 is_stmt bool basic_block bool end_sequence bool prologue_end bool epilogue_begin bool isa uint32 discriminator uint32 } func initRegister(is_stmt bool) register { var reg register reg.is_stmt = is_stmt reg.file = 1 reg.line = 1 return reg } func (reg *register) reset() { reg.basic_block = false reg.prologue_end = false reg.epilogue_begin = false reg.discriminator = 0 } // Opcodes const ( Copy uint8 = 1 AdvancePc uint8 = 2 AdvanceLine uint8 = 3 SetFile uint8 = 4 SetColumn uint8 = 5 NegateStmt uint8 = 6 SetBasicBlock uint8 = 7 ConstAddPc uint8 = 8 FixedAdvancePc uint8 = 9 SetPrologueEnd uint8 = 10 SetEpiloqueBegin uint8 = 11 SetIsa uint8 = 12 // Extended opcodes EndSequence uint8 = 1 SetAddress uint8 = 2 DefineFile uint8 = 3 SetDiscriminator uint8 = 4 ) type row struct { address uint64 op_index uint32 directory string file string line int32 column int32 end_sequence bool } type filename struct { name string dir uint } type DebuglineParser struct { buf *bytes.Buffer byteorder binary.ByteOrder header header standard_opcode_lengths []uint8 include_directories []string file_names []filename row []row } func (parser *DebuglineParser) Find(address uint64) (bool, uint32, string, string) { for _, row := range parser.row { if address == row.address { return true, uint32(row.line), row.directory, row.file } } return false, 0, "", "" } func (parser *DebuglineParser) Parse(buf *bytes.Buffer, byteorder binary.ByteOrder) { parser.buf = buf parser.byteorder = byteorder parser.parseHeader() parser.parseOpcode() parser.parseDirectoryTable() parser.parseFilenameTable() parser.parseStatementProgram() } func (parser *DebuglineParser) parseHeader() { binary.Read(parser.buf, parser.byteorder, &parser.header.unit_length) binary.Read(parser.buf, parser.byteorder, &parser.header.version) binary.Read(parser.buf, parser.byteorder, &parser.header.header_length) binary.Read(parser.buf, parser.byteorder, &parser.header.minimum_instruction_length) if parser.header.version >= 4 { binary.Read(parser.buf, parser.byteorder, &parser.header.maximum_operations_per_instruction) } binary.Read(parser.buf, parser.byteorder, &parser.header.default_is_stmt) binary.Read(parser.buf, parser.byteorder, &parser.header.line_base) binary.Read(parser.buf, parser.byteorder, &parser.header.line_range) binary.Read(parser.buf, parser.byteorder, &parser.header.opcode_base) } func (parser *DebuglineParser) parseOpcode() { var standard_opcode_lengths []uint8 for i := uint8(1); i < parser.header.opcode_base; i++ { var num uint8 binary.Read(parser.buf, parser.byteorder, &num) standard_opcode_lengths = append(standard_opcode_lengths, num) } parser.standard_opcode_lengths = standard_opcode_lengths } func (parser *DebuglineParser) parseDirectoryTable() { var include_directories []string for { dir, _ := parser.buf.ReadString(0) if dir == "\x00" { break } include_directories = append(include_directories, dir) } parser.include_directories = include_directories } func (parser *DebuglineParser) parseFilenameTable() { var file_names []filename for { file, _ := parser.buf.ReadString(0) if file == "\x00" { break } dir, _ := ReadULEB(parser.buf) // directory index ReadULEB(parser.buf) // time ReadULEB(parser.buf) // length file_names = append(file_names, filename{ name: file, dir: dir, }) } parser.file_names = file_names } func (parser *DebuglineParser) parseStatementProgram() { reg := initRegister(parser.header.default_is_stmt == 1) runloop: for { var opcode uint8 binary.Read(parser.buf, parser.byteorder, &opcode) if opcode >= parser.header.opcode_base { adjusted_opcode := opcode - parser.header.opcode_base operation_advance := adjusted_opcode / parser.header.line_range increment_address_and_op_index(®, &parser.header, uint(operation_advance)) reg.line += uint32(int32(parser.header.line_base) + int32(adjusted_opcode%parser.header.line_range)) parser.register_to_matrix(®) reg.reset() } else if opcode == 0 { size_, _ := ReadULEB(parser.buf) size := size_ - 1 var extended_opcode uint8 binary.Read(parser.buf, parser.byteorder, &extended_opcode) switch extended_opcode { case EndSequence: reg.end_sequence = true parser.register_to_matrix(®) reg = initRegister(parser.header.default_is_stmt == 1) break runloop case SetAddress: if size == 8 { binary.Read(parser.buf, parser.byteorder, ®.address) } else if size == 4 { var v uint32 binary.Read(parser.buf, parser.byteorder, &v) reg.address = uint64(v) } else { parser.buf.Next(int(size)) } reg.op_index = 0 case SetDiscriminator: v, _ := ReadULEB(parser.buf) reg.discriminator = uint32(v) default: parser.buf.Next(int(size)) } } else { switch opcode { case Copy: parser.register_to_matrix(®) reg.reset() case AdvancePc: v, _ := ReadULEB(parser.buf) increment_address_and_op_index(®, &parser.header, v) case AdvanceLine: v, _ := ReadSLEB(parser.buf) if v < 0 { reg.line -= uint32(-v) } else { reg.line += uint32(v) } case SetFile: v, _ := ReadULEB(parser.buf) reg.file = uint32(v) case SetColumn: v, _ := ReadULEB(parser.buf) reg.column = uint32(v) case NegateStmt: reg.is_stmt = !reg.is_stmt case SetBasicBlock: reg.basic_block = true case ConstAddPc: adjusted_opcode := 255 - parser.header.opcode_base operation_advance := adjusted_opcode / parser.header.line_range increment_address_and_op_index(®, &parser.header, uint(operation_advance)) case FixedAdvancePc: var v uint16 binary.Read(parser.buf, parser.byteorder, &v) reg.address += uint64(v) reg.op_index = 0 case SetPrologueEnd: reg.prologue_end = true case SetEpiloqueBegin: reg.epilogue_begin = true case SetIsa: v, _ := ReadULEB(parser.buf) reg.isa = uint32(v) default: break } } } } func increment_address_and_op_index(reg *register, h *header, operation_advance uint) { min_length := uint64(h.minimum_instruction_length) max_op := uint64(h.maximum_operations_per_instruction) advance := uint64(operation_advance) op_index := uint64(reg.op_index) if h.maximum_operations_per_instruction == 1 { reg.address += advance * min_length } else { reg.address += min_length * ((op_index + advance) / max_op) reg.op_index = uint32((op_index + advance) % max_op) } } func (parser *DebuglineParser) register_to_matrix(reg *register) { if !(reg.is_stmt || (reg.line > 0 && reg.column > 0)) { return } file := parser.file_names[reg.file-1] path := parser.include_directories[file.dir-1] row := row{ address: reg.address, op_index: reg.op_index, directory: path, file: file.name, line: int32(reg.line), column: int32(reg.column), end_sequence: reg.end_sequence, } parser.row = append(parser.row, row) log.WithFields(log.Fields{ "address": row.address, "directory": row.directory, "file": row.file, "line": row.line, }).Trace("Debugline Row") }