6 def _GetIntegerDataFromTask(u_ptr
, task_abi
):
9 u_ptr : int - pointer in user memory
10 task_abi : int - what kind of user program is running
12 int - value stored at specified u_ptr.
14 if kern
.arch
!= "x86_64":
15 raise ValueError("This function does not work for non x86_64 arch")
17 return unsigned(dereference(kern
.GetValueFromAddress(u_ptr
, 'uint64_t *')))
19 return unsigned(dereference(kern
.GetValueFromAddress(u_ptr
, 'uint32_t *')))
21 def GetBinaryNameForPC(pc_val
, user_lib_info
= None):
22 """ find the binary in user_lib_info that the passed pc_val falls in range of.
24 pc_val : int - integer form of the pc address
25 user_lib_info: [] of [] which hold start, end, binary name
27 str - Name of binary or "unknown" if not found.
33 for info
in user_lib_info
:
34 if pc_val
>= info
[0] and pc_val
<= info
[1]:
35 matches
.append((pc_val
- info
[0], info
[2]))
38 retval
= matches
[0][1]
41 def ShowX86UserStack(thread
, user_lib_info
= None):
42 """ Display user space stack frame and pc addresses.
44 thread: obj referencing thread value
48 iss
= Cast(thread
.machine
.iss
, 'x86_saved_state_t *')
52 user_abi_ret_offset
= 0
54 debuglog("User process is 64 bit")
55 user_ip
= iss
.uss
.ss_64
.isf
.rip
56 user_frame
= iss
.uss
.ss_64
.rbp
57 user_abi_ret_offset
= 8
59 debuglog("user process is 32 bit")
60 user_ip
= iss
.uss
.ss_32
.eip
61 user_frame
= iss
.uss
.ss_32
.ebp
62 user_abi_ret_offset
= 4
65 print "This activation does not appear to have a valid user context."
69 cur_frame
= user_frame
70 debuglog("ip= 0x%x , fr = 0x%x " % (cur_ip
, cur_frame
))
71 kdp_pmap_addr
= unsigned(addressof(kern
.globals.kdp_pmap
))
72 if not WriteInt64ToMemoryAddress(unsigned(thread
.task
.map.pmap
), kdp_pmap_addr
):
73 print "Failed to write in kdp_pmap = 0x{0:0>16x} value.".format(thread
.task
.map.pmap
)
75 debuglog("newpmap = 0x{:x}".format(kern
.globals.kdp_pmap
))
77 frameformat
= "{0:d} FP: 0x{1:x} PC: 0x{2:x}"
78 if user_lib_info
is not None:
79 frameformat
= "{0:d} {3: <30s} 0x{2:x}"
80 print frameformat
.format(0, cur_frame
, cur_ip
, GetBinaryNameForPC(cur_ip
, user_lib_info
))
82 print kern
.Symbolicate(cur_ip
)
83 tmp_frame
= unsigned(cur_frame
)
84 prev_frame
= _GetIntegerDataFromTask(tmp_frame
, abi
)
85 prev_ip
= _GetIntegerDataFromTask(tmp_frame
+ user_abi_ret_offset
, abi
)
87 while prev_frame
and prev_frame
!= 0x0000000800000008:
88 print frameformat
.format(frameno
, prev_frame
, prev_ip
, GetBinaryNameForPC(prev_ip
, user_lib_info
))
89 print kern
.Symbolicate(prev_ip
)
90 prev_ip
= _GetIntegerDataFromTask(prev_frame
+ user_abi_ret_offset
, abi
)
91 prev_frame
= _GetIntegerDataFromTask(prev_frame
, abi
)
93 if not WriteInt64ToMemoryAddress(0, kdp_pmap_addr
):
94 print "Failed to write in kdp_pmap = 0"
98 def _PrintARMUserStack(task
, cur_pc
, cur_fp
, framesize
, frametype
, frameformat
, user_lib_info
=None):
100 "No valid user context for this activation."
103 print frameformat
.format(frameno
, cur_fp
, cur_pc
, GetBinaryNameForPC(cur_pc
, user_lib_info
))
105 frameno
= frameno
+ 1
106 frame
= GetUserDataAsString(task
, cur_fp
, framesize
)
107 cur_fp
= _ExtractDataFromString(frame
, 0, frametype
)
108 cur_pc
= _ExtractDataFromString(frame
, (framesize
/ 2), frametype
)
111 print frameformat
.format(frameno
, cur_fp
, cur_pc
, GetBinaryNameForPC(cur_pc
, user_lib_info
))
113 def ShowARMUserStack(thread
, user_lib_info
= None):
114 cur_pc
= unsigned(thread
.machine
.PcbData
.pc
)
115 cur_fp
= unsigned(thread
.machine
.PcbData
.r
[7])
116 frameformat
= "{0:>2d} FP: 0x{1:x} PC: 0x{2:x}"
117 if user_lib_info
is not None:
118 frameformat
= "{0:>2d} {3: <30s} 0x{2:0>8x}"
120 frametype
= "uint32_t"
121 _PrintARMUserStack(thread
.task
, cur_pc
, cur_fp
, framesize
, frametype
, frameformat
, user_lib_info
=user_lib_info
)
124 @lldb_command('showthreaduserstack')
125 def ShowThreadUserStack(cmd_args
=None):
126 """ Show user stack for a given thread.
127 Syntax: (lldb) showthreaduserstack <thread_ptr>
130 raise ArgumentError("Insufficient arguments")
132 thread
= kern
.GetValueFromAddress(ArgumentStringToInt(cmd_args
[0]), 'thread *')
133 if kern
.arch
== "x86_64":
134 ShowX86UserStack(thread
)
135 elif kern
.arch
== "arm":
136 ShowARMUserStack(thread
)
139 @lldb_command('showtaskuserstacks')
140 def ShowTaskUserStacks(cmd_args
=None):
141 """ Print out the user stack for each thread in a task, followed by the user libraries.
142 Syntax: (lldb) showtaskuserstacks <task_t>
143 The format is compatible with CrashTracer. You can also use the speedtracer plugin as follows
144 (lldb) showtaskuserstacks <task_t> -p speedtracer
146 Note: the address ranges are approximations. Also the list may not be completely accurate. This command expects memory read failures
147 and hence will skip a library if unable to read information. Please use your good judgement and not take the output as accurate
150 raise ArgumentError("Insufficient arguments")
152 task
= kern
.GetValueFromAddress(cmd_args
[0], 'task *')
153 #print GetTaskSummary.header + " " + GetProcSummary.header
154 pval
= Cast(task
.bsd_info
, 'proc *')
155 #print GetTaskSummary(task) + " " + GetProcSummary(pval) + "\n \n"
156 crash_report_format_string
= """\
159 Identifier: {pname: <30s}
161 Code Type: {parch: <20s}
162 Parent Process: {ppname: >20s}[{ppid:d}]
164 Date/Time: {timest:s}.000 -0800
165 OS Version: {osversion: <20s}
172 Application Specific Information:
173 Synthetic crash log generated from Kernel userstacks
176 user_lib_rex
= re
.compile("([0-9a-fx]+)\s-\s([0-9a-fx]+)\s+(.*?)\s", re
.IGNORECASE|re
.MULTILINE
)
177 from datetime
import datetime
178 ts
= datetime
.fromtimestamp(int(pval
.p_start
.tv_sec
))
179 date_string
= ts
.strftime('%Y-%m-%d %H:%M:%S')
181 if pval
.p_flag
& 0x4 :
185 if kern
.arch
== "x86_64" or kern
.arch
== "i386":
186 osversion
= "Mac OS X 10.8"
187 parch_s
= "I386 (32 bit)"
189 parch_s
= "X86-64 (Native)"
193 osversion
+= " ({:s})".format(kern
.globals.osversion
)
194 print crash_report_format_string
.format(pid
= pval
.p_pid
,
198 ppname
= GetProcNameForPid(pval
.p_ppid
),
199 timest
= date_string
,
201 osversion
= osversion
204 print "Binary Images:"
205 ShowTaskUserLibraries([hex(task
)])
206 usertask_lib_info
= [] # will host [startaddr, endaddr, lib_name] entries
207 for entry
in ShowTaskUserLibraries
.found_images
:
208 #print "processing line %s" % line
209 arr
= user_lib_rex
.findall(entry
[3])
213 usertask_lib_info
.append([int(arr
[0][0],16), int(arr
[0][1],16), str(arr
[0][2]).strip()])
215 printthread_user_stack_ptr
= ShowX86UserStack
216 if kern
.arch
== "arm":
217 printthread_user_stack_ptr
= ShowARMUserStack
220 for thval
in IterateQueue(task
.threads
, 'thread *', 'task_threads'):
221 print "\nThread {0:d} name:0x{1:x}\nThread {0:d}:".format(counter
, thval
)
224 printthread_user_stack_ptr(thval
, usertask_lib_info
)
225 except Exception as exc_err
:
226 print "Failed to show user stack for thread 0x{0:x}".format(thval
)
230 print "Enable debugging ('(lldb) xnudebug debug') to see detailed trace."
234 def GetUserDataAsString(task
, addr
, size
):
235 """ Get data from task's address space as a string of bytes
237 task: task object from which to extract information
238 addr: int - start address to get data from.
239 size: int - no of bytes to read.
241 str - a stream of bytes. Empty string if read fails.
244 if GetConnectionProtocol() == "kdp":
245 kdp_pmap_addr
= unsigned(addressof(kern
.globals.kdp_pmap
))
246 if not WriteInt64ToMemoryAddress(unsigned(task
.map.pmap
), kdp_pmap_addr
):
247 debuglog("Failed to write in kdp_pmap from GetUserDataAsString.")
249 content
= LazyTarget
.GetProcess().ReadMemory(addr
, size
, err
)
250 if not err
.Success():
251 debuglog("Failed to read process memory. Error: " + err
.description
)
253 if not WriteInt64ToMemoryAddress(0, kdp_pmap_addr
):
254 debuglog("Failed to reset in kdp_pmap from GetUserDataAsString.")
256 elif kern
.arch
in ['arm'] and long(size
) < (2 * kern
.globals.page_size
):
257 # Without the benefit of a KDP stub on the target, try to
258 # find the user task's physical mapping and memcpy the data.
259 # If it straddles a page boundary, copy in two passes
260 range1_addr
= long(addr
)
261 range1_size
= long(size
)
262 if kern
.StraddlesPage(range1_addr
, range1_size
):
263 range2_addr
= long(kern
.TruncPage(range1_addr
+ range1_size
))
264 range2_size
= long(range1_addr
+ range1_size
- range2_addr
)
265 range1_size
= long(range2_addr
- range1_addr
)
271 paddr_range1
= PmapWalk(task
.map.pmap
, range1_addr
, vSILENT
)
273 debuglog("Not mapped task 0x{:x} address 0x{:x}".format(task
, addr
))
276 range1_in_kva
= kern
.PhysToKernelVirt(paddr_range1
)
277 content
= LazyTarget
.GetProcess().ReadMemory(range1_in_kva
, range1_size
, err
)
278 if not err
.Success():
279 raise RuntimeError("Failed to read process memory. Error: " + err
.description
)
282 paddr_range2
= PmapWalk(task
.map.pmap
, range2_addr
, vSILENT
)
284 debuglog("Not mapped task 0x{:x} address 0x{:x}".format(task
, addr
))
286 range2_in_kva
= kern
.PhysToKernelVirt(paddr_range2
)
287 content
+= LazyTarget
.GetProcess().ReadMemory(range1_in_kva
, range1_size
, err
)
288 if not err
.Success():
289 raise RuntimeError("Failed to read process memory. Error: " + err
.description
)
291 raise NotImplementedError("GetUserDataAsString does not support this configuration")
295 def _ExtractDataFromString(strdata
, offset
, data_type
, length
=0):
296 """ Extract specific data from string buffer
298 strdata: str - string data give from GetUserDataAsString
299 offset: int - 0 based offset into the data.
300 data_type: str - defines what type to be read as. Supported values are:
301 'uint64_t', 'uint32_t', 'string'
302 length: int - used when data_type=='string'
304 None - if extraction failed.
305 obj - based on what is requested in data_type
308 if data_type
== 'uint64_t':
311 elif data_type
== "uint32_t":
315 unpack_str
= "%ds" % length
317 data_len
= len(strdata
)
318 if offset
> data_len
or (offset
+ length
) > data_len
or offset
< 0:
319 debuglog("Invalid arguments to _ExtractDataFromString.")
321 return struct
.unpack(unpack_str
, strdata
[offset
:(offset
+ length
)])[0]
323 def GetPathForImage(task
, path_address
):
324 """ Maps 32 bytes at a time and packs as string
326 task: obj - referencing task to read data from
327 path_address: int - address where the image path is stored
329 str - string path of the file. "" if failed to read.
334 if path_address
== 0:
338 path_str_data
= GetUserDataAsString(task
, path_address
, 32)
339 if len(path_str_data
) == 0:
343 if ord(path_str_data
[i
]):
344 retval
+= path_str_data
[i
]
354 def GetImageInfo(task
, mh_image_address
, mh_path_address
, approx_end_address
=None):
355 """ Print user library informaiton.
357 task : obj referencing the task for which Image info printed
358 mh_image_address : int - address which has image info
359 mh_path_address : int - address which holds path name string
360 approx_end_address: int - address which lldbmacros think is end address.
362 str - string representing image info. "" if failure to read data.
364 if approx_end_address
:
365 image_end_load_address
= int(approx_end_address
) -1
367 image_end_load_address
= int(mh_image_address
) + 0xffffffff
369 print_format
= "0x{0:x} - 0x{1:x} {2: <50s} (??? - ???) <{3: <36s}> {4: <50s}"
370 # 32 bytes enough for mach_header/mach_header_64
371 mh_data
= GetUserDataAsString(task
, mh_image_address
, 32)
372 if len(mh_data
) == 0:
373 debuglog("unable to get userdata for task 0x{:x} img_addr 0x{:x} path_address 0x{:x}".format(
374 task
, mh_image_address
, mh_path_address
))
376 mh_magic
= _ExtractDataFromString(mh_data
, (4 * 0), "uint32_t")
377 mh_cputype
= _ExtractDataFromString(mh_data
,(4 * 1), "uint32_t")
378 mh_cpusubtype
= _ExtractDataFromString(mh_data
,(4 * 2), "uint32_t")
379 mh_filetype
= _ExtractDataFromString(mh_data
,(4 * 3), "uint32_t")
380 mh_ncmds
= _ExtractDataFromString(mh_data
,(4 * 4), "uint32_t")
381 mh_sizeofcmds
= _ExtractDataFromString(mh_data
,(4 * 5), "uint32_t")
382 mh_flags
= _ExtractDataFromString(mh_data
,(4 * 6), "uint32_t")
384 if mh_magic
== 0xfeedfacf:
386 lc_address
= mh_image_address
+ 32
389 lc_address
= mh_image_address
+ 28
393 found_uuid_data
= False
395 while lc_idx
< mh_ncmds
:
396 # 24 bytes is the size of uuid_command
397 lcmd_data
= GetUserDataAsString(task
, lc_address
, 24)
398 lc_cmd
= _ExtractDataFromString(lcmd_data
, 4 * 0, "uint32_t")
399 lc_cmd_size
= _ExtractDataFromString(lcmd_data
, 4 * 1, "uint32_t")
400 lc_data
= _ExtractDataFromString(lcmd_data
, 4*2, "string", 16)
406 # need to print the uuid now.
407 uuid_data
= [ord(x
) for x
in lc_data
]
408 found_uuid_data
= True
409 uuid_out_string
= "{a[0]:02X}{a[1]:02X}{a[2]:02X}{a[3]:02X}-{a[4]:02X}{a[5]:02X}-{a[6]:02X}{a[7]:02X}-{a[8]:02X}{a[9]:02X}-{a[10]:02X}{a[11]:02X}{a[12]:02X}{a[13]:02X}{a[14]:02X}{a[15]:02X}".format(a
=uuid_data
)
410 #also print image path
411 path_out_string
= GetPathForImage(task
, mh_path_address
)
412 path_base_name
= path_out_string
.split("/")[-1]
413 retval
= print_format
.format(mh_image_address
, image_end_load_address
, path_base_name
, uuid_out_string
, path_out_string
)
415 ShowTaskUserLibraries
.exec_load_path
= lc_address
+ _ExtractDataFromString(lcmd_data
, 4*2, "uint32_t")
416 lc_address
= lc_address
+ lc_cmd_size
419 if not found_uuid_data
:
420 path_out_string
= GetPathForImage(task
, mh_path_address
)
421 path_base_name
= path_out_string
.split("/")[-1]
424 retval
= print_format
.format(mh_image_address
, image_end_load_address
, path_base_name
, uuid_out_string
, path_out_string
)
427 @static_var("found_images", []) # holds entries of format (startaddr, endaddr, image_path_addr, infostring)
428 @static_var("exec_load_path", 0)
429 @lldb_command("showtaskuserlibraries")
430 def ShowTaskUserLibraries(cmd_args
=None):
431 """ Show binary images known by dyld in target task
432 For a given user task, inspect the dyld shared library state and print information about all Mach-O images.
433 Syntax: (lldb)showtaskuserlibraries <task_t>
434 Note: the address ranges are approximations. Also the list may not be completely accurate. This command expects memory read failures
435 and hence will skip a library if unable to read information. Please use your good judgement and not take the output as accurate
438 raise ArgumentError("Insufficient arguments")
440 #reset the found_images array
441 ShowTaskUserLibraries
.found_images
= []
443 task
= kern
.GetValueFromAddress(cmd_args
[0], 'task_t')
444 is_task_64
= int(task
.t_flags
) & 0x1
445 dyld_all_image_infos_address
= unsigned(task
.all_image_info_addr
)
447 if dyld_all_image_infos_address
== 0:
448 print "No dyld shared library information available for task"
450 vers_info_data
= GetUserDataAsString(task
, dyld_all_image_infos_address
, 112)
451 version
= _ExtractDataFromString(vers_info_data
, cur_data_offset
, "uint32_t")
454 print "Unknown dyld all_image_infos version number %d" % version
455 image_info_count
= _ExtractDataFromString(vers_info_data
, cur_data_offset
, "uint32_t")
456 ShowTaskUserLibraries
.exec_load_path
= 0
459 image_info_array_address
= _ExtractDataFromString(vers_info_data
, 8, "uint64_t")
460 dyld_load_address
= _ExtractDataFromString(vers_info_data
, 8*4, "uint64_t")
461 dyld_all_image_infos_address_from_struct
= _ExtractDataFromString(vers_info_data
, 8*13, "uint64_t")
464 image_info_array_address
= _ExtractDataFromString(vers_info_data
, 4*2, "uint32_t")
465 dyld_load_address
= _ExtractDataFromString(vers_info_data
, 4*5, "uint32_t")
466 dyld_all_image_infos_address_from_struct
= _ExtractDataFromString(vers_info_data
, 4*14, "uint32_t")
467 # Account for ASLR slide before dyld can fix the structure
468 dyld_load_address
= dyld_load_address
+ (dyld_all_image_infos_address
- dyld_all_image_infos_address_from_struct
)
472 while i
< image_info_count
:
473 image_info_address
= image_info_array_address
+ i
* image_info_size
474 n_im_info_addr
= None
477 img_data
= GetUserDataAsString(task
, image_info_address
, image_info_size
)
479 debuglog("Failed to read user data for task 0x{:x} addr 0x{:x}, exception {:s}".format(task
, image_info_address
, str(e
)))
482 image_info_addr
= _ExtractDataFromString(img_data
, 0, "uint64_t")
483 image_info_path
= _ExtractDataFromString(img_data
, 8, "uint64_t")
485 image_info_addr
= _ExtractDataFromString(img_data
, 0, "uint32_t")
486 image_info_path
= _ExtractDataFromString(img_data
, 4, "uint32_t")
489 image_info_list
.append((image_info_addr
, image_info_path
))
492 image_info_list
.sort()
493 num_images_found
= len(image_info_list
)
495 for ii
in range(num_images_found
):
496 n_im_info_addr
= dyld_load_address
497 if ii
+ 1 < num_images_found
:
498 n_im_info_addr
= image_info_list
[ii
+1][0]
500 image_info_addr
= image_info_list
[ii
][0]
501 image_info_path
= image_info_list
[ii
][1]
503 image_print_s
= GetImageInfo(task
, image_info_addr
, image_info_path
, approx_end_address
=n_im_info_addr
)
504 if len(image_print_s
) > 0:
506 ShowTaskUserLibraries
.found_images
.append((image_info_addr
, n_im_info_addr
, image_info_path
, image_print_s
))
508 debuglog("Failed to print image info for task 0x{:x} image_info 0x{:x}".format(task
, image_info_addr
))
513 # load_path might get set when the main executable is processed.
514 if ShowTaskUserLibraries
.exec_load_path
!= 0:
515 image_print_s
= GetImageInfo(task
, dyld_load_address
, ShowTaskUserLibraries
.exec_load_path
)
516 if len(image_print_s
) > 0:
518 ShowTaskUserLibraries
.found_images
.append((dyld_load_address
, dyld_load_address
+ 0xffffffff,
519 ShowTaskUserLibraries
.exec_load_path
, image_print_s
))
521 debuglog("Failed to print image for main executable for task 0x{:x} dyld_load_addr 0x{:x}".format(task
, dyld_load_address
))