前些日子,我在淘寶上淘了一塊 TQ2440 開發板,順便一起買了塊山寨的 J-Link,今天研究一下如何在 Linux 下使用它。在 http://www.segger.com/ 上可以找到 J-Link Commander 在 linux 下的可執行程序。將下載的 JLink_Linux_090804.tar 解壓到 /home/jianglu/arm-tools/jlink/ 目錄下。按照 README 的說明,安裝 libusb 庫、復制 udev 規則文件 45-jlink.rules 到 /etc/udev/rules.d/ 、添加用戶到 plugdev 組、重啟計算機。這樣 jlink 就可以使用了。
插入 J-Link 到 USB 口,使用 root 權限啟動 ./start 腳本
sudo ./start
程序錯誤,提示 ./JLinkExe: error while loading shared libraries: ./libjlinkarm.so.0: file too short
刪除 2 個無效的軟連接 libjlinkarm.so 和 libjlinkarm.so.0
rm libjlinkarm.so
rm libjlinkarm.so.0
重建 libjlinkarm.so 和 libjlinkarm.so.0 連接
ln -s libjlinkarm.so.0.0 libjlinkarm.so.0
ln -s libjlinkarm.so.0.0 libjlinkarm.so
再次啟動腳本
sudo ./start
出現 J-Link> 提示符,J-Link 成功啟動。
輸入 ? 命令可以查看幫助。幫助說明的最後兩行提示該程序可以執行命令腳本。
NOTE: Specifying a filename in command line
will start J-Link Commander in script mode.
在嘗試各種命令時,我發現該版本的 jlink 有一個嚴重的問題:在使用腳本模式或 loadbin 命令時會產生段錯誤。譬如在 J-Link 中執行如下指令:
J-Link>loadbin u-boot.bin,0x40000000
Loading binary file... [u-boot.bin]
./start: line 6: 8689 Segmentation fault
LD_LIBRARY_PATH="." ./JLinkExe
放狗搜索一番,無功而返。沒轍了,抄起 GDB 大刀,先確認錯誤原因再說。修改 ./start 文件為:
LD_LIBRARY_PATH="." <strong>gdb</strong> ./JLinkExe
再次執行,進入 GDB 調試模式,使用 r 指令運行程序,在 J-Link> 提示符下再次使用 loadbin 命令,GDB捕獲到異常。
J-Link>loadbin u-boot.bin,0x40000000
Loading binary file... [u-boot.bin]
Program received signal SIGSEGV, Segmentation fault.
0xb7bf6e87 in fclose@@GLIBC_2.1 () from /lib/libc.so.6
(gdb) backtrace
#0 0xb7bf6e87 in fclose@@GLIBC_2.1 () from /lib/libc.so.6
#1 0x0805952c in _ExecLoadBin ()
#2 0x08051e47 in _ExecCommandLine ()
#3 0x080520dd in main ()
再次放狗搜索 fclose 引發的斷錯誤,果然有很多,這種情況可能是 glibc 版本不匹配導致的,暈死。
我只好下載了 glibc 三個不同的版本 2.1、2.4、2.8 版。通過修改 ./start 腳本替換本機的 glibc 執行,結果都有各種其他問題。沒辦法,既然調用 fclose 出錯,那讓程序不調用它應該就可以了,後果嘛,只不過是洩露一個文件句柄而已。
使用 objdump 程序反匯編 JLinkExe
objdump -D JLinkExe > jlink.dasm
找到異常地址處的代碼
8059527: <strong>e8 c4 11 ff ff</strong> call 804a6f0 <fclose@plt>
使用 Hex 編輯器,將 JLinkExe 中的 e8 c4 11 ff ff 替換為 NOP 指令 90 90 90 90 90
大功告成,再次運行 ./start 並嘗試 loadbin 命令,段錯誤沒有了,但是又有了新的問題:
J-Link>loadbin u-boot.bin,0x40000000
Loading binary file... [u-boot.bin]
ERROR: Could not read file.
再次查看反匯編代碼:
8059465: e8 d6 0e ff ff call 804a340 <open@plt>
805946a: 83 f8 ff cmp $0xffffffff,%eax
805946d: 89 85 e0 fc ff ff mov %eax,-0x320(%ebp)
8059473: 0f 84 3a 01 00 00 je 80595b3 <_ExecLoadBin+0x2f3>
8059479: c7 44 24 08 02 00 00 movl $0x2,0x8(%esp)
8059480: 00
8059481: c7 44 24 04 00 00 00 movl $0x0,0x4(%esp)
8059488: 00
8059489: 89 04 24 mov %eax,(%esp)
805948c: e8 5f 16 ff ff call 804aaf0 <lseek@plt>
8059491: 89 c3 mov %eax,%ebx
8059493: 89 04 24 mov %eax,(%esp)
8059496: e8 c5 14 ff ff call 804a960 <malloc@plt>
805949b: 85 c0 test %eax,%eax
805949d: 89 c6 mov %eax,%esi
805949f: 0f 84 60 01 00 00 je 8059605 <_ExecLoadBin+0x345>
80594a5: 8b 95 e0 fc ff ff mov -0x320(%ebp),%edx
80594ab: 89 5c 24 08 mov %ebx,0x8(%esp)
80594af: 89 44 24 04 mov %eax,0x4(%esp)
80594b3: 89 14 24 mov %edx,(%esp)
80594b6: e8 f5 10 ff ff call 804a5b0 <read@plt>
80594bb: 39 d8 cmp %ebx,%eax
程序中先用 open 打開,在 lseek 到文件尾獲取文件大小,但是在調用 read 之前竟然沒有重新使用 lseek 將文件指針重新移動到文件頭!所以 read 返回值永遠是 0,表示讀取到文件尾。這下沒辦法了,只能在其他地方寫上正確的代碼,再用 jmp 指令替換原先的 lseek 調用,來跑我們正確的代碼。太麻煩了,先不搞了,提交Bug,等 J-Link 發布更新吧。