2008年6月21日 星期六

『孩子阿,現在已經沒有人在讀詩了,大家都看電視,所以別當詩人,去當修電視工人吧』

2008年6月7日 星期六

我渴望能見你一面,
但請你記得,我不會開口要求要見你。
這不是因為驕傲,你知道我在你面前毫無驕傲可言,
而是因為,唯有你也想見我的時候,我們見面才有意義。
          Simone de Beauvoir (1950)(法國作家)

Easytag 2.1.5


以編輯 Mp3 Tag 來說,我覺得最好用、最 Powerful 的工具就是 Easytag 了,但在 Windows 下也找不到比他好用的軟體。

前一陣子一直遇到 Tag 改完之後寫入,用 Rhythmbox 讀卻無法讀出的問題,弄了許久都無法修正好。另外 Easytag 2.1.4 有一個 Bug 就是你如果選擇多個檔案,然後選 Settings -> Preference ,Easytag 就會掛掉。 XD

於是上官網抓了最新的 2.1.5 來用,沒想到問題迎刃而解。

以下是方便以後重編的 Script:
#!/bin/bash
mkdir tmp
cd tmp
pkg_name=easytag-2.1.5
wget -c http://nchc.dl.sourceforge.net/sourceforge/easytag/$pkg_name.tar.bz2
tar xvf $pkg_name.tar.bz2
cd $pkg_name
sudo apt-get -y build-dep easytag
sudo apt-get -y install libid3-3.8.3-dev libogg-dev libflac-dev libflac++-dev libmp4v2-dev libmp4v2-0 libwavpack-dev gettext
sudo apt-get -y install libgmp3-dev libvorbis0a libvorbis-dev libogg0 libogg-dev
sudo apt-get -y install libfaad-dev libfaad2-0 libmp4v2-dev libfaad2-0 libmp4v2-0
sudo apt-get -y install libid3tag0 libid3tag0-dev libspeex-dev
./configure
make
sudo make install

2008年6月6日 星期五

在 Linux 下使用 GNU AS 編寫組合語言 - 使用 gdb 進行除錯

日前有寫一篇『在 Linux 下使用 GNU AS 編寫組合語言』,現在以 cpuid 的範例來示範如何使用 gdb 除錯。

首先我們在組譯的時候,需加上 -gstabs 參數,as 會將除錯所需要的資訊編進去,以便 gdb 除錯。
[ cpuid:c9s-desktop : 19:05:43 ] $ as -gstabs -o cpuid.o cpuid.s
[ cpuid:c9s-desktop : 19:05:58 ] $ ld -o cpuid cpuid.o
除錯,只需要將連結完的執行檔丟給 gdb 即可:
[ cpuid:c9s-desktop : 19:06:04 ] $ gdb cpuid
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...
(gdb)
這樣就進入 gdb 了。

接下來我們可以在 _start 標籤之位址插入中斷點,然後將此程式執行,程式應在 _start 中斷
(gdb) break *_start
Breakpoint 1 at 0x8048074: file cpuid.s, line 7.
(gdb) run
Starting program: /home/src/asm-code/cpuid/cpuid
The processor Vendor ID is 'GenuineIntel'

Program exited normally.
(gdb) quit
怎麼回事,中斷點無效了?這是 gdb 一個 Bug,他把一開始的中斷點_start忽略了。XD

經過測試發現似乎是在一開始的第一個指令中斷點會無法作用?解法是,在 _start 後加入一個 nop 指令。nop 指令並沒有功能,他不做任何事情。
.globl _start
_start:
nop
movl $0, %eax
cpuid
movl $output,%edi
movl %ebx, 28(%edi)
接下來重新組譯後,使用 gdb 除錯:
(gdb) break *_start+1
Breakpoint 1 at 0x8048075: file cpuid.s, line 8.
(gdb) run
Starting program: /home/src/asm-code/cpuid/cpuid

Breakpoint 1, _start () at cpuid.s:8
8 movl $0, %eax
Current language: auto; currently asm
因為 nop 只有一個 byte ,我們將中斷點設置在 _start 位址後的一個 byte ,就會停在中斷點了。

接著你可使用 s 或 n 來作單步執行。 (s for step , n for next )

gdb 的基本指令:
info registers顯示所有暫存器內容
print印出特定暫存器或者變數的值
x印出特定記憶體位置的內容

print 可搭配不同的修飾符來選擇以何種格式印出 ( print/d 印出十進位數值 , print/t 印出二進位數值 , print/x 印出十六進制數值 )

此外 x 指令也可搭配修飾符來選擇格式

x/nyz
  • n 代表要印出幾個欄位 ( 1 , 2 , 3 ... )
  • y 為輸出格式,可為 c (字元) , d (十進制) , x (十六進制)
  • z 為大小
其中 z 可為:
  • b 為 byte
  • h 為 16 bit word (halt-word)
  • w 為 32 bit word
