2012年6月14日 星期四

PandaBoard 教學筆記 - 調整 Android ICS Partition 大小

aosp-partition-resize


最近編完 AOSP 後,想要塞一堆資料進去,但明明一張記憶卡 8GB,但 Android ICS 裡頭任一個 Partition 最多才 500 多MB 耶,總空間很小,真是奇怪。但用 Ubuntu 的磁碟工具還是可以正常顯示出 7.2 GB 的 userdata,只是在 Ubuntu 系統上,透過讀卡機將資料搬進去,依舊看到檔案系統的大小限制。


磁碟公用程式-sdcard


實在找不出怎樣調整,於是就去追 AOSP 的 makefile 了,在 device/ti/panda/BoardConfig.mk 可以看到 /system、/userdata 的大小規畫:


aosp-partition-resize-mk


於是,把它調大後,接著砍掉 out/target/product/panda/system.img 和 out/target/product/panda/userdata.img 後,直接用 make 或分別用 make systemimage 和 make userdataimage,就能夠再生出新的 image 來,再透過 fastboot 燒進 SDCard 後,就能夠看到比較大的空間囉。


2012年6月11日 星期一

[LLVM] Building LLVM for Android ICS / Pandaboard @ Ubuntu 10.04

 + 


研究了一下怎樣編譯 LLVM for Pandaboard,過程中發現並不是那麼順利的,網路上僅有少數的資訊,並且頂多是 bug report 的等級,沒有講任何的解法,而我只是暫時找到應對的惡搞方式。


首先,先練習一下 Ubuntu 12.04 on Pandaboard,流程就像在 x86 編一樣:


Device: Pandaboard ES
OS: ubuntu-12.04-preinstalled-server-armhf+omap4
SD: Transcend SDHC Class10 8GB



  1. Install Ubuntu 12.04 server on Pandaboard (ubuntu-12.04-preinstalled-server-armhf+omap4.img.gz)

  2. Initial building enviroments 


    • $ sudp apt-get update && sudo apt-get upgrade 

    • $ sudo apt-get install subversion gcc g++ make cmake 


  3. Get LLVM/Clang source code


    • $ svn co http://llvm.org/svn/llvm-project/llvm/tags/RELEASE_31/final/ ~/llvm-src/

    • $ svn co http://llvm.org/svn/llvm-project/cfe/tags/RELEASE_31/final/ ~/llvm-src/tools/clang

    • $ svn co http://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_31/final/ ~/llvm-src/projects/compiler-rt


  4. Built it


    • $ mkdir ~/bulid && cd ~/build && cmake -DCMAKE_INSTALL_PREFIX:PATH=$HOME/out ../llvm-src

    • $ make -j2 (約120分鐘,且 Pandaboard 是雙核心,故 -j2)

    • $ make install


  5. Testing


    • $ ~/out/bin/llvm-config --version
      3.1svn

    • $ ~/out/bin/clang --version
      clang version 3.1 (tags/RELEASE_31/final 158045)
      Target: armv7l-unknown-linux-gnueabihf
      Thread model: posix



很不錯,編出來了,指令操作也很正常,然而,我以為這樣編出來就可以移到 Android ICS 上跑,真是個錯誤的觀念 XD


$ ~/android-sdks/platform-tools/adb shell mkdir /data/ullvm
$ ~/android-sdks/platform-tools/adb push arm-ubuntu-out/bin/clang /data/ullvm/
$ ~/android-sdks/platform-tools/adb shell /data/ullvm/clang
/system/bin/sh: /data/ullvm/clang: No such file or directory


爾後,我才想起 cross compiler 的事情,找了一下 AOSP 裡頭的東西,也就是那些 prebuilt/prebuilts 目錄,翻啊翻,我才發現 NDK 那一包裡頭已經有了,而使用的方式並不是把 CC/CXX 直接指到目錄,先透過 android-ndk-r8/build/tools/make-standalone-toolchain.sh 建立 standalone toolchain 囉,在指定 CC/CXX 位置(此處用 cmake 設定檔)。


工作環境:


OS: Ubuntu 10.04 server x84_64


建立 standalone-toolchain:


$ sh android-ndk-r8/build/tools/make-standalone-toolchain.sh
$ sh android-ndk-r8/build/tools/make-standalone-toolchain.sh --install-dir=$HOME/android-14-toolchain --platform=android-14


透過 CMAKE 編譯前,先寫隻 ~/Android.cmake 初始工作環境:


$ vim ~/Android.cmake
SET(CMAKE_SYSTEM_NAME Linux) # Tell CMake we're cross-compiling
include(CMakeForceCompiler)
# Prefix detection only works with compiler id "GNU"
# CMake will look for prefixed g++, cpp, ld, etc. automatically
CMAKE_FORCE_C_COMPILER(/home/user/android-14-toolchain/bin/arm-linux-androideabi-gcc GNU)
CMAKE_FORCE_CXX_COMPILER(/home/user/android-14-toolchain/bin/arm-linux-androideabi-g++ GNU)
SET(ANDROID TRUE)


正式編譯:


$ mkdir build && cd ~/build && cmake -DCMAKE_TOOLCHAIN_FILE=$HOME/Android.cmake -DCMAKE_INSTALL_PREFIX:PATH=$HOME/out ../llvm-src/
$ make
...
Scanning dependencies of target gtest
[100%] Building CXX object utils/unittest/CMakeFiles/gtest.dir/googletest/gtest.cc.o
/home/user/llvm-src/utils/unittest/googletest/gtest.cc: In static member function 'static bool
esting::internal::String::CaseInsensitiveWideCStringEquals(const wchar_t*, const wchar_t*)':
/home/user/llvm-src/utils/unittest/googletest/gtest.cc:1625: error: 'wcscasecmp' was not declared in this scope
make[2]: *** [utils/unittest/CMakeFiles/gtest.dir/googletest/gtest.cc.o] Error 1
make[1]: *** [utils/unittest/CMakeFiles/gtest.dir/all] Error 2
make: *** [all] Error 2


此處不能用 make -j# 的方式進行,似乎無法平行進行?暫時沒空研究。此外並不是這樣就搞定了,只搞定編譯出 bin/lib/include/share 的產出物,且原先編譯完還會跑 testing,只是碰到 test case 無法編譯出來,暫時就不先管了,有興趣的可以在研究囉。


取出 ~/build/bin 丟進 PandaBoard 中(請留意空間,bin約3xxMB):


$ ~/android-sdks/platform-tools/adb shell df
Filesystem Size Used Free Blksize
/dev 363M 32K 363M 4096
/mnt/asec 363M 0K 363M 4096
/mnt/obb 363M 0K 363M 4096
/system 251M 141M 110M 4096
/data 503M 51M 452M 4096
/cache 251M 4M 247M 4096
/mnt/sdcard 503M 51M 452M 4096
$ ~/android-sdks/platform-tools/adb shell mkdir /data/llvm
$ ~/android-sdks/platform-tools/adb push ~/build/bin /data/ullvm/bin
push: ...
42 files pushed. 0 files skipped.
3065 KB/s (343386572 bytes in 109.394s)
$ ~/android-sdks/platform-tools/adb shell /data/llvm/bin/clang --version
clang version 3.1 (tags/RELEASE_31/final)
Target: x86_64-unknown-linux-gnu
Thread model: posix
$ ~/android-sdks/platform-tools/adb shell /data/llvm/bin/llvm-config --version
3.1svn


 測試:


$ vim hello.c
#include <stdio.h>
int main() {
        printf( "Hello World\n" );
        return 0;
}
$ clang -emit-llvm -o hello.bc -c hello.c
$ ~/android-sdks/platform-tools/adb push hello.bc /data/llvm/


出現以下訊息,代表你用的 clang/LLVM 版本跟 LLVM on Android 版本不一致(LLVM沒有向下相容且更新變化快):


$ ~/android-sdks/platform-tools/adb shell /data/llvm/bin/lli /data/llvm/hello.bc
/data/llvm/bin/lli: /data/llvm/hello.mc: error: Invalid MODULE_CODE_GLOBALVAR record


若版本一致的結果:


$ ~/android-sdks/platform-tools/adb shell /data/llvm/bin/lli /data/llvm/hello.bc
LLVM ERROR: Program used external function 'printf' which could not be resolved!


最後分享一下改了啥東西:



參考資料:


Beuc's Blog/ posts/ Cross-compiling with CMake


[LLVM] Using Bitcode on Hadoop Streaming @ Ubuntu 10.04

 + 


前幾天突發奇想,在 LLVM/Bitcode 的架構可以用在哪邊,結果看了一下 LLVM 官網介紹,其實 bitcode 也可以像 script 一樣被執行:


