GCC - wlshiu/my_note GitHub Wiki

C99

  • structure alignment

    #pragma pack(1)
    typedef struct sSampleStruct
    {
         char Data1;
         int Data2;
         unsigned short Data3;
         char Data4;
    }__attribute__((packed)) sSampleStruct_t;
    #pragma pack()
  • flexible array member

    struct test
    {
        int a;    // It MUST exist a member in this structure.
        char c[]; // flexible array, it MUST put at the last member.
                  // Some compiler support 'char c[0]'
    };
    struct test *p = (struct test*)malloc(sizeof(struct test) + 100*sizeof(char));
    • It not occupies the size when sizeof().
    • It must be the last member of a structure.
  • function point

    int foo(int a, int b)
    { return a + b; }
    
    int main()
    {
        int (*fp_func)(int, int);
        fp_func = foo;
        fp_func(3, 5);
    }
  • macro return

    #include <stdio.h>
    
    #define DBG(s, b...)                                                   \
        {                                                                  \
            printf(__FILE__ "@%d, %s(): " s, __LINE__, __FUNCTION__, ##b); \
            fflush(stdout);                                                \
        }
    
    #define round_down(f)      \
        ({                     \
            int __ret = 0;     \
            __ret = (int)f;    \
            __ret;             \
        })
    
    int main(int argc, char *argv[])
    {
        DBG("This is a debug message\n");
        printf("round_down(%f) = %d\n", 4.5, round_down(4.5));
        printf("round_down(%f) = %d\n", 9.5, round_down(9.5));
        return 0;
    }
  • static assert

    #define __STRCAT(a, b)       a##b
    #define __XSTRCAT(a, b)      __STRCAT(a, b)
    #define __static_assert(const_expr, _mess)  enum { __XSTRCAT(_verify_static_, __LINE__) = 1/(!!(const_expr)) }
    
    // example
    typedef struct __attribute__ ((packed))
    {
        uint8_t  bLength;
        uint8_t  bDescriptorType;
        uint8_t  bEndpointAddress;
    
        struct __attribute__ ((packed))
        {
            uint8_t xfer  : 2;
            uint8_t sync  : 2;
            uint8_t usage : 2;
            uint8_t       : 2;
        } bmAttributes;
    
        uint16_t wMaxPacketSize;
        uint8_t  bInterval;
    } tusb_desc_endpoint_t;
    
    __static_assert( sizeof(tusb_desc_endpoint_t) == 7, "size is not correct");
    

Toolchain

  • _write v.s _write_r

    _write_r is the Reentrant version of _write

  • _sbrk 改變堆的配置(對 malloc而言)

  • _open 開啟檔案(基於控點的)

  • _close 關閉檔案

  • _write 寫檔案

  • _read 讀檔案

  • _lseek 重新定向檔案中的位置

  • _fcntl 執行一個檔案說明符的作業

  • _fstat 得到檔案狀態的控點

  • _stat 按名稱得到檔案狀態

  • _link 生成一個檔案鏈結(檔案命名)

  • _unlink 移除目錄項

  • _times 讀取時間資訊

  • _gettimeofday 得到時間日期

  • _execve 執行一個檔案

  • _kill 殺死一個程式

  • _getpid 得到程式識別

  • _fork 建立一個新的程式

  • _wait 等待子程式的終止

查看 man GCC 的 -dletters 或 -dCHARS 可以得知更多用法

  • 查看目前 GCC 內, 所有定義的 macro

    $ gcc -E -dM - < /dev/null
    
  • GCC 展開巨集 (macro)

    $ gcc -E macro.c -o macroDebug_Expand.c
    
  • 保留 define 內容

    gcc -E -dM 可以保留 define 內容, 方便查詢原始名稱

    • 反查 error no
      # 查詢 errorno: 111
      $ echo "#include <errno.h>" | gcc -E -dM - | grep " 111$"
      #define ECONNREFUSED 111
      
  • 列出 include header files

    若希望過濾掉系統標頭檔, 可以改用 -MM

    $ echo "#include <stdio.h>" | gcc -E -M -
    -:  /usr/include/stdc-predef.h /usr/include/stdio.h \
     /usr/include/features.h /usr/include/x86_64-linux-gnu/sys/cdefs.h \
     /usr/include/x86_64-linux-gnu/bits/wordsize.h \
     /usr/include/x86_64-linux-gnu/gnu/stubs.h \
     /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \
     /usr/lib/gcc/x86_64-linux-gnu/5/include/stddef.h \
     /usr/include/x86_64-linux-gnu/bits/types.h \
     /usr/include/x86_64-linux-gnu/bits/typesizes.h /usr/include/libio.h \
     /usr/include/_G_config.h /usr/include/wchar.h \
     /usr/lib/gcc/x86_64-linux-gnu/5/include/stdarg.h \
     /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \
     /usr/include/x86_64-linux-gnu/bits/sys_errlist.h
    
  • 查看 #define 從那裡 include 的

    $ gcc -E -dD ./src/XSync.c | egrep "(GetReq|include)"
    # 418 "/usr/include/X11/Xlibint.h" 3 4
    extern void *_XGetRequest(Display *dpy, CARD8 type, size_t len);
    #define GetReqSized(name,sz,req) req = (x ##name ##Req *) _XGetRequest(dpy, X_ ##name, sz)
    # 435 "/usr/include/X11/Xlibint.h" 3 4
    #define GetReq(name,req) GetReqSized(name, SIZEOF(x ##name ##Req), req)
    
  • Example 找出 GetReq() 的 source code

    1. 先查在目前 XSync.c 中, GetReq() 是由那個 header 引入的

      $ gcc -E ./src/XSync.c | grep GetReq
      extern void *_XGetRequest(Display *dpy, CARD8 type, size_t len);
         = (xSyncInitializeReq *) _XGetRequest(dpy, 0, 8)
      
    2. 前項結果沒看到宣告, 猜測 GetReq() 可能是 macro API, 改成從 #define

      $ gcc -E -dM ./src/XSync.c | grep GetReq
      #define GetReqSized(name,sz,req) req = (x ##name ##Req *) _XGetRequest(dpy, X_ ##name, sz)
      #define GetReq(name,req) GetReqSized(name, SIZEOF(x ##name ##Req), req)
      

      得知 GetReq()其實是 _XGetRequest()

    3. 查看 _XGetRequest() 從那裡引入的

      gcc -E ./src/XSync.c | less
      ...
      # 418 "/usr/include/X11/Xlibint.h" 3 4
      extern void *_XGetRequest(Display *dpy, CARD8 type, size_t len);
      ...
      
    4. 接著查 /usr/include/X11/Xlibint.h 在個套件

      # 查詢已安裝套件, 速度較快
      $ dpkg --search /usr/include/X11/Xlibint.h
      libx11-dev:amd64: /usr/include/X11/Xlibint.h
      
      # 查詢全部套件
      $ apt-file search /usr/include/X11/Xlibint.h
      libx11-dev: /usr/include/X11/Xlibint.h
      
    5. 取得 libx11 然後找到 source code 的位置

      $ apt-get source libx11-dev
      ...
      $ grep -RI _XGetRequest .
      ./src/XlibInt.c:void *_XGetRequest(Display *dpy, CARD8 type, size_t len)
      

Cortex-M

  • __SSAT

    #if defined  (__GNUC__)
        static inline int __SSAT_GUN(int32_t VAL, int32_t BITPOS) {
            int32_t min = -(1<<(BITPOS-1));
            int32_t max = (1<<(BITPOS-1)) - 1;
            if (VAL < min)
                return min;
            else if (VAL > max)
                return max;
            else
                return VAL;
        }
        #define __SSAT(VAL, BITPOS)     __SSAT_GUN(VAL,BITPOS)
    #else
        #define __SSAT(VAL, BITPOS)     _ssatl(VAL , 0, BITPOS)
    #endif
     
    #if defined(__GNUC__)
        #define __CLZ   __builtin_clz
    #else
         #define __CLZ  __clz
    #endif
    
  • get PC register

#define __GET_PC(pAddr)           __asm volatile ("MOV %0, PC\n" : "=r" (*pAddr))

{
    uint32_t    addr = 0;
    __GET_PC(&addr);
    printf("@ %x\n", addr);
}
  • __libc_init_array()

    void
    __libc_init_array (void)
    {
      size_t count;
      size_t i;
    
      count = __preinit_array_end - __preinit_array_start;
      for (i = 0; i < count; i++)
            __preinit_array_start[i]();
    
      _init();
    
      count = __init_array_end - __init_array_start;
      for (i = 0; i < count; i++)
            __init_array_start[i]();
    }
    /* link script */
    SECTIONS
    {
        ...
    
        .preinit_array :
        {
            . = ALIGN(8);
            PROVIDE_HIDDEN (__preinit_array_start = .);
            KEEP (*(.preinit_array *))
            PROVIDE_HIDDEN (__preinit_array_end = .);
            . = ALIGN(8);
        } > FLASH
    
        .init_array :
        {
            . = ALIGN(8);
            PROVIDE_HIDDEN (__init_array_start = .);
            KEEP (*(SORT(.init_array.*)))
            KEEP (*(.init_array *))
            PROVIDE_HIDDEN (__init_array_end = .);
            . = ALIGN(8);
        } > FLASH
    
        .fini_array :
        {
            . = ALIGN(8);
            PROVIDE_HIDDEN (__fini_array_start = .);
            KEEP (*(SORT(.fini_array.*)))
            KEEP (*(.fini_array *))
            PROVIDE_HIDDEN (__fini_array_end = .);
            . = ALIGN(8);
        } > FLASH
    
        ...
    }

attribute

  • section

    int foo_var  __attribute__((section(".xdata"))) = 0;
    
    // no declare, directly put attribute at function
    __attribute__((section(".xinit"))) int foo_func(void) { return 1111; }
    
    int foo_func2(void) __attribute__((section(".xinit"))); // put attribute at declare
    int foo_func2(void) { return 2222; }
  • constructor/destructor

    • 在執行 main()前, 先 call 有 attribute((constructor))修飾的 function
    • 在結束 main()後, 再 call 有 attribute((destructor))修飾的 function
    static  __attribute__((constructor)) void before()
    {
        printf("before...\n");
    }
    
    static  __attribute__((destructor)) void after()
    {
        printf("after...\n");
    }
    • constructor(PRIORITY)/destructor(PRIORITY)

      In constructor, PRIORITY小的先執行. In destructor, PRIORITY大的先執行

      constructor 和 destructor 的 PRIORITY順序相反

  • replace function

    • __attribute__ (alias)

      /* 所有調用到 malloc() 的地方都將調用 my_malloc(), 即使是 gcc 內建的 malloc() 也不再可用 */
      
      void *malloc(size_t) __attribute__((alias("my_malloc")));
      void *my_malloc(size_t size)
      {
          printf("Try to malloc %ld bytes\n", size);
          return __libc_malloc(size);
      }
    • -Wl,--wrap=[symbol]

      任何對symbol未定義的引用(undefined reference)會被解析成__wrap_symbol, 而任何對__real_symbol未定義的引用會被解析成symbol. 即當一個名為 symbol 符號使用 wrap 功能時, 工程中任何用到 symbol 符號的地方實際使用的是 __wrap_symbol符號, 任何用到__real_symbol的地方實際使用的是真正的 symbol

      # linker 會將 malloc() 替換為 __wrap_malloc()
      LDFLAGS += -Wl,--wrap=malloc
      extern void *__real_malloc(size_t size); // 連結到真正的 libc malloc() function
      void *__wrap_malloc(size_t size)
      {
          fprintf(stdout, "===> malloc %d\n", size);
          return __real_malloc(size);
      }
      
      int main(void)
      {
          char    *pbuf = 0;
          pbuf = malloc(10);
          if( pbuf )  free(pbuf);
          return 0;
      }
  • condition

    (so_size > size) ? (stream[0].size = so_size) : (stream[0].size = size);
  • vmlinux.lds

    arch/arm/kernel/compressed/vmlinux.lds是由arch/arm/kernel/compressed/vmlinux.lds.S生成的

    • 生成規則
    # path: scripts/Makefile.build
    # Linker scripts preprocessor (.lds.S -> .lds)
    # ---------------------------------------------------------------------------
    quiet_cmd_cpp_lds_S = LDS     $@
          cmd_cpp_lds_S = $(CPP) $(cpp_flags) -P -C -U$(ARCH) \
                             -D__ASSEMBLY__ -DLINKER_SCRIPT -o $@ $<
    
    $(obj)/%.lds: $(src)/%.lds.S FORCE
        $(call if_changed_dep,cpp_lds_S)
    • 擴展命令
    $ arm-none-eabi-gcc -E -Wp,-MD,arch/arm/boot/compressed/.vmlinux.lds.d
        -nostdinc -isystem /home/liminghao/bin/bin/arm-none-eabi-4.7.3/bin/../lib/gcc/arm-none-eabi/4.7.3/include
        -I./arch/arm/include
        -Iarch/arm/include/generated/uapi
        -Iarch/arm/include/generated
        -Iinclude
        -I./arch/arm/include/uapi
        -Iarch/arm/include/generated/uapi
        -I./include/uapi
        -Iinclude/generated/uapi
        -include ./include/linux/kconfig.h
        -D__KERNEL__ -DTEXT_START="0" -DBSS_START="ALIGN(8)"
        -mlittle-endian -P -C -Uarm -D__ASSEMBLY__ -DLINKER_SCRIPT
        -o arch/arm/boot/compressed/vmlinux.lds
        arch/arm/boot/compressed/vmlinux.lds.S
    
    生成命令中的參數-E,它並不編譯,而只是進行預處理。它的格式與源文件格式是一致的。
    $ arm-linux-gcc -E -Wp,-MD,arch/arm/kernel/.vmlinux.lds.d -nostdinc ...... -D__KERNEL__ -mlittle-endian ......
    -DTEXT_OFFSET=0x00008000 -P -C -Uarm -D__ASSEMBLY__ -o arch/arm/kernel/vmlinux.lds     arch/arm/kernel/vmlinux.lds.S
  • get current function

#define GET_CALLER_ADDR()     __builtin_return_address(0) // the caller of this function (backtrace frame)

link script

  • 只想要把某個library的.o放入的話

    使用 *xxx.a:*yyy.o (.bss*)

    .bss_RAM2 : ALIGN(4)
    {
        *libmytest.a:*.o (.bss*)
        *(.bss.$RAM2*)
        *(.bss.$RamLoc64*)
       . = ALIGN(4) ;
    } > RamLoc64
    
  • 不想要把特定檔案的section放入

    可以使用 EXCLUDE_FILE

    /* 想放除了 foo.o、bar.o 外, 所有的 '.bss section' */
    (*(EXCLUDE_FILE (*foo.o *bar.o) .bss))
    

compile error

  • relocation truncated to fit: xxxx

    這種情況一般出現在 assembly code 中, 有些跳轉指令的跳轉範圍很小. 而當要跳轉到的地址超出了範圍, 那麼就會報錯. 原則上將替換更大範圍的跳轉指令即可解決.

check compile options

$ readelf -w ./libmsmart.a | grep DW_AT_producer

MSYS2

當 MSYS2 安裝成功後, 會有三種版本的 Shell 供您使用

如果需要編譯32位元的版本的執行檔, 就使用 MinGW-w64 Win32 Shell 如果需要編譯64位元的版本的執行檔, 就使用 MinGW-w64 Win64 Shell 至於 MSYS2 Shell 和其他兩者又有何差別?

MSYS2 Shell 使用 gcc 編譯出來的執行檔會需要依賴 msys-2.0.dll, 但是 MSYS2 Shell 有非常良好的 POSIX 支援.

一般來說 MSYS2 Shell 通常用於編譯 GNU 相關的 Linux 作業系統工具, 或者需要編譯 POSIX 支援的軟體, 才使用 MSYS2 這個 Shell

⚠️ **GitHub.com Fallback** ⚠️