譬如:
(gdb) x/42xb *_start
0x8048074 <_start>: 0x90 0xb8 0x00 0x00 0x00 0x00 0x0f 0xa2
0x804807c <_start+8>: 0xbf 0xac 0x90 0x04 0x08 0x89 0x5f 0x1c
0x8048084 <_start+16>: 0x89 0x4f 0x24 0xb8 0x04 0x00 0x00 0x00
0x804808c <_start+24>: 0xbb 0x01 0x00 0x00 0x00 0xb9 0xac 0x90
0x8048094 <_start+32>: 0x04 0x08 0xba 0x2a 0x00 0x00 0x00 0xcd
0x804809c <_start+40>: 0x80 0xb8

(gdb) print/x $ebx
$1 = 0x756e6547

(gdb)
其中 x/42xb 代表印出 _start 標籤開始之後的 42 個 Byte ,並以 16 進制印出。 print/x 代表以 16 進制印出。 print/d 以十進制,print/t 以二進制。

以上簡略介紹至此。

2008年6月3日 星期二

Muzik


在 blue97 Music Daily 看到的。台灣唯一的古典音樂雜誌。
不過市面上好像還是很少見阿?

2008年6月2日 星期一

aMule 是很讚的


經過調校後速度也可以很快!
#!/bin/bash
apt-get build-dep amule

cd /tmp
wget -nv -c http://www.hirnriss.net/?area=cvs -O tmpfile
URL='http://www.hirnriss.net/'`grep -oE -m 1 'files/cvs/aMule-CVS-[0-9]+.tar.bz2' tmpfile`

wget -nv -c $URL -O amule.tar.bz2
tar xvf amule.tar.bz2
cd amule-cvs
cd src

perl -i.bak -pe 's{(\s+CheckUlDlRatio)}{// $1}g' Preferences.cpp
perl -i.bak -pe 's{\s+thePrefs::CheckUlDlRatio();}{}g' PrefsUnifiedDlg.cpp
cd ..

./configure --prefix=/usr --enable-optimize
make
make install

如何在 Linux 下使用 GNU AS 撰寫組合語言(1)

在 DOS 下寫組合語言有 MASM 組譯器,那麼在 Linux 下呢?我們有 GAS (GNU AS) 以及 NASM 等等。

NASM 一開始是為商用軟體為導向而開發的,但最近已經為開放原始碼釋出。NASM 支援 Windows 以及 UNIX 環境,能夠產生 UNIX , 16-bit MS-DOS , 32-bit Windows 格式的執行檔。

GAS 是由 GNU 所開發的自由軟體,在 UNIX 上是最受歡迎的跨平台組譯器。基本上組譯器都是針對特定處理器所設計的,但 GAS 特別之處在於他支援多種處理器,可產生不同種處理器的指令碼( instruction code ),通常 GAS 能夠自動的偵測硬體平台並產生相對應的指令碼。關於 GNU AS 所支援之處理器可參考 gas Manual ( Machine-Dependencies )

當然不同組譯器,語法也都不相同,NASM 和 GAS 語法是有差異的,稍後會說明。在此我們選擇 GAS 為組譯工具。

如何在 Linux 底下使用 gas 撰寫組合語言呢?首先,必須先安裝 binutils 這個套件,若你是在 Debian 或 Ubuntu 底下可透過 apt 安裝:

sudo apt-get install binutils