lli - directly execute programs from LLVM bitcode
Getting Started with the LLVM System#Optional Configuration Items


故 bitcode 也能像 script 一樣被系統認出、執行,只是這樣的情況就是每一台 node 都必須必備 LLVM 環境才能執行 bitcode ,跟每台 node 都要安裝 JVM 才能跑 Hadoop 一樣。


此外,在 2009 年有篇文章提出 Hadoop should target C++/LLVM, not Java (because of watts) 設計觀點也可以參考一下,但這一切就是取捨,想要好效能就得付出些什麼,一切都是等價交換啦!


簡易 LLVM/clang 操作範例:



  • 從 source code 產生 native code 


    • $ clang -o source.exe source.c


  • 從 source code 產生 bitcode


    • $ clang -emit-llvm -c -o source.bc source.c


  • 從 bitcode 產生 native code


    • $ llvm-ld source.bc -native -o source.exe


  • 從 bitcode 執行


    • $ lli source.bc



因此,在 Hadoop 的架構下,可以用 Hadoop Streaming 與 lli bitcode.bc 來結合:


$ hadoop jar /path/Streaming.jar -file /path/my_mapper.bc -mapper "lli my_mapper.bc" -file /path/my_reducer.bc -reducer "lli my_reducer.bc" -input in -output out


測試方面,如同 Hadoop Streaming 之 Word Count 範例 技巧:


$ echo "A B AB BA B A" | lli /path/wordcount_mapper.bc | sort -k1,1 | lli /path/wordcount_reducer.bc


2012年5月29日 星期二

[LLVM] 編譯、使用 Clang Plugin @ Ubuntu 10.04 64Bit


圖: http://llvm.org/Logo.html


身為速食工作者,我本身不太喜歡浪費太多時間在瑣碎的細節,在加上本業非 Compiler 背景,只是從 Google 那邊翻到一些編譯、執行 Clang Plugin 的方式,順便筆記下來,畢竟網路上的資料真的不多,也該為繁體中文留下點足跡吧。


操作環境:


OS: Ubuntu Server 64Bit
llvm src location: ~/llvm
clang src location: ~/llvm/tools/clang
gcc: gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3


首先依照 LLVM 官網介紹的方式,下載 LLVM、clang 等相關程式碼,接著編譯,之後換到 llvm/tools/clang/example 目錄中,裡頭有一個 clang plugin 範例程式 PrintFunctionNames,僅需切換進入此目錄打 make 後,即可編出來 (細節都寫在llvm/tools/clang/examples/PrintFunctionNames/README.txt)


其中,有兩種編譯方式,第一種是直接在 llvm 目錄下,執行 configure 後再打 make -j4 即可編譯出 llvm 和 clang,接著切換到 llvm/tools/clang/examples/PrintFunctionNames 裡頭在執行 make 後,東西就產生了;另一個編法是用 cmake 把 source 跟產出分開,例如 mkdir ~/build && cd ~/build && cmake ~/llvm && make -j4 後,接著切換到 ~/build/tools/clang/examples/PrintFunctionNames,執行 make 後,產出物在 ~/build/lib/PrintFunctionNames.so。由於使用官方推薦的 configure 方式有碰到問題,所以我就改用 cmake 方式,也意外發現 cmake 編得比較快(不知有沒遺漏什麽)。


編譯完,就可以用 clang -ccl -load /path/libPrintFunctionNames.so -plugin print-fns some-input-file.c 執行。(若用 cmake 編出的是 /path/PrintFunctionNames.so)


大部分的 clang plugin 範例就是從 PrintFunctionNames 改來的,介紹一下 PrintFunctionNames 的使用方式:


$ vim t1.c
int main() {
return 0;
}

$ clang -ccl -load /path/PrintFunctionNames.so -plugin print-fns t1.c
top-level-decl: "__va_list_tag"
top-level-decl: "__va_list_tag"
top-level-decl: "__builtin_va_list"
top-level-decl: "main"

$ vim t2.c
#include
int main(int argc,char *argv[] ) {
printf("Hello World!\n");
return 0;
}

$ clang -ccl -load /path/PrintFunctionNames.so -plugin print-fns t2.c
top-level-decl: "__va_list_tag"
top-level-decl: "__va_list_tag"
/path/t2.c:1:10: fatal error: 'stdio.h' file not found
#include
^
top-level-decl: "__builtin_va_list"
top-level-decl: "main"
1 error generated.

這是因為 clang 不知哪邊找 stdio.h 檔,所以就透過 -I 指令去指定吧(過程中還需 stddef.h)


$ clang -cc1 -I/usr/include -I/usr/lib/gcc/x86_64-linux-gnu/4.4.3/include -load /path/PrintFunctionNames.so -plugin print-fns ~/t2.c
top-level-decl: "__va_list_tag"
top-level-decl: "__va_list_tag"
top-level-decl: "__builtin_va_list"
top-level-decl: "size_t"
...
top-level-decl: "ctermid"
top-level-decl: "flockfile"
top-level-decl: "ftrylockfile"
top-level-decl: "funlockfile"
top-level-decl: "main"

其中 -I/usr/include 是找 stdio.h,而 I/usr/lib/gcc/x86_64-linux-gnu/4.4.3/include 是找 stddef.h (依個人安裝位置不同)


然而,每次執行都用 clang -load *.so -plugin ... 指令進行有點麻煩,所以另一種使用方式就是寫隻 main 程式,在裡頭呼叫 clang 相關函數物件來操作,這樣的好處是可以編出一隻 tool 來用,也不用每次執行時下很長的指令了:


$ mkdir ~/print-fns-tools
$ cp ~/llvm/tools/clang/examples/PrintFunctionNames/PrintFunctionNames.cpp ~/print-fns-tools/main.cpp
$ vim ~/print-fns-tools/main.cpp

// 在檔案最後面新增程式碼
#include "llvm/Support/Host.h"
#include "clang/Parse/ParseAST.h"
#include "clang/Basic/FileManager.h"


#include "clang/Frontend/HeaderSearchOptions.h"
#include "clang/Frontend/Utils.h"


#include <iostream>
int main(int argc, char *argv[]) {
        clang::CompilerInstance *ci = new clang::CompilerInstance();
        ci->createDiagnostics(0,NULL);
        clang::TargetOptions to;
        to.Triple = llvm::sys::getDefaultTargetTriple();
        clang::TargetInfo *pti = TargetInfo::CreateTargetInfo(ci->getDiagnostics(), to);
        ci->setTarget(pti);
        ci->createFileManager();
        ci->createSourceManager( ci->getFileManager() );
        ci->createPreprocessor();
        ci->createASTContext();


        if( argc < 2 ) {
                std::cout << "Usage: " << argv[0] << " [-I /usr/include] file.c " << std::endl;
                return 0;
        } else if ( argc >= 4 ) {
                clang::HeaderSearchOptions headerSearchOptions;
                for( int i=1 ; i<argc-1 ; ++i )
                        if( argv[i][0] == '-' && argv[i][1] == 'I' && i+1 < argc ) {
                                std::cout << "Search header: " << argv[i+1] << std::endl;
                                headerSearchOptions.AddPath( argv[++i] , clang::frontend::Angled, false, false, false );
                        }


                clang::PreprocessorOptions preprocessorOptions;
                clang::FrontendOptions frontendOptions;
                clang::InitializePreprocessor( ci->getPreprocessor(), preprocessorOptions, headerSearchOptions, frontendOptions );
        }


        std::cout << "Target: " << argv[argc-1] << std::endl;
        ci->getSourceManager().createMainFileID( ci->getFileManager().getFile(argv[argc-1]) );


        PrintFunctionsConsumer *mConsumer = new PrintFunctionsConsumer();
        ParseAST(ci->getPreprocessor(), mConsumer, ci->getASTContext());


        delete ci;
        delete mConsumer;


        return 0;
}


直接編譯 main.cpp (假設環境變數中可以取得 clang++、llvm-config 指令,否則取代成 /path/clang++、/path/llvm-config 的用法):


$ cd print-fns-tools/
$ clang++ `llvm-config --cxxflags` -fno-rtti main.cpp -lclangFrontend -lclangDriver -lclangSerialization -lclangParse -lclangSema -lclangAnalysis -lclangRewrite -lclangEdit -lclangAST -lclangLex -lclangBasic -lLLVMMC -lLLVMSupport `llvm-config --ldflags --libs cppbackend`


操作使用:


$ ./a.out
Usage: ./a.out [-I /usr/include] file.c

$ ./a.out t1.c
Target: t1.c
top-level-decl: "__va_list_tag"
top-level-decl: "__va_list_tag"
top-level-decl: "__builtin_va_list"
top-level-decl: "main"

