#!/usr/bin/expect ####################################################################### # Name: umlgdl_wrapper (original umlgdb by Chandan Kudige) # # Description: # umlgdb_wrapper is a expect shell for gdb, which is specialised # to handle loading symbols of kernel modules as they # are loaded using insmod inside of the virtual machine. # # When any module is loaded with an insmod command, the kernel executes # the function sys_init_module in kernel/modules.c of its source tree. A # breakpoint is set properly at inside of this sys_init_module function in the proc # 'initgdb' (see below in this file), in a way that at this point, the module is # already loaded in a kernel data structure (struct module in # include/linux/module.h) and pointed by a global list pointer to a module # struct named 'modules', and so this wrapper executes the appropriate gdb # commands to load the symbols of the proper module loaded. The module paths are # searched from the variable array MODULE_PATHS (change it properly below) and # if not found in /usr/lib/$version/, and just returning if not found. And such # point must be changed when it becomes needed by changes made on future more # recent versions of kernel/module.c at MODULE_BREAKPOINT (see below). # # It then passes control to the user who can proceed with their debugging # as usual righ before to execute module->init() - allowing the user to step to # it and see the first line of the module being executed at its init function. # # Its very easy to extend the behaviour of umlgdb_wrapper to handle different # debugging situations that need automation. # # Author: Chandan Kudige, April 11, 2002 (original umlgdb) # Modified by Rodrigo F. Baroni to update with the last uml kernel version # available currently (2.6.21-rc1) and to be used by umlgdb_dispatcher at March # 28, 2007 ####################################################################### ####################################################################### # Customisation: most of the defaults here should suffice. But feel # free to change anything to suit custom requirements. ####################################################################### ## # GDB program name ## set PROG "gdb" ## # UML kernel full path ## set ARG "/usr/src/v2.6.21-rc1.uml/linux" ## # GDB prompt - Anyway valid TCL regex. Defaults to '(gdb)' ## set GDB {\(gdb\)} ## # Regex pattern printed out by the gdb when the breakpoint at 'sys_init_module' # # set RE_BREAKPOINT {Breakpoint 1.*} ## # Special character for quitting the UML session. # Type the actual character (using your editor's escape) ## set QUIT_CHAR "" ## # Set the point that the module inserted is already loaded and pointed by global # list 'modules', and right before the sys_init_module at kernel/module.c calls # module->init() (stepping the execution flow to the first line of the module # being loaded) to load the simbols properly before that, to finally allow the # module to be debuged by gdb. # set MODULE_BREAKPOINT "module.c:2015" ### # Module paths: # You can add paths for modules that are not in the gdb load-path. # This is basically a list with alternating module name and module path. # # When a module is loaded, umlgdb_wrapper tries to load the symbols from the # path given here. If the module is not listed then no symbols are loaded. ### # WARNING: when using module names with hiphen "-", substitute it by underscore # "_" when putting it in the first column of MODULE_PATHS (as are the examples # used below), because this is what happens when the module name is put in the # struct module at the kernel memory, and how its name is got when extracted # from there automatically (see below at proc do_insmod_module_loaded) set MODULE_PATHS { "klog_mod" "/usr/src/mm-project/modules-in-use/klog/klog-mod.ko" "kleak_mod" "/usr/src/mm-project/modules-in-use/kleak/kleak-mod.ko" } ####################################################################### # Script starts here. ####################################################################### log_user 0 system stty -echo raw set timeout -1 proc get_module_path {modname} { global MODULE_PATHS ARG env set idx [lsearch $MODULE_PATHS $modname] if {$idx == -1} { set version "[exec ./linux --version]" set path "[exec find $env(PWD)/lib/modules/$version/ -name ${modname}.ko ]" if {$path != ""} { return $path } return 0 } incr idx return [lindex $MODULE_PATHS $idx] } ####################################################################### # do_insmod_module_loaded() hook is invoked when umlgdb_wrapper detects that the # breakpoint at sys_init_module (set at MODULE_BREAKPOINT) has been hit. # # Returns: None ####################################################################### proc do_insmod_module_loaded {} { global GDB ARG # find the start address of the modules list pointer set query "p/x (int)(struct module * ) modules - (int)&((struct module *)0).list\r" send $query set answer [prompt_wait {= (0x[0-9a-f]+)}] set succ [regexp "= (.+)" $answer query modules_list_addr ] #find de module name loaded righ now at userspace set query "p ( (struct module *) $modules_list_addr )->name\r" send $query set answer [prompt_wait {= "(.+)", }] set succ [regexp "\"(.+)\"" $answer query module_name ] # find the start address that the module was loaded set query "p ( (struct module *) $modules_list_addr )->module_core\r" send $query set answer [prompt_wait {= \(void \*\) (0x[0-9a-f]+)}] set succ [regexp "=.+(0x.+)" $answer query module_core_addr ] set module_path [get_module_path $module_name] if { $module_path != 0 } { # Load the symbols for our module prompt_wait $GDB send "add-symbol-file $module_path $module_core_addr\r" prompt_wait "y or n" send "y\r" } puts "\r *** Loaded symbols of module $module_name inserted *** \r" # Send a step and so gdb prints the next instruction: mod->init() # and leave in the prompt another 's'tep to be ed by the user send "s\rs" prompt_wait $GDB # Back to user set timeout -1 } ####################################################################### # shell() is the main dispatch loop. # # - Passes user # # Returns: None ####################################################################### proc shell {} { global spawn_id, user_spawn_id global RE_BREAKPOINT global QUIT_CHAR global GDB while 1 { expect { ## # gracefully exit when the gdb exits. ## eof { return } ## # Hooks for gdb output. # Note: order is important ## -re $RE_BREAKPOINT { prompt_wait $GDB do_insmod_module_loaded } ## # Catch all from gdb and pass it to the user ## -re ".+" { send_user -raw -- $expect_out(buffer); } ## # Catch user QUIT sequence. # Currently should be a single character. ## -i $user_spawn_id $QUIT_CHAR {return} ## # Catch all from user and pass it to gdb ## -i $user_spawn_id -re .+ { send -- $expect_out(buffer); } } } } ####################################################################### # prompt_wait() : Can be called anytime we wait for a prompt. # Respects user input while waiting for the prompt. ####################################################################### proc prompt_wait {prompt} { global user_spawn_id global QUIT_CHAR set buf "" expect { -re $prompt { set buf $expect_out(buffer); send_user -raw -- $buf } -re ".+" { set buf $expect_out(buffer); send_user -raw -- $buf exp_continue; } timeout { puts "\rTIMEDOUT on prompt $prompt!!!\r" return 0; } -i $user_spawn_id $QUIT_CHAR {return} -i $user_spawn_id -re .+ { send -- $expect_out(buffer); } } return $buf } proc initgdb {pid} { global GDB global MODULE_BREAKPOINT ## # See at the top of this file the explanation about this breakpoint, and # what point it should be if changes becomes needed ## prompt_wait $GDB send "b $MODULE_BREAKPOINT \r" } ## # Spawn our gdb with linux ## set pid [spawn $PROG $ARG] ## # Start make the gdb enviroment and run the uml kernel ## initgdb $pid ## # Continue the uml execution righ after the virtual uml machine receive # a SIGINT sent by umlgdb_dispatcher because without this, the gdb # doesn't stop at the desired breakpoints made (set in initgdb function above) # without reason (the guess goes to some gdb trick) ## prompt_wait $GDB send "c\r" ## # Main wrapper loop ## shell ## # Cleanup ## system stty sane puts "\r----- umlgdb_wrapper finished----\r"