若沒有,可於 GNU 官方網站(http://ftp.gnu.org/gnu/binutils/)取得程式碼,解壓縮之後編譯,安裝他

./configure
make
make install


binutils 套件除了有 GNU as 之外,還有其他工具如:addr2line ar c++filt gprof nlmconv nm objcopy objdump 等等,可參考此說明(中華民國軟體自由協會),或者參考 GNU 官方文件 binutils 的說明

一支以 GAS 為組譯器之程式的基本架構如下:
.section .data
# 已初始化的資料

.section .bss
# 未初始化的資料

.section .text
.globl _start
_start:
# 程式碼由此開始
其中 .data 區塊,為放置初始化資料區塊,也就是變數已經有了初始值。.bss 則為未初始化資料區塊,此區塊為非必要。.text 則為程式碼區塊。 _start 為程式一開始的進入點,在 Linking 的時候會自動找到 _start 這個標籤為進入點,若是沒有 _start 標籤的話,Linking 時會出現以下訊息:

$ ld -o test test.o
ld: warning: cannot find entry symbol _start; defaulting to xxxxxxx


對於 GNU AS 所使用的語法在此針對幾點說明:

GAS 的原開發者選擇實做 AT&T opcode 語法作為此編譯器之語法,是因為 AT&T opcode 語法是由 AT&T Bell Labs 所發展,在當時是為那些實做 UNIX 系統的處理器而設計的。

也因此以 GNU AS 來撰寫 Intel 平台的組合語言程式是比較令人容易混淆。

有幾點差異如下:
  • 立即定址( immediate operands) 使用 $ 符號作為前綴,譬如說要使用數值 4 作為值,則寫作 $4。
  • 暫存器名稱一律使用 % 作為前綴,譬如說 EAX 則寫做 %EAX
  • 來源與目的之運算元位置與 Intel 語法不同之在於,AT&T 之來源運算元在前面,目的運算元在後。譬如將十進位數字 3 放入 EAX 暫存器,AT&T 寫作 movl $3, %eax ,Intel 寫法則寫為 mov eax, 3。
  • 為表示資料位址,AT&T 語法寫做 movl $test, %eax ,Intel 語法則為 mov eax, dword ptr test。
  • 跳躍或者呼叫使用不同的語法來定義區段(segment)以及偏移量(offset values),AT&T 使用 ljmp $section , $offset 而 Intel 使用 jmp section:offset。
如想知道的更清楚,這裡有一篇文章討論 NASM 與 GAS 之差異 (Linux assemblers: A comparison of GAS and NASM)

先寫一個最基本的小程式,此範例為一程式之最基本的架構,在此以 cpuid 為範例:
# cpuid.s Sample program to extract the processor Vendor ID

.section .data
output:
.ascii "The processor vendor id is 'xxxxxxxxxxxx''\n"
.section .text
.global _start
_start:
movl $0,%eax
cpuid

movl $output,%edi
movl %ebx, 28(%edi)
movl %edx, 32(%edi)
movl %ecx, 36(%edi)

movl $4, %eax
movl $1, %ebx
movl $output, %ecx
movl $42, %edx
int $0x80

movl $1,%eax
movl $0,%ebx
int $0x80
接下來做大略的解說,為示範整個編寫程式的流程,在此不闡述太多細節。第一段
.section .data
output:
.ascii "The processor vendor id is 'xxxxxxxxxxxx''\n"
在資料區塊裡頭宣告一個名為 output 的字串變數。
 movl $0,%eax
cpuid
movl $output,%edi
movl %ebx, 28(%edi)
movl %edx, 32(%edi)
movl %ecx, 36(%edi)
其中第一行, movl $0, %eax 將 0 移至 EAX 暫存器,執行 cpuid 時,會判斷 EAX 的內容取得相對資料。EAX 為 0 時,cpuid 是取得廠商之 ID,譬如說 Intel , Geniue 等等,cpuid 執行後,會將資料分別放入 EBX , EDX , ECX 。第 3 行,將 output 字串之起始位址存至 EDI 暫存器 ( EDI 暫存器是用來存放操作目的字串指標之暫存器 ),接著將 EBX,EDX,ECX 裡面存放之結果字串放入 EDI 暫存器指向之位址加上偏移量的位置內。
movl %ebx, 28(%edi)
此行代表將 %ebx 內四個 Byte 之內容搬移到 EDI 存放之位址 + 28 Bytes 之位址,其 28 剛好是 xxxxxxxxxxxx 之起始的位址。因此 28,32,36 這三行搬移指令剛好將 12 個 x 補滿。

現在我們有等待輸出的字串了,接下來要做的事情就是要將字串印出來。
 movl $4, %eax
movl $1, %ebx
movl $output, %ecx
movl $42, %edx
int $0x80
這五行為執行系統呼叫 ( 何謂系統呼叫?可參見此 ),其中 EAX 存放系統呼叫之值,EBX 存放要寫入之檔案敘述元 (STDOUT),ECX 存放字串起始位址,EDX 存放字串的長度。其等同於 write() 系統呼叫。關於系統呼叫,可以在以下定義的檔案找到:

/usr/include/asm/unistd.h

譬如說上頭使用的 write 可以在裡頭找到這麼一行:

#define __NR_write 4

4 就是這麼來的。(其他系統呼叫可參考 System Call Table)

最後以 0x80 之值執行軟體中斷。( Kernel System Call )
 movl $1,%eax
movl $0,%ebx
int $0x80
這三行等同於 exit(0) 系統呼叫。傳回 0 作為程式執行結果。

輸入以下命令,組譯

$ as -o cpuid.o cpuid.s

執行 Linking 的動作 (在此就不闡述何謂組譯以及連結,可參考 System Software )

$ ld -o cpuid cpuid.o

最後便可以執行了。

./cpuid

[1] gas 官方文件
[2] System Software / Beck

Norah Jones - Don't Know Why



Live 的,超有感覺。重點是,我居然弄到了這首歌的譜。

手放開 / 李聖傑

我把自己關起來只留下一個陽台
每當天黑推開窗 我對著夜幕發呆
看著往事 一幕一幕 再次演出你我的愛

我把電視機打開聽著別人的對白
也許那些故事可以給我一個交代
你要的愛 我學不來
眼睜睜看情變壞 人怔怔看情感慨

不能給你未來 我還你現在
安靜結束也是另一種對待 當眼淚流下來
傷已超載 分開也是另一種明白

我給你最後的疼愛是手放開
不要一張雙人床中間隔著一片海
感情的污點就留給時間慢慢漂白
把愛收進胸前左邊口袋

最後的疼愛是手放開
不想用言語拉扯所以選擇不責怪
感情就像候車月台 有人走有人來
我的心是一個站牌 寫著等待