$ ./a.out t2.c
Target: t2.c
top-level-decl: "__va_list_tag"
top-level-decl: "__va_list_tag"
a.out: /path/llvm/tools/clang/lib/Frontend/TextDiagnosticPrinter.cpp:158: virtual void clang::TextDiagnosticPrinter::HandleDiagnostic(clang::DiagnosticsEngine::Level, const clang::Diagnostic&): Assertion `TextDiag && "Unexpected diagnostic outside source file processing"' failed.
Stack dump:
0. t2.c:1:2: current parser token 'include'
Aborted

$ ./a.out -I /usr/include -I /usr/lib/gcc/x86_64-linux-gnu/4.4.3/include t2.c
Search header: /usr/include
Search header: /usr/lib/gcc/x86_64-linux-gnu/4.4.3/include
Target: t2.c
top-level-decl: "__va_list_tag"
top-level-decl: "__va_list_tag"
top-level-decl: "__builtin_va_list"
top-level-decl: "size_t"
...
top-level-decl: "ftrylockfile"
top-level-decl: "funlockfile"
top-level-decl: "main"

總結一下,原先使用 clang -plugin 的方式啟動,本身有 clang 這個環境可以操作,而單純寫成一隻 toolbase 方式,則需要初始化 clang 的操作環境,這就是在 main 裡頭做的事,包含建立一個 Compiler Instance 及其初始化、設定 header search 位置、要處理的 file.c 等等,如此一來就完成了。


其他筆記(用 clang++ 編譯時出現訊息): 



  • undefined reference to `typeinfo for clang::ASTConsumer'


    • 編譯時加上 "-fno-rtti" 參數即可解決


  • error: no member named 'getDefaultTargetTriple' in namespace 'llvm::sys'
    to.Triple = llvm::sys::getDefaultTargetTriple();


    • 加上 #include "llvm/Support/Host.h" 即可解決


  • error: member access into incomplete type 'clang::FileManager'
    note: forward declaration of 'clang::FileManager'


    • 加上 #include "clang/Basic/FileManager.h" 即可解決 



最後一提,網路上資源不見得少,但因為語系與背景不同,常常關鍵字下很久都找不到,無意間還找到 Google 員工在去年論壇討論串裡,正抱怨著 clang plugin 很難寫 XDD 當下我才發現,大家都是普通人 XDDD 此外,有興趣可以多多參考這裡:https://github.com/loarabia/Clang-tutorial,裡頭有簡單又豐富的程式碼、makefile 喔。


2012年5月28日 星期一

[iOS] 使用 absinthe-win-2.0.2 來 Jailbreak iPad 1 iOS 5.1.1 @ VirtualBox


圖:http://greenpois0n.com/


這年頭 Jailbreak 越來越方便 XD 最重要的是漸漸可以用 VirtualBox 完工了!甚至 JB 程式不只 WindowsMac 版,還推出了 Linux 版。真是有夠威。建議要使用的人還是連去官網下載吧(還用Google Docs哩)!以免不小心抓到木馬。


jailbreak jailbreak_done


此次環境:


Guest OS:Windows XP SP3
Host OS:Windows 7 64Bit
Target:iPad 1 WiFi 版 with iOS 5.1.1 (9B206)


首先把 iPad 1 升到 iOS 5.1.1,再來使用 VirtualBox 來進行 JB,在 C:\ 點選剛下載好的 absinthe-win-2.0.2.exe 後自動解壓縮一個目錄,接著再進去點選該執行檔即可。(如果 iPad 1 已在 JB 狀態下了,無法透過網路升級時,可直接下載 iPad1,1_5.1.1_9B206_Restore.ipsw 並透過 iTunes 回復方式來升級)


如果使用上執行進度卡住時,可以試試把 VirtualBox 重新接上 iPad 看看,畢竟 iPad 裝置有 3 個 status,每一個狀態被偵測的項目是不一樣的,如 Apple Inc. Apple Mobile Device (Recovery Mode)、Apple Inc. Apple Mobile Device (DFU Mode)、Apple Inc. iPad [0001] 等等或是把 iPad 各個狀態都在 VirtualBox 設定好 USB 自動偵測使用應該也能解掉,此外也要留意 Host 上頭若有裝 iTunes 程式,要小心這些程式佔用資源,將導致 VirtualBox 無法取用 iPad 喔,解法大概就用 taskmgr 去把 AppleMobileDeviceService.exe 、iPosService.exe 等相關的關掉在試看看吧