In this tutorial I will explain how to debug the encoder application to discover how it is controlling camera motor.
Inside camera (serial terminal) execute:
# ./gdbserver 192.168.1.1:3333 /mnt/system/bin/encoder Process /mnt/system/bin/encoder created; pid = 326 Listening on port 3333
On your workstation:
$ arm-linux-gdb encoder (gdb) target remote 192.168.1.126:3333
Listing all defined functions:
(gdb) info fun Non-debugging symbols: 0x0000a930 _init 0x0000a958 fileno 0x0000a964 getpagesize 0x0000a970 fputs 0x0000a97c abort 0x0000a988 __errno_location 0x0000a994 sigemptyset 0x0000a9a0 sprintf 0x0000a9ac popen 0x0000a9b8 open 0x0000a9c4 connect 0x0000a9d0 mmap 0x0000a9dc snd_pcm_hw_params_any 0x0000a9e8 statfs64 0x0000a9f4 getpid 0x0000aa00 snd_output_close 0x0000aa0c strerror 0x0000aa18 snd_pcm_close 0x0000aa24 getsockname 0x0000aa30 memcmp 0x0000aa3c inet_ntoa 0x0000aa48 shutdown 0x0000aa54 snd_pcm_status_get_state 0x0000aa60 msgget 0x0000aa6c signal 0x0000aa84 realloc 0x0000aa90 snd_pcm_hw_params_set_period_time_near 0x0000aa9c snd_pcm_sw_params_get_xfer_align 0x0000aaa8 __xstat64 0x0000aab4 localtime 0x0000aac0 snd_pcm_prepare 0x0000aacc snd_pcm_sw_params_sizeof 0x0000aad8 getgrnam 0x0000aae4 strchr 0x0000aaf0 vsnprintf 0x0000aafc recv 0x0000ab08 getenv 0x0000ab14 sem_wait 0x0000ab20 inet_addr 0x0000ab2c system 0x0000ab38 strncpy 0x0000ab44 putchar 0x0000ab50 write 0x0000ab5c sendto 0x0000ab68 listen 0x0000ab74 lseek64 0x0000ab80 fgets 0x0000ab8c memset 0x0000ab98 setitimer 0x0000aba4 fopen64 0x0000abb0 snd_pcm_status_get_trigger_tstamp 0x0000abbc __strtol_internal 0x0000abc8 snd_pcm_hw_params_get_period_size 0x0000abd4 __libc_start_main 0x0000abe0 execl 0x0000abec _IO_getc 0x0000abf8 _exit 0x0000ac04 snd_pcm_hw_params 0x0000ac10 snd_pcm_sw_params_set_avail_min 0x0000ac1c strrchr 0x0000ac28 tcgetattr 0x0000ac34 snd_pcm_hw_params_sizeof 0x0000ac40 set_gpio_input 0x0000ac4c piulib_init 0x0000ac58 read 0x0000ac64 msgsnd 0x0000ac70 perror 0x0000ac7c usleep 0x0000ac88 snd_pcm_open 0x0000ac94 gettimeofday 0x0000aca0 fdopen 0x0000acac __ctype_toupper_loc 0x0000acb8 free 0x0000acc4 siglongjmp 0x0000acd0 __lxstat64 0x0000acdc snd_pcm_sw_params_set_xfer_align 0x0000ace8 snd_pcm_sw_params_set_sleep_min 0x0000acf4 access 0x0000ad00 sigaction 0x0000ad0c fflush 0x0000ad18 snd_pcm_sw_params 0x0000ad24 piulib_exit 0x0000ad30 opendir 0x0000ad3c accept 0x0000ad48 tcflush 0x0000ad54 ioctl 0x0000ad60 socket 0x0000ad6c dup2 0x0000ad78 __ctype_b_loc 0x0000ad84 fseek 0x0000ad90 inet_aton 0x0000ad9c pthread_mutex_unlock 0x0000ada8 snd_pcm_status_sizeof 0x0000adb4 isatty 0x0000adc0 umask 0x0000adcc fclose 0x0000add8 setuid 0x0000ade4 snd_pcm_writei 0x0000adf0 mktime 0x0000adfc readdir64 0x0000ae08 memcpy 0x0000ae14 cfsetospeed 0x0000ae20 strlen 0x0000ae2c snd_pcm_wait 0x0000ae38 fopen 0x0000ae44 snd_pcm_readi 0x0000ae50 snd_pcm_resume 0x0000ae5c snd_strerror 0x0000ae68 unlink 0x0000ae74 snd_pcm_sw_params_set_stop_threshold 0x0000ae80 getpwuid 0x0000ae8c getppid 0x0000ae98 waitpid 0x0000aea4 feof 0x0000aeb0 strcpy 0x0000aebc longjmp 0x0000aec8 printf 0x0000aed4 ftok 0x0000aee0 chdir 0x0000aeec ctime 0x0000aef8 bind 0x0000af04 getuid 0x0000af10 snd_config_update_free_global 0x0000af1c atoi 0x0000af28 mkstemp64 0x0000af34 select 0x0000af40 closedir 0x0000af4c close 0x0000af58 fwrite 0x0000af64 snd_pcm_format_physical_width 0x0000af70 snd_pcm_hw_params_get_buffer_time_max 0x0000af7c fprintf 0x0000af88 strstr 0x0000af94 time 0x0000afa0 setvbuf 0x0000afac execve 0x0000afb8 malloc 0x0000afc4 snd_pcm_state_name 0x0000afd0 snd_pcm_hw_params_get_buffer_size 0x0000afdc pthread_mutex_lock 0x0000afe8 sigprocmask 0x0000aff4 snd_pcm_info_sizeof 0x0000b000 gethostname 0x0000b00c snd_pcm_hw_params_set_rate_near 0x0000b018 pthread_create 0x0000b024 snd_pcm_hw_params_set_access 0x0000b030 sleep 0x0000b03c sigaddset 0x0000b048 msgrcv 0x0000b054 strncasecmp 0x0000b060 memmove 0x0000b06c strcat 0x0000b078 send 0x0000b084 snd_pcm_hw_params_set_channels 0x0000b090 getcwd 0x0000b09c sem_post 0x0000b0a8 snd_pcm_format_set_silence 0x0000b0b4 settimeofday 0x0000b0c0 puts 0x0000b0cc fork 0x0000b0d8 __fxstat64 0x0000b0e4 setsockopt 0x0000b0f0 tcsetattr 0x0000b0fc fcntl 0x0000b108 snd_pcm_hw_params_set_buffer_time_near 0x0000b114 sscanf 0x0000b120 gmtime 0x0000b12c memchr 0x0000b138 strncmp 0x0000b144 open64 0x0000b150 snd_pcm_info 0x0000b15c munmap 0x0000b168 pipe 0x0000b174 fread 0x0000b180 getsockopt 0x0000b18c setgid 0x0000b198 snprintf 0x0000b1a4 mmap64 0x0000b1b0 init_gpio_lib 0x0000b1bc snd_output_stdio_attach 0x0000b1c8 gethostbyname 0x0000b1d4 recvfrom 0x0000b1e0 ferror 0x0000b1ec getpwnam 0x0000b1f8 strcmp 0x0000b204 __sigsetjmp 0x0000b210 herror 0x0000b21c piu_register 0x0000b228 __strdup 0x0000b234 exit 0x0000b240 cfsetispeed 0x0000b24c pclose 0x0000b258 sem_init 0x0000b264 set_gpio_output 0x0000b270 snd_pcm_hw_params_set_format 0x0000b27c getgid 0x0000b288 snd_pcm_sw_params_current 0x0000b294 get_gpio_value 0x0000b2a0 snd_pcm_status 0x0000b2ac piu_tx 0x0000b2b8 snd_pcm_sw_params_set_start_threshold 0x0000b2c4 geteuid 0x00049070 _fini 0x40008b20 _dl_rtld_di_serinfo 0x0000b294 get_gpio_value 0x0000b2a0 snd_pcm_status 0x0000b2ac piu_tx 0x0000b2b8 snd_pcm_sw_params_set_start_threshold 0x0000b2c4 geteuid 0x00049070 _fini 0x40008b20 _dl_rtld_di_serinfo 0x4000f660 _dl_debug_state 0x40010f60 _dl_mcount 0x40011320 __tls_get_addr 0x40011670 _dl_tls_setup 0x40011740 _dl_get_tls_static_info 0x40011850 _dl_allocate_tls_init 0x40011a90 _dl_allocate_tls 0x40011ac0 _dl_deallocate_tls 0x40011e60 ___tls_get_addr 0x400122a0 _dl_make_stack_executable 0x400157b0 __libc_memalign 0x400158c0 malloc 0x400158f0 calloc 0x40015950 free 0x40015a30 realloc (gdb)
We are insterested to know which files were opened and what was write to it.
Then we will put breackpoints at open, open64, fopen and write:
(gdb) b open Breakpoint 1 at 0xa9b8 (gdb) b fopen Breakpoint 2 at 0xae38 (gdb) b write Breakpoint 3 at 0xab50 (gdb) b open64 Breakpoint 6 at 0xb144 (gdb) b popen Breakpoint 7 at 0xa9ac (gdb) c Continuing.
Now let us to discover what is happening.
(gdb) target remote run A program is being debugged already. Kill it? (y or n) n Program not killed. (gdb) c Continuing. Breakpoint 3, 0x0000ab50 in write () (gdb)
Let’s find out which file descriptor and what is written on it:
(gdb) p /x $r0 $1 = 0x4
Hmm, file descriptor 4, let us to know what is it…
$ telnet 192.168.1.126 Trying 192.168.1.126... Connected to 192.168.1.126. Escape character is '^]'. (none) login: root warning: cannot change to home directory / # ps ax ... 323 root 1728 S ./gdbserver 192.168.1.126:3333 /mnt/system/bin/encode 326 root 3868 T /mnt/system/bin/encoder 327 root 2840 S -sh 351 root 2840 R ps ax / #
Ok, Process ID of encoder is 326, then let see which files are opened for this process:
/ # ls -l /proc/326/fd/ lrwx------ 1 root root 64 Jan 1 01:13 0 -> /dev/ttyS0 lrwx------ 1 root root 64 Jan 1 01:13 1 -> /dev/ttyS0 lrwx------ 1 root root 64 Jan 1 01:13 2 -> /dev/ttyS0 lrwx------ 1 root root 64 Jan 1 01:13 3 -> socket:[148] lrwx------ 1 root root 64 Jan 1 01:13 4 -> /dev/i2c-0
Wow, it is /dev/i2c-0
Now let us to return to gdb interface to know what will be write to it:
Breakpoint 3, 0x0000ab50 in write () (gdb) p /x $r0 $1 = 0x4 (gdb) x /1x $r1 0xbed7d9d7: 0x0000000a (gdb) p /x $r2 $2 = 0x1
Hmm, it will write only 1 byte (r2 = 1) and this byte it 0x0A (pointer at r1).
Ok, move on…
Another write operation:
Breakpoint 3, 0x0000ab50 in write () (gdb) p /x $r0 $3 = 0x4 (gdb) x /1x $r1 0xbed7d9d7: 0x0000000b (gdb) p /x $r2 $4 = 0x1
Hey, it should be reading something from i2c as well, let us to put a breakpoint at read function:
(gdb) b read Breakpoint 4 at 0xac58 (gdb) c Continuing. Breakpoint 4, 0x0000ac58 in read ()
Let gather more information:
(gdb) info frame Stack level 0, frame at 0xbed7d9d4: pc = 0xac58 in read; saved pc 0x28d5c called by frame at 0xbed7d9d4 Arglist at 0xbed7d9d4, args: Locals at 0xbed7d9d4, Previous frame's sp is 0xbed7d9d4
This function was called from 0x28d5c (saved pc).
We could obtain this information from link register (lr) as well, lr is an alias to r14:
(gdb) p /x $r14 $5 = 0x28d5c
Good, let put a breakpoint at 0x28d5c
(gdb) b *0x28d5c Breakpoint 5 at 0x28d5c
Now, let run the read function and wait it return to 0x28d5c
Breakpoint 5, 0x00028d5c in ?? () (gdb) p /x $r0 $6 = 0x1 (gdb) x /1x $r1 0xbed7d9d6: 0x21
Ok, it read 1 byte as expected and this byte is 0x21.
Keep going:
(gdb) c Continuing. Breakpoint 3, 0x0000ab50 in write ()
A new write:
(gdb) p /x $r0 $7 = 0x4 (gdb) x /1x $r1 0xbed7d9e0: 0x12 (gdb) p /x $r2 $8 = 0x2
It is writing at same file descriptor (4 = i2c-0), but now it is write 2 bytes, first one is 0x12, but let us see both:
(gdb) x /1x 0xbed7d9e0 0xbed7d9e0: 0x12 (gdb) x /1x 0xbed7d9e1 0xbed7d9e1: 0x80
Ok, let’s to continue:
(gdb) c Continuing. Breakpoint 3, 0x0000ab50 in write () (gdb) p /x $r0 $9 = 0x4 (gdb) x /1x $r1 0xbed7d9e0: 0x12 (gdb) p /x $r2 $10 = 0x2 (gdb) x /1x 0xbed7d9e1 0xbed7d9e1: 0x00
And another write, but now the second value is 0x00 instead of 0x80.
In this mean time I noticed these text printed at minicom (where gdbserver is running) :
ov7725 id1 id2:77-21 this is ov7725 reglen 97
So, this writing/reading is to initialize ov7725 camera, nice to know, but I’m not interested on camera sensor, not yet.
Then let to remove breakpoints at write and read, and keep only with open and fopen:
Delete write breakpoint: (gdb) delete 3 Delete read breakpoint: (gdb) delete 4 Delete breakpoint of read function return: (gdb) delete 5 Program received signal SIG32, Real-time event 32. 0x4002712c in ?? () (gdb) c
Then it will open in the sequence:
/dev/piu
/dev/mem
/dev/vip-note
/dev/dv
/dev/vip
avc_enc.dlm
And now this is a very suspect file:
Breakpoint 6, 0x0000b144 in open64 () (gdb) x /1s $r0 0x4ad40: "/dev/ttyS1"
Let us to put a breakpoint at return of this function, then we will know the file descriptor number of this file:
(gdb) p /x $r14 $12 = 0x12ac4 (gdb) b *0x12ac4 Breakpoint 9 at 0x12ac4 (gdb) c Continuing.
You will see many this signal events, before open64 function to return:
Program received signal SIG32, Real-time event 32. [Switching to Thread 369] 0x4002712c in ?? () (gdb) c Continuing. [Switching to Thread 326]
If you get bored then you could remove the breakpoint at open64, it will speed up this process:
Breakpoint 6, 0x0000b144 in open64 () (gdb) delete 6 (gdb) c Continuing.
And finally:
Breakpoint 9, 0x00012ac4 in ?? () (gdb)
Now we can see the file descriptor number:
(gdb) p /x $r0 $13 = 0xc
In fact I can see it at proc:
/ # ls -l /proc/326/fd/ ... lrwx------ 1 root root 64 Jan 1 01:47 12 -> /dev/ttyS1
This is file descriptor 12 (0xC).
Please continue with debugging until point where camera start to do self calibration.
Now we can return with breackpoint at write function:
(gdb) b write Breakpoint 10 at 0xab50 (gdb) c
Now switch to telnet terminal and execute the CGI script to move camera up:
/tmp # export QUERY_STRING=command=1\&user=admin\&pwd= /tmp # ./decoder_control.cgi Content-Type:text/plain Cache-Control:no-cache /tmp #
Switch back to gdb terminal and continue with execution:
(gdb) c Continuing. [New Thread 355] [Switching to Thread 355] Breakpoint 10, 0x0000ab50 in write ()
The encoder is now trying to write something in the camera:
(gdb) p /x $r0 $16 = 0xc (gdb) x /1s $r1 0xbe5fe044: "\34201\n\377" (gdb) p /x $r2 $17 = 0x8
It is expecting to write 8 bytes, let see what are these bytes:
(gdb) x /1x 0xbe5fe044 0xbe5fe044: 0xe2 (gdb) x /1x 0xbe5fe045 0xbe5fe045: 0x01 (gdb) x /1x 0xbe5fe046 0xbe5fe046: 0x0a (gdb) x /1x 0xbe5fe047 0xbe5fe047: 0xff (gdb) x /1x 0xbe5fe048 0xbe5fe048: 0x00 (gdb) x /1x 0xbe5fe049 0xbe5fe049: 0x00 (gdb) x /1x 0xbe5fe04a 0xbe5fe04a: 0x00 (gdb) x /1x 0xbe5fe04b 0xbe5fe04b: 0x23 (gdb)
Great, we could try to send these sequence directly from telnet terminal:
/tmp # printf "\xE2\x01\x0A\xFF\x00\x00\x00\x23" > /dev/ttyS1
Worked fine!!! Now these are other commands:
Move Up: # printf "\xE2\x01\x0A\xFF\x00\x00\x00\x23" > /dev/ttyS1 Move Down: # printf "\xE2\x02\x0A\xFF\x00\x00\x00\x23" > /dev/ttyS1 Move Right: # printf "\xE2\x03\x0A\xFF\x00\x00\x00\x23" > /dev/ttyS1 Move Left # printf "\xE2\x06\x0A\xFF\x00\x00\x00\x23" > /dev/ttyS1 Move Right-Up # printf "\xE2\x04\x0A\xFF\x00\x00\x00\x23" > /dev/ttyS1 Move Left-Down: # printf "\xE2\x08\x0A\xFF\x00\x00\x00\x23" > /dev/ttyS1 Move Right-Down: # printf "\xE2\x05\x0A\xFF\x00\x00\x00\x23" > /dev/ttyS1 Move Left-Up: # printf "\xE2\x07\x0A\xFF\x00\x00\x00\x23" > /dev/ttyS1
Other commands are also supported as horizontal/vertical patrol.
Update: Other interesting start point to you know more about GDB:
http://brundlelab.wordpress.com/2010/06/21/playing-with-gdb-reverse-engineer-your-way/
Another good advice would be use ‘strace’. But very good to know how to do it.
Thanks.
Hi Tinti,
Thanks for your suggestion. I used strace as well, but strace shows only the tip of iceberg.
To use strace in multithread programs (like this one) use: strace -fxi program.
You can use -e parameter to show only syscalls you are interested.
Hi,
please can you tell me password for telnet access?
Hi Francesco,
It doesn’t use password. Just use “root” as login:
Source: http://hardenedgentoo.blogspot.com.br/2012/07/fun-with-vstarcam-ip-camera.html
BR,
Alan