; -------------------------------------------------------------------------------- ; @Title: Linux Kernel Module Debugging Script ; @Description: ; Module Debugging (AutoLoader version) ; ; This script waits for a module to be started, ; loads the symbols and halts the module at init_module() ; ; NOTE: Linux and the Linux awareness must be up. ; ; Start this script with the module name (without .ko) as ; argument to run the script as command line version or ; use /dialog to run the script in a dialog. ; ; Examples: ; do mod_debug mymod ; waits for "mymod.ko" to be inserted ; do mod_debug /dialog ; opens a dialog window ; ; Prerequisites: ; - Linux must be configured with CONFIG_KALLSYMS=y ; - Linux must be booted ; - Linux awareness must be configured ; - Symbol Autoloader must be configured ; ; @Keywords: Linux module insmod ; @Author: DIE ; @Copyright: (C) 1989-2019 Lauterbach GmbH, licensed for use with TRACE32(R) only ; -------------------------------------------------------------------------------- ; $Id: mod_debug.cmm 4820 2019-11-22 10:20:31Z amerkle $ // Define local macros LOCAL &mod_cmd &cmdline LOCAL &module &dialog &exec &timeout LOCAL &machname &TASK &EXT &autoloader_opt GLOBAL &true &false &breakaddr &breaklabel &breakoffset &true=(1==1) &false=(1==0) &breakaddr=0 &breakoffset=0 &exec=&false // Get the script parameters (" /term /timeout " or "/dialog") ENTRY %LINE &cmdline &dialog=&false &module="" // Check parameters IF "&cmdline"=="" ( // no parameter given -> print usage GOSUB print_usage ENDDO 1. ) IF STRING.LoWer(STRING.SPLIT("&cmdline"," ",0))=="/dialog" ( // /dialog -> open a dialog to select process &dialog=&true ) ELSE ( // parameter contains module name &module=STRING.SPLIT("&cmdline"," ",0) ) GOSUB parse_cmdline IF "&mod_cmd"=="" &mod_cmd="insmod &module.ko" IF "&machname"=="" ( &TASK="TASK" &EXT="TASK" &autoloader_opt="" ) ELSE ( &TASK="TASK.&machname" &EXT="EXT.&machname" &autoloader_opt="/machine ""&machname""" ) // Activate autoloader for modules: &EXT.sYmbol.Option AutoLoad Module // Ensure windows update SCREEN.ALways // Dialog or Command line version? IF &dialog GOTO dialog ; -------------------------------------------------------------------------------- ; Command line interface ; -------------------------------------------------------------------------------- // Convert module's name dashes if necessary GOSUB convertmodulename &module ENTRY &module // Check if the module already exists in the module list. LOCAL &existed GOSUB checkexisting &module ENTRY &existed IF &existed ENDDO 2. // Wait for module to be loaded. // The module must be started by executing insmod at the Linux console. IF ("&exec"=="&false") PRINT "Please insert module &module..." GOSUB waitforstart &module &exec ENTRY ¬ifier // Yep! The module is loaded and we found it. // First check, if sections are already available PRINT "waiting for sections to be loaded..." GOSUB waitforsections &module ¬ifier // Now load the module symbols with relocation PRINT "module &module started, loading symbols..." GOSUB loadsymbols &module ENTRY &init IF &init==0 ENDDO 1. // We got the init routine of the module. // We let the system run until it reaches it. PRINT "waiting for reaching module init..." GOSUB waitforinit &module &init // That's it, we halted at the init routine! PRINT "done." ENDDO 0. ; -------------------------------------------------------------------------------- ; Dialog interface ; -------------------------------------------------------------------------------- dialog: DIALOG.view ( HEADER "Debug Module on init" POS 0. 0. 24. 3. BOX "module name (without .ko)" POS 1. 1. 22. 1. modu: DEFHOTEDIT "" ( IF DIALOG.STRing(modu)!="" DIALOG.Enable bok ELSE DIALOG.Disable bok ) POS 1. 3. 22. 1. exec: CHECKBOX "send command to TERM window" "" POS 1. 4. 22. 1. cmdl: EDIT "" "&mod_cmd=DIALOG.STRING(cmdl)" POS 2. 5.5 8. bok: DEFBUTTON "Ok" ( LOCAL &module DIALOG.Disable bok DIALOG.Disable modu &module=DIALOG.STRing(modu) GOSUB convertmodulename &module ENTRY &module DIALOG.Set mess "Checking module &module..." GOSUB checkexisting &module ENTRY &existed IF &existed JUMPTO winclose &mod_cmd=DIALOG.STRING(cmdl) IF "&mod_cmd"!="" &exec=DIALOG.BOOLEAN(exec) DIALOG.Set mess "Please insert module &module" GOSUB waitforstart &module &exec ENTRY ¬ifier DIALOG.Set mess "Waiting for sections to be loaded..." GOSUB waitforsections &module ¬ifier DIALOG.Set mess "Module &module started. Loading symbols..." GOSUB loadsymbols &module ENTRY &init IF &init==0 JUMPTO winclose DIALOG.Set mess "Waiting for reaching init..." GOSUB waitforinit &module &init JUMPTO winclose ) POS 14. 5.5 8. BUTTON "Cancel" "JUMPTO winclose" POS 0. 7. 24. 1. mess: EDIT "Please enter module name" "" CLOSE "JUMPTO winclose" ) DIALOG.Disable bok DIALOG.Disable mess DIALOG.Set modu "&module" DIALOG.Set exec &exec IF &exec DIALOG.Set cmdl "&mod_cmd" STOP winclose: IF &breakoffset!=0 ( IF STATE.RUN() Break.direct Break.Delete &breakaddr ON PBREAKAT &breakoffset &breakaddr=0 &breakoffset=0 ) DIALOG.END ENDDO 0. ; -------------------------------------------------------------------------------- ; Subroutine: Convert module name if necessary ; -------------------------------------------------------------------------------- // change "-" to "_" (see linux/scripts/Makefile.lib) convertmodulename: ENTRY &module // cut of trailing .ko if there IF STRing.MID("&module",string.len("&module")-3,3)==".ko" &module=STRing.CUT("&module",-3) &dash=STRing.SCAN("&module","-",0) IF &dash!=-1 ( // conversion necessary &len=STRing.LENgth("&module") WHILE &dash!=-1 ( &module=STRing.CUT("&module",&dash-&len)+"_"+STRing.CUT("&module",&dash+1) &dash=STRing.SCAN("&module","-",0) ) ) RETURN &module ; -------------------------------------------------------------------------------- ; Subroutine: Check if module already exists ; -------------------------------------------------------------------------------- checkexisting: ENTRY &process IF STATE.RUN() Break.direct IF (&TASK.MOD.MAGIC("&module")&0xFFFFFFFF)!=0xFFFFFFFF ( sYmbol.AutoLOAD.CLEAR "&module" &autoloader_opt sYmbol.AutoLOAD.CHECK &autoloader_opt sYmbol.AutoLOAD.TOUCH "&module" &autoloader_opt IF sYmbol.EXIST(\\&module) GOSUB message "Module &module already loaded." "Symbols loaded." ELSE GOSUB message "Module &module already loaded." "No symbol file found." RETURN &true ) // Delete possibly existing breakpoints of previous module runs IF sYmbol.EXIST("\\&module") Break.Delete sYmbol.SECRANGE(\\&module\.text) RETURN &false ; -------------------------------------------------------------------------------- ; Subroutine: Wait for start of module ; -------------------------------------------------------------------------------- waitforstart: ENTRY &module &exec // Wait for module to be loaded. // The module must be started by executing insmod at the Linux console. // We cannot load module symbols as long as we don't // know to which address it is loaded. So we wait for // the initialization of the module // sys_init_module() is called for every module to start, // and starts notifier_call_chain() after loading, but // right before initializing the module. // The conditional breakpoint halts only, if the desired // module is found in the module table. IF "&machname"!="" ( LOCAL &domain &domain=STRING.LoWeR("&machname") ¬ifier="" IF sYmbol.EXIST(\\&domain\kernel/module\do_init_module) ¬ifier="`\\&domain\kernel/module\do_init_module`" ELSE IF sYmbol.EXIST(\\&domain\\blocking_notifier_call_chain) ¬ifier="\\&domain\\blocking_notifier_call_chain" // since 2.6.17 ELSE ¬ifier="\\&domain\\notifier_call_chain" &breakaddr=¬ifier ) ELSE ( IF sYmbol.EXIST(do_init_module) ¬ifier="do_init_module" IF sYmbol.EXIST(blocking_notifier_call_chain) ¬ifier="blocking_notifier_call_chain" // since 2.6.17 ELSE ¬ifier="notifier_call_chain" &breakaddr=¬ifier ) &breakoffset=ADDRESS.OFFSET(&breakaddr) Break.Delete &breakaddr // delete previous set breakpoints Break.Set &breakaddr /CONDition (&TASK.MOD.MAGIC("&module")&0xFFFFFFFF)!=0xFFFFFFFF ON PBREAKAT ADDRESS.OFFSET(&breakaddr) GOTO continue1 // if breakpoint reached: continue IF ("&timeout"!="") ON TIme &timeout GOTO timeout1 // if breakpoint is not reached in time Go.direct // let the target run and load the module IF &exec TERM.Out "&mod_cmd" 0x0a STOP // halt script until breakpoint reached // breakpoint hit, continue script continue1: Break.Delete &breakaddr // delete breakpoint ON PBREAKAT &breakoffset // delete script action IF ("&timeout"!="") ON TIme // delete timeout action RETURN &breakaddr // breakpoint not hit, stop upon timeout timeout1: LOCAL &nname IF STATE.RUN() ( Break.direct Break.Delete &breakaddr // delete breakpoint Go.direct ) ELSE Break.Delete &breakaddr // delete breakpoint &breakaddr=0 ON PBREAKAT &breakoffset // delete script action ON TIme // delete timeout action &nname=sYmbol.NAME(p:¬ifier) GOSUB message "Error: breakpoint at &nname not reached after timeout of &timeout" ENDDO 1. ; -------------------------------------------------------------------------------- ; Subroutine: Waiting for sections to be loaded ; -------------------------------------------------------------------------------- waitforsections: ENTRY &module ¬ifier LOCAL &secaddr &breakoffset // Yep! The module is loaded and we found it. // First check, if sections are already available &magic=&TASK.MOD.MAGIC("&module") &secaddr=&TASK.MOD.SECADDR(&magic,1) IF (&secaddr==0)||((&secaddr&0xFFFFFFFF)==0xFFFFFFFF) ( // sections not yet available, wait for another notifier call &breakaddr=¬ifier &breakoffset=ADDRESS.OFFSET(&breakaddr) Break.Set &breakaddr /CONDition &TASK.MOD.SECADDR(&magic,1)!=0 ON PBREAK GOTO continue3 IF ("&timeout"!="") ON TIme &timeout GOTO timeout3 // if breakpoint is not reached in time repeat3: Go.direct STOP continue3: &secaddr=&TASK.MOD.SECADDR(&magic,1) IF (&secaddr==0)||((&secaddr&0xFFFFFFFF)==0xFFFFFFFF) ( GOTO repeat3 ) Break.Delete &breakaddr ON PBREAKAT &breakoffset IF ("&timeout"!="") ON TIme // delete timeout &breakaddr=0 ) RETURN timeout3: IF STATE.RUN() ( Break.direct Break.Delete &breakaddr // delete breakpoint Go.direct ) ELSE Break.Delete &breakaddr // delete breakpoint ON PBREAKAT &breakoffset // delete script action ON TIme // delete timeout action GOSUB message "Error: section &secaddr of module not reached after timeout of &timeout" ENDDO 1. ; -------------------------------------------------------------------------------- ; Subroutine: Load symbols of module ; -------------------------------------------------------------------------------- loadsymbols: ENTRY &module LOCAL &modaddr // Now load the module symbols with relocation sYmbol.AutoLOAD.CLEAR "&module" &autoloader_opt // clear possibly previous set sYmbol.AutoLOAD.CHECK &autoloader_opt // force new autoloader list sYmbol.AutoLOAD.TOUCH "&module" &autoloader_opt // force loading of process symbols // get the address of the module's init routine &modaddr=&TASK.MOD.MCB(&TASK.MOD.MAGIC("&module")) IF "&machname"!="" ( LOCAL &id &id=TASK.MACHINEID("&machname") &modaddr="&id:::&modaddr" ) &init=Var.VALUE(((struct module*)(&modaddr))->init)&~1 IF &init==0 ( GOSUB message "no init routine of module &module found" RETURN 0 ) RETURN &init ; -------------------------------------------------------------------------------- ; Subroutine: Wait for reaching init ; -------------------------------------------------------------------------------- waitforinit: LOCAL &breakoffset ENTRY &module &init // Set a breakpoint onto the module's init routine IF "&breaklabel"!="" &breakaddr=ADDRESS.OFFSET(&breaklabel) ELSE &breakaddr=&init &breakoffset=&breakaddr IF "&machname"!="" ( LOCAL &id &id=TASK.MACHINEID("&machname") &breakaddr="&id:::&breakaddr" ) Break.Set &breakaddr /Program ON PBREAKAT &breakoffset GOTO continue2 // if breakpoint reached: continue IF ("&timeout"!="") ON TIme &timeout GOTO timeout2 // if breakpoint is not reached in time Go.direct // let the target run and start the module STOP // halt script until breakpoint reached // breakpoint hit, continue script continue2: Break.Delete &breakaddr // delete breakpoint ON PBREAKAT &breakoffset // delete script action IF ("&timeout"!="") ON TIme // delete timeout action RETURN // breakpoint not hit, stop upon timeout timeout2: LOCAL &pc IF STATE.RUN() ( Break.direct Break.Delete &breakaddr // delete breakpoint &pc=Register(PC) Go.direct ) ELSE ( Break.Delete &breakaddr // delete breakpoint &pc=Register(PC) ) &breakaddr=0 ON PBREAKAT &breakoffset // delete script action ON TIme // delete timeout action GOSUB message "Error: init routine (at &breakaddr) of module not reached after timeout of &timeout (PC=&pc)" ENDDO 1. ; -------------------------------------------------------------------------------- ; Subroutine: print message in dialog box or command line ; -------------------------------------------------------------------------------- message: ENTRY &msg1 &msg2 IF &dialog DIALOG.OK &msg1 &msg2 ELSE PRINT &msg1 " " &msg2 RETURN parse_cmdline: LOCAL &arg &i &i=1 &cmdline=STRING.TRIM("&cmdline") RePeaT ( &cmdline=STRING.REPLACE("&cmdline"," "," ",0) ) WHILE STRING.SCAN("&cmdline"," ",0)!=-1 RePeat ( &arg=STRING.SPLIT("&cmdline"," ",&i) IF "&arg"=="" RETURN IF STRing.LoWeR("&arg")=="/term" ( LOCAL &arg2 &exec=&true &arg2=STRING.SPLIT("&cmdline"," ",&i+1) IF STRING.CHAR("&arg2",0)==0x22 ( LOCAL &pos &cmd &pos=STRING.SCAN("&cmdline","&arg2",0) &cmd=STRING.CUT("&cmdline",&pos+1) &pos=STRING.SCAN("&cmd","""",0) IF &pos==-1 ( PRINT %ERROR "Syntax error" GOSUB print_usage ENDDO 1 ) &cmd=STRING.CUT("&cmd", &pos-STRING.LEN("&cmd")) &mod_cmd="&cmd" &i=&i+STRING.COUNT("&mod_cmd"," ")+1 ) ELSE ( IF STRING.CHAR("&arg2",0)!=0x2F ( PRINT %ERROR "Syntax error" GOSUB print_usage ENDDO 1 ) ) ) IF STRing.LoWeR("&arg")=="/timeout" ( &i=&i+1 &timeout=STRING.SPLIT("&cmdline"," ",&i) IF ("&timeout"=="")||("&timeout"<="0") ( PRINT %ERROR "Syntax error: missing or illegal timeout parameter" ENDDO 1. ) PRINT "timeout = &timeout" ) IF STRing.LoWeR("&arg")=="/stopat" ( &i=&i+1 &breaklabel=STRING.SPLIT("&cmdline"," ",&i) ) IF STRing.LoWeR("&arg")=="/machine" ( &i=&i+1 &machname=STRING.SPLIT("&cmdline"," ",&i) IF ("&machname"=="") ( PRINT %ERROR "Syntax error: missing machine parameter" ENDDO 1. ) IF TASK.MACHINEID("&machname")==0x1f ( PRINT %ERROR "Syntax error: illegal machine parameter" ENDDO 1. ) PRINT "machine = &machname" ) IF STRing.LoWeR("&arg")=="/dialog" ( &dialog=&true ) &i=&i+1 ) RETURN print_usage: PRINT "Usage: ""do mod_debug [/term """"] [/timeout ] [/stopat