2017-12-23 19:08:53 -08:00
const std = @import ( " ../index.zig " ) ;
2017-10-31 01:47:55 -07:00
const math = std . math ;
const mem = std . mem ;
const io = std . io ;
const os = std . os ;
2017-12-23 19:08:53 -08:00
const elf = std . elf ;
const DW = std . dwarf ;
2018-02-19 14:06:54 -08:00
const macho = std . macho ;
2017-10-31 01:47:55 -07:00
const ArrayList = std . ArrayList ;
2017-05-01 10:12:38 -07:00
const builtin = @import ( " builtin " ) ;
2016-08-17 20:11:04 -07:00
2017-12-24 01:10:26 -08:00
pub const FailingAllocator = @import ( " failing_allocator.zig " ) . FailingAllocator ;
2017-12-23 20:47:04 -08:00
2017-10-31 01:47:55 -07:00
/// Tries to write to stderr, unbuffered, and ignores any error returned.
/// Does not append a newline.
/// TODO atomic/multithread support
2018-02-10 17:55:13 -08:00
var stderr_file : os . File = undefined ;
2017-11-07 00:22:27 -08:00
var stderr_file_out_stream : io . FileOutStream = undefined ;
2018-02-05 04:38:24 -08:00
var stderr_stream : ? & io . OutStream ( io . FileOutStream . Error ) = null ;
2018-01-25 01:10:11 -08:00
pub fn warn ( comptime fmt : [ ] const u8 , args : . . . ) void {
2018-01-07 14:28:20 -08:00
const stderr = getStderrStream ( ) catch return ;
stderr . print ( fmt , args ) catch return ;
2017-10-31 01:47:55 -07:00
}
2018-02-05 04:38:24 -08:00
fn getStderrStream ( ) ! & io . OutStream ( io . FileOutStream . Error ) {
2017-10-31 01:47:55 -07:00
if ( stderr_stream ) | st | {
return st ;
} else {
2018-01-07 13:51:46 -08:00
stderr_file = try io . getStdErr ( ) ;
2017-11-07 00:22:27 -08:00
stderr_file_out_stream = io . FileOutStream . init ( & stderr_file ) ;
const st = & stderr_file_out_stream . stream ;
2017-10-31 01:47:55 -07:00
stderr_stream = st ;
return st ;
2017-12-21 21:50:30 -08:00
}
2017-10-31 01:47:55 -07:00
}
2018-01-15 13:26:13 -08:00
var self_debug_info : ? & ElfStackTrace = null ;
2018-01-30 22:51:15 -08:00
pub fn getSelfDebugInfo ( ) ! & ElfStackTrace {
2018-01-15 13:26:13 -08:00
if ( self_debug_info ) | info | {
return info ;
} else {
2018-04-15 17:15:19 -07:00
const info = try openSelfDebugInfo ( getDebugInfoAllocator ( ) ) ;
2018-01-15 13:26:13 -08:00
self_debug_info = info ;
return info ;
}
}
2018-01-11 23:12:11 -08:00
/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned.
2018-03-09 19:21:13 -08:00
pub fn dumpCurrentStackTrace ( start_addr : ? usize ) void {
2018-01-11 23:12:11 -08:00
const stderr = getStderrStream ( ) catch return ;
2018-01-15 13:26:13 -08:00
const debug_info = getSelfDebugInfo ( ) catch | err | {
2018-02-14 20:00:53 -08:00
stderr . print ( " Unable to dump stack trace: Unable to open debug info: {} \n " , @errorName ( err ) ) catch return ;
2018-01-14 07:19:21 -08:00
return ;
} ;
2018-04-15 17:15:19 -07:00
writeCurrentStackTrace ( stderr , getDebugInfoAllocator ( ) , debug_info , stderr_file . isTty ( ) , start_addr ) catch | err | {
2018-01-14 07:19:21 -08:00
stderr . print ( " Unable to dump stack trace: {} \n " , @errorName ( err ) ) catch return ;
return ;
} ;
2018-01-11 23:12:11 -08:00
}
2017-10-31 01:47:55 -07:00
/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.
2018-01-25 01:10:11 -08:00
pub fn dumpStackTrace ( stack_trace : & const builtin . StackTrace ) void {
2018-01-07 14:28:20 -08:00
const stderr = getStderrStream ( ) catch return ;
2018-01-15 13:26:13 -08:00
const debug_info = getSelfDebugInfo ( ) catch | err | {
2018-02-14 20:00:53 -08:00
stderr . print ( " Unable to dump stack trace: Unable to open debug info: {} \n " , @errorName ( err ) ) catch return ;
2018-01-14 07:19:21 -08:00
return ;
} ;
2018-04-15 17:15:19 -07:00
writeStackTrace ( stack_trace , stderr , getDebugInfoAllocator ( ) , debug_info , stderr_file . isTty ( ) ) catch | err | {
2018-01-14 07:19:21 -08:00
stderr . print ( " Unable to dump stack trace: {} \n " , @errorName ( err ) ) catch return ;
return ;
} ;
2017-10-31 01:47:55 -07:00
}
/// This function invokes undefined behavior when `ok` is `false`.
/// In Debug and ReleaseSafe modes, calls to this function are always
/// generated, and the `unreachable` statement triggers a panic.
/// In ReleaseFast and ReleaseSmall modes, calls to this function can be
/// optimized away.
2018-01-25 01:10:11 -08:00
pub fn assert ( ok : bool ) void {
2017-08-25 07:11:58 -07:00
if ( ! ok ) {
// In ReleaseFast test mode, we still want assert(false) to crash, so
// we insert an explicit call to @panic instead of unreachable.
2017-10-31 01:47:55 -07:00
// TODO we should use `assertOrPanic` in tests and remove this logic.
2017-08-25 07:11:58 -07:00
if ( builtin . is_test ) {
2017-12-21 21:50:30 -08:00
@panic ( " assertion failure " ) ;
2017-08-25 07:11:58 -07:00
} else {
2017-12-21 21:50:30 -08:00
unreachable ; // assertion failure
2017-08-25 07:11:58 -07:00
}
}
2016-05-17 13:32:43 -07:00
}
2017-10-31 01:47:55 -07:00
/// Call this function when you want to panic if the condition is not true.
/// If `ok` is `false`, this function will panic in every release mode.
2018-01-25 01:10:11 -08:00
pub fn assertOrPanic ( ok : bool ) void {
2017-10-31 01:47:55 -07:00
if ( ! ok ) {
@panic ( " assertion failure " ) ;
}
}
2018-01-25 01:10:11 -08:00
pub fn panic ( comptime format : [ ] const u8 , args : . . . ) noreturn {
2018-02-28 18:19:51 -08:00
@setCold ( true ) ;
2018-03-09 19:21:13 -08:00
const first_trace_addr = @ptrToInt ( @returnAddress ( ) ) ;
panicExtra ( null , first_trace_addr , format , args ) ;
}
var panicking : u8 = 0 ; // TODO make this a bool
2018-04-30 17:35:54 -07:00
pub fn panicExtra ( trace : ? & const builtin . StackTrace , first_trace_addr : ? usize , comptime format : [ ] const u8 , args : . . . ) noreturn {
2018-03-09 19:21:13 -08:00
@setCold ( true ) ;
2017-06-13 21:04:34 -07:00
2018-02-28 18:19:51 -08:00
if ( @atomicRmw ( u8 , & panicking , builtin . AtomicRmwOp . Xchg , 1 , builtin . AtomicOrder . SeqCst ) = = 1 ) {
2017-02-08 23:50:03 -08:00
// Panicked during a panic.
2018-02-28 18:19:51 -08:00
2017-02-08 23:50:03 -08:00
// TODO detect if a different thread caused the panic, because in that case
// we would want to return here instead of calling abort, so that the thread
// which first called panic can finish printing a stack trace.
os . abort ( ) ;
}
2018-01-07 14:28:20 -08:00
const stderr = getStderrStream ( ) catch os . abort ( ) ;
stderr . print ( format + + " \n " , args ) catch os . abort ( ) ;
2018-03-09 19:21:13 -08:00
if ( trace ) | t | {
dumpStackTrace ( t ) ;
2018-01-15 13:26:13 -08:00
}
2018-03-09 19:21:13 -08:00
dumpCurrentStackTrace ( first_trace_addr ) ;
2018-01-15 13:26:13 -08:00
os . abort ( ) ;
}
2017-04-24 09:01:19 -07:00
const GREEN = " \x1b [32;1m " ;
const WHITE = " \x1b [37;1m " ;
const DIM = " \x1b [2m " ;
const RESET = " \x1b [0m " ;
2018-04-30 17:35:54 -07:00
pub fn writeStackTrace ( stack_trace : & const builtin . StackTrace , out_stream : var , allocator : & mem . Allocator , debug_info : & ElfStackTrace , tty_color : bool ) ! void {
2018-01-14 07:19:21 -08:00
var frame_index : usize = undefined ;
var frames_left : usize = undefined ;
if ( stack_trace . index < stack_trace . instruction_addresses . len ) {
frame_index = 0 ;
frames_left = stack_trace . index ;
} else {
frame_index = ( stack_trace . index + 1 ) % stack_trace . instruction_addresses . len ;
frames_left = stack_trace . instruction_addresses . len ;
}
2018-01-11 23:12:11 -08:00
2018-01-14 07:19:21 -08:00
while ( frames_left ! = 0 ) : ( {
frames_left - = 1 ;
frame_index = ( frame_index + 1 ) % stack_trace . instruction_addresses . len ;
} ) {
const return_address = stack_trace . instruction_addresses [ frame_index ] ;
try printSourceAtAddress ( debug_info , out_stream , return_address ) ;
}
}
2018-01-11 23:12:11 -08:00
2018-04-30 17:35:54 -07:00
pub fn writeCurrentStackTrace ( out_stream : var , allocator : & mem . Allocator , debug_info : & ElfStackTrace , tty_color : bool , start_addr : ? usize ) ! void {
2018-03-09 19:06:24 -08:00
const AddressState = union ( enum ) {
NotLookingForStartAddress ,
LookingForStartAddress : usize ,
} ;
// TODO: I want to express like this:
2018-03-09 19:21:13 -08:00
//var addr_state = if (start_addr) |addr| AddressState { .LookingForStartAddress = addr }
2018-03-09 19:06:24 -08:00
// else AddressState.NotLookingForStartAddress;
var addr_state : AddressState = undefined ;
2018-03-09 19:21:13 -08:00
if ( start_addr ) | addr | {
2018-04-30 17:35:54 -07:00
addr_state = AddressState { . LookingForStartAddress = addr } ;
2018-03-09 19:06:24 -08:00
} else {
addr_state = AddressState . NotLookingForStartAddress ;
}
2018-01-11 23:12:11 -08:00
2018-01-14 07:19:21 -08:00
var fp = @ptrToInt ( @frameAddress ( ) ) ;
2018-04-30 17:35:54 -07:00
while ( fp ! = 0 ) : ( fp = @intToPtr ( & const usize , fp ) . * ) {
const return_address = @intToPtr ( & const usize , fp + @sizeOf ( usize ) ) . * ;
2018-03-09 19:06:24 -08:00
switch ( addr_state ) {
2018-03-09 19:21:13 -08:00
AddressState . NotLookingForStartAddress = > { } ,
2018-03-09 19:06:24 -08:00
AddressState . LookingForStartAddress = > | addr | {
if ( return_address = = addr ) {
2018-03-09 19:21:13 -08:00
addr_state = AddressState . NotLookingForStartAddress ;
2018-03-09 19:06:24 -08:00
} else {
continue ;
}
} ,
}
2018-01-14 07:19:21 -08:00
try printSourceAtAddress ( debug_info , out_stream , return_address ) ;
}
}
2018-01-11 23:12:11 -08:00
2018-02-05 04:38:24 -08:00
fn printSourceAtAddress ( debug_info : & ElfStackTrace , out_stream : var , address : usize ) ! void {
2018-03-09 18:05:41 -08:00
const ptr_hex = " 0x{x} " ;
2018-01-14 07:19:21 -08:00
2018-02-19 14:06:54 -08:00
switch ( builtin . os ) {
builtin . Os . windows = > return error . UnsupportedDebugInfo ,
builtin . Os . macosx = > {
// TODO(bnoordhuis) It's theoretically possible to obtain the
// compilation unit from the symbtab but it's not that useful
// in practice because the compiler dumps everything in a single
// object file. Future improvement: use external dSYM data when
// available.
2018-04-30 17:35:54 -07:00
const unknown = macho . Symbol {
. name = " ??? " ,
. address = address ,
} ;
2018-02-19 14:06:54 -08:00
const symbol = debug_info . symbol_table . search ( address ) ? ? & unknown ;
2018-04-30 17:35:54 -07:00
try out_stream . print ( WHITE + + " {} " + + RESET + + " : " + + DIM + + ptr_hex + + " in ??? (???) " + + RESET + + " \n " , symbol . name , address ) ;
2018-02-19 14:06:54 -08:00
} ,
else = > {
const compile_unit = findCompileUnit ( debug_info , address ) catch {
2018-04-30 17:35:54 -07:00
try out_stream . print ( " ???:?:?: " + + DIM + + ptr_hex + + " in ??? (???) " + + RESET + + " \n ??? \n \n " , address ) ;
2018-02-19 14:06:54 -08:00
return ;
} ;
const compile_unit_name = try compile_unit . die . getAttrString ( debug_info , DW . AT_name ) ;
if ( getLineNumberInfo ( debug_info , compile_unit , address - 1 ) ) | line_info | {
defer line_info . deinit ( ) ;
2018-04-30 17:35:54 -07:00
try out_stream . print ( WHITE + + " {}:{}:{} " + + RESET + + " : " + + DIM + + ptr_hex + + " in ??? ({}) " + + RESET + + " \n " , line_info . file_name , line_info . line , line_info . column , address , compile_unit_name ) ;
2018-02-19 14:06:54 -08:00
if ( printLineFromFile ( debug_info . allocator ( ) , out_stream , line_info ) ) {
if ( line_info . column = = 0 ) {
try out_stream . write ( " \n " ) ;
} else {
2018-04-30 17:35:54 -07:00
{
var col_i : usize = 1 ;
while ( col_i < line_info . column ) : ( col_i + = 1 ) {
try out_stream . writeByte ( ' ' ) ;
}
}
2018-02-19 14:06:54 -08:00
try out_stream . write ( GREEN + + " ^ " + + RESET + + " \n " ) ;
}
} else | err | switch ( err ) {
error . EndOfFile = > { } ,
else = > return err ,
}
} else | err | switch ( err ) {
2018-05-26 15:16:39 -07:00
error . MissingDebugInfo , error . InvalidDebugInfo = > {
2018-02-19 14:06:54 -08:00
try out_stream . print ( ptr_hex + + " in ??? ({}) \n " , address , compile_unit_name ) ;
} ,
else = > return err ,
2018-01-11 23:12:11 -08:00
}
} ,
}
}
2018-01-30 22:51:15 -08:00
pub fn openSelfDebugInfo ( allocator : & mem . Allocator ) ! & ElfStackTrace {
2017-05-01 10:12:38 -07:00
switch ( builtin . object_format ) {
builtin . ObjectFormat . elf = > {
2018-01-14 07:19:21 -08:00
const st = try allocator . create ( ElfStackTrace ) ;
2018-02-19 14:06:54 -08:00
errdefer allocator . destroy ( st ) ;
2018-04-30 17:35:54 -07:00
st . * = ElfStackTrace {
2017-10-31 01:47:55 -07:00
. self_exe_file = undefined ,
2016-09-22 23:00:23 -07:00
. elf = undefined ,
. debug_info = undefined ,
. debug_abbrev = undefined ,
. debug_str = undefined ,
2017-04-24 09:01:19 -07:00
. debug_line = undefined ,
2017-12-12 08:33:14 -08:00
. debug_ranges = null ,
2017-05-04 11:05:06 -07:00
. abbrev_table_list = ArrayList ( AbbrevTableHeader ) . init ( allocator ) ,
. compile_unit_list = ArrayList ( CompileUnit ) . init ( allocator ) ,
2016-09-22 23:00:23 -07:00
} ;
2018-01-07 13:51:46 -08:00
st . self_exe_file = try os . openSelfExe ( ) ;
2018-01-23 20:08:09 -08:00
errdefer st . self_exe_file . close ( ) ;
2016-08-17 20:11:04 -07:00
2018-01-07 13:51:46 -08:00
try st . elf . openFile ( allocator , & st . self_exe_file ) ;
2018-01-23 20:08:09 -08:00
errdefer st . elf . close ( ) ;
2016-08-17 20:11:04 -07:00
2018-01-07 13:51:46 -08:00
st . debug_info = ( try st . elf . findSection ( " .debug_info " ) ) ? ? return error . MissingDebugInfo ;
st . debug_abbrev = ( try st . elf . findSection ( " .debug_abbrev " ) ) ? ? return error . MissingDebugInfo ;
st . debug_str = ( try st . elf . findSection ( " .debug_str " ) ) ? ? return error . MissingDebugInfo ;
st . debug_line = ( try st . elf . findSection ( " .debug_line " ) ) ? ? return error . MissingDebugInfo ;
st . debug_ranges = ( try st . elf . findSection ( " .debug_ranges " ) ) ;
try scanAllCompileUnits ( st ) ;
2018-01-14 07:19:21 -08:00
return st ;
2016-08-17 20:11:04 -07:00
} ,
2018-02-19 14:06:54 -08:00
builtin . ObjectFormat . macho = > {
var exe_file = try os . openSelfExe ( ) ;
defer exe_file . close ( ) ;
const st = try allocator . create ( ElfStackTrace ) ;
errdefer allocator . destroy ( st ) ;
2018-04-30 17:35:54 -07:00
st . * = ElfStackTrace { . symbol_table = try macho . loadSymbols ( allocator , & io . FileInStream . init ( & exe_file ) ) } ;
2018-02-19 14:06:54 -08:00
return st ;
} ,
2017-05-01 10:12:38 -07:00
builtin . ObjectFormat . coff = > {
2018-01-14 07:19:21 -08:00
return error . TodoSupportCoffDebugInfo ;
2016-08-17 20:11:04 -07:00
} ,
2017-06-19 18:36:54 -07:00
builtin . ObjectFormat . wasm = > {
2018-01-14 07:19:21 -08:00
return error . TodoSupportCOFFDebugInfo ;
2017-06-19 18:36:54 -07:00
} ,
2017-05-01 10:12:38 -07:00
builtin . ObjectFormat . unknown = > {
2018-01-14 07:19:21 -08:00
return error . UnknownObjectFormat ;
2016-08-17 20:11:04 -07:00
} ,
2016-05-17 13:32:43 -07:00
}
}
2016-08-11 22:25:13 -07:00
2018-02-05 14:42:13 -08:00
fn printLineFromFile ( allocator : & mem . Allocator , out_stream : var , line_info : & const LineInfo ) ! void {
2018-02-10 17:55:13 -08:00
var f = try os . File . openRead ( allocator , line_info . file_name ) ;
2017-04-24 09:01:19 -07:00
defer f . close ( ) ;
// TODO fstat and make sure that the file has the correct size
var buf : [ os . page_size ] u8 = undefined ;
var line : usize = 1 ;
var column : usize = 1 ;
var abs_index : usize = 0 ;
while ( true ) {
2018-01-07 13:51:46 -08:00
const amt_read = try f . read ( buf [ 0 . . ] ) ;
2017-05-19 07:39:59 -07:00
const slice = buf [ 0 . . amt_read ] ;
2017-04-24 09:01:19 -07:00
for ( slice ) | byte | {
if ( line = = line_info . line ) {
2018-01-07 13:51:46 -08:00
try out_stream . writeByte ( byte ) ;
2017-04-24 09:01:19 -07:00
if ( byte = = '\n' ) {
return ;
}
}
if ( byte = = '\n' ) {
line + = 1 ;
column = 1 ;
} else {
column + = 1 ;
}
}
2018-04-30 17:35:54 -07:00
if ( amt_read < buf . len ) return error . EndOfFile ;
2017-04-24 09:01:19 -07:00
}
}
2018-02-19 14:06:54 -08:00
pub const ElfStackTrace = switch ( builtin . os ) {
builtin . Os . macosx = > struct {
symbol_table : macho . SymbolTable ,
2017-04-24 09:01:19 -07:00
2018-02-19 14:06:54 -08:00
pub fn close ( self : & ElfStackTrace ) void {
self . symbol_table . deinit ( ) ;
}
} ,
else = > struct {
self_exe_file : os . File ,
elf : elf . Elf ,
debug_info : & elf . SectionHeader ,
debug_abbrev : & elf . SectionHeader ,
debug_str : & elf . SectionHeader ,
debug_line : & elf . SectionHeader ,
debug_ranges : ? & elf . SectionHeader ,
abbrev_table_list : ArrayList ( AbbrevTableHeader ) ,
compile_unit_list : ArrayList ( CompileUnit ) ,
pub fn allocator ( self : & const ElfStackTrace ) & mem . Allocator {
return self . abbrev_table_list . allocator ;
}
2018-01-14 07:19:21 -08:00
2018-02-19 14:06:54 -08:00
pub fn readString ( self : & ElfStackTrace ) ! [ ] u8 {
var in_file_stream = io . FileInStream . init ( & self . self_exe_file ) ;
const in_stream = & in_file_stream . stream ;
return readStringRaw ( self . allocator ( ) , in_stream ) ;
}
pub fn close ( self : & ElfStackTrace ) void {
self . self_exe_file . close ( ) ;
self . elf . close ( ) ;
}
} ,
2016-12-18 16:40:26 -08:00
} ;
2016-09-22 23:00:23 -07:00
2017-04-24 10:03:32 -07:00
const PcRange = struct {
start : u64 ,
end : u64 ,
} ;
2016-12-18 16:40:26 -08:00
const CompileUnit = struct {
2017-04-24 10:03:32 -07:00
version : u16 ,
2016-09-22 23:00:23 -07:00
is_64 : bool ,
die : & Die ,
2017-04-24 09:01:19 -07:00
index : usize ,
2017-04-24 10:03:32 -07:00
pc_range : ? PcRange ,
2016-12-18 16:40:26 -08:00
} ;
2016-09-22 23:00:23 -07:00
2017-05-04 11:05:06 -07:00
const AbbrevTable = ArrayList ( AbbrevTableEntry ) ;
2016-09-22 23:00:23 -07:00
2016-12-18 16:40:26 -08:00
const AbbrevTableHeader = struct {
2016-09-22 23:00:23 -07:00
// offset from .debug_abbrev
offset : u64 ,
table : AbbrevTable ,
2016-12-18 16:40:26 -08:00
} ;
2016-09-22 23:00:23 -07:00
2016-12-18 16:40:26 -08:00
const AbbrevTableEntry = struct {
2016-09-22 23:00:23 -07:00
has_children : bool ,
abbrev_code : u64 ,
tag_id : u64 ,
2017-05-04 11:05:06 -07:00
attrs : ArrayList ( AbbrevAttr ) ,
2016-12-18 16:40:26 -08:00
} ;
2016-09-22 23:00:23 -07:00
2016-12-18 16:40:26 -08:00
const AbbrevAttr = struct {
2016-09-22 23:00:23 -07:00
attr_id : u64 ,
form_id : u64 ,
2016-12-18 16:40:26 -08:00
} ;
2016-09-20 13:10:34 -07:00
2017-12-03 17:43:56 -08:00
const FormValue = union ( enum ) {
2016-09-20 13:10:34 -07:00
Address : u64 ,
Block : [ ] u8 ,
Const : Constant ,
ExprLoc : [ ] u8 ,
Flag : bool ,
SecOffset : u64 ,
Ref : [ ] u8 ,
RefAddr : u64 ,
RefSig8 : u64 ,
String : [ ] u8 ,
StrPtr : u64 ,
2016-12-18 16:40:26 -08:00
} ;
2016-09-20 13:10:34 -07:00
2016-12-18 16:40:26 -08:00
const Constant = struct {
2016-09-20 13:10:34 -07:00
payload : [ ] u8 ,
signed : bool ,
2016-09-22 23:00:23 -07:00
2018-01-30 22:51:15 -08:00
fn asUnsignedLe ( self : & const Constant ) ! u64 {
2018-04-30 17:35:54 -07:00
if ( self . payload . len > @sizeOf ( u64 ) ) return error . InvalidDebugInfo ;
if ( self . signed ) return error . InvalidDebugInfo ;
2017-12-04 07:35:55 -08:00
return mem . readInt ( self . payload , u64 , builtin . Endian . Little ) ;
2016-09-22 23:00:23 -07:00
}
2016-12-18 16:40:26 -08:00
} ;
2016-09-22 23:00:23 -07:00
2016-12-18 16:40:26 -08:00
const Die = struct {
2016-09-22 23:00:23 -07:00
tag_id : u64 ,
has_children : bool ,
2017-05-04 11:05:06 -07:00
attrs : ArrayList ( Attr ) ,
2016-09-22 23:00:23 -07:00
2016-12-18 16:40:26 -08:00
const Attr = struct {
2016-09-22 23:00:23 -07:00
id : u64 ,
value : FormValue ,
2016-12-18 16:40:26 -08:00
} ;
2016-09-22 23:00:23 -07:00
2018-01-25 01:10:11 -08:00
fn getAttr ( self : & const Die , id : u64 ) ? & const FormValue {
2017-02-28 00:07:11 -08:00
for ( self . attrs . toSliceConst ( ) ) | * attr | {
2018-04-30 17:35:54 -07:00
if ( attr . id = = id ) return & attr . value ;
2016-09-22 23:00:23 -07:00
}
return null ;
}
2018-01-30 22:51:15 -08:00
fn getAttrAddr ( self : & const Die , id : u64 ) ! u64 {
2017-04-24 10:03:32 -07:00
const form_value = self . getAttr ( id ) ? ? return error . MissingDebugInfo ;
2018-04-30 17:35:54 -07:00
return switch ( form_value . * ) {
2016-12-31 14:10:29 -08:00
FormValue . Address = > | value | value ,
2016-09-22 23:00:23 -07:00
else = > error . InvalidDebugInfo ,
} ;
}
2018-01-30 22:51:15 -08:00
fn getAttrSecOffset ( self : & const Die , id : u64 ) ! u64 {
2017-12-12 08:33:14 -08:00
const form_value = self . getAttr ( id ) ? ? return error . MissingDebugInfo ;
2018-04-30 17:35:54 -07:00
return switch ( form_value . * ) {
2017-12-12 08:33:14 -08:00
FormValue . Const = > | value | value . asUnsignedLe ( ) ,
FormValue . SecOffset = > | value | value ,
else = > error . InvalidDebugInfo ,
} ;
}
2018-01-30 22:51:15 -08:00
fn getAttrUnsignedLe ( self : & const Die , id : u64 ) ! u64 {
2017-04-24 10:03:32 -07:00
const form_value = self . getAttr ( id ) ? ? return error . MissingDebugInfo ;
2018-04-30 17:35:54 -07:00
return switch ( form_value . * ) {
2016-12-31 14:10:29 -08:00
FormValue . Const = > | value | value . asUnsignedLe ( ) ,
2016-09-22 23:00:23 -07:00
else = > error . InvalidDebugInfo ,
} ;
}
2018-01-30 22:51:15 -08:00
fn getAttrString ( self : & const Die , st : & ElfStackTrace , id : u64 ) ! [ ] u8 {
2017-04-24 10:03:32 -07:00
const form_value = self . getAttr ( id ) ? ? return error . MissingDebugInfo ;
2018-04-30 17:35:54 -07:00
return switch ( form_value . * ) {
2016-12-31 14:10:29 -08:00
FormValue . String = > | value | value ,
FormValue . StrPtr = > | offset | getString ( st , offset ) ,
2016-09-22 23:00:23 -07:00
else = > error . InvalidDebugInfo ,
2017-12-21 21:50:30 -08:00
} ;
2016-09-22 23:00:23 -07:00
}
2016-12-18 16:40:26 -08:00
} ;
2016-09-20 13:10:34 -07:00
2017-04-24 09:01:19 -07:00
const FileEntry = struct {
file_name : [ ] const u8 ,
dir_index : usize ,
mtime : usize ,
len_bytes : usize ,
} ;
const LineInfo = struct {
line : usize ,
column : usize ,
file_name : [ ] u8 ,
allocator : & mem . Allocator ,
2018-01-25 01:10:11 -08:00
fn deinit ( self : & const LineInfo ) void {
2017-04-24 09:01:19 -07:00
self . allocator . free ( self . file_name ) ;
}
} ;
const LineNumberProgram = struct {
address : usize ,
file : usize ,
line : isize ,
column : usize ,
is_stmt : bool ,
basic_block : bool ,
end_sequence : bool ,
target_address : usize ,
include_dirs : [ ] const [ ] const u8 ,
2017-05-04 11:05:06 -07:00
file_entries : & ArrayList ( FileEntry ) ,
2017-04-24 09:01:19 -07:00
prev_address : usize ,
prev_file : usize ,
prev_line : isize ,
prev_column : usize ,
prev_is_stmt : bool ,
prev_basic_block : bool ,
prev_end_sequence : bool ,
2018-04-30 17:35:54 -07:00
pub fn init ( is_stmt : bool , include_dirs : [ ] const [ ] const u8 , file_entries : & ArrayList ( FileEntry ) , target_address : usize ) LineNumberProgram {
return LineNumberProgram {
2017-04-24 09:01:19 -07:00
. address = 0 ,
. file = 1 ,
. line = 1 ,
. column = 0 ,
. is_stmt = is_stmt ,
. basic_block = false ,
. end_sequence = false ,
. include_dirs = include_dirs ,
. file_entries = file_entries ,
. target_address = target_address ,
. prev_address = 0 ,
. prev_file = undefined ,
. prev_line = undefined ,
. prev_column = undefined ,
. prev_is_stmt = undefined ,
. prev_basic_block = undefined ,
. prev_end_sequence = undefined ,
2017-12-21 21:50:30 -08:00
} ;
2017-04-24 09:01:19 -07:00
}
2018-01-30 22:51:15 -08:00
pub fn checkLineMatch ( self : & LineNumberProgram ) ! ? LineInfo {
2017-04-24 09:01:19 -07:00
if ( self . target_address > = self . prev_address and self . target_address < self . address ) {
const file_entry = if ( self . prev_file = = 0 ) {
return error . MissingDebugInfo ;
} else if ( self . prev_file - 1 > = self . file_entries . len ) {
return error . InvalidDebugInfo ;
2018-04-30 17:35:54 -07:00
} else
& self . file_entries . items [ self . prev_file - 1 ] ;
2017-12-21 21:50:30 -08:00
2017-04-24 09:01:19 -07:00
const dir_name = if ( file_entry . dir_index > = self . include_dirs . len ) {
return error . InvalidDebugInfo ;
2018-04-30 17:35:54 -07:00
} else
self . include_dirs [ file_entry . dir_index ] ;
2018-01-07 13:51:46 -08:00
const file_name = try os . path . join ( self . file_entries . allocator , dir_name , file_entry . file_name ) ;
2018-01-23 20:08:09 -08:00
errdefer self . file_entries . allocator . free ( file_name ) ;
2018-04-30 17:35:54 -07:00
return LineInfo {
2017-04-24 09:01:19 -07:00
. line = if ( self . prev_line > = 0 ) usize ( self . prev_line ) else 0 ,
. column = self . prev_column ,
. file_name = file_name ,
. allocator = self . file_entries . allocator ,
} ;
}
self . prev_address = self . address ;
self . prev_file = self . file ;
self . prev_line = self . line ;
self . prev_column = self . column ;
self . prev_is_stmt = self . is_stmt ;
self . prev_basic_block = self . basic_block ;
self . prev_end_sequence = self . end_sequence ;
return null ;
}
} ;
2018-02-05 04:38:24 -08:00
fn readStringRaw ( allocator : & mem . Allocator , in_stream : var ) ! [ ] u8 {
2017-05-04 11:05:06 -07:00
var buf = ArrayList ( u8 ) . init ( allocator ) ;
2016-09-22 23:00:23 -07:00
while ( true ) {
2018-01-07 13:51:46 -08:00
const byte = try in_stream . readByte ( ) ;
2018-04-30 17:35:54 -07:00
if ( byte = = 0 ) break ;
2018-01-07 13:51:46 -08:00
try buf . append ( byte ) ;
2016-09-22 23:00:23 -07:00
}
2017-04-24 09:01:19 -07:00
return buf . toSlice ( ) ;
2016-09-22 23:00:23 -07:00
}
2018-01-30 22:51:15 -08:00
fn getString ( st : & ElfStackTrace , offset : u64 ) ! [ ] u8 {
2016-09-22 23:00:23 -07:00
const pos = st . debug_str . offset + offset ;
2018-01-07 13:51:46 -08:00
try st . self_exe_file . seekTo ( pos ) ;
2017-04-24 09:01:19 -07:00
return st . readString ( ) ;
2016-09-22 23:00:23 -07:00
}
2016-09-20 13:10:34 -07:00
2018-02-05 04:38:24 -08:00
fn readAllocBytes ( allocator : & mem . Allocator , in_stream : var , size : usize ) ! [ ] u8 {
2018-04-15 17:15:19 -07:00
const buf = try allocator . alloc ( u8 , size ) ;
errdefer allocator . free ( buf ) ;
2018-01-07 13:51:46 -08:00
if ( ( try in_stream . read ( buf ) ) < size ) return error . EndOfFile ;
2016-09-20 13:10:34 -07:00
return buf ;
}
2018-02-05 04:38:24 -08:00
fn parseFormValueBlockLen ( allocator : & mem . Allocator , in_stream : var , size : usize ) ! FormValue {
2018-01-07 13:51:46 -08:00
const buf = try readAllocBytes ( allocator , in_stream , size ) ;
2018-04-30 17:35:54 -07:00
return FormValue { . Block = buf } ;
2016-09-20 13:10:34 -07:00
}
2018-02-05 04:38:24 -08:00
fn parseFormValueBlock ( allocator : & mem . Allocator , in_stream : var , size : usize ) ! FormValue {
2018-01-07 13:51:46 -08:00
const block_len = try in_stream . readVarInt ( builtin . Endian . Little , usize , size ) ;
2017-04-24 09:01:19 -07:00
return parseFormValueBlockLen ( allocator , in_stream , block_len ) ;
2016-09-20 13:10:34 -07:00
}
2018-02-05 04:38:24 -08:00
fn parseFormValueConstant ( allocator : & mem . Allocator , in_stream : var , signed : bool , size : usize ) ! FormValue {
2018-05-26 15:16:39 -07:00
return FormValue {
. Const = Constant {
. signed = signed ,
. payload = try readAllocBytes ( allocator , in_stream , size ) ,
} ,
} ;
2016-09-20 13:10:34 -07:00
}
2018-02-05 04:38:24 -08:00
fn parseFormValueDwarfOffsetSize ( in_stream : var , is_64 : bool ) ! u64 {
2018-04-30 17:35:54 -07:00
return if ( is_64 ) try in_stream . readIntLe ( u64 ) else u64 ( try in_stream . readIntLe ( u32 ) ) ;
2016-09-20 13:10:34 -07:00
}
2018-02-05 04:38:24 -08:00
fn parseFormValueTargetAddrSize ( in_stream : var ) ! u64 {
2018-04-30 17:35:54 -07:00
return if ( @sizeOf ( usize ) = = 4 ) u64 ( try in_stream . readIntLe ( u32 ) ) else if ( @sizeOf ( usize ) = = 8 ) try in_stream . readIntLe ( u64 ) else unreachable ;
2016-09-22 23:00:23 -07:00
}
2018-02-05 04:38:24 -08:00
fn parseFormValueRefLen ( allocator : & mem . Allocator , in_stream : var , size : usize ) ! FormValue {
2018-01-07 13:51:46 -08:00
const buf = try readAllocBytes ( allocator , in_stream , size ) ;
2018-04-30 17:35:54 -07:00
return FormValue { . Ref = buf } ;
2016-09-20 13:10:34 -07:00
}
2018-02-05 04:38:24 -08:00
fn parseFormValueRef ( allocator : & mem . Allocator , in_stream : var , comptime T : type ) ! FormValue {
2018-01-07 13:51:46 -08:00
const block_len = try in_stream . readIntLe ( T ) ;
2017-04-24 09:01:19 -07:00
return parseFormValueRefLen ( allocator , in_stream , block_len ) ;
2016-09-20 13:10:34 -07:00
}
2018-05-26 15:16:39 -07:00
const ParseFormValueError = error {
2018-02-05 04:38:24 -08:00
EndOfStream ,
Io ,
BadFd ,
Unexpected ,
InvalidDebugInfo ,
EndOfFile ,
OutOfMemory ,
} ;
2018-02-04 22:49:14 -08:00
2018-04-30 17:35:54 -07:00
fn parseFormValue ( allocator : & mem . Allocator , in_stream : var , form_id : u64 , is_64 : bool ) ParseFormValueError ! FormValue {
2016-09-20 13:10:34 -07:00
return switch ( form_id ) {
2018-04-30 17:35:54 -07:00
DW . FORM_addr = > FormValue { . Address = try parseFormValueTargetAddrSize ( in_stream ) } ,
2017-04-24 09:01:19 -07:00
DW . FORM_block1 = > parseFormValueBlock ( allocator , in_stream , 1 ) ,
DW . FORM_block2 = > parseFormValueBlock ( allocator , in_stream , 2 ) ,
DW . FORM_block4 = > parseFormValueBlock ( allocator , in_stream , 4 ) ,
2017-12-21 21:50:30 -08:00
DW . FORM_block = > x : {
2018-01-07 13:51:46 -08:00
const block_len = try readULeb128 ( in_stream ) ;
2017-12-21 21:50:30 -08:00
return parseFormValueBlockLen ( allocator , in_stream , block_len ) ;
2016-09-20 13:10:34 -07:00
} ,
2017-04-24 09:01:19 -07:00
DW . FORM_data1 = > parseFormValueConstant ( allocator , in_stream , false , 1 ) ,
DW . FORM_data2 = > parseFormValueConstant ( allocator , in_stream , false , 2 ) ,
DW . FORM_data4 = > parseFormValueConstant ( allocator , in_stream , false , 4 ) ,
DW . FORM_data8 = > parseFormValueConstant ( allocator , in_stream , false , 8 ) ,
2018-05-26 15:16:39 -07:00
DW . FORM_udata , DW . FORM_sdata = > {
2018-01-07 13:51:46 -08:00
const block_len = try readULeb128 ( in_stream ) ;
2016-09-20 13:10:34 -07:00
const signed = form_id = = DW . FORM_sdata ;
2017-12-21 21:50:30 -08:00
return parseFormValueConstant ( allocator , in_stream , signed , block_len ) ;
2016-09-20 13:10:34 -07:00
} ,
DW . FORM_exprloc = > {
2018-01-07 13:51:46 -08:00
const size = try readULeb128 ( in_stream ) ;
const buf = try readAllocBytes ( allocator , in_stream , size ) ;
2018-04-30 17:35:54 -07:00
return FormValue { . ExprLoc = buf } ;
2016-09-20 13:10:34 -07:00
} ,
2018-04-30 17:35:54 -07:00
DW . FORM_flag = > FormValue { . Flag = ( try in_stream . readByte ( ) ) ! = 0 } ,
DW . FORM_flag_present = > FormValue { . Flag = true } ,
DW . FORM_sec_offset = > FormValue { . SecOffset = try parseFormValueDwarfOffsetSize ( in_stream , is_64 ) } ,
2016-09-20 13:10:34 -07:00
2017-04-24 09:01:19 -07:00
DW . FORM_ref1 = > parseFormValueRef ( allocator , in_stream , u8 ) ,
DW . FORM_ref2 = > parseFormValueRef ( allocator , in_stream , u16 ) ,
DW . FORM_ref4 = > parseFormValueRef ( allocator , in_stream , u32 ) ,
DW . FORM_ref8 = > parseFormValueRef ( allocator , in_stream , u64 ) ,
2016-09-20 13:10:34 -07:00
DW . FORM_ref_udata = > {
2018-01-07 13:51:46 -08:00
const ref_len = try readULeb128 ( in_stream ) ;
2017-12-21 21:50:30 -08:00
return parseFormValueRefLen ( allocator , in_stream , ref_len ) ;
2016-09-20 13:10:34 -07:00
} ,
2018-04-30 17:35:54 -07:00
DW . FORM_ref_addr = > FormValue { . RefAddr = try parseFormValueDwarfOffsetSize ( in_stream , is_64 ) } ,
DW . FORM_ref_sig8 = > FormValue { . RefSig8 = try in_stream . readIntLe ( u64 ) } ,
2016-09-20 13:10:34 -07:00
2018-04-30 17:35:54 -07:00
DW . FORM_string = > FormValue { . String = try readStringRaw ( allocator , in_stream ) } ,
DW . FORM_strp = > FormValue { . StrPtr = try parseFormValueDwarfOffsetSize ( in_stream , is_64 ) } ,
2016-09-20 13:10:34 -07:00
DW . FORM_indirect = > {
2018-01-07 13:51:46 -08:00
const child_form_id = try readULeb128 ( in_stream ) ;
2017-12-21 21:50:30 -08:00
return parseFormValue ( allocator , in_stream , child_form_id , is_64 ) ;
2016-09-20 13:10:34 -07:00
} ,
2016-09-21 14:40:50 -07:00
else = > error . InvalidDebugInfo ,
2017-12-21 21:50:30 -08:00
} ;
2016-08-17 20:11:04 -07:00
}
2018-01-30 22:51:15 -08:00
fn parseAbbrevTable ( st : & ElfStackTrace ) ! AbbrevTable {
2017-11-07 00:22:27 -08:00
const in_file = & st . self_exe_file ;
var in_file_stream = io . FileInStream . init ( in_file ) ;
const in_stream = & in_file_stream . stream ;
2017-04-24 09:01:19 -07:00
var result = AbbrevTable . init ( st . allocator ( ) ) ;
2016-09-22 23:00:23 -07:00
while ( true ) {
2018-01-07 13:51:46 -08:00
const abbrev_code = try readULeb128 ( in_stream ) ;
2018-04-30 17:35:54 -07:00
if ( abbrev_code = = 0 ) return result ;
try result . append ( AbbrevTableEntry {
2016-09-22 23:00:23 -07:00
. abbrev_code = abbrev_code ,
2018-01-07 13:51:46 -08:00
. tag_id = try readULeb128 ( in_stream ) ,
. has_children = ( try in_stream . readByte ( ) ) = = DW . CHILDREN_yes ,
2017-05-04 11:05:06 -07:00
. attrs = ArrayList ( AbbrevAttr ) . init ( st . allocator ( ) ) ,
2016-09-22 23:00:23 -07:00
} ) ;
const attrs = & result . items [ result . len - 1 ] . attrs ;
2016-08-23 07:10:09 -07:00
2016-09-22 23:00:23 -07:00
while ( true ) {
2018-01-07 13:51:46 -08:00
const attr_id = try readULeb128 ( in_stream ) ;
const form_id = try readULeb128 ( in_stream ) ;
2018-04-30 17:35:54 -07:00
if ( attr_id = = 0 and form_id = = 0 ) break ;
try attrs . append ( AbbrevAttr {
2016-09-22 23:00:23 -07:00
. attr_id = attr_id ,
. form_id = form_id ,
} ) ;
}
}
}
/// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,
/// seeks in the stream and parses it.
2018-01-30 22:51:15 -08:00
fn getAbbrevTable ( st : & ElfStackTrace , abbrev_offset : u64 ) ! & const AbbrevTable {
2017-01-16 12:24:03 -08:00
for ( st . abbrev_table_list . toSlice ( ) ) | * header | {
2016-09-22 23:00:23 -07:00
if ( header . offset = = abbrev_offset ) {
return & header . table ;
}
}
2018-01-07 13:51:46 -08:00
try st . self_exe_file . seekTo ( st . debug_abbrev . offset + abbrev_offset ) ;
2018-04-30 17:35:54 -07:00
try st . abbrev_table_list . append ( AbbrevTableHeader {
2016-09-22 23:00:23 -07:00
. offset = abbrev_offset ,
2018-01-07 13:51:46 -08:00
. table = try parseAbbrevTable ( st ) ,
2016-09-22 23:00:23 -07:00
} ) ;
return & st . abbrev_table_list . items [ st . abbrev_table_list . len - 1 ] . table ;
}
2018-01-25 01:10:11 -08:00
fn getAbbrevTableEntry ( abbrev_table : & const AbbrevTable , abbrev_code : u64 ) ? & const AbbrevTableEntry {
2017-02-28 00:07:11 -08:00
for ( abbrev_table . toSliceConst ( ) ) | * table_entry | {
2018-04-30 17:35:54 -07:00
if ( table_entry . abbrev_code = = abbrev_code ) return table_entry ;
2016-09-22 23:00:23 -07:00
}
return null ;
}
2018-01-30 22:51:15 -08:00
fn parseDie ( st : & ElfStackTrace , abbrev_table : & const AbbrevTable , is_64 : bool ) ! Die {
2017-10-31 01:47:55 -07:00
const in_file = & st . self_exe_file ;
2017-11-07 00:22:27 -08:00
var in_file_stream = io . FileInStream . init ( in_file ) ;
const in_stream = & in_file_stream . stream ;
2018-01-07 13:51:46 -08:00
const abbrev_code = try readULeb128 ( in_stream ) ;
2016-09-22 23:00:23 -07:00
const table_entry = getAbbrevTableEntry ( abbrev_table , abbrev_code ) ? ? return error . InvalidDebugInfo ;
2018-04-30 17:35:54 -07:00
var result = Die {
2016-09-22 23:00:23 -07:00
. tag_id = table_entry . tag_id ,
. has_children = table_entry . has_children ,
2017-05-04 11:05:06 -07:00
. attrs = ArrayList ( Die . Attr ) . init ( st . allocator ( ) ) ,
2016-09-22 23:00:23 -07:00
} ;
2018-01-07 13:51:46 -08:00
try result . attrs . resize ( table_entry . attrs . len ) ;
2017-02-28 00:07:11 -08:00
for ( table_entry . attrs . toSliceConst ( ) ) | attr , i | {
2018-04-30 17:35:54 -07:00
result . attrs . items [ i ] = Die . Attr {
2016-09-22 23:00:23 -07:00
. id = attr . attr_id ,
2018-01-07 13:51:46 -08:00
. value = try parseFormValue ( st . allocator ( ) , in_stream , attr . form_id , is_64 ) ,
2016-09-22 23:00:23 -07:00
} ;
}
return result ;
}
2018-01-30 22:51:15 -08:00
fn getLineNumberInfo ( st : & ElfStackTrace , compile_unit : & const CompileUnit , target_address : usize ) ! LineInfo {
2018-01-07 13:51:46 -08:00
const compile_unit_cwd = try compile_unit . die . getAttrString ( st , DW . AT_comp_dir ) ;
2017-04-24 09:01:19 -07:00
2017-10-31 01:47:55 -07:00
const in_file = & st . self_exe_file ;
2017-04-24 09:01:19 -07:00
const debug_line_end = st . debug_line . offset + st . debug_line . size ;
var this_offset = st . debug_line . offset ;
var this_index : usize = 0 ;
2017-11-07 00:22:27 -08:00
var in_file_stream = io . FileInStream . init ( in_file ) ;
const in_stream = & in_file_stream . stream ;
2017-05-03 15:12:07 -07:00
while ( this_offset < debug_line_end ) : ( this_index + = 1 ) {
2018-01-07 13:51:46 -08:00
try in_file . seekTo ( this_offset ) ;
2017-04-24 09:01:19 -07:00
var is_64 : bool = undefined ;
2018-02-05 04:38:24 -08:00
const unit_length = try readInitialLength ( @typeOf ( in_stream . readFn ) . ReturnType . ErrorSet , in_stream , & is_64 ) ;
2018-04-30 17:35:54 -07:00
if ( unit_length = = 0 ) return error . MissingDebugInfo ;
2017-04-24 09:01:19 -07:00
const next_offset = unit_length + ( if ( is_64 ) usize ( 12 ) else usize ( 4 ) ) ;
if ( compile_unit . index ! = this_index ) {
this_offset + = next_offset ;
continue ;
}
2018-01-07 13:51:46 -08:00
const version = try in_stream . readInt ( st . elf . endian , u16 ) ;
2018-03-02 13:26:22 -08:00
// TODO support 3 and 5
if ( version ! = 2 and version ! = 4 ) return error . InvalidDebugInfo ;
2017-04-24 09:01:19 -07:00
2018-04-30 17:35:54 -07:00
const prologue_length = if ( is_64 ) try in_stream . readInt ( st . elf . endian , u64 ) else try in_stream . readInt ( st . elf . endian , u32 ) ;
2018-01-07 13:51:46 -08:00
const prog_start_offset = ( try in_file . getPos ( ) ) + prologue_length ;
2017-04-24 09:01:19 -07:00
2018-01-07 13:51:46 -08:00
const minimum_instruction_length = try in_stream . readByte ( ) ;
2017-04-24 09:01:19 -07:00
if ( minimum_instruction_length = = 0 ) return error . InvalidDebugInfo ;
2018-03-02 13:26:22 -08:00
if ( version > = 4 ) {
// maximum_operations_per_instruction
_ = try in_stream . readByte ( ) ;
}
2018-01-07 13:51:46 -08:00
const default_is_stmt = ( try in_stream . readByte ( ) ) ! = 0 ;
const line_base = try in_stream . readByteSigned ( ) ;
2017-04-24 09:01:19 -07:00
2018-01-07 13:51:46 -08:00
const line_range = try in_stream . readByte ( ) ;
2018-04-30 17:35:54 -07:00
if ( line_range = = 0 ) return error . InvalidDebugInfo ;
2017-04-24 09:01:19 -07:00
2018-01-07 13:51:46 -08:00
const opcode_base = try in_stream . readByte ( ) ;
2017-04-24 09:01:19 -07:00
2018-01-07 13:51:46 -08:00
const standard_opcode_lengths = try st . allocator ( ) . alloc ( u8 , opcode_base - 1 ) ;
2017-04-24 09:01:19 -07:00
2018-04-30 17:35:54 -07:00
{
var i : usize = 0 ;
while ( i < opcode_base - 1 ) : ( i + = 1 ) {
standard_opcode_lengths [ i ] = try in_stream . readByte ( ) ;
}
}
2017-04-24 09:01:19 -07:00
2017-05-04 11:05:06 -07:00
var include_directories = ArrayList ( [ ] u8 ) . init ( st . allocator ( ) ) ;
2018-01-07 13:51:46 -08:00
try include_directories . append ( compile_unit_cwd ) ;
2017-04-24 09:01:19 -07:00
while ( true ) {
2018-01-07 13:51:46 -08:00
const dir = try st . readString ( ) ;
2018-04-30 17:35:54 -07:00
if ( dir . len = = 0 ) break ;
2018-01-07 13:51:46 -08:00
try include_directories . append ( dir ) ;
2017-04-24 09:01:19 -07:00
}
2017-05-04 11:05:06 -07:00
var file_entries = ArrayList ( FileEntry ) . init ( st . allocator ( ) ) ;
2018-04-30 17:35:54 -07:00
var prog = LineNumberProgram . init ( default_is_stmt , include_directories . toSliceConst ( ) , & file_entries , target_address ) ;
2017-04-24 09:01:19 -07:00
while ( true ) {
2018-01-07 13:51:46 -08:00
const file_name = try st . readString ( ) ;
2018-04-30 17:35:54 -07:00
if ( file_name . len = = 0 ) break ;
2018-01-07 13:51:46 -08:00
const dir_index = try readULeb128 ( in_stream ) ;
const mtime = try readULeb128 ( in_stream ) ;
const len_bytes = try readULeb128 ( in_stream ) ;
2018-04-30 17:35:54 -07:00
try file_entries . append ( FileEntry {
2017-04-24 09:01:19 -07:00
. file_name = file_name ,
. dir_index = dir_index ,
. mtime = mtime ,
. len_bytes = len_bytes ,
} ) ;
}
2018-01-07 13:51:46 -08:00
try in_file . seekTo ( prog_start_offset ) ;
2017-04-24 09:01:19 -07:00
while ( true ) {
2018-01-07 13:51:46 -08:00
const opcode = try in_stream . readByte ( ) ;
2017-04-24 09:01:19 -07:00
var sub_op : u8 = undefined ; // TODO move this to the correct scope and fix the compiler crash
if ( opcode = = DW . LNS_extended_op ) {
2018-01-07 13:51:46 -08:00
const op_size = try readULeb128 ( in_stream ) ;
2018-04-30 17:35:54 -07:00
if ( op_size < 1 ) return error . InvalidDebugInfo ;
2018-01-07 13:51:46 -08:00
sub_op = try in_stream . readByte ( ) ;
2017-04-24 09:01:19 -07:00
switch ( sub_op ) {
DW . LNE_end_sequence = > {
prog . end_sequence = true ;
2018-01-07 13:51:46 -08:00
if ( try prog . checkLineMatch ( ) ) | info | return info ;
2017-04-24 09:01:19 -07:00
return error . MissingDebugInfo ;
} ,
DW . LNE_set_address = > {
2018-01-07 13:51:46 -08:00
const addr = try in_stream . readInt ( st . elf . endian , usize ) ;
2017-04-24 09:01:19 -07:00
prog . address = addr ;
} ,
DW . LNE_define_file = > {
2018-01-07 13:51:46 -08:00
const file_name = try st . readString ( ) ;
const dir_index = try readULeb128 ( in_stream ) ;
const mtime = try readULeb128 ( in_stream ) ;
const len_bytes = try readULeb128 ( in_stream ) ;
2018-04-30 17:35:54 -07:00
try file_entries . append ( FileEntry {
2017-04-24 09:01:19 -07:00
. file_name = file_name ,
. dir_index = dir_index ,
. mtime = mtime ,
. len_bytes = len_bytes ,
} ) ;
} ,
else = > {
2018-01-07 14:28:20 -08:00
const fwd_amt = math . cast ( isize , op_size - 1 ) catch return error . InvalidDebugInfo ;
2018-01-07 13:51:46 -08:00
try in_file . seekForward ( fwd_amt ) ;
2017-04-24 09:01:19 -07:00
} ,
}
} else if ( opcode > = opcode_base ) {
// special opcodes
const adjusted_opcode = opcode - opcode_base ;
const inc_addr = minimum_instruction_length * ( adjusted_opcode / line_range ) ;
const inc_line = i32 ( line_base ) + i32 ( adjusted_opcode % line_range ) ;
prog . line + = inc_line ;
prog . address + = inc_addr ;
2018-01-07 13:51:46 -08:00
if ( try prog . checkLineMatch ( ) ) | info | return info ;
2017-04-24 09:01:19 -07:00
prog . basic_block = false ;
} else {
switch ( opcode ) {
DW . LNS_copy = > {
2018-01-07 13:51:46 -08:00
if ( try prog . checkLineMatch ( ) ) | info | return info ;
2017-04-24 09:01:19 -07:00
prog . basic_block = false ;
} ,
DW . LNS_advance_pc = > {
2018-01-07 13:51:46 -08:00
const arg = try readULeb128 ( in_stream ) ;
2017-04-24 09:01:19 -07:00
prog . address + = arg * minimum_instruction_length ;
} ,
DW . LNS_advance_line = > {
2018-01-07 13:51:46 -08:00
const arg = try readILeb128 ( in_stream ) ;
2017-04-24 09:01:19 -07:00
prog . line + = arg ;
} ,
DW . LNS_set_file = > {
2018-01-07 13:51:46 -08:00
const arg = try readULeb128 ( in_stream ) ;
2017-04-24 09:01:19 -07:00
prog . file = arg ;
} ,
DW . LNS_set_column = > {
2018-01-07 13:51:46 -08:00
const arg = try readULeb128 ( in_stream ) ;
2017-04-24 09:01:19 -07:00
prog . column = arg ;
} ,
DW . LNS_negate_stmt = > {
prog . is_stmt = ! prog . is_stmt ;
} ,
DW . LNS_set_basic_block = > {
prog . basic_block = true ;
} ,
DW . LNS_const_add_pc = > {
const inc_addr = minimum_instruction_length * ( ( 255 - opcode_base ) / line_range ) ;
prog . address + = inc_addr ;
} ,
DW . LNS_fixed_advance_pc = > {
2018-01-07 13:51:46 -08:00
const arg = try in_stream . readInt ( st . elf . endian , u16 ) ;
2017-04-24 09:01:19 -07:00
prog . address + = arg ;
} ,
2018-04-30 17:35:54 -07:00
DW . LNS_set_prologue_end = > { } ,
2017-04-24 09:01:19 -07:00
else = > {
2018-04-30 17:35:54 -07:00
if ( opcode - 1 > = standard_opcode_lengths . len ) return error . InvalidDebugInfo ;
2017-04-24 09:01:19 -07:00
const len_bytes = standard_opcode_lengths [ opcode - 1 ] ;
2018-01-07 13:51:46 -08:00
try in_file . seekForward ( len_bytes ) ;
2017-04-24 09:01:19 -07:00
} ,
}
}
}
this_offset + = next_offset ;
}
return error . MissingDebugInfo ;
}
2018-01-30 22:51:15 -08:00
fn scanAllCompileUnits ( st : & ElfStackTrace ) ! void {
2016-09-22 23:00:23 -07:00
const debug_info_end = st . debug_info . offset + st . debug_info . size ;
var this_unit_offset = st . debug_info . offset ;
2017-04-24 09:01:19 -07:00
var cu_index : usize = 0 ;
2017-11-07 00:22:27 -08:00
var in_file_stream = io . FileInStream . init ( & st . self_exe_file ) ;
const in_stream = & in_file_stream . stream ;
2016-09-22 23:00:23 -07:00
while ( this_unit_offset < debug_info_end ) {
2018-01-07 13:51:46 -08:00
try st . self_exe_file . seekTo ( this_unit_offset ) ;
2016-08-23 07:10:09 -07:00
2016-09-20 13:10:34 -07:00
var is_64 : bool = undefined ;
2018-02-05 04:38:24 -08:00
const unit_length = try readInitialLength ( @typeOf ( in_stream . readFn ) . ReturnType . ErrorSet , in_stream , & is_64 ) ;
2018-04-30 17:35:54 -07:00
if ( unit_length = = 0 ) return ;
2016-09-22 23:00:23 -07:00
const next_offset = unit_length + ( if ( is_64 ) usize ( 12 ) else usize ( 4 ) ) ;
2016-09-20 13:10:34 -07:00
2018-01-07 13:51:46 -08:00
const version = try in_stream . readInt ( st . elf . endian , u16 ) ;
2017-04-24 10:03:32 -07:00
if ( version < 2 or version > 5 ) return error . InvalidDebugInfo ;
2016-09-20 13:10:34 -07:00
2018-04-30 17:35:54 -07:00
const debug_abbrev_offset = if ( is_64 ) try in_stream . readInt ( st . elf . endian , u64 ) else try in_stream . readInt ( st . elf . endian , u32 ) ;
2016-09-20 13:10:34 -07:00
2018-01-07 13:51:46 -08:00
const address_size = try in_stream . readByte ( ) ;
2016-09-20 13:10:34 -07:00
if ( address_size ! = @sizeOf ( usize ) ) return error . InvalidDebugInfo ;
2018-01-07 13:51:46 -08:00
const compile_unit_pos = try st . self_exe_file . getPos ( ) ;
const abbrev_table = try getAbbrevTable ( st , debug_abbrev_offset ) ;
2016-09-22 23:00:23 -07:00
2018-01-07 13:51:46 -08:00
try st . self_exe_file . seekTo ( compile_unit_pos ) ;
2016-09-20 13:10:34 -07:00
2018-01-07 13:51:46 -08:00
const compile_unit_die = try st . allocator ( ) . create ( Die ) ;
2018-04-30 17:35:54 -07:00
compile_unit_die . * = try parseDie ( st , abbrev_table , is_64 ) ;
2016-09-20 13:10:34 -07:00
2018-04-30 17:35:54 -07:00
if ( compile_unit_die . tag_id ! = DW . TAG_compile_unit ) return error . InvalidDebugInfo ;
2016-09-22 23:00:23 -07:00
2017-12-21 21:50:30 -08:00
const pc_range = x : {
2017-05-03 14:23:11 -07:00
if ( compile_unit_die . getAttrAddr ( DW . AT_low_pc ) ) | low_pc | {
if ( compile_unit_die . getAttr ( DW . AT_high_pc ) ) | high_pc_value | {
2018-04-30 17:35:54 -07:00
const pc_end = switch ( high_pc_value . * ) {
2017-04-24 10:03:32 -07:00
FormValue . Address = > | value | value ,
2017-12-21 21:50:30 -08:00
FormValue . Const = > | value | b : {
2018-01-07 13:51:46 -08:00
const offset = try value . asUnsignedLe ( ) ;
2017-12-21 21:50:30 -08:00
break : b ( low_pc + offset ) ;
2017-04-24 10:03:32 -07:00
} ,
else = > return error . InvalidDebugInfo ,
} ;
2018-04-30 17:35:54 -07:00
break : x PcRange {
2017-04-24 10:03:32 -07:00
. start = low_pc ,
. end = pc_end ,
2017-12-21 21:50:30 -08:00
} ;
2017-04-24 10:03:32 -07:00
} else {
2017-12-21 21:50:30 -08:00
break : x null ;
2017-04-24 10:03:32 -07:00
}
} else | err | {
2018-04-30 17:35:54 -07:00
if ( err ! = error . MissingDebugInfo ) return err ;
2017-12-21 21:50:30 -08:00
break : x null ;
2017-04-24 10:03:32 -07:00
}
2016-09-22 23:00:23 -07:00
} ;
2018-04-30 17:35:54 -07:00
try st . compile_unit_list . append ( CompileUnit {
2017-04-24 10:03:32 -07:00
. version = version ,
2016-09-22 23:00:23 -07:00
. is_64 = is_64 ,
2017-04-24 10:03:32 -07:00
. pc_range = pc_range ,
2016-09-22 23:00:23 -07:00
. die = compile_unit_die ,
2017-04-24 09:01:19 -07:00
. index = cu_index ,
2016-09-22 23:00:23 -07:00
} ) ;
this_unit_offset + = next_offset ;
2017-04-24 09:01:19 -07:00
cu_index + = 1 ;
2016-09-22 23:00:23 -07:00
}
}
2018-01-30 22:51:15 -08:00
fn findCompileUnit ( st : & ElfStackTrace , target_address : u64 ) ! & const CompileUnit {
2017-12-12 08:33:14 -08:00
var in_file_stream = io . FileInStream . init ( & st . self_exe_file ) ;
const in_stream = & in_file_stream . stream ;
2016-09-22 23:00:23 -07:00
for ( st . compile_unit_list . toSlice ( ) ) | * compile_unit | {
2017-05-03 14:23:11 -07:00
if ( compile_unit . pc_range ) | range | {
2018-04-30 17:35:54 -07:00
if ( target_address > = range . start and target_address < range . end ) return compile_unit ;
2017-04-24 10:03:32 -07:00
}
2017-12-12 08:33:14 -08:00
if ( compile_unit . die . getAttrSecOffset ( DW . AT_ranges ) ) | ranges_offset | {
var base_address : usize = 0 ;
if ( st . debug_ranges ) | debug_ranges | {
2018-01-07 13:51:46 -08:00
try st . self_exe_file . seekTo ( debug_ranges . offset + ranges_offset ) ;
2017-12-12 08:33:14 -08:00
while ( true ) {
2018-01-07 13:51:46 -08:00
const begin_addr = try in_stream . readIntLe ( usize ) ;
const end_addr = try in_stream . readIntLe ( usize ) ;
2017-12-12 08:33:14 -08:00
if ( begin_addr = = 0 and end_addr = = 0 ) {
break ;
}
if ( begin_addr = = @maxValue ( usize ) ) {
base_address = begin_addr ;
continue ;
}
if ( target_address > = begin_addr and target_address < end_addr ) {
return compile_unit ;
}
}
}
} else | err | {
2018-04-30 17:35:54 -07:00
if ( err ! = error . MissingDebugInfo ) return err ;
2017-12-12 08:33:14 -08:00
continue ;
}
2016-09-20 13:10:34 -07:00
}
2017-12-12 08:33:14 -08:00
return error . MissingDebugInfo ;
2016-09-20 13:10:34 -07:00
}
2018-02-05 04:38:24 -08:00
fn readInitialLength ( comptime E : type , in_stream : & io . InStream ( E ) , is_64 : & bool ) ! u64 {
2018-01-07 13:51:46 -08:00
const first_32_bits = try in_stream . readIntLe ( u32 ) ;
2018-04-30 17:35:54 -07:00
is_64 . * = ( first_32_bits = = 0xffffffff ) ;
if ( is_64 . * ) {
2017-12-21 21:50:30 -08:00
return in_stream . readIntLe ( u64 ) ;
2016-09-20 13:10:34 -07:00
} else {
if ( first_32_bits > = 0xfffffff0 ) return error . InvalidDebugInfo ;
2017-12-21 21:50:30 -08:00
return u64 ( first_32_bits ) ;
}
2016-09-20 13:10:34 -07:00
}
2018-02-05 04:38:24 -08:00
fn readULeb128 ( in_stream : var ) ! u64 {
2016-09-20 13:10:34 -07:00
var result : u64 = 0 ;
bit shifting safety
* add u3, u4, u5, u6, u7 and i3, i4, i5, i6, i7
* shift operations shift amount parameter type is
integer with log2 bit width of other param
- This enforces not violating undefined behavior on
shift amount >= bit width with the type system
* clean up math.log, math.ln, math.log2, math.log10
closes #403
2017-08-18 22:32:15 -07:00
var shift : usize = 0 ;
2016-09-20 13:10:34 -07:00
while ( true ) {
2018-01-07 13:51:46 -08:00
const byte = try in_stream . readByte ( ) ;
bit shifting safety
* add u3, u4, u5, u6, u7 and i3, i4, i5, i6, i7
* shift operations shift amount parameter type is
integer with log2 bit width of other param
- This enforces not violating undefined behavior on
shift amount >= bit width with the type system
* clean up math.log, math.ln, math.log2, math.log10
closes #403
2017-08-18 22:32:15 -07:00
2016-09-20 13:10:34 -07:00
var operand : u64 = undefined ;
2018-04-30 17:35:54 -07:00
if ( @shlWithOverflow ( u64 , byte & 0b01111111 , u6 ( shift ) , & operand ) ) return error . InvalidDebugInfo ;
2016-09-20 13:10:34 -07:00
result | = operand ;
2018-04-30 17:35:54 -07:00
if ( ( byte & 0b10000000 ) = = 0 ) return result ;
2016-09-20 13:10:34 -07:00
shift + = 7 ;
}
}
2018-02-05 14:42:13 -08:00
fn readILeb128 ( in_stream : var ) ! i64 {
2016-09-20 13:10:34 -07:00
var result : i64 = 0 ;
bit shifting safety
* add u3, u4, u5, u6, u7 and i3, i4, i5, i6, i7
* shift operations shift amount parameter type is
integer with log2 bit width of other param
- This enforces not violating undefined behavior on
shift amount >= bit width with the type system
* clean up math.log, math.ln, math.log2, math.log10
closes #403
2017-08-18 22:32:15 -07:00
var shift : usize = 0 ;
2016-09-20 13:10:34 -07:00
while ( true ) {
2018-01-07 13:51:46 -08:00
const byte = try in_stream . readByte ( ) ;
bit shifting safety
* add u3, u4, u5, u6, u7 and i3, i4, i5, i6, i7
* shift operations shift amount parameter type is
integer with log2 bit width of other param
- This enforces not violating undefined behavior on
shift amount >= bit width with the type system
* clean up math.log, math.ln, math.log2, math.log10
closes #403
2017-08-18 22:32:15 -07:00
2016-09-20 13:10:34 -07:00
var operand : i64 = undefined ;
2018-04-30 17:35:54 -07:00
if ( @shlWithOverflow ( i64 , byte & 0b01111111 , u6 ( shift ) , & operand ) ) return error . InvalidDebugInfo ;
2016-09-20 13:10:34 -07:00
result | = operand ;
shift + = 7 ;
if ( ( byte & 0b10000000 ) = = 0 ) {
2018-04-30 17:35:54 -07:00
if ( shift < @sizeOf ( i64 ) * 8 and ( byte & 0b01000000 ) ! = 0 ) result | = - ( i64 ( 1 ) < < u6 ( shift ) ) ;
2016-09-20 13:10:34 -07:00
return result ;
}
2016-08-23 07:10:09 -07:00
}
}
2018-04-15 17:15:19 -07:00
/// This should only be used in temporary test programs.
2017-12-11 14:27:31 -08:00
pub const global_allocator = & global_fixed_allocator . allocator ;
2018-02-11 23:27:02 -08:00
var global_fixed_allocator = std . heap . FixedBufferAllocator . init ( global_allocator_mem [ 0 . . ] ) ;
2017-12-11 14:27:31 -08:00
var global_allocator_mem : [ 100 * 1024 ] u8 = undefined ;
2018-04-15 17:15:19 -07:00
// TODO make thread safe
var debug_info_allocator : ? & mem . Allocator = null ;
var debug_info_direct_allocator : std . heap . DirectAllocator = undefined ;
var debug_info_arena_allocator : std . heap . ArenaAllocator = undefined ;
fn getDebugInfoAllocator ( ) & mem . Allocator {
if ( debug_info_allocator ) | a | return a ;
debug_info_direct_allocator = std . heap . DirectAllocator . init ( ) ;
debug_info_arena_allocator = std . heap . ArenaAllocator . init ( & debug_info_direct_allocator . allocator ) ;
debug_info_allocator = & debug_info_arena_allocator . allocator ;
return & debug_info_arena_allocator . allocator ;
}