2012年11月29日 星期四

[iOS] tweak project development


在jailed break的手機當中, 我們如果想要取代掉/新增一些原本apple提供的功能, 我們就必須要寫tweak(我自己理解就是寫hook, 然後讓系>統會自動呼叫到你寫的callback function).

要達到這件事情, 除了之前教的先安裝theos之外, 我們還必須使用ios private framework (也就是apple沒公開的SDK), 這邊有兩種方式


  1. generate header from class-dump or class-dump-z tool
    $ class-dump -H /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices
    $ ls
    BKSAccelerometerDelegate-Protocol.h              SBSCardItem.h
    CDStructures.h                                   SBSCardItemsController.h
    NSCoding-Protocol.h                              SBSCardItemsControllerRemoteInterface-Protocol.h
    NSCopying-Protocol.h                             SBSCompassAssertion.h
    NSObject-Protocol.h                              SBSLocalNotificationClient.h
    SBAppLaunchUtilities.h                           SBSPushStore.h
    SBCardItemsControllerRemoteInterface-Protocol.h  SBSRemoteNotificationClient.h
    SBLaunchAppListener.h                            XPCProxyTarget-Protocol.h
    SBSAccelerometer.h
    
  2. copy others from internet
    (1) https://github.com/nst/iOS-Runtime-Headers/tree/master/PrivateFrameworks
    (2) https://github.com/rpetrich/iphoneheaders
        這是我用的, 使用這個你必須要做一些額外的工作
        
$ find /Applications/Xcode.app/ -name IOSurfaceAPI.h
$ cp /Applications/Xcode.app//Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk/System/Library/Frameworks/IOSurface.framework/Versions/A/Headers/IOSurfaceAPI.h $THEOS/include/IOSurface/
Comment out the following line in this file (IOSurfaceAPI.h):

/* This call lets you get an xpc_object_t that holds a reference to the IOSurface.                                           
   Note: Any live XPC objects created from an IOSurfaceRef implicity increase the IOSurface's global use
   count by one until the object is destroyed. */
//xpc_object_t IOSurfaceCreateXPCObject(IOSurfaceRef aSurface)
//  IOSFC_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_NA);
                              
/* This call lets you take an xpc_object_t created via IOSurfaceCreatePort() and recreate an IOSurfaceRef from it. */
//IOSurfaceRef IOSurfaceLookupFromXPCObject(xpc_object_t xobj)
//  IOSFC_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_NA);


我自己是使用別人產生的header, 把這些header copy到$THEOS/include 下面, 接下來就可以進行tweak project了

$ /opt/theos/bin/nic.pl 
NIC 2.0 - New Instance Creator
------------------------------
  [1.] iphone/application
  [2.] iphone/library
  [3.] iphone/preference_bundle
  [4.] iphone/tool
  [5.] iphone/tweak
Choose a Template (required): 5
Project Name (required): ooooo
Package Name [com.yourcompany.ooooo]: 
Author/Maintainer Name [ytshen]:       
[iphone/tweak] MobileSubstrate Bundle filter [com.apple.springboard]: 
Instantiating iphone/tweak in ooooo/...
Done.
$ ls ooooo/
Makefile    Tweak.xm    control     ooooo.plist theos


接下來你就可以進行tweak的實作, 這邊我在xcode 4.5 + mac os 10.8 lion下面會遇到一些問題, 像是找不到arm6 symbol, 這是因為後來只支援arm7.

要修改Makefile

export ARCHS=armv7
export TARGET=iphone:latest:4.3
SDKVERSION=6.0
 
include theos/makefiles/common.mk
   
TWEAK_NAME = ooooo
ooooo_FILES = Tweak.xm
   
include $(THEOS_MAKE_PATH)/tweak.mk


並且要設定environment $SYSROOT 到你sdk的目錄.

$ echo $SYSROOT 
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/
這邊使用一個很簡單的例子, 我們要改spring board一開始秀出一個alert (spring board就是iOS的桌面程式), 修改Tweak.xm:

#import 
 
%hook SpringBoard
 
-(void)applicationDidFinishLaunching:(id)application {
    %orig; // call the original method
    
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Welcome" 
        message:@"Welcome to your iPhone Brandon!" 
        delegate:nil 
        cancelButtonTitle:@"Thanks" 
        otherButtonTitles:nil];
    [alert show];
    [alert release];
}
%end
接下來就按照之前介紹的theos, make, make package, make package install. 就可以把這個tweak安裝在iPhone上面!

Reference

http://brandontreb.com/beginning-jailbroken-ios-development-your-first-tweak/
http://www.andyibanez.com/2012/07/02/create-a-mobilesubstrate-tweaks/
class-dump:
http://stevenygard.com/projects/class-dump/

2012年11月16日 星期五

[C++] static array size macro

We usually get array size by using the following macro.
#define count_of(arg) (sizeof(arg) / sizeof(arg[0]))
But this code would not get any compile error when we pass the pointer as argument like below:
void Test(int C[3])
{
  int A[3];
  int *B = Foo();
  size_t x = count_of(A); // Ok
  x = count_of(B); // Error
  x = count_of(C); // Error
}
One way is to declare like this: (pass the reference which indicate the number of array)
void Test(int (&C)[3])
{
  ...
}
// reference http://www.cplusplus.com/articles/D4SGz8AR/
Now the following code snippet is creating template function. The function template is named ArraySizeHelper, for a function that takes one argument, a reference to a T [N], and returns a reference to a char [N]. If you give it a non-array type (e.g. a T *), then the template parameter inference will fail.
// reference http://www.viva64.com/en/a/0074/
// http://stackoverflow.com/questions/6376000/how-does-this-array-size-template-work
template 
char (&ArraySizeHelper(T (&array)[N]))[N];
#define arraysize(array) (sizeof(ArraySizeHelper(array)))

2012年10月25日 星期四

[GCC] C++ compiler option for static analysis

GCC compiler option for some C++ checking.

==========================================


-Weffc++ (C++ and Objective-C++ only)                                                                                                                                                   
   Warn about violations of the following style guidelines from Scott Meyers' Effective C++ book:
   
   ·   Item 11:  Define a copy constructor and an assignment operator for classes with dynamically allocated memory.
   
   ·   Item 12:  Prefer initialization to assignment in constructors.
   
   ·   Item 14:  Make destructors virtual in base classes.
   
   ·   Item 15:  Have "operator=" return a reference to *this.
   
   ·   Item 23:  Don't try to return a reference when you must return an object.
   
   Also warn about violations of the following style guidelines from Scott Meyers' More Effective C++ book:
   
   ·   Item 6:  Distinguish between prefix and postfix forms of increment and decrement operators.
   
   ·   Item 7:  Never overload "&&", "||", or ",".
   
   When selecting this option, be aware that the standard library headers do not obey all of these guidelines; use grep -v to filter out those warnings.


-Woverloaded-virtual (C++ and Objective-C++ only)
    Warn when a function declaration hides virtual functions from a
    base class.

-Wsign-promo (C++ and Objective-C++ only)
    Warn when overload resolution chooses a promotion from unsigned or
    enumerated type to a signed type, over a conversion to an unsigned
    type of the same size.  Previous versions of G++ would try to
    preserve unsignedness, but the standard mandates the current
    behavior.

-Wextra
    This enables some extra warning flags that are not enabled by
    -Wall.

2012年10月17日 星期三

[C++] flexible array

最近看到squid某個patch, 看到比較少用的flexible array, 在clang下面支援不足 (non-POD type), 所取得的備案.
What is Flexible arrays: Flexible array通常是使用在struct 最後一個member, 來讓struct可以使用不定長度的array. Ex:
     struct line {
       int length;
       char contents[0];
     };
     
     struct line *thisline = (struct line *)
       malloc (sizeof (struct line) + this_length);
     thisline->length = this_length;

Reference:
http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Zero-Length.html

Notes:
Flexible was introduced in C99 standard but not in C++. Clang didn't support non-POD type flexible array.

Discuss thread

remove flexible array for clang support 
http://www.squid-cache.org/mail-archive/squid-dev/201208/0008.html

find bug about previous patch
http://www.squid-cache.org/mail-archive/squid-dev/201210/0156.html

other solution
http://www.squid-cache.org/mail-archive/squid-dev/201210/0186.html

It introduce a wrapper which help to preserve shared memory usage by using placement-new operator. The caller must allocate enough memory when constructing FlexibleArray which capacity must match the memory.

+template 
+class FlexibleArray
+{
+public:
+    explicit FlexibleArray(const int capacity) {
+        if (capacity > 1) // the first item is initialized automatically
+            new (items+1) Item[capacity-1];
+    }
+
+    Item &operator [](const int idx) { return items[idx]; }
+    const Item &operator [](const int idx) const { return items[idx]; }
+
+    //const Item *operator ()() const { return items; }
+    //Item *operator ()() { return items; }
+
+    Item *raw() { return items; }
+
+private:
+    Item items[1]; // ensures proper alignment of array elements
+};

2012年7月26日 星期四

[iOS] JB program writting


iOS下面的APP限制蠻多的 (ex: 進入背景之後, 最多只有10min可以作網路相關的事情), 如果想要突破限制就得要JB, 紀錄一下如何寫JB之後的程式.

在iOS底下, 所有的APP可以放在兩個地方/Applications/<your app>.app and /var/mobile/Applications/<id>/<your app>.app (一般經由iTune or XCode 安裝的APP都會在此), 所有在/var/mobile/Applications的app 都會跑在自己的sandbox, 而且也沒有讀取整個系統的權限, 為了能夠突破iOS的限制, 以下是如何產生可以直接安裝到/Applications 取得root 權限 APP作法 (注意必須手機要經過JB):

一般都是使用 Theos 來產生你想要得APP project.

安裝步驟:
安裝iOS SDK
安裝theos
利用theos建立新的project (可以看到自動幫你產生出的檔案)

$ export THEOS=/opt/theos
$ svn co http://svn/howett.net/svn/theos/trunk $THEOS
$ curl -s http://dl.dropbox.com/u/3157793/ldid > ~/Desktop/ldid
$ chmod +x ~/Desktop/ldid
$ mv ~/Desktop/ldid $THEOS/bin/ldid
$ sudo port install dpkg
$ ./$THEOS/bin/nic.pl
IC 1.0 - New Instance Creator
------------------------------
  [1.] iphone/application
  [2.] iphone/library
  [3.] iphone/preference_bundle
  [4.] iphone/tool
  [5.] iphone/tweak
Choose a Template (required): 1
Project Name (required): zzz
Package Name [com.yourcompany.zzz]: 
Author/Maintainer Name [ytshen]: zzz
Instantiating iphone/application in zzz/...
Done.
$ls zzz
Makefile              RootViewController.h  control               theos
Resources             RootViewController.mm main.m                zzzApplication.mm
$


從Makefile上面你可以更改你的application name, 所有你需要compile object-c file (ex: .m .mm), 還有你需要include的framework:

include theos/makefiles/common.mk                                                                               
                                                                                                                
APPLICATION_NAME = xxx                                                                                 
xxx_FILES = main.m XXXApplication.mm RootViewController.mm 
xxx_FRAMEWORKS = UIKit CoreGraphics 
                                                                                                                
include $(THEOS_MAKE_PATH)/application.mk


接下來就是要如何build project, 並且deploy到手機上面:

$ export SDKVERSION=5.1
$ export THEOS_DEVICE_IP=192.168.1.11

# build project
$ make
Making all for application xxx...
 Compiling main.m...
 Compiling XXXApplication.mm...
 Compiling RootViewController.mm...
 Linking application XXX...
 Stripping XXX...
 Signing XXX...

# build deb
$ make package
Making all for application CameraUpload...
make[2]: Nothing to be done for `internal-application-compile'.
Making stage for application XXX...
 Copying resource directories into the application wrapper...
dpkg-deb: building package `com.ttt.xxx' in `./com.ttt.xxx_0.0.1-50_iphoneos-arm.deb'.

# install deb in device 
$ make package install


其他像是你想要更改APP icon或是跟一些resource相關, 你可以修改 Resource/Info.plist 達到你的要求.

Debugging:
使用這種方式的APP在debug上面沒辦法使用xcode的debug console, 所以只能透過syslog. 所以你可以透過Cydia安裝 "Erica Utilities" (for 'tail' utility), "Mobile Terminal", "OpenSSH", "SBSettings", "syslogd", "Syslog Toggle" 來達到debug的目的 安裝完之後, 你就可以透過ssh連上device在上面直接觀看syslog log.

root# tail -f /var/log/syslog 
Jul 27 13:19:16 hayokushin-mato-iPhone com.apple.launchd[1] (com.ikey.bbot): (com.ikey.bbot) Throttling respawn: Will start in 10 seconds
Jul 27 13:19:16 hayokushin-mato-iPhone syncdefaultsd[1374]: com.apple.stocks has no valid com.apple.developer.ubiquity-kvstore-identifier entitlement
Jul 27 13:19:16 hayokushin-mato-iPhone syncdefaultsd[1374]: Can't get application info for com.apple.stocks
Jul 27 13:19:31 hayokushin-mato-iPhone syncdefaultsd[1374]: com.apple.stocks has no valid com.apple.developer.ubiquity-kvstore-identifier entitlement

Reference:

2012年6月17日 星期日

[TCP] TIME_WAIT state


Usually when I terminate the server and want to bind the port again, it will show some error that I can not bind the port because the port is in TIME_WAIT state. As a result, I will need to set socket option (SO_REUSEADDR) to re-bind the port. In the past, I only know that there is some work left inside the kernel to handle the connection (from http://stackoverflow.com/questions/577885/uses-of-so-reuseaddr). But according to the link in the bottom, there could be two issues which we need TIME_WAIT state!


  1. there is no way to be sure that the last ack was communicated successfully, TIME_WAIT to make sure it can wait and retransmission last ack if the other end sned FIN again.
  2. there may be "wandering duplicates" left on the net that must be dealt with if they are delivered.


More detail in TIME_WAIT explainthis and protocol design implication

2012年4月22日 星期日

[C++] class member pointer usage

Use C++ member pointer for container.
#include <iostream>
#include <vector>
#include <map>
#include <functional>
                
class Foo       
{               
public:         
    Foo() {}    
    ~Foo() {}   
    int A(int a)
    {           
        return a+1;
    }             
    int B(int a)
    {           
        return a+2;
    }           
};              
                
int main()      
{               
    // use mem_fun_ref which can be used for for_each ... etc
    std::vector<std::mem_fun1_ref_t<int, Foo, int> > vec;   
    std::map<std::string, std::mem_fun1_ref_t<int, Foo, int> > map1;
    Foo a;      
    std::string s("aa");
                
    vec.push_back(std::mem_fun_ref(&Foo::A));
    map1.insert(std::pair<std::string, std::mem_fun1_ref_t<int, Foo, int> >(s, std::mem_fun_ref(&Foo::A)));
    std::cout << vec[0](a, 1) << std::endl;
    std::cout << map1.begin()->second(a, 2) << std::endl;   
                
    // use function pointer directly
    typedef int (Foo::*FuncPtr)(int);
    std::vector<FuncPtr> vec2;
    vec2.push_back(&Foo::A);
    vec2.push_back(&Foo::B);
    std::cout << (a.*(vec2[1]))(3) << std::endl; // it should be 5
                
    std::map<std::string, FuncPtr> map2;
    map2[s] = &Foo::B;
    std::cout < (a.*(map2[s]))(3) < std::endl; // it should be 5
    return 0;   
}               

2012年1月28日 星期六

[PThread] condition variable usage pattern

producer/consumer model. The return value for each pthread function call need to be checked too.

Producer:


while(/* condition for producer to contintue */)
{
pthread_mutex_lock(&mutex);

// do something for producer

// the order of the two following call is not important
// unlock before signal may more efficient than signal before unlock
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond);
}

Consumer:


while(/* condition for consumer to continue */)
{
// must contain lock before calling condition wait
pthread_mutex_lock(&mutex);

// the condition wait need to be recheck for
// 1. spurious wakeup
// 2. other thread can be waken early
while(/* condition to check for state not valid */)
{
s = pthread_cond_wait(&cond, &mutex);
}
// do consumer work, now under mutex protection
pthread_mutex_unlock(&mutex);
// some other work which don't need mutex
}

Spurious wakeup:
Some condition wait implementation can cause the thread to be waken up when no signal to the condition variable.
man pthreadconidsignal for further detail.

2012年1月15日 星期日

[TCP] some tuning options

The IPTOS_LOWDELAY keyword is most appropriate for low-delay networks such as LANs, while IPTOS_THROUGHPUT is for higher-latency WAN links. Your network may be configured differently, so it's possible that using the options might have the opposite effect.

The TCP_NODELAY option disables the Nagle algorithm.

If you have firewalls or other devices that keep state in your network, you may be interested in the SO_KEEPALIVE option, which turns on TCP keepalives.

$ netstat -d -i 2

Every 2 seconds, the status of the available interfaces is shown. In the example above, the RX-OK and TX-OKcolumns show that packets are flowing in and out. The errors, drops, and overruns are all 0 in both directions, showing that there has been no packet loss.


2012年1月9日 星期一

[C++] Exception-safe code



Some guide

Exception-safe definition:
- Basic
    invariants and no resource are leaked
- Strong
    operations has either completed successfully or throw exception leaving program
    state exactlyly as it was before the operation started
- No-throw
    operation will not emit an exception

Exception guide line:
- Throw by value
- Catch by reference
- no exception specification
                                                                                                                                                                                                           
* function can not template partial specialization

Some code example:

#include <iostream>     
void foo() throw ()
{                  
    // it will cause abort 
    // because it throw when the exception specification said NO
    throw(1);      
}                  
                   
void goo()         
{                  
    try {          
        int a = 1; 
        throw(1);  
    } catch(...) { 
        // this will cause abort too 
        // because you can not rethrow in catch statement
        throw;     
    }              
}                  
                   
/*                 
 * The following is the C API for C++ library pattern to handle exception
 */                
                   
int getReturnCodeFromException()
{                  
    try {          
        // rethrow 
        throw;     
    } catch(int a) 
    {              
        return -1; 
    } catch(double b)
    {              
        return -2; 
    }              
    // for each type of exception, you can create different return code
    // ...         
}                  
void cppAPI1()
{           
    throw (1);
}           
            
            
void cppAPI2()
{           
    throw (1.12);
}           
            
int cAPI1() 
{           
    try {   
        cppAPI1();
    } catch(...) {                           
        int rc = getReturnCodeFromException();
        return rc;
    }       
}           
            
int cAPI2() 
{           
    try {   
        cppAPI2();
    } catch(...) {                           
        int rc = getReturnCodeFromException();
        return rc;
    }       
}           
            
int main()  
{           
//    foo();
//    goo();
    std::cout << "cAPI1 rc = " << cAPI1() << std::endl;
    std::cout << "cAPI2 rc = " << cAPI2() << std::endl;
    return 0;
}                                
Reference http://exceptionsafecode.com/

2012年1月6日 星期五

[C++] dynamic_cast

You can use dynamic_cast to identify the type. Then you can do something different according to Derived class with the same Base class pointer.
Just a reminder, when you call destructor of derived class. The vtable pointer and RTTI is no longer exist.
As a result, you will get NULL at second dynamic_cast.
#include <iostream>
#include <cstdio>
#include <cstdlib>

class Base
{
public:
    Base():a(1){};
    ~Base(){};
    virtual void foo() {};
    int a;
};

class Derived : public Base
{
public:
    Derived(){};
    ~Derived(){};
    virtual void foo() {};
};

int main()
{
    void *p = malloc(sizeof(Derived));

    Base *base = new (p) Derived();
    Derived *d = dynamic_cast(base);
    if(d)
        std::cout << "cast ok!\n";     d->~Derived();
    Derived *dd = dynamic_cast(base);
    if(dd)
        std::cout << "cast ok!\n";
    else std::cout << "cast fail!\n";
    return 0;
}