306 lines
7.9 KiB
Go
306 lines
7.9 KiB
Go
|
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")
|
||
|
}
|