Ubuntu GCC Link Library 的順序問題

今天在 ubuntu 中了招,Makefile 中這樣寫:

結果一直顯示錯誤:

檢查了 libssl-dev 有安裝,也跑去 /usr/lib 建立一些 link,但是問題都沒有解決。

最後原因是這個:

The –as-needed option also makes the linker sensitive to the ordering of libraries on the command-line. You may need to move some libraries later in the command-line, so they come after other libraries or files that require symbols from them.

Ubuntu 自 11.04 後,預設會將 –as-needed 選項打開,所以參數的順序突然變得很重要了,改成這樣就可以了:

參考資料:
https://wiki.ubuntu.com/NattyNarwhal/ToolchainTransition

使用 fstat() 判斷 File Descriptor 的種類

Beej’s Guide 看到這一句話:select() only works with socket descriptors, not file descriptors (like 0 for stdin),想到我之前曾經把 tun/tap 的 file descriptor 丟進 select 裡面,不曉得我是不是做錯了。於是開始了一連串的搜尋,想要搞清楚 select 到底可以接受哪些 file descriptor…

大約花了快兩天的時間,我把相關的關鍵字都搜尋了一遍,然後把 man 2 select 與 man 2 select_tut 也看完了,接著跑去找出之前的程式,確認 tun/tap 並不是 socket descriptor 但是程式卻正常運作….

最後翻出手邊的 Linux 系統程式設計 這本書,把第二章重新看了一遍,發現書中直接就拿 STDIN_FILENO 丟進 select 來當範例。

嗯…難道是 Beej 亂寫嗎?
跑回去看那一篇文章,才發現他寫的是 Note for Windows Programmers 也就是說其實指的是 Winsock 的情況。

算了,反正看了那堆東西我還是有點收穫的,其中之一就是這個,如何判斷 fd 的種類:

參考:http://pubs.opengroup.org/onlinepubs/009695399/functions/fstat.html

Android ToyVpn by native code

今兒個練習使用 Android 的 VpnService,為了能使用手邊現有的 C++ 程式碼,需要使用 NDK 來寫原生程式。弄一弄覺得在 Java 與 C 之間切換實在有夠麻煩,索性把 Android SDK 裡面附的 ToyVpn 改用 C 語言重寫一次。Java 部份就只負責 UI 的互動,與一些初始化的動作而已,其餘的部份都交給 C 去執行,應該效能也會更好(吧?)。

大致上運作方式都沒有變,傳輸也依然沒有加密,使用者密碼也還是寫死狀態。
只加上了一個 Keep Alive 的功能,讓它閒置一段時間也不會斷線。

完整的程式碼在 GitHub:
https://github.com/weichenlin/NativeToyVpn

2013/12/20 更新:
增加了一個支援多 client 的 server
主要差異在於需要給每個 client 不同的 ip 做區別
另外還需要稍微分析一下封包的位址才能分辨到底是要給誰的
程式碼一樣放在 GitHub

[參考資料]
一篇很好的 tun/tap 入門教學:http://backreference.org/2010/03/26/tuntap-interface-tutorial/
比較詳細的運作原理:http://www.ibm.com/developerworks/cn/linux/l-tuntap/index.html
tcp/ip stack:https://github.com/saminiir/level-ip

JNI Type Signatures

想要從 C 語言呼叫 Java 時會用到這個。
基本上就是用一些代號來表達函式的參數與回傳值。

各個資料型態與對應的代號:

要注意的地方:

  • 物件名稱以L開頭後面接上完整的名稱,並以分號結尾。
  • 除了表示物件以外,其它的資料型態不需要分號,直接連著就可以。

一些參考網站:
http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/types.html
http://87showmin.blogspot.tw/2009/06/java-java-native-interfacejni.html

Android NDK Eclipse 開發環境自動編譯設定

NDK不太常用,每次要用時都要重新翻資料找設定方法,乾脆寫一篇記錄下來。

環境設定,只需要一開始做一次:
1. 先確認 NDK 有安裝好:http://developer.android.com/sdk/ndk/index.html

2. 安裝 Eclipse 需要的套件:
CDT package
NDK package

3. 設定 NDK 路徑,工具列 Window -> Preferences -> Android -> NDK ,輸入 NDK 的路徑:
ndk path

專案設定,每個要使用 NDK 的專案都要做:
1. 專案上面按右鍵,Android Tools -> Add Native Support :
add_ndk_support
然後設定一個 library 的名稱:
enter library name
這時 Android.mk 與 C++ 原始碼檔案都幫你建立好了。

2. 設定 C++ Include 路徑,進入專案 Properties ,C/C++ General -> Paths and Symbols,加入:

include path
${env_var:ANDROID_NDK} 是我在系統 PATH 裡設定好的 NDK 所在位置。

