Last active
March 6, 2024 00:02
-
-
Save briandw/a07a9974db06fd148ff43ae329a7825d to your computer and use it in GitHub Desktop.
Compile a c program with Clang, debug it with LLDB and trap at a function. Why doesn't the line number match?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from pathlib import Path | |
import lldb | |
import os | |
from lldb import SBError, SBThread, SBFrame | |
def main(): | |
c_code = """#include <stdio.h> | |
struct recursive_struct; //forward declaration. | |
//function pointer type | |
typedef void (*function_pointer_type)(struct recursive_struct *, int); | |
//Struct type with a pointer to itself | |
struct recursive_struct | |
{ | |
char **data; | |
int data_size; | |
struct recursive_struct *next; | |
// function pointer type | |
function_pointer_type function_pointer; | |
}; | |
typedef struct recursive_struct recursive_struct_type; | |
void init_bar(recursive_struct_type *b, char **data, int data_size){ | |
b->data = data; | |
b->data_size = data_size; | |
} | |
void print_bar(recursive_struct_type *b){ | |
printf("%d ", b->data_size); | |
} | |
int main(void){ | |
char *data[] = {"Hello", "World!", NULL}; | |
recursive_struct_type b; | |
init_bar(&b, data, 2); | |
print_bar(&b); | |
printf("Data size: %d\\n", b.data_size); | |
return 0; | |
} | |
""" | |
#Compile the main code | |
main_file = Path("main.c") | |
main_file.write_text(c_code) | |
os.system('clang -g -O0 main.c -o main') | |
debugger = lldb.SBDebugger.Create() | |
debugger.SetAsync(False) | |
target = debugger.CreateTarget("main") | |
function_name_breakpoint = "init_bar" | |
break_point_main = target.BreakpointCreateByName(function_name_breakpoint) | |
launch_info = lldb.SBLaunchInfo(["./main"]) | |
launch_info.SetWorkingDirectory(os.getcwd()) | |
error = SBError() | |
process = target.Launch(launch_info, error) | |
assert target.IsValid() | |
assert process.IsValid() | |
assert break_point_main.IsValid() | |
state = process.GetState() | |
assert state == lldb.eStateStopped | |
thread: SBThread = process.GetSelectedThread() | |
frame = thread.GetSelectedFrame() | |
assert frame.IsValid() | |
assert thread.IsValid() | |
frames:list[SBFrame] = thread.frames | |
frames = [frame for frame in frames] | |
frames.reverse() | |
current_frame = frames[-1] | |
previous_frame = frames[-2] | |
assert current_frame.IsValid() | |
print(f"Frame: {current_frame}") | |
print(f"Function: {current_frame.GetFunctionName()}") | |
calling_line_number = previous_frame.GetLineEntry().GetLine() | |
print(f"calling_line_number: {calling_line_number}") | |
line = main_file.read_text().split("\n")[calling_line_number - 1] | |
print(f"calling_line_number: {line}") | |
assert function_name_breakpoint in line, "<function name> not found in line, Why doesn't the line number match?" | |
vars = current_frame.GetVariables(True, True, True, True) | |
arg = [arg for arg in current_frame.arguments][0] | |
print(f"Arguments: {arg}") | |
struct = arg.Dereference() | |
print(f"Dereferenced: {struct}") | |
data = struct.GetChildAtIndex(0, lldb.eNoDynamicValues, True) | |
print(data) | |
hello = data.GetChildAtIndex(0, lldb.eNoDynamicValues, True) | |
world = data.GetChildAtIndex(1, lldb.eNoDynamicValues, True) | |
print(f"Hello: {hello}") | |
print(f"World: {world}") | |
length = struct.GetChildAtIndex(1, lldb.eNoDynamicValues, True) | |
print(f"Length: {length}") | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment