顯示具有 C 標籤的文章。 顯示所有文章
顯示具有 C 標籤的文章。 顯示所有文章

2016年9月17日 星期六

build 32bit app on 64bit Machine -- /usr/bin/ld: skipping incompatible


我將Ubuntu從14.04換到16.04後,系統好像移除了一些package,導致build 32bit的程式會failed。 安裝lib32gcc-4.7-dev之後就解了。
brook@vista:~$ uname -a
Linux vista 4.4.0-36-generic #55-Ubuntu SMP Thu Aug 11 18:01:55 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
brook@vista:~$ cat /etc/issue
Ubuntu 16.04.1 LTS \n \l
brook@vista:~$ gcc -m32 x.c
/usr/bin/ld: skipping incompatible /usr/lib/gcc/x86_64-linux-gnu/4.7/libgcc.a when searching for -lgcc
/usr/bin/ld: cannot find -lgcc
/usr/bin/ld: skipping incompatible /usr/lib/gcc/x86_64-linux-gnu/4.7/libgcc_s.so when searching for -lgcc_s
/usr/bin/ld: cannot find -lgcc_s
collect2: error: ld returned 1 exit status
brook@vista:~$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.7/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.7.4-3ubuntu12' --with-bugurl=file:///usr/share/doc/gcc-4.7/README.Bugs --enable-languages=c,c++,go,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.7 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.7 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-gnu-unique-object --disable-libmudflap --enable-plugin --with-system-zlib --enable-objc-gc --with-cloog --enable-cloog-backend=ppl --disable-cloog-version-check --disable-ppl-version-check --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.7.4 (Ubuntu/Linaro 4.7.4-3ubuntu12)
brook@vista:~$ sudo apt-get install lib32gcc-4.7-dev





2012年4月7日 星期六

常用的regular expression


這篇會慢慢增加內容,所以我也會更新發布日期。
JavaScript
    var mac = /^\s*([\d[a-f]{2}:){5}[\d[a-f]{2}\s*$/i;
    var ip = /^((\d)|(([1-9])\d)|(1\d\d)|(2(([0-4]\d)|5([0-5]))))\.((\d)|(([1-9])\d)|(1\d\d)|(2(([0-4]\d)|5([0-5]))))\.((\d)|(([1-9])\d)|(1\d\d)|(2(([0-4]\d)|5([0-5]))))\.((\d)|(([1-9])\d)|(1\d\d)|(2(([0-4]\d)|5([0-5]))))$/;


C language
    char *mac =  "([[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}";
    char *ip = "^(([0-9])|(([1-9])[0-9])|(1[0-9][0-9])|(2(([0-4][0-9])|5([0-5]))))\\.(([0-9])|(([1-9])[0-9])|(1[0-9][0-9])|(2(([0-4][0-9])|5([0-5]))))\\.(([0-9])|(([1-9])[0-9])|(1[0-9][0-9])|(2(([0-4][0-9])|5([0-5]))))\\.(([0-9])|(([1-9])[0-9])|(1[0-9][0-9])|(2(([0-4][0-9])|5([0-5]))))$";


    參考資料
  1. http://en.wikipedia.org/wiki/Regular_expression



2011年3月19日 星期六

寫作小技巧in C


有些code寫的有些trick,不過常常因為太久沒用就忘記了,所以我決定特別留一篇,專門收集這種短小精幹的code。

判斷是不是2的n次方
if_power_of_2(n) (n != 0 && ((n & (n -1)) == 0))


XOR swap
void swap(int *x, int *y) {
    if (x != y) {
        *x ^= *y;
        *y ^= *x;
        *x ^= *y;
    }
}


Memory Alignment
作embedded常常會需要作一些Memory alignment的動作的動作,Linux的Netlink就有一小段macro可以拿來用。
#define NLMSG_ALIGNTO       4U // 作4byte alignment
#define NLMSG_ALIGN(len)    (((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1))
比如要讓NLMSG_HDRLEN能符合4byte-alignment就是定義如下的macro
#define NLMSG_HDRLEN        ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
陸續收集中...


2010年12月18日 星期六

Inline Assembly


最近在看kernel的code,裡面有些組語的語法,所以就花點時間把它看了一下,基本上,這邊幾乎都是參考Brennan's Guide to Inline Assembly[1]。

GCC使用的asm是AT&T/UNIX的語法而不是Intel的語法,所以有些不同必須先弄清楚。

GCC 的 inline assembly 基本格式是:
 asm ( assembler template
     : output operands     (optional)
     : input operands     (optional)
     : list of clobbered registers     (optional)
     );
沒用的欄位可以空下來。最後一個欄位是用來告訴GCC在asm code中,我們已經用了哪些register。

Register name:
Register的名稱前面必須加上”%”
  • AT&T:%eax
  • Intel:eax
為了讓GCC的asm能跨平台,所以可以用%0、%1...%n代表後面依序出現的register。


Source/Destination ordering:
AT&T的source永遠在左邊而destination永遠在右邊
  • AT&T:movl %eax, %ebx
  • Intel:mov ebx, eax
  • 您可以在Instruction後面會被加上b、w和l,用以區分operand的size,分別代表byte、word和long,在不加的情況下,gcc會自動判斷,但有可能誤判。



Constant value/immediate value format:
Constant value/immediate value前面必須加上”$”
  • AT&T:movl $boo, %eax
  • Intel:mov eax, boo
  • 將boo的address載到eax中,boo必須是static變數。
#include <stdio.h>

int main(int argc, char *argv[])
{
    static int x __asm__ ("x") = 10;
    int y;

    x = atoi(argv[1]);
    __asm__("movl $x, %0"  // 這邊的%0代表後面出現的第一個register,即y。
            : "=r" (y));
    // output operand前一定要有個"="表示這個constraint是write-only,
    //  "="叫constraint modifier。
    // input output operands後一定要跟著相對應的C 變數的參數,
    // 這是給asm的參數。
    printf("x=%p, y=0x%x\n", &x, y);
    return 0;
}

  • AT&T:movl $0xabcd, %eax
  • Intel:mov eax, abcd
  • 將eax設為0xabcd。
#include <stdio.h>

int main(int argc, char *argv[])
{
    int y;

    __asm__("movl $0xabcd, %0"
            : "=r" (y));

    printf("y=0x%x\n", y);
    return 0;
}


Referencing memory:
  • AT&T:immed32(basepointer,indexpointer,indexscale)
  • Intel:[basepointer + indexpointer*indexscale + immed32]
  • 沒用的欄位可以空下來。
Addressing a particular C variable:
  • AT&T:_booga
  • Intel:[_booga]
#include <stdio.h>

int main(int argc, char *argv[])
{
    static int i __asm__ ("i");
    int io;

    i = atoi(argv[1]);

    __asm__("movl i, %0;\n"
            : "=r"(io));
    printf("i=%d, io=%d\n", i, io);

    return 0;
}

Addressing a variable offset by a value in a register:
  • AT&T:_variable(%eax)
  • Intel:[eax + _variable]
#include <stdio.h>

int main(int argc, char *argv[])
{
    struct ic {
        int i;
        char c;
    };

    int i = 0;
    char c = 0;
    static struct ic ic __asm__ ("ic");

    ic.i = 11;
    ic.c = 'b';

    __asm__("movl $ic, %%eax;\n"
            "mov (%%eax), %0;\n"
            "mov 4(%%eax), %1;\n"
            : "=r"(i), "=r"(c)
            : "m"(ic)
            : "%eax");
    printf("i=%d, c=%c\n", i, c);

    return 0;
}

Addressing a value in an array of integers (scaling up by 4):
  • AT&T:_array(,%eax,4)
  • Intel:[eax*4 + array]
#include <stdio.h>

int main(int argc, char *argv[])
{   
    int i; 
    static int ary[2][3] __asm__("ary") = {
        {0, 1, 2},
        {10, 11, 12}
    };

    // i = ary[1][1]
    __asm__("movl $12, %%eax;\n" // cal how many size of one raw
            "movl $4, %%ebx;\n"  // which column
            "movl ary(%%ebx, %%eax, 1), %0;\n" 
            : "=r" (i)
            :
            : "%eax", "%ebx");
    printf("i=%d\n", i);

    return 0;
}


參考資料
  1. http://www.delorie.com/djgpp/doc/brennan/brennan_att_inline_djgpp.html
  2. http://www.study-area.org/cyril/opentools/opentools/x969.html



2010年10月9日 星期六

GCC (4.4.1)之C Preprocessor part III


Conditionals
CPPP的Conditionals和C中的if很像,不過C的if是在run-time決定是否要被執行,而CPP則是在compiler time就決定code是否被編譯。

Conditionals主要有三個指令#if、#ifdef和#ifndef。

#ifdef MACRO
#endif
當MACRO被定義這個block才會被編譯,而#ifndef剛好相反。


#if expression
#endif
當expression為非0這個block才會被編譯。而#if defined(MACRO)就等同於#ifdef MACRO,#if也有巢狀寫法。
#if X == 1
#elif X >= 10
#else
#endif
個人偏愛"#if"勝於"#ifdef",主要因為"#if"可以取代"#ifdef",而且"#if"還支援數學運算,如+,-*,/還有bitwise operations, shifts, comparisons, and logical operations (&& and ||)等等,不過僅限於整數型態的運算
#if (1<<2) > 5
#warning "(1<<2) > 5"
#else
#warning "!((1<<2) > 5)"
#endif


Diagnostics
CPP有兩個Diagnostics指令#error和#warning,差異在於一旦執行到#error就會終止處理。



2010年6月6日 星期日

GCC (4.4.1)之C Preprocessor part II


這一篇主要是CPP manual第三章macro的心得,所以都是在講述macro。

object-like macro是最簡單的macro,就是直接將macro的name取代成code。
如:
#define NUMBER 1, \
               2, \
               3,
int x[] = {NUMBER}
 -> int x[] = { 1, 2 3 };
NUMBER會被展開誠1, 2, 3

當macro被展開之後,會再度的被檢驗是否還有macro需要被展開。
如:
#define TABLESIZE BUFSIZE
#define BUFSIZE 1024
TABLESIZE
 -> BUFSIZE
 -> 1024
TABLESIZE會先被展開成BUFSIZE,接著BUFSIZE再被展成1024。


Function-like macro用起來就像在用function,所以被稱為function-like macro。function-like macro是在macro name後面"緊"接括號。
如:
#define lang_init() c_init()
lang_init()
 -> c_init()

當macro name後面緊接著(),這個macro才會被當成function-like macro,否則會當成一般macro展開。
如:
extern void foo(void);
#define foo() boo()
....
 foo(); // macro
 funcptr = foo; // function

#define lang_init () c_init() // 多了空白在name和()之間
lang_init()
 -> () c_init()() // 結果就會造成錯誤


macro arguments,function macro可以像一般function一帶參數,這些參數會先被展開,接著再展開macro,參數要以","區隔開來(和一般的function一樣)。
如:
#define min(X, Y) ((X) < (Y) ? (X) : (Y))
x = min(a + 28, *p)
 -> x = ((a + 28), (*p) ? (a + 28) : (*p));
y = min(min(a, b), c)
 -> min(((a) < (b) ? (a) : (b)), c)
 -> .... // 產生的結果可能會和你想的不一樣


stringification(中文不知道怎麼稱呼,暫稱他字串化吧),當macro所帶的參數前面加上"#"之後,這個參數就會被展成字串,這就是stringification。
如:
#define WARN_IF(EXP) \
     do { if (EXP) \
             fprintf (stderr, "Warning: " #EXP "\n"); } \
     while (0)

WARN_IF (x == 0);
 -> do { if (x == 0) fprintf (stderr, "Warning: " "x == 0" "\n"); } while (0);

目前只有stringification,並沒有辦法轉成character。另外,stringification的優先權會高於argument expand。
如:
#define xstr(s) str(s)
#define str(s) #s
#define foo 4
str (foo)
 -> "foo"
xstr (foo)
 -> xstr (4)
 -> str (4)
 -> "4"


token concatenation或稱token pasting,是利用"##"將兩個token組成一個token,'##'被置於兩個token之間,如:
#define COMMAND(name) {#name, name ## _command}
struct command {
    char *name;
    void (*f)(void);
} cmds[] = {
    COMMAND(quit),
    COMMAND(help),
};
->
 struct command {
     char *name;
     void (*f)(void);
 } cmds[] = {
     {"quit", quit_command},
     {"help", help_command},
 };


variadic其實就是不定參數的function-like macro,__VA_ARGS__代表"..."(即後面所有的參數),如:
#define eprintf(...) fprintf(stderr, __VA_ARGS__)
eprintf("%s(#%d)\n", __FUNCTION__, __LINE__)
 -> fprintf(stderr, "%s(#%d)\n", __FUNCTION__, 7)

cpp也可以使用name取代__VA_ARGS__,用法上只是在"..."前面加上name,"_VA_ARGS__"則用name取代即可,如:
#define eprintf(args...) fprintf(stderr, args)
eprintf("%s(#%d)\n", __FUNCTION__, __LINE__)
 -> fprintf(stderr, "%s(#%d)\n", __FUNCTION__, 7)

來看另外一個例子
#define eprintf(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
eprintf("hello\n");
 -> fprintf(stderr, "hello\n", ); // error!!
這裡的問題就是如何將__VA_ARGS__前面的","在沒有參數帶入的時候刪除,答案就是"##"。
#define eprintf(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)
eprintf("hello\n");
 -> fprintf(stderr, "hello\n"); // correct!!



2009年11月3日 星期二

C macro的誤用


macro主要是拿來取代,比如#define MAX_NUM 10,當用到MAX_NUM時,就會被轉成10,我們常用拿macro來define一些函數,比如:
#include <stdio.h>
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))

int main(int argc, char *argv[])
{
    int x=10, y=11, big;
    big = MAX(++x, ++y);
    printf("the bigger + 1 = %d\n", big);

    return 0;
}

可是這裡就會發生問題了,當你使用CPP你可以看到,X全部被++x取代,而Y全部被++y取代,所以執行完的結果顯然就會錯誤了。
int main(int argc, char *argv[])
{
    int x=10, y=11, big;
    big = ((++x) > (++y) ? (++x) : (++y));
    printf("the bigger + 1 = %d\n", big);

    return 0;
}
結果是13而不是12。

這邊應該改寫為
#include <stdio.h>
#define MAX(x, y) \
({ typeof(x) _x = (x); \
    typeof(y) _y = (y); \
 _x > _y ? _x : _y; })

int main(int argc, char *argv[])
{
    int x=10, y=11, big;
    big = MAX(++x, ++y);
    printf("the bigger + 1 = %d\n", big);

    return 0;
}
因為你無法預期使用者會輸入何種表示法,為了避免side effect,還是在內部宣告一個變數儲存使用者的輸入吧。
至於typeof可以參考一下GCC的說明http://gcc.gnu.org/onlinedocs/gcc/Typeof.html。



2009年8月15日 星期六

GCC (4.4.1)之C Preprocessor part I


C Preprocessor系列,其實就是CPP manual的讀書心得。

initial processing 在初始化的處理上,CPP主要有四個任務:
  1. 讀取檔案到記憶體中。
  2. 如果有啟動trigraphs,那麼會做trigraph轉換,如??/轉換成\,??-轉成~等等。
  3. 合併連續行成一行,就是每行後面有\當結尾的,會將該行和下一行合併成一行。
  4. 將註解的部分用一個空白取代。
Tokenization CPP使用空白區分token,如果沒有token之間沒有空白,那麼CPP會使用greedy法則,也就是盡可能的找最長字串的token,比如a++++b會被解釋成a++ ++ +b,而不是a++ + ++b,而token之間預設也是一個空白,比如: #define foo() bar foo()baz 會被解釋成bar baz,不管#define中的foo()和bar有幾個空白。
brook@debian:~$ echo -e "#define foo() bar\nfoo()baz"|gcc -E -
# 1 "<stdin>"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "<stdin>"

bar baz

The preprocessing language 所謂的preprocessing language,就是以#開頭的那些preprocessing directive,這些指令是固定的,無法讓使用者自訂新的。這些指令主要有四種用途:
  1. include of header file(比如#include)。
  2. Macro expansion(比如將#define展開)。
  3. Conditional Compilation(比如#if,決定哪些要被編譯,哪些不被編譯)。
  4. Line control(我不是很清楚,只知道#line num下一行就會由num當行數重新計算,也可以簡寫成# num)。
  5. Diagnostic(比如#error)。


Header files Header files主要分成兩類:
  1. System header files:提供OS部分的interface,<xx.h>。
  2. Your own header file:提供source files的interface,"xx.h"。
傳統上,C的header files都使用.h當結尾,當您在#include 時,其實就是將xx.h複製到該行上,並利用Line control,重新計算後面的行數,比如:
brook@desktop:~$ cat a.h
#ifndef A_H
#define A_H
int hello(char *str);
#endif
brook@desktop:~$ cat a.c
#include 
#include "a.h"

int main(int argc, char * argv[])
{
    printf("hello world\n", __LINE__);

    return 0;
}
brook@desktop:~$ cpp -I./ a.c
... 略 ...
# 2 "a.c" 2
# 1 "a.h" 1


int hello(char *str);
# 3 "a.c" 2

int main(int argc, char * argv[])
{
    printf("hello world\n", 6);

    return 0;
}
看到把a.h中的int hello(char *str)複製到source file中了嗎?隨後又重新計算行數# 3,而header files並沒有規定只能放啥東西,不過通常就是放置一些宣告。 include syntax #include有兩種變形:
  1. #include <file>:for system header files,就是會尋找系統路徑。
  2. #include "file":for your own header files,會先尋找目前目錄,找不到就依循的search path尋找。
注意:有些OS會使用'\'當pathname separator,比如M$,但是還是請使用'/',因為GCC一律都使用'/'當pathname separator。

search path 一般在UNIX底下,system header files的search path為 /usr/local/include libdir/gcc/target/version/include /usr/target/include /usr/include 透過GCC實際觀察一下吧
brook@desktop:~$ gcc -I./ -v a.c
... 略  ...
ignoring nonexistent directory "/usr/local/include/i486-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/i486-linux-gnu/4.3.2/../../../../i486-linux-gnu/include"
ignoring nonexistent directory "/usr/include/i486-linux-gnu"
#include "..." search starts here:
#include <...> search starts here:
 ./
 /usr/local/include
 /usr/lib/gcc/i486-linux-gnu/4.3.2/include
 /usr/lib/gcc/i486-linux-gnu/4.3.2/include-fixed
 /usr/include
End of search list.

... 略  ...
您可以透過-I新增search path,新增的search path會優先被尋找,但是如果新增的search path已經在預設的(系統的)search path中,該search path會被忽略,避免影響預設的search path順序。有興趣可以再研究一下-I-和-iquote。

Once-Only headers 一個header files很可能被include兩次,進而造成重複define,引起compile error,標準做法應該是在header file中,使用conditional compilation防止被引入兩次,如:
#ifndef XX_H
#define XX_H
 the entire file
#endif /* !XX_H */
XX_H我們稱為controlling macro或guard macro。macro name不應該使用'_'開頭的命名方式,而在system header files中應該使用'__'開頭,避免與user program造成衝突。 computed include 有時候我們會需要根據不同的configure引入不同的header files,比如:
#if SYSTEM_1
#include "system1.h"
#elif SYSTEM_2
#include "system2.h"
#elif
... 略 ...
#endif
像這樣需要經過計算然後才引入的行為就稱為computed include,你可以發現很就會被許多的#elif淹沒,你可以使用簡單的方式替代,比如:
#define SYSTEM1_H <stdio.h>
#define SYSTEM2_H "system3.h"
... 略 ...
#include SYSTEM1_H
#include SYSTEM2_H
甚至SYSTEM_H可以由Makefile傳遞進來。

System Headers是用來宣告OS和runtime libraries的interface,system header所產生的warning除了#warning會顯示出來,其他的都會被抑制住。一般而言,當GCC在編譯時就已經設定哪些目錄會被當成system header存放的目錄了,不過我們還是可以透過-isystem#pragma GCC system_header兩種方式,將一般的header file當成system header file。
  • -isystem後面接的目錄會被當成system header,用法上和-I一樣。
  • #pragma GCC system_header這個指令告訴GCC把這個header file當成system header。

brook@ubuntu:~$ cat syshdr.c 
#include <stdio.h>
#include "syshdr1.h"
#include "syshdr2.h"
#include "syshdr3.h"

int main(int argc, char *argv[])
{
    syshdr1();
    syshdr2();
    syshdr3();
    return 0;
}
brook@ubuntu:~$ cat syshdr/syshdr1.h 
#ifndef SYSHDR1_H
#define SYSHDR1_H
int syshdr1(void) { printf("%s\n", __FUNCTION__); }
#endif
brook@ubuntu:~$ cat syshdr2.h 
#ifndef SYSHDR2_H
#define SYSHDR2_H
#pragma GCC system_header
int syshdr2(void) { printf("%s\n", __FUNCTION__); }
#endif
brook@ubuntu:~$ cat syshdr3.h 
#ifndef SYSHDR3_H
#define SYSHDR3_H
int syshdr3(void) { printf("%s\n", __FUNCTION__); }
#endif
brook@ubuntu:~$ gcc -Wall -isystem syshdr syshdr.c -o my_syshdr
syshdr.c: In function ‘syshdr3’:
syshdr3.h:3: warning: control reaches end of non-void function