0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

【gcc編譯優(yōu)化系列】如何(不)回收未發(fā)生調(diào)用的函數(shù)

嵌入式物聯(lián)網(wǎng)開發(fā) ? 來源:嵌入式物聯(lián)網(wǎng)開發(fā) ? 作者:嵌入式物聯(lián)網(wǎng)開發(fā) ? 2022-07-11 09:12 ? 次閱讀

1 問題場景

大家都知道,我們在開發(fā)單片機類的嵌入式固件時,一般使用的FLASH存儲空間都是比較有限的,小的可能幾十KB,大一點的可能也就幾百KB,可以說是寸金寸土的FLASH空間,可容不得我們半點垃圾代碼。 如果我們在寫代碼的過程中,隨便寫一些沒用的代碼,比如一些測試代碼,最后版本釋放的時候,這些測試代碼又沒有刪掉,還是參與了編譯,那么勢必最后這個函數(shù)的代碼實現(xiàn)就會保留在我們的固件包里面,這樣我們的固件包的bin文件大小勢必會增加,這顯然不是我們想要的。 另外,還有一種場景下,有些函數(shù)我們使用static修飾的局部函數(shù),只在初始化的時候通過初始化列表的形式調(diào)用一下,比如RT-Thread的初始化實現(xiàn),INIT_DEVICE_EXPORT(device_init_func),那么我們是不希望這個函數(shù)被優(yōu)化掉的,否則最后會出邏輯問題。 在使用GCC作為編譯器的環(huán)境下,有什么辦法可以實現(xiàn)呢?

2 需求分析

這里的需求兩點:

  1. 沒有被調(diào)用的函數(shù)需要移除,不出現(xiàn)在最后的固件文件里面;
  1. 某些特殊的函數(shù)實現(xiàn),沒有被顯式調(diào)用,但是需要保留它,不能被優(yōu)化掉。

3 需求實現(xiàn)

3.1 示例代碼

實現(xiàn)的一個示例代碼如下所示,功能很簡單就定義了2個沒被調(diào)用的函數(shù),一個我希望優(yōu)化移除,一個我希望被優(yōu)化保留。

#include 

#define CODE_SECTION(x)               __attribute__((section(x)))
#define CODE_KEEP_USED                CODE_SECTION(".text.keep.used.code")

void unused_func1(int a)
{
    printf("a: %d\n", a);
}

CODE_KEEP_USED void unused_func2(int a)
{
    printf("a: %d\n", a);
}

int main(int argc, const char *argv[])
{
    printf("Hello world !\n");
    return 0;
}

3.2 鏈接腳本

鏈接腳本是GCC在鏈接所有目標(biāo)文件變成可執(zhí)行文件的時候,需要讀取的一個配置文件,該文件決定了最后的可執(zhí)行文件是如何分布的。 我這里使用的是Ubuntu X64平臺 GCC默認(rèn)的鏈接腳本,由此改造而來。 至于如何獲取GCC默認(rèn)的鏈接腳本,請參考這里的教程。 修改后的鏈接腳本如下:

/* Script for -z combreloc -z separate-code */
/* Copyright (C) 2014-2020 Free Software Foundation, Inc.
   Copying and distribution of this script, with or without modification,
   are permitted in any medium without royalty provided the copyright
   notice and this notice are preserved.  */
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64",
          "elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_start)
SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu"); SEARCH_DIR("=/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu64"); SEARCH_DIR("=/usr/local/lib64"); SEARCH_DIR("=/lib64"); SEARCH_DIR("=/usr/lib64"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib64"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib");
SECTIONS
{
  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;
  .interp         : { *(.interp) }
  .note.gnu.build-id  : { *(.note.gnu.build-id) }
  .hash           : { *(.hash) }
  .gnu.hash       : { *(.gnu.hash) }
  .dynsym         : { *(.dynsym) }
  .dynstr         : { *(.dynstr) }
  .gnu.version    : { *(.gnu.version) }
  .gnu.version_d  : { *(.gnu.version_d) }
  .gnu.version_r  : { *(.gnu.version_r) }
  .rela.dyn       :
    {
      *(.rela.init)
      *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
      *(.rela.fini)
      *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
      *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
      *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
      *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
      *(.rela.ctors)
      *(.rela.dtors)
      *(.rela.got)
      *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
      *(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*)
      *(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*)
      *(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*)
      *(.rela.ifunc)
    }
  .rela.plt       :
    {
      *(.rela.plt)
      PROVIDE_HIDDEN (__rela_iplt_start = .);
      *(.rela.iplt)
      PROVIDE_HIDDEN (__rela_iplt_end = .);
    }
  . = ALIGN(CONSTANT (MAXPAGESIZE));
  .init           :
  {
    KEEP (*(SORT_NONE(.init)))
  }
  .plt            : { *(.plt) *(.iplt) }
.plt.got        : { *(.plt.got) }
.plt.sec        : { *(.plt.sec) }
  .text           :
  {
    *(.text.unlikely .text.*_unlikely .text.unlikely.*)
    *(.text.exit .text.exit.*)
    *(.text.startup .text.startup.*)
    *(.text.hot .text.hot.*)
    *(SORT(.text.sorted.*))
    *(.text .stub .text.* .gnu.linkonce.t.*)
    /* .gnu.warning sections are handled specially by elf.em.  */
    *(.gnu.warning)
  }
  .fini           :
  {
    KEEP (*(SORT_NONE(.fini)))
  }
  PROVIDE (__etext = .);
  PROVIDE (_etext = .);
  PROVIDE (etext = .);
  . = ALIGN(CONSTANT (MAXPAGESIZE));
  /* Adjust the address for the rodata segment.  We want to adjust up to
     the same address within the page on the next page up.  */
  . = SEGMENT_START("rodata-segment", ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)));
  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
  .rodata1        : { *(.rodata1) }
  .eh_frame_hdr   : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) }
  .eh_frame       : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) }
  .gcc_except_table   : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }
  .gnu_extab   : ONLY_IF_RO { *(.gnu_extab*) }
  /* These sections are generated by the Sun/Oracle C++ compiler.  */
  .exception_ranges   : ONLY_IF_RO { *(.exception_ranges*) }
  /* Adjust the address for the data segment.  We want to adjust up to
     the same address within the page on the next page up.  */
  . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
  /* Exception handling  */
  .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) }
  .gnu_extab      : ONLY_IF_RW { *(.gnu_extab) }
  .gcc_except_table   : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
  .exception_ranges   : ONLY_IF_RW { *(.exception_ranges*) }
  /* Thread Local Storage sections  */
  .tdata      :
   {
     PROVIDE_HIDDEN (__tdata_start = .);
     *(.tdata .tdata.* .gnu.linkonce.td.*)
   }
  .tbss          : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
  .preinit_array    :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  }
  .init_array    :
  {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
    KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
    PROVIDE_HIDDEN (__init_array_end = .);
  }
  .fini_array    :
  {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
    KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
    PROVIDE_HIDDEN (__fini_array_end = .);
  }

  /* Here use to keep some sections, which are not wanted to be removed. */
  .text_keep_used_code :
  {
    KEEP (*(.text.keep.used.code))
  }

  .ctors          :
  {
    /* gcc uses crtbegin.o to find the start of
       the constructors, so we make sure it is
       first.  Because this is a wildcard, it
       doesn't matter if the user does not
       actually link against crtbegin.o; the
       linker won't look for a file to match a
       wildcard.  The wildcard also means that it
       doesn't matter which directory crtbegin.o
       is in.  */
    KEEP (*crtbegin.o(.ctors))
    KEEP (*crtbegin?.o(.ctors))
    /* We don't want to include the .ctor section from
       the crtend.o file until after the sorted ctors.
       The .ctor section from the crtend file contains the
       end of ctors marker and it must be last */
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*(.ctors))
  }
  .dtors          :
  {
    KEEP (*crtbegin.o(.dtors))
    KEEP (*crtbegin?.o(.dtors))
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
    KEEP (*(SORT(.dtors.*)))
    KEEP (*(.dtors))
  }
  .jcr            : { KEEP (*(.jcr)) }
  .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) }
  .dynamic        : { *(.dynamic) }
  .got            : { *(.got) *(.igot) }
  . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .);
  .got.plt        : { *(.got.plt) *(.igot.plt) }
  .data           :
  {
    *(.data .data.* .gnu.linkonce.d.*)
    SORT(CONSTRUCTORS)
  }
  .data1          : { *(.data1) }
  _edata = .; PROVIDE (edata = .);
  . = .;
  __bss_start = .;
  .bss            :
  {
   *(.dynbss)
   *(.bss .bss.* .gnu.linkonce.b.*)
   *(COMMON)
   /* Align here to ensure that the .bss section occupies space up to
      _end.  Align after .bss to ensure correct alignment even if the
      .bss section disappears because there are no input sections.
      FIXME: Why do we need it? When there is no .bss section, we do not
      pad the .data section.  */
   . = ALIGN(. != 0 ? 64 / 8 : 1);
  }
  .lbss   :
  {
    *(.dynlbss)
    *(.lbss .lbss.* .gnu.linkonce.lb.*)
    *(LARGE_COMMON)
  }
  . = ALIGN(64 / 8);
  . = SEGMENT_START("ldata-segment", .);
  .lrodata   ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
  {
    *(.lrodata .lrodata.* .gnu.linkonce.lr.*)
  }
  .ldata   ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
  {
    *(.ldata .ldata.* .gnu.linkonce.l.*)
    . = ALIGN(. != 0 ? 64 / 8 : 1);
  }
  . = ALIGN(64 / 8);
  _end = .; PROVIDE (end = .);
  . = DATA_SEGMENT_END (.);
  /* Stabs debugging sections.  */
  .stab          0 : { *(.stab) }
  .stabstr       0 : { *(.stabstr) }
  .stab.excl     0 : { *(.stab.excl) }
  .stab.exclstr  0 : { *(.stab.exclstr) }
  .stab.index    0 : { *(.stab.index) }
  .stab.indexstr 0 : { *(.stab.indexstr) }
  .comment       0 : { *(.comment) }
  .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) }
  /* DWARF debug sections.
     Symbols in the DWARF debugging sections are relative to the beginning
     of the section so we begin them at 0.  */
  /* DWARF 1 */
  .debug          0 : { *(.debug) }
  .line           0 : { *(.line) }
  /* GNU DWARF 1 extensions */
  .debug_srcinfo  0 : { *(.debug_srcinfo) }
  .debug_sfnames  0 : { *(.debug_sfnames) }
  /* DWARF 1.1 and DWARF 2 */
  .debug_aranges  0 : { *(.debug_aranges) }
  .debug_pubnames 0 : { *(.debug_pubnames) }
  /* DWARF 2 */
  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
  .debug_abbrev   0 : { *(.debug_abbrev) }
  .debug_line     0 : { *(.debug_line .debug_line.* .debug_line_end) }
  .debug_frame    0 : { *(.debug_frame) }
  .debug_str      0 : { *(.debug_str) }
  .debug_loc      0 : { *(.debug_loc) }
  .debug_macinfo  0 : { *(.debug_macinfo) }
  /* SGI/MIPS DWARF 2 extensions */
  .debug_weaknames 0 : { *(.debug_weaknames) }
  .debug_funcnames 0 : { *(.debug_funcnames) }
  .debug_typenames 0 : { *(.debug_typenames) }
  .debug_varnames  0 : { *(.debug_varnames) }
  /* DWARF 3 */
  .debug_pubtypes 0 : { *(.debug_pubtypes) }
  .debug_ranges   0 : { *(.debug_ranges) }
  /* DWARF Extension.  */
  .debug_macro    0 : { *(.debug_macro) }
  .debug_addr     0 : { *(.debug_addr) }
  .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
  /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
}

