2015年6月1日 星期一

[Embedded] 利用 gdb script 實作 strcmp 函數

要在 gdb 內直接使用條件式中斷(Conditional breakpoint),例如:當某個字串出現時程式才進入中斷點,應該怎麼做呢?

1. 可以使用 commands 命令來設定條件式中斷
break foo
commands
if x>0
continue
else
printf "x is %d\n",x
end
end

2. 問題在於如何比較字串,以下整理網路上找到的幾種作法。若gdb版本較舊,則可能需要找尋其他方法。

方法一:使用 strcmp
(gdb) set $x = malloc(strlen("foobar") + 1)(gdb) call strcpy($x, "foobar")(gdb) break a_leg if strcmp(foo, $x) == 0

方法二:透過 python
可以直接使用下列函數 (Convenient functions) 
$_memeq(buf1buf2length)Returns one if the length bytes at the addresses given by buf1 and buf2 are equal. Otherwise it returns zero.  
$_regex(strregex)Returns one if the string str matches the regular expression regex. Otherwise it returns zero. The syntax of the regular expression is that specified by Python's regular expression support.  
$_streq(str1str2)Returns one if the strings str1 and str2 are equal. Otherwise it returns zero. 




也可以自行實作 strcmp
(gdb) define strcmp>py print cmp(gdb.execute("output $arg0", to_string=True).strip('"'), $arg1)>end(gdb) strcmp $x "hello"0

針對舊版本的 gdb,例如我所使用的 gdb 7.1,並不支援上述函數,因此需要自行實作一個 conevenient function,範例如下: 
py
class MyStrcmp (gdb.Function):
    """My Own Strcmp"""

    def __init__ (self):
        super (MyStrcmp, self).__init__ ("mystrcmp")

    def invoke (self, arg0, arg1):
        print "input '" + arg0.string() + "' and '" + arg1.string() + "'"
        if arg0.string() == arg1.string() :
            print "equal"
            return 0
        else:
            print "not equal"  
            return 1
MyStrcmp()
end
用法: 
print $mystrcmp("hello", "hello")

方法三:使用 gdb script 實作 strcmp
set var  $_isEq=0

# Yes! GDB_STRCMP, below, is a gdb function.
# Function that provides strcmp-like functionality for gdb script;
# this function will be used to match the password string provided in command line argument
# with the string argument of strcmp in program
define GDB_STRCMP
set var  $_i=0
set var  $_c1= *(unsigned char *) ($arg0 + $_i)
set var  $_c2= *(unsigned char *) ($arg1 + $_i)
while (  ($_c1 != 0x0) && ($_c2 != 0x0) && ($_c1 == $_c2) )

#printf "\n i=%d, addr1=%x(%d,%c), addr2=%x(%d,%c)", $_i, ($arg0 + $_i),$_c1, $_c1, ($arg1 + $_i), $_c2,$_c2
set  $_i++
set  $_c1= *(unsigned char *) ($arg0 + $_i)
set  $_c2= *(unsigned char *) ($arg1 + $_i)

#while end
end

if( $_c1 == $_c2)
set $_isEq=1
else
set $_isEq=0
end

#GDB_STRCMP end
end

Reference:

  1. https://sourceware.org/gdb/onlinedocs/gdb/Break-Commands.html
  2. http://stackoverflow.com/questions/13961368/conditional-breakpoint-using-strcmp-in-gdb-on-mac-os-x-conflicts-with-objectiv
  3. https://sourceware.org/gdb/current/onlinedocs/gdb/Convenience-Funs.html
  4. http://stackoverflow.com/questions/7423577/how-to-compare-a-stored-string-variable-in-gdb
  5. http://www.opensourceforu.com/2011/09/modify-function-return-value-hack-part-2/