額外的工具:使用 javah 自動產生 .h 檔
1. 設定 Eclipse External Tools ,工具列 Run -> External Tools -> External Tools Configurations :

  • Name 取一個與專案有關的比較好記。
  • Location 填入 javah 執行檔的路徑。
  • Working Directory 填入

    其中 NdkProject 是專案名稱。
  • Arguments 填入

    external tool 1
    最後的 com.example.ndkproject.MainActivity 是要使用 NDK 的類別的 fully qualified class name 。(若是在 Linux 底下,classpath 要改成 ${workspace_loc:/NdkProject/bin/classes})
  • 切換到 Refresh 分頁,勾選 Refresh resources upon completion ,使用 Specific resources 選項,在右邊選擇 jni 資料夾
    external tool 2

2. 在需要呼叫 NDK 的 Java 原始碼中加入程式,用來載入 library 並定義原生函式:

3. 按下工具列的 Run -> External Tools -> 剛剛設定的 javah ,即會幫你產生 Native 用的 .h 檔案。

可能遇到的問題
1. 出現警告: Android NDK: WARNING: APP_PLATFORM android-14 is larger than android:minSdkVersion 8 in ./AndroidManifest.xml
解決方法:在 jni 資料夾中新增 Application.mk 檔案,指定 SDK 的版本

以上都設定好,就會看到每次執行 debug 時,會自動使用 ndk 編譯了。

Win32 安裝 openssl 及 vc6 設定

網路上找得到一些編譯成DLL的, 但這邊要做的是static library, 也就是將openssl包進exe中不依賴DLL

1. 安裝ActivePerl

http://www.activestate.com/activeperl/downloads

2. 下載OpenSSL
http://www.openssl.org/source/

3. 將OpenSSL解壓縮到C:\
我這邊使用的是1.0.1c, 所以路徑是C:\openssl-1.0.1c

4. 打開cmd.exe, 執行 perl Configure, prefix是openssl要安裝的目錄, 並不是指下載來的source
cd c:\openssl-1.0.1c
perl Configure VC-WIN32 no-asm –prefix=c:/openssl/
ms\do_ms

5. 這時openssl已經準備好編譯了, 接續上面的cmd, 執行指令
nmake -f ms\nt.mak

6. 若過程順利, 沒有出現錯誤訊息, 即可進行安裝
nmake -f ms\nt.mak install

7. 設定vc6:
Tools -> Options -> Directories -> Show directories for: Include files
add: C:\openssl\include

Tools -> Options -> Directories -> Show directories for: Library files
add: C:\openssl\lib

** 錯誤:
a. 找不到 in6_addr?
編輯 C:\openssl-1.0.1c\apps\s_cb.c 並加入以下程式:
struct in6_addr {
union {
u_char Byte[16];
u_short Word[8];
} u;
};

b. 找不到nmake?
在cmd執行:
“c:\Program Files\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT”

perlbrew – 幫你安裝及管理多種版本的Perl

Perl用久了, 總是有機會遇到這種情況, 就是你需要另一個版本的Perl
不管是要安裝新程式, 要除錯, 要測試, 或是你只是想玩玩
想安裝新版Perl又不想動到原本運作得很好的版本, 有了perlbrew就變得很簡單了

perlbrew的安裝, 參考 http://search.cpan.org/dist/App-perlbrew/lib/App/perlbrew.pm
請確認你有安裝curl, 然後執行以下命令:

這樣子perlbrew就安裝好了, 接著你可以使用以下命令將perlbrew加入你的$PATH中
或是將此命令加到~/.bashrc中, 就可以不用每次都執行此指令

要怎麼知道有哪些版本可以安裝呢? 使用 perlbrew available 命令:

看得出來從古老的版本到新版本都有, 由於我現有的版本是5.8.8, 所以來安裝5.10.1試試看
使用 perlbrew install perl-5.10.1 來安裝, 也可以偷懶只打版本號 perlbrew install 5.10.1:

在這個畫面會一直停留到安裝好為止, 安裝時間視硬體設備而定, 我想十幾分鐘跑不掉
所以想要知道安裝進度的話, 就另開一個終端機, 使用這個指令看進度:

若是需要調整一些選項, 可以在 perlbrew install 後面加上 -D -U -A 等等參數
例如我需要Perl支援thread跟64bit等等的, 就這樣用:

等安裝好了以後, 我們可以用 perl -v 來確認現在跑的是什麼版本:

目前跑的是5.8.8, 使用 perlbrew switch來切換:

你看, 馬上就變成 5.10.1 了, 真是簡單又方便

當需要測試多個版本時, perlbrew還允許你一次讓所有的版本都執行同樣的指令:

這真是太神奇了阿滋

perlbrew 主要的指令有:

另外,這篇介紹也蠻詳細的:http://www.openfoundry.org/tw/tech-column/8514-perlbrew-perl-installation-management-tool