關(guān)鍵地方在于.textkeepused_code段的定義,其他都是默認(rèn)鏈接腳本就已有的內(nèi)容。

3.3 編譯腳本

Ubuntu x64下的編譯腳本,支持編譯啟用回收優(yōu)化和不啟用回收優(yōu)化的情況,參考如下:

#! /bin/bash -e

CFLAGS="-save-temps=obj -Wall"
LDFLAGS="-Wl,-Map=test.map"

CFLAGS_GC="-fdata-sections -ffunction-sections"
LDFLAGS_GC="-Wl,--gc-sections"
LDFLAGS_MAP_GC="-Wl,-Map=test_gc.map"

PRINT_GC="-Wl,--print-gc-sections"

GCC_LDS=default.lds

if [ "$1" = "clean" ]; then
    rm -rf test* *.i *.s *.o *.map
    echo "Clean build done !"
    exit 0
elif [ "$1" = "gc" ]; then
    echo "gcc compile with gc ..."
    single_c_file=`ls *.c | cut -d . -f 1`
    cmd1="gcc -o $single_c_file.o -c *.c $CFLAGS $CFLAGS_GC"
    cmd2="gcc -o test_gc $single_c_file.o $LDFLAGS_GC $LDFLAGS_MAP_GC -T $GCC_LDS $PRINT_GC"
    echo "$cmd1 && $cmd2" && $cmd1 && $cmd2
else
    echo "gcc compile without gc ... (default)"
    cmd="gcc *.c $CFLAGS $LDFLAGS -o test"
    echo $cmd && $cmd
fi

exit 0

3.4 驗證測試

3.4.1 驗證不啟用編譯回收優(yōu)化的情況

使用./build.sh編譯輸出各種文件,使用grep-rsn unused_func驗證:

**grep -rsnw unused_func1**
main.c:9:void unused_func1(int a)
**Binary file test matches**
test.i:735:void unused_func1(int a)
Binary file test.o matches
test.s:7:       .globl  unused_func1
test.s:8:       .type   unused_func1, @function
test.s:9:unused_func1:
test.s:31:      .size   unused_func1, .-unused_func1
test.map:193:                0x0000000000001169                unused_func1

**grep -rsnw unused_func2**
main.c:14:CODE_KEEP_USED void unused_func2(int a)
**Binary file test matches**
test.i:740:__attribute__((section(".text.keep.used.code"))) void unused_func2(int a)
Binary file test.o matches
test.s:33:      .globl  unused_func2
test.s:34:      .type   unused_func2, @function
test.s:35:unused_func2:
test.s:57:      .size   unused_func2, .-unused_func2
test.map:197:                0x00000000000011b7                unused_func2

我們可以發(fā)現(xiàn),在最后生成的test可執(zhí)行文件中,都找到了unusedfunc1和testfunc2,也就是說在不啟用回收優(yōu)化的情況下,跟我們之前的預(yù)期是一樣的,這樣增加固件包的尺寸。

3.4.2 驗證啟用編譯回收優(yōu)化的情況

使用./build.sh gc編譯輸出各種文件,使用grep-rsn unused_func驗證:

**grep -rsnw unused_func1**
Binary file main.o matches
main.c:9:void unused_func1(int a)
test_gc.map:35: .text.unused_func1
main.i:735:void unused_func1(int a)
main.s:6:       .section        .text.unused_func1,"ax",@progbits
main.s:7:       .globl  unused_func1
main.s:8:       .type   unused_func1, @function
main.s:9:unused_func1:
main.s:31:      .size   unused_func1, .-unused_func1

**grep -rsnw unused_func2**
Binary file main.o matches
main.c:14:CODE_KEEP_USED void unused_func2(int a)
test_gc.map:215:                0x0000000000401169                unused_func2
main.i:740:__attribute__((section(".text.keep.used.code"))) void unused_func2(int a)
main.s:33:      .globl  unused_func2
main.s:34:      .type   unused_func2, @function
main.s:35:unused_func2:
main.s:57:      .size   unused_func2, .-unused_func2
**Binary file test_gc matches**

從中,我們發(fā)現(xiàn)最后的可執(zhí)行文件testgc里面只有unusedfunc2,而unused_func1就沒回收了,這個就實現(xiàn)了我們前面定義的需求。

4 原理分析

4.1 實現(xiàn)原理

這里實現(xiàn)的原理主要有4個部分: 第1部分主要修改的是代碼編寫階段,在不希望被回收優(yōu)化的函數(shù)前面添加特殊的段名稱,比如__attribute__((section(".text.keep.used.code"))), 第2部分主要修改的是編譯階段,通過在CFLAGS中添加-fdata-sections-ffunction-sections來實現(xiàn), 第3部分主要修改的是鏈接階段,通過在LDFLAGS中添加-Wl,-gc-sections來實現(xiàn), 第4部分主要修改的是鏈接腳本,通過在段名稱中,新增下面的段申明,主要是為了限制指定的段,不被回收。

.text_keep_used_code :
  {
    KEEP (*(.text.keep.used.code))
  }

從原理上說,編譯時使用-fdata-sections-ffunction-sections是為了讓data數(shù)據(jù)和每一個函數(shù)都生成特定的段,以函數(shù)xxx為例,那么它將會放在.text.xxx段里面,然后在鏈接階段的時候使用-Wl,-gc-sections回收那些不使用的段。 注意這里回收的最小單位是,所以編譯階段那兩個選項是必不可少的。

4.2 原理驗證分析

4.2.1 確認(rèn)編譯階段的函數(shù)所在的段

這個確認(rèn)我們可以map文件和.s匯編文件就可以確認(rèn),

/* map文件中 unused_fun1的描述 */
35  .text.unused_func1
36                 0x0000000000000000       0x28 main.o

/* 文件中 unused_fun2的描述 */
213  .text.keep.used.code
214                 0x0000000000401169       0x28 main.o
215                 0x0000000000401169                unused_func2

/* 匯編文件中 unused_fun1的描述 */
  6         .section        .text.unused_func1,"ax",@progbits
  7         .globl  unused_func1
  8         .type   unused_func1, @function
  9 unused_func1:
 10 .LFB0:
 11         .cfi_startproc
 12         endbr64
 13         pushq   %rbp
 14         .cfi_def_cfa_offset 16
 15         .cfi_offset 6, -16

/* 匯編文件中 unused_fun2的描述 */
 32         .section        .text.keep.used.code,"ax",@progbits
 33         .globl  unused_func2
 34         .type   unused_func2, @function
 35 unused_func2:
 36 .LFB1:
 37         .cfi_startproc
 38         endbr64
 39         pushq   %rbp
 40         .cfi_def_cfa_offset 16
 41         .cfi_offset 6, -16
 42         movq    %rsp, %rbp
 43         .cfi_def_cfa_register 6

從上面的分析,可以知道函數(shù)的段分布是完全符合預(yù)期的。

4.2.2 確認(rèn)鏈接階段的函數(shù)所在的段的回收情況

為了驗證這一點,我們可以在LDFLAGS里面添加這一個選項-Wl,--print-gc-sections,這樣我們就可以觀察到鏈接階段最后移除了那些沒有引用的段,從而確認(rèn)unusedfunc1和unusedfunc2是否被回收。 輸出的關(guān)鍵log如下:

 ./build.sh gc
gcc compile with gc ...
gcc -o main.o -c *.c -save-temps=obj -Wall -fdata-sections -ffunction-sections && gcc -o test_gc main.o -Wl,--gc-sections -Wl,-Map=test_gc.map -T default.lds -Wl,--print-gc-sections
/usr/bin/ld: removing unused section '.rodata.cst4' in file '/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/Scrt1.o'
/usr/bin/ld: removing unused section '.data' in file '/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/Scrt1.o'
/usr/bin/ld: removing unused section '.text.unused_func1' in file 'main.o'

log很明顯就告訴我們,unused section '.text.unused_func1'被回收移除了,而.text.keep.used.code是沒有被回收的,這切好證明了我們的猜想。

5 經(jīng)驗總結(jié)

  • 使用-gc-sections可以回收不使用的代碼段,從而減少代碼尺寸,降低固件占用FLASH的存儲空間;
  • 在特定場景下,修改鏈接腳本可以實現(xiàn)某個函數(shù)不被鏈接優(yōu)化,達到特定的目的。

6 更多分享

本項目的所有測試代碼和編譯腳本,均可以在我的github倉庫01workstation中找到。

歡迎關(guān)注我的github倉庫01workstation,日常分享一些開發(fā)筆記和項目實戰(zhàn),歡迎指正問題。

同時也非常歡迎關(guān)注我的CSDN主頁和專欄:

【CSDN主頁:架構(gòu)師李肯】

【RT-Thread主頁:架構(gòu)師李肯】

【C/C++語言編程專欄】

【GCC專欄】

信息安全專欄】

【RT-Thread開發(fā)筆記】

freeRTOS開發(fā)筆記】

有問題的話,可以跟我討論,知無不答,謝謝大家。

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 單片機
    +關(guān)注

    關(guān)注

    6026

    文章

    44459

    瀏覽量

    631247
  • GCC
    GCC
    +關(guān)注

    關(guān)注

    0

    文章

    105

    瀏覽量

    24798
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4264

    瀏覽量

    62251
收藏 人收藏

    評論

    相關(guān)推薦

    GCC編譯優(yōu)化系列】前后編譯的兩版本固件bin大小不一樣?

    GCC編譯優(yōu)化系列】前后編譯的兩個版本固件bin大小不一樣,怎么辦?
    的頭像 發(fā)表于 09-09 09:01 ?4386次閱讀
    【<b class='flag-5'>GCC</b><b class='flag-5'>編譯</b><b class='flag-5'>優(yōu)化</b><b class='flag-5'>系列</b>】前后<b class='flag-5'>編譯</b>的兩版本固件bin大小不一樣?

    Linux 下GCC編譯

    一、Linux 下多文件編譯 在上一篇 Linux 下的 C 編程我們知道了 Linux 下的編譯器為 GCC ,以及如何使用 GCC 進行編譯
    的頭像 發(fā)表于 09-11 15:18 ?2299次閱讀
    Linux 下<b class='flag-5'>GCC</b>的<b class='flag-5'>編譯</b>

    使用gcc編譯優(yōu)化優(yōu)化問題

    同樣的程序,使用gcc編譯優(yōu)化優(yōu)化的結(jié)果不一代碼如下:1. #include 2.3. int main()4. {5.int i
    發(fā)表于 09-27 10:33

    gcc編譯中斷函數(shù)的方法

    在x86中,一般函數(shù)通過"call"指令調(diào)用,"ret"指令返回,但是中斷函數(shù)不同,它在中斷或者異常發(fā)生時自動切入(或者使用"int"指令
    發(fā)表于 12-09 06:20

    【原創(chuàng)精選】RT-Thread征文精選技術(shù)文章合集

    編譯優(yōu)化系列】使用GCC如何把C文件編譯成可執(zhí)行文件【GCC
    發(fā)表于 07-26 14:56

    AVR系列單片機GCC免費編譯工具

    AVR系列單片機GCC免費編譯工具
    發(fā)表于 04-13 15:23 ?54次下載

    高效的C編程之函數(shù)調(diào)用

    14.9 函數(shù)調(diào)用 函數(shù)設(shè)計的基本原則是使其函數(shù)體盡量的小。這樣編譯器可以對函數(shù)做更多的
    發(fā)表于 10-17 16:49 ?6次下載
    高效的C編程之<b class='flag-5'>函數(shù)</b><b class='flag-5'>調(diào)用</b>

    常見gcc編譯警告整理以及解決方法

     GCC有很多的編譯選項,警告選項;指定頭文件、庫路徑;優(yōu)化選項。本文針整理一下GCC的警告選項以及gcc
    發(fā)表于 11-14 11:19 ?2.1w次閱讀

    GCC編譯優(yōu)化指南

    (cpp) → 編譯(gcc或g++) → 匯編(as) → 連接(ld) ;括號中表示每個階段所使用的程序,它們分別屬于 GCC 和 Binutils 軟件包。顯然的,優(yōu)化應(yīng)當(dāng)從
    發(fā)表于 04-02 14:36 ?486次閱讀

    編譯優(yōu)化函數(shù)的影響

    編譯器如gcc,可以指定不同的優(yōu)化參數(shù),在某些條件下,有些函數(shù)可能會被優(yōu)化掉。
    的頭像 發(fā)表于 06-22 14:58 ?2775次閱讀
    <b class='flag-5'>編譯</b>器<b class='flag-5'>優(yōu)化</b>對<b class='flag-5'>函數(shù)</b>的影響

    如何讓gcc編譯中斷函數(shù)

    在x86中,一般函數(shù)通過"call"指令調(diào)用,"ret"指令返回,但是中斷函數(shù)不同,它在中斷或者異常發(fā)生時自動切入(或者使用"int"指令
    發(fā)表于 11-26 11:06 ?7次下載
    如何讓<b class='flag-5'>gcc</b><b class='flag-5'>編譯</b>中斷<b class='flag-5'>函數(shù)</b>

    GCC編譯優(yōu)化系列】實戰(zhàn)分析C代碼遇到的編譯問題及解決思路

    GCC編譯優(yōu)化系列】實戰(zhàn)分析C工程代碼可能遇到的編譯問題及其解決思路
    的頭像 發(fā)表于 07-10 23:15 ?1323次閱讀
    【<b class='flag-5'>GCC</b><b class='flag-5'>編譯</b><b class='flag-5'>優(yōu)化</b><b class='flag-5'>系列</b>】實戰(zhàn)分析C代碼遇到的<b class='flag-5'>編譯</b>問題及解決思路

    GCC編譯優(yōu)化系列】multiple-definition

    GCC編譯優(yōu)化系列】這種讓人看不懂的multiple-definition真的有點讓人頭疼
    的頭像 發(fā)表于 07-11 09:26 ?6784次閱讀
    【<b class='flag-5'>GCC</b><b class='flag-5'>編譯</b><b class='flag-5'>優(yōu)化</b><b class='flag-5'>系列</b>】multiple-definition

    GCC編譯優(yōu)化系列】-specs=kernel.specs

    GCC編譯優(yōu)化系列GCC編譯鏈接時候--specs=kernel.specs鏈接屬性究竟是個
    的頭像 發(fā)表于 07-11 09:25 ?3219次閱讀
    【<b class='flag-5'>GCC</b><b class='flag-5'>編譯</b><b class='flag-5'>優(yōu)化</b><b class='flag-5'>系列</b>】-specs=kernel.specs

    Linux使用gcc編譯程序的語法

    01. 調(diào)試相關(guān)的宏 在Linux使用gcc編譯程序的時候,對于調(diào)試的語句還具有一些特殊的語法。 gcc編譯的過程中,會生成一些宏,可以使用這些宏分別打印當(dāng)前源文件的信息,主要內(nèi)容是當(dāng)
    的頭像 發(fā)表于 06-22 10:51 ?639次閱讀