카테고리 없음

[디버깅] 함수 단계 / 추적 단계에 대한 lldb 명령 : 다음 함수 호출까지 또는 현재 함수가 반환 될 때까지 계속

행복을전해요 2021. 1. 22. 04:27

다음은 "단계 기능"명령을 추가하는 lldb 대상 파이썬 스크립트입니다. 호출 스택 구조가 변경 될 때마다 명령이 중지됩니다.

step_func.py

import lldb

def step_func(debugger, command, result, internal_dict):
    thread = debugger.GetSelectedTarget().GetProcess().GetSelectedThread()
    
        start_num_frames = thread.GetNumFrames()
            if start_num_frames == 0:
                    return
                    
                        while True:
                                thread.StepInstruction(0)
                                        if thread.GetNumFrames() != start_num_frames:
                                                    stream = lldb.SBStream()
                                                                thread.GetStatus(stream)
                                                                            description = stream.GetData()
                                                                            
                                                                                        print >>result, "Call stack depth changed %d -> %d" % (start_num_frames, thread.GetNumFrames())
                                                                                                    print >>result, description,
                                                                                                    
                                                                                                                break
                                                                                                                
                                                                                                                def __lldb_init_module (debugger, dict):
                                                                                                                    debugger.HandleCommand('command script add -f %s.step_func sf' % __name__)
                                                                                                                    

사용 예 :

$ lldb /bin/ls
Current executable set to '/bin/ls' (x86_64).
(lldb) command script import step_func                                                                                                                                                                             (lldb) process launch --stop-at-entry                                                                                                                                                                              Process 12944 launched: '/bin/ls' (x86_64)
Process 12944 stopped
* thread #1: tid = 0x438b0, 0x00007fff5fc01028 dyld`_dyld_start, stop reason = signal SIGSTOP
    frame #0: 0x00007fff5fc01028 dyld`_dyld_start
    dyld`_dyld_start:
    -> 0x7fff5fc01028:  popq   %rdi
       0x7fff5fc01029:  pushq  $0
          0x7fff5fc0102b:  movq   %rsp, %rbp
             0x7fff5fc0102e:  andq   $-16, %rsp
             (lldb) sf
             Call stack depth changed 1 -> 2
             * thread #1: tid = 0x438b0, 0x00007fff5fc0109e dyld`dyldbootstrap::start(macho_header const*, int, char const**, long, macho_header const*, unsigned long*), stop reason = instruction step into
                 frame #0: 0x00007fff5fc0109e dyld`dyldbootstrap::start(macho_header const*, int, char const**, long, macho_header const*, unsigned long*)
                 dyld`dyldbootstrap::start(macho_header const*, int, char const**, long, macho_header const*, unsigned long*):
                 -> 0x7fff5fc0109e:  pushq  %rbp
                    0x7fff5fc0109f:  movq   %rsp, %rbp
                       0x7fff5fc010a2:  pushq  %r15
                          0x7fff5fc010a4:  pushq  %r14
                          (lldb) 
                          Call stack depth changed 2 -> 3
                          * thread #1: tid = 0x438b0, 0x00007fff5fc22f9b dyld`mach_init, stop reason = instruction step into
                              frame #0: 0x00007fff5fc22f9b dyld`mach_init
                              dyld`mach_init:
                              -> 0x7fff5fc22f9b:  pushq  %rbp
                                 0x7fff5fc22f9c:  movq   %rsp, %rbp
                                    0x7fff5fc22f9f:  movb   326075(%rip), %al         ; mach_init.mach_init_inited
                                       0x7fff5fc22fa5:  testb  %al, %al
                                       (lldb) 
                                       Call stack depth changed 3 -> 4
                                       * thread #1: tid = 0x438b0, 0x00007fff5fc22fb9 dyld`mach_init_doit, stop reason = instruction step into
                                           frame #0: 0x00007fff5fc22fb9 dyld`mach_init_doit
                                           dyld`mach_init_doit:
                                           -> 0x7fff5fc22fb9:  pushq  %rbp
                                              0x7fff5fc22fba:  movq   %rsp, %rbp
                                                 0x7fff5fc22fbd:  callq  0x7fff5fc23210            ; task_self_trap
                                                    0x7fff5fc22fc2:  movl   %eax, 69740(%rip)         ; mach_task_self_
                                                    (lldb) 
                                                    Call stack depth changed 4 -> 5
                                                    * thread #1: tid = 0x438b0, 0x00007fff5fc23210 dyld`task_self_trap, stop reason = instruction step into
                                                        frame #0: 0x00007fff5fc23210 dyld`task_self_trap
                                                        dyld`task_self_trap:
                                                        -> 0x7fff5fc23210:  movq   %rcx, %r10
                                                           0x7fff5fc23213:  movl   $16777244, %eax
                                                              0x7fff5fc23218:  syscall 
                                                                 0x7fff5fc2321a:  ret    
                                                                 (lldb) 
                                                                 Call stack depth changed 5 -> 4
                                                                 * thread #1: tid = 0x438b0, 0x00007fff5fc22fc2 dyld`mach_init_doit + 9, stop reason = instruction step into
                                                                     frame #0: 0x00007fff5fc22fc2 dyld`mach_init_doit + 9
                                                                     dyld`mach_init_doit + 9:
                                                                     -> 0x7fff5fc22fc2:  movl   %eax, 69740(%rip)         ; mach_task_self_
                                                                        0x7fff5fc22fc8:  callq  0x7fff5fc231f8            ; mach_reply_port
                                                                           0x7fff5fc22fcd:  leaq   69724(%rip), %rcx         ; _task_reply_port
                                                                              0x7fff5fc22fd4:  movl   %eax, (%rcx)
                                                                              (lldb) 
                                                                              
-------------------

LLDB에서 현재 어셈블리 수준 함수가 남을 때까지 어떻게 할 수 있습니까? (까지 수행 할 수있는 소스 코드가 없습니다). 스택 프레임 구조가 변경 될 때까지 즉, 함수가 호출되거나 현재 함수가 반환 될 때까지 step-inst를 수행하는 자동화 된 방법을 찾고 있습니다.

내가 확인했듯이 현재 LLVM 버전에는 이러한 스테핑 모드가 없으며 함수 반환 또는 모든 함수 호출에서 중지됩니다.

기능 종료시 중지하는 "마침"( "스레드 스텝 아웃")이 있습니다. 호출 된 함수를 방문하지 않고 단일 스테핑에 대한 "nexti"( "thread step-inst-over")도 있습니다.

지원되는 모든 모드 목록이있는 LLDB 소스가 있습니다. http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Commands/CommandObjectThread.cpp?revision=194531&view=markup- 파일의 맨 끝 확인 명령 목록-inCommandObjectMultiwordThread::CommandObjectMultiwordThread

나는 그것이 모두 스테핑 돌아올 때까지 (구현하는 구성 요소가되기 때문에 LLDB에 필요한 스텝 모드를 쉽게 구현할 될 수 있다고 생각 CommandObjectThreadStepWithTypeAndScope (... eStepTypeOut, eStepScopeSource)=> QueueThreadPlanForStepOut(용) 및 함수 호출 검출기 CommandObjectThreadStepWithTypeAndScope (...eStepTypeTraceOver,eStepScopeInstruction)=>가 QueueThreadPlanForStepSingleInstruction). Target / ThreadPlanStepInstruction.cpp 의 코드 가 도움이 될 것입니다.

-------------------

단계를 수행할지 아니면 기능이 종료 될 때까지 계속할지 명확하지 않습니다. 후자의 경우 스택에서 반환 주소 위치를 파악할 수 있으면 읽기 감시를 배치 할 수 있습니다. RET결국 현재의 기능을 떠나 명령은 반환 주소를 찾아 그 위치를 읽을해야합니다.

유효한 프레임 포인터가 있으면 반송 주소 위치 찾기를 자동화 할 수 있습니다. 다음은 사용하는 예입니다 gdb.

Breakpoint 1, 0x080483e6 in foo ()
(gdb) disas foo
Dump of assembler code for function foo:
   0x080483e3 <+0>:     push   %ebp
      0x080483e4 <+1>:     mov    %esp,%ebp
      => 0x080483e6 <+3>:     nop
         0x080483e7 <+4>:     xor    %eax,%eax
            0x080483e9 <+6>:     mov    %ebp,%esp
               0x080483eb <+8>:     pop    %ebp
                  0x080483ec <+9>:     ret
                     0x080483ed <+10>:    nop
                        0x080483ee <+11>:    nop
                           0x080483ef <+12>:    nop
                           End of assembler dump.
                           (gdb) p/a $ebp+4
                           $1 = 0xffffd9f8
                           (gdb) rwatch *(int*)0xffffd9f8
                           Hardware read watchpoint 2: *(int*)0xffffd9f8
                           (gdb) c
                           Continuing.
                           Hardware read watchpoint 2: *(int*)0xffffd9f8
                           
                           Value = 134513633
                           0x080483e1 in main ()
                           (gdb) disas main
                           Dump of assembler code for function main:
                              0x080483dc <+0>:     call   0x80483e3 <foo>
                              => 0x080483e1 <+5>:     nop
                                 0x080483e2 <+6>:     ret
                                 End of assembler dump.
                                 

반환 주소가 있으면 함수가 재진입되지 않는 경우 매일 임시 중단 점을 사용할 수도 있습니다.

(gdb) x/a $ebp+4
0xffffd9f8:     0x80483e1 <main+5>
(gdb) tbreak *0x80483e1
Temporary breakpoint 3 at 0x80483e1
(gdb) c
Continuing.

Temporary breakpoint 3, 0x080483e1 in main ()

프레임 포인터가 없으면 함수 시작 부분에서 반환 주소를 쉽게 찾을 수 있습니다. 그렇지 않으면 함수 입력 이후 스택 포인터가 어떻게 변경되었는지 확인하기 위해 리버스 엔지니어링을 수행해야합니다.



출처
https://stackoverflow.com/questions/22009815