macho/macho-go/pkg/dwarf/dwarf.go

306 lines
7.9 KiB
Go
Raw Permalink Normal View History

2023-05-31 16:17:03 +07:00
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(&reg, &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)
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)
reg = initRegister(parser.header.default_is_stmt == 1)
break runloop
case SetAddress:
if size == 8 {
binary.Read(parser.buf, parser.byteorder, &reg.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)
reg.reset()
case AdvancePc:
v, _ := ReadULEB(parser.buf)
increment_address_and_op_index(&reg, &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(&reg, &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")
}