関数セクションとデータセクションのオプションについては、以下のGCCページで説明されています。
-ffunction-sections -fdata-sections
ターゲットが任意のセクションをサポートしている場合、各関数またはデータ項目を出力ファイルの独自のセクションに配置します。関数の名前またはデータ項目の名前によって、出力ファイル内のセクションの名前が決まります。これらのオプションは、リンカが最適化を実行して命令空間での参照の局所性を向上できるシステムで使用します。 ELFオブジェクト形式とSPARC Solaris 2を実行するプロセッサーを使用するほとんどのシステムには、そのような最適化を備えたリンカーがあります。AIXは将来これらの最適化を行う可能性があります。
これらのオプションは、そうすることによる大きなメリットがある場合にのみ使用してください。 これらのオプションを指定すると、アセンブラーとリンカーはより大きなオブジェクトと実行可能ファイルを作成し、速度も低下します。このオプションを指定すると、すべてのシステムでgprofを使用できなくなり、このオプションと-gの両方を指定すると、デバッグに問題が発生する可能性があります。
これらのオプションは実行可能ファイルのサイズを小さくするのに役立つという印象を受けました。なぜこのページはより大きな実行可能ファイルを作成すると言っているのですか?何か不足していますか?
これらのコンパイラオプションを使用する場合、リンカーオプション-Wl,--gc-sections
を追加して、未使用のコードをすべて削除できます。
興味深いことに、-fdata-sections
を使用すると、関数のリテラルプールを作成できるため、関数自体が大きくなります。私はこれにARM=特に)気づきましたが、他の場所では本当である可能性があります。テストしていたバイナリは4分の1パーセントしか伸びませんでしたが、大きくなりました。変更された機能の理由は明らかでした。
オブジェクトファイル内のすべてのBSS(またはDATA)エントリが1つのセクションに割り当てられている場合、コンパイラはそのセクションのアドレスを関数リテラルプールに格納し、関数内のそのアドレスからの既知のオフセットでロードを生成して、データ。ただし、-fdata-sections
を有効にすると、BSS(またはDATA)データの各部分が独自のセクションに配置されます。また、これらのセクションのどれが後でガベージコレクションされる可能性があるか、またはリンカがすべての順序を配置するかどうかがわからないためこれらのセクションを最終的な実行可能イメージに含めると、単一のアドレスからのオフセットを使用してデータをロードできなくなります。そのため、代わりに、使用済みデータごとにリテラルプールにエントリを割り当てる必要があります。リンカが最終的なイメージの内容と場所を特定したら、これらのリテラルプールエントリを実際のアドレスで修正できます。データ。
つまり、-Wl,--gc-sections
を使用しても、実際の関数テキストが大きくなるため、結果の画像が大きくなる可能性があります。
以下に最小限の例を追加しました
以下のコードは、私が話している動作を確認するのに十分です。実際のコードで疑わしいvolatile宣言とグローバル変数の使用に惑わされないでください。ここでは、-fdata-sectionsが使用されたときに2つのデータセクションの作成を保証します。
static volatile int head;
static volatile int tail;
int queue_empty(void)
{
return head == tail;
}
このテストに使用されたGCCのバージョンは次のとおりです。
gcc version 6.1.1 20160526 (Arch Repository)
まず、-fdata-sectionsを指定しないと、次のようになります。
> arm-none-eabi-gcc -march=armv6-m \
-mcpu=cortex-m0 \
-mthumb \
-Os \
-c \
-o test.o \
test.c
> arm-none-eabi-objdump -dr test.o
00000000 <queue_empty>:
0: 4b03 ldr r3, [pc, #12] ; (10 <queue_empty+0x10>)
2: 6818 ldr r0, [r3, #0]
4: 685b ldr r3, [r3, #4]
6: 1ac0 subs r0, r0, r3
8: 4243 negs r3, r0
a: 4158 adcs r0, r3
c: 4770 bx lr
e: 46c0 nop ; (mov r8, r8)
10: 00000000 .Word 0x00000000
10: R_ARM_ABS32 .bss
> arm-none-eabi-nm -S test.o
00000000 00000004 b head
00000000 00000014 T queue_empty
00000004 00000004 b tail
arm-none-eabi-nm
から、queue_emptyが20バイト長(14 hex)であることがわかり、arm-none-eabi-objdump
出力は、関数の最後に単一の再配置ワードがあることを示しています。これは、BSSセクションのアドレスです(初期化されていないデータのセクション)。関数の最初の命令は、その値(BSSのアドレス)をr3にロードします。次の2つの命令は、r3に関連してロードされ、それぞれ0バイトと4バイトずつオフセットされます。これらの2つの荷重は、ヘッドとテールの値の荷重です。これらのオフセットは、arm-none-eabi-nm
からの出力の最初の列で確認できます。関数の最後のnop
は、リテラルプールのアドレスをWordが揃えるためのものです。
次に、-fdata-sectionsを追加するとどうなるかを確認します。
arm-none-eabi-gcc -march=armv6-m \
-mcpu=cortex-m0 \
-mthumb \
-Os \
-fdata-sections \
-c \
-o test.o \
test.c
arm-none-eabi-objdump -dr test.o
00000000 <queue_empty>:
0: 4b03 ldr r3, [pc, #12] ; (10 <queue_empty+0x10>)
2: 6818 ldr r0, [r3, #0]
4: 4b03 ldr r3, [pc, #12] ; (14 <queue_empty+0x14>)
6: 681b ldr r3, [r3, #0]
8: 1ac0 subs r0, r0, r3
a: 4243 negs r3, r0
c: 4158 adcs r0, r3
e: 4770 bx lr
...
10: R_ARM_ABS32 .bss.head
14: R_ARM_ABS32 .bss.tail
arm-none-eabi-nm -S test.o
00000000 00000004 b head
00000000 00000018 T queue_empty
00000000 00000004 b tail
すぐに、queue_emptyの長さが4バイト増えて24バイト(18 hex)になり、queue_emptyのリテラルプールで2つの再配置が行われるようになりました。これらの再配置は、グローバル変数ごとに1つずつ、作成された2つのBSSセクションのアドレスに対応しています。コンパイラはリンカが2つのセクションを配置することになる相対位置を知ることができないため、ここには2つのアドレスが必要です。queue_emptyの先頭にある命令を見ると、追加の負荷があることがわかります。コンパイラセクションのアドレスを取得してから、そのセクションの変数の値を取得するには、個別のロードペアを生成する必要があります。このバージョンのqueue_emptyの追加の命令は、関数の本体を長くするものではなく、以前はnopであったスポットを取得するだけですが、一般的にはそうではありません。
静的ライブラリで-ffunction-sections
および-fdata-sections
を使用できます。これにより、各関数とグローバルデータ変数が別々のセクションに配置されるため、静的ライブラリのサイズが大きくなります。
次に、この静的ライブラリとリンクしているプログラムで-Wl,--gc-sections
を使用します。これにより、未使用のセクションが削除されます。
したがって、最終的なバイナリは、これらのフラグがない場合よりも小さくなります。
ただし、-Wl,--gc-sections
は問題を引き起こす可能性があるので注意してください。
追加のステップを追加して.a
アーカイブを構築すると、より良い結果が得られます。
-ffunction-sections
-fdata-sections
フラグと共に使用されます.o
オブジェクトが.a
とともにar rcs file.a *.o
アーカイブに入れられます-Wl,-gc-sections,-u,main
オプションで呼び出されます-Os
に設定されています。しばらく前に試してみましたが、結果を見ると、サイズの増加は、配置が異なるオブジェクトの順序に起因しているようです。通常、リンカはオブジェクト間のパディングを小さくするためにオブジェクトを並べ替えますが、それはセクション内ではなく、セクション内でのみ機能するように見えます。そのため、各関数のデータセクション間に余分なパディングが発生し、全体的なスペースが増えることがよくあります。
-Wl、-gc-sectionsを指定した静的ライブラリの場合、未使用のセクションを削除すると、わずかな増加だけで十分ではなくなります。