4.2.4 拡張仕様の使用方法

この項では、下記の拡張機能の使用方法について説明します。

-

セクション切り替え記述

-

スタックセクション作成記述

-

割り込み関数作成記述

-

関数のインライン展開記述

-

アセンブラ記述関数インライン展開

-

エントリ関数指定

-

ビットフィールド並び順指定

-

構造体/クラスメンバのアライメント指定

-

変数の絶対アドレス割り付け指定

-

初期値のエンディアン指定

-

分岐先の命令実行向け整合を行う関数の指定

-

スタック破壊検出コードを生成する関数の指定

 

(1)

セクション切り替え記述

#pragma section [<セクション種別>] [△<変更セクション名>]
<セクション種別>: { P | C | D | B }

 

コンパイラの出力するセクション名を切り替えます。

セクション種別と変更セクションを指定した場合、セクション種別がPであればその#pragma 宣言以降に記述された関数のセクション名を変更します。セクション種別がC、D、またはBの場合は、その#pragma 宣言以降に実体を定義した全てのセクション名を変更します。

変更セクション名のみを指定した場合、その#pragma 宣言以降にあるプログラム領域、定数領域、初期化データ領域、および未初期化データ領域のすべてのセクション名を変更します。この場合、各セクションの変更後のセクション名は、各デフォルトセクション名の後に<変更セクション名>の文字列を追加したセクション名となります。

セクション種別も変更セクション名も記述しなかった場合は、その#pragma 宣言以降にあるプログラム領域、定数領域、初期化データ領域、および未初期化データ領域の全てのセクション名をデフォルトセクション名に戻します。

各セクション種別のデフォルトセクション名は、sectionオプションの指定があればそれに従います。ない場合はセクション種別名をそのまま用います。

例 1.

セクション名とセクション種別を指定した場合

#pragma section B Ba
int i; // Baセクションに配置
void func(void)
{
(省略)
}
 
#pragma section B Bb
int j; // Bbセクションに配置
void sub(void)
{
(省略)
}
 

 

例 2.

セクション種別を省略した場合

#pragma section abc
int a; // Babcセクションに配置
const int c=1; // Cabcセクションに配置
 
void f(void)// Pabcセクションに配置
 
{
    a=c;
}
 
 
#pragma section
int b;// Bセクションに配置
 
void g(void)// Pセクションに配置
{
    b=c;
}

 

#pragma sectionは関数定義の外で宣言しなければなりません。

次の項目のセクション名は変更できません。sectionオプションを使用してください。

(1) 文字列リテラルおよび集成体の動的初期化で用いる初期化子

(2) switch文の分岐テーブル

1ファイルあたりの#pragma sectionで指定できるセクション数は最大2045個です。

静的クラスメンバ変数のセクションを指定する場合は、クラスのメンバ宣言と実体の定義の両方または実態の定義時のみに#pragma section の指定が必要になります。

/*
 ** クラスメンバ宣言
 */
class A
{
private:
  // 初期値なし
    #pragma section DATA
    static int data_;
    #pragma section
 
  // 初期値あり
    #pragma section TABLE
    static int table_[2];
    #pragma section
};
 
/*
** 実体定義
*/
 
// 初期値なし
#pragma section DATA
int A::data_;
#pragma section
 
// 初期値あり
#pragma section TABLE
int A::table_[2]={0, 1};
#pragma section
 

 

(2)

スタックセクション作成記述

#pragma stacksize {si=<定数> | su=<定数>}

 

si=<定数>を指定した場合、セクション名SI、サイズ<定数>のスタックとして使用するデータセクションを作成します。

su=<定数>を指定した場合、セクション名SU、サイズ<定数>のスタックとして使用するデータセクションを作成します。

Cソース:

#pragma stacksize si=100
#pragma stacksize su=200

コード展開例:

.SECTION    SI,DATA,ALIGN=4
.BLKB       100
.SECTION    SU,DATA,ALIGN=4
.BLKB       200

si, su指定はファイル内でそれぞれ1回しか指定できません。

<定数>は必ず4の倍数を指定してください。

<定数>に記述できる値の範囲は、4から2147483644(0x7ffffffc)までです。

 

(3)

割り込み関数作成記述

#pragma interrupt [(]<関数名>[(<割り込み仕様>[,…])][,…][)]

 

#pragma interruptを用いて割り込み関数となる関数を宣言します。

関数名には、グローバル関数および静的関数メンバを指定できます。

割り込み仕様の一覧を表4.22に示します。

表 4.24

割り込み仕様の一覧

 

項目

形式

オプション

指定内容

1

ベクタテーブル指定

vect=

<ベクタ番号>

割り込み関数のアドレスを配置するベクタ番号

2

高速割り込み指定

fint

なし

高速割り込みに使用する関数の指定

RTFI命令でリターン

3

割り込み関数レジスタ制限指定

save

なし

割り込み関数内で使用するレジスタの本数を制限し、退避・回復の数を減らす

4

多重割り込み許可指定

enable

なし

関数の先頭でPSWのIフラグを1にし、多重割り込みを許可する

5

アキュムレータ保存指定

acc

なし

割り込み関数内でアキュムレータを退避・回復する

6

アキュムレータ非保存指定

no_acc

なし

割り込み関数内でアキュムレータを退避・回復しない

 

#pragma interruptを用いて宣言した関数は、関数の処理の前後で全レジスタを保証(関数入口/出口において関数内で使用する全レジスタを退避・回復)し、通常RTE命令でリターンします。

割り込み仕様を指定しない場合は単純な割り込み関数として処理します。

ベクタテーブル指定(vect=)をした場合はC$VECTセクション内の指定したベクタテーブル番号位置にその関数アドレスを設定します。

高速割り込み指定(fint)をした場合は、RTFI命令でリターンします。また、fint_registerオプションを指定した場合は、オプションで指定したレジスタを退避、回復せずに割り込み関数で使用します。

割り込み関数レジスタ制限指定(save)をした場合は、割り込み関数内で使用するレジスタの本数をR1〜R5、およびR14〜R15に制限します。R6〜R13は割り込みで使用しないため、退避・回復命令は生成しません。

多重割り込み許可指定(enable)をした場合は、割り込み関数の先頭でPSWのIフラグを1にし、多重割り込みを許可します。

アキュムレータ保存指定(acc)をした場合で、指定された関数から別の関数呼び出しがあったり、関数内でアキュムレータを書き換える命令を使う場合は、アキュムレータを退避および回復する命令を生成します。なお、ISA(*1)にRXv1を選択するか、CPUでマイコン種別を選択(*2)した場合は、ACCを退避および回復する命令を生成します。ISA(*1)にRXv2を選択した場合は、ACC0と ACC1を退避および回復する命令を生成します。

*1) isaオプションまたは環境変数ISA_RXによる選択を指します。
*2) cpuオプションまたは環境変数CPU_RXによる選択を指します。

 

アキュムレータ非保存 (no_acc) を指定した場合は、ACC, ACC0, ACC1 のいずれに対しても退避・回復する命令を生成しません。

acc およびno_accのどちらの指定もない場合は、コンパイルオプションに従います。

割り込み関数の定義に対して指定できる関数は、グローバル関数(C/C++言語)と静的関数メンバ(C++言語)です。

関数の返却値の型はvoid のみです。return 文の返却値を指定することはできません。指定があった場合はエラーを出力します。

例 1.

正しい宣言と誤った宣言の例

#pragma interrupt (f1, f2)
void f1(){…}// 正しい宣言です。
int f2(){…}// 返却値の型がvoidではないのでエラーになります。

 

例 2.

通常の割り込み関数の例

Cソース:

#pragma interrupt func
void func(){ .... }

 

出力コード:

_func:
    PUSHM R1-R3; 関数内で使用しているレジスタを退避
    ....
    (R1,R2,R3を関数内で使用)
    ....
    POPM R1-R3; 入口で退避したレジスタを回復
    RTE

 

例 3.

関数呼び出しがある割り込み関数の例

関数内で使用しているレジスタに加えて、関数呼び出し前後で保証しないレジスタについても、割り込み関数の入口で退避し、出口で回復します。

 

Cソース:

#pragma interrupt func
void func(){
  ...
  sub();
  ...
}
 

 

出力コード:

_func:
    PUSHM R14-R15
    PUSHM R1-R5
    ...
    BSR _sub
    ...
    POPM R1-R5
    POPM R14-R15
    RTE
 

 

例 4.

割り込み仕様fintを使用した場合の例

Cソース: fint_register=2オプションを指定してコンパイル

#pragma interrupt func1(fint) 
void func1(){ a=1; }    //  割り込み関数 
void func2(){ a=2; }    //  通常関数 
 

 

出力コード:

_func1:
    PUSHM R1-R3 ;  関数内で使用しているレジスタを退避 
    ...         ; (但し、R12,R13 は退避しない) 
    ...
    (R1,R2,R3,R12,R13 を関数内で使用) 
    ...
    POPM R1-R3  ;  入口で退避したレジスタを回復 
    RTFI
 
_func2: 
    ...         ; #pragma interrupt fint 指定した関数以外では、 
    ...         ; R12,R13 を使用しないコードを生成する 
    RTE

 

例 5.

割り込み仕様accを使用した場合の例

Cソース:

void func5(void);
#pragma interrupt accsaved_ih(acc)      /* "acc"を指定 */
void accsaved_ih(void)
{
     func5();
}
 

 

出力コード:

_accsaved_ih:
     PUSHM R14-R15
     PUSHM R1-R5
     MVFACMI R4
     SHLL #10H, R4
     MVFACHI R5
     PUSHM R4-R5
     BSR _func5
     POPM R4-R5
     MVTACLO R4
     MVTACHI R5
     POPM R1-R5
     POPM R14-R15
     RTE
 

 

[備考]

-

RX命令セットの仕様のため、accフラグで退避・回復できるのは、ACCの上位48ビットに限られます。
ACCの下位16ビットは退避・回復されません。

-

各割り込み仕様は、小文字のみ指定できます。大文字を指定してもエラーになります。

-

割り込み仕様としてvectを使った場合、指定の無い空きベクタのアドレスは0になります。このアドレスは、最適化リンケージエディタで任意のアドレスやシンボルに変更することができます。詳しくは、VECTおよびVECTNオプションの項目を参照してください。

-

#pragma interruptの関数には、引数(仮引数)を定義することはできません。定義してもエラーにはなりませんが、引数から値を読み出しても、不定値が返されます。

-

- saveフラグとfintフラグを同時に有効にした場合は、R1〜R5、およびR14〜R15のほか、fint_registerオプションで確保したレジスタもコード生成に使用します。

<acc、no_accの用途>

acc、no_accは次のような用途を考慮したものです。

-

アキュムレータの補償をsave_accで行う場合の割り込み応答速度低下の対策(no_acc)

既存の割り込み関数でアキュムレータの補償にはsave_accオプションが有効ですが、割り込み応答速度が悪化することがあるため、不要なACCの退避・回復を関数単位で抑止する手段としてno_accを用意します。

-

アキュムレータの退避・回復をソースコードで制御(accとno_acc)

アキュムレータの退避・回復の考慮が完了している割り込み関数には、明示的にacc、no_accを選択しておくことで、ソースプログラムでアキュムレータの退避・回復をsave_accに依存せずに定義できます。

 

(4)

関数のインライン展開記述

#pragma inline [(]<関数名>[,…][)]
#pragma noinline [(]<関数名>[,…][)]

 

#pragma inline は、インライン展開する関数を宣言します。

noinlineオプションが指定された場合でも、#pragma inline指定された関数はインライン展開の対象となります。

#pragma noinline は、inlineオプションの指定を抑止する関数を宣言します。

関数名には、グローバル関数および静的関数メンバを指定できます。

#pragma inline で指定した関数名の関数と関数指定子inline(C++言語およびC(C99)言語)を指定した関数は、その関数を呼び出したところにインライン展開されます。

ソースファイル

#pragma inline(func)
static int func (int a, int b)
{
    return (a+b)/2;
}
int x;
main()
{
    x=func(10,20);
}

展開イメージ

int x;
main()
{
    int func_result;
    {
        int a_1=10, b_1=20;
        func_result=(a_1+b_1)/2;
    }
    x=func_result;
}

#pragma inlineが指定された場合でも、以下のいずれかに該当する場合はインライン展開しません。

-

可変引数を持つ関数である。

-

展開対象関数のアドレスを介して呼び出しを行っている。

#pragma inlineは、インライン展開されることを保証するものではありません。コンパイル時間やメモリ使用量の増大を考慮した制限により、インライン展開を抑止することがあります。なお、インライン展開が抑止される際に、noscopeオプションを指定すると、インライン展開されるようになる場合があります。

#pragma inlineは、関数本体の定義の前に指定してください。

#pragma inline で指定した関数に対して外部定義を生成します。static関数に#pragma inlineを指定した場合、関数定義はインライン展開後に削除されます。

C++言語のコンパイルでは、inlineが指定された関数には、外部定義を生成しません。

C(C99)言語のコンパイルでは、inline指定された関数には、その関数にextern宣言がなければ、外部定義を生成しません。

 

(5)

アセンブラ記述関数インライン展開

#pragma inline_asm[(]<関数名>[,…][)]

#pragma inline_asmで宣言したアセンブリ記述関数をインライン展開します。

アセンブラ埋め込みインライン関数の呼び出し規則は通常関数の呼び出し規則と同様です。

Cソース

#pragma inline_asm func
static int func(int a, int b){
   ADD R2,R1; アセンブリ記述
}
main(int *p){
*p = func(10,20);
}

 

生成コード

_main:
   PUSH.L R6
   MOV.L R1, R6
   MOV.L #20, R2
   MOV.L #10, R1
   ADD  R2,R1; アセンブリ記述
   MOV.L R1, [R6]
   MOV.L #0, R1
   RTSD #04H, R6-R6

#pragma inline_asmは、関数本体の定義の前に指定してください。

#pragma inline_asmで指定したstaticではない関数に対して外部定義を生成します。

アセンブラ埋め込みインライン関数内で関数の出入口で保証するレジスタ(表 9.1 レジスタ使用規則を参照)を使用する場合は、アセンブラ埋め込みインライン関数の先頭と最後でこれらのレジスタの退避・回復が必要です。

 

[備考]

- アセンブラ埋め込みインライン関数には、RXファミリの命令およびテンポラリラベルだけを記述してください。テンポラリラベル以外のラベルを定義したり、アセンブラ制御命令を記述することはできません。

-アセンブラ埋め込みインライン関数の最後にRTS を記述しないでください。

関数名に関数メンバを指定することはできません。

- static関数に#pragma inline_asmを指定した場合、関数定義はインライン展開後に削除されます。

- アセンブリ記述は、プリプロセッサの処理対象となります。このため、アセンブリ言語で使用される命令やレジスタと同じ名前のマクロ(例: "MOV"や"R5"など)を#defineでマクロ定義する場合はご注意ください。

-スタック情報ファイルは、#pragma inline_asmのアセンブリ記述内ではスタックが消費されないものとして扱います。アセンブリ記述内にR0を操作するコードを含める場合はご注意ください。

 

(6)

エントリ関数指定

#pragma entry[(]<関数名>[)]

 

<関数名>で指定した関数をエントリ関数として扱います。

エントリ関数では、レジスタの退避・回復コードを一切作成しません。

#pragma stacksize 宣言があると、関数先頭でスタックポインタの初期設定コードを出力します。

baseオプションを指定した場合、オプションで指定したベースレジスタへの設定を行います。

Cソース:-base=rom=R13 を指定

#pragma stacksize su=100
#pragma entry INIT
void INIT() {
:
}

出力コード

.SECTION    SU,DATA,ALIGN=4
.BLKB       100
.SECTION    P,CODE
_INIT:
MVTC        (TOPOF SU + SIZEOF SU),USP
MOV.L       #__ROM_TOP,R13

#pragma entry指定は、関数の宣言前に行ってください。

ロードモジュール全体でエントリ関数を複数指定することはできません。

 

(7)

ビットフィールド並び順指定

#pragma bit_order [{left | right}]

 

ビットフィールドの並び順の切り替えを指定します。

leftを指定した場合は上位ビット側から、rightを指定した場合は下位ビット側から、それぞれメンバが割り付けられます。

デフォルトの設定はrightです。

left|rightを省略すると、以降はオプションに従います。

Cソース

ビット配置

#pragma bit_order right

struct tbl_r {

unsigned char a:2;

unsigned char b:3;

} x;

 

#pragma bit_order left

struct tbl_l {

unsigned char a:2;

unsigned char b:3;}

y;

 

// 異なるサイズのメンバの場合

#pragma bit_order right

struct tbl_r {

unsigned short a:4;

unsigned char b:3;

} x;

 

// 型のサイズを超える場合

#pragma bit_order right

struct tbl_r {

unsigned char a:4;

unsigned char b:5;

} x;

 

 

(8)

構造体/クラスメンバのアライメント指定

#pragma pack
#pragma unpack
#pragma packoption

 

ソースプログラム中の指定位置以降の構造体メンバおよびクラスメンバのアライメント数を指定します。本拡張子が指定されていない場合または#pragma packoption 指定位置以降で宣言された構造体メンバおよびクラスメンバのアライメント数はpackオプションの指定に従います。#pragma pack拡張子とアライメント数の関係を表4.23に示します。

表 4.25

#pragma packとメンバのアライメント数

メンバの型

#pragma pack

#pragma unpack

#pragma packoption

または指定なし

(signed) char

1

1

1

(unsigned) short

1

2

packオプションに従う

(unsigned) int *, (unsigned) long,

(unsigned) long long, 浮動小数点型,

ポインタ型

1

4

packオプションに従う

#pragma pack
struct S1 {
   char a;/* バイトオフセット=0*/
   int b;/* バイトオフセット=1*/
   char c;/* バイトオフセット=5*/
} ST1;/* 合計サイズ6バイト*/
 
#pragma unpack
struct S2 {
   char a;/* バイトオフセット=0*/
/* 3バイト空き領域		*/
   int b;/* バイトオフセット=4*/
   char c;/* バイトオフセット=8*/
/* 3バイト空き領域*/
} ST2;/* 合計サイズ12バイト*/
 

構造体、共用体、クラスメンバのアライメント数はpackオプションでも指定できます。オプションと#pragmaの両方が指定された場合は、#pragmaの指定を優先します。

 

(9)

変数の絶対アドレス割り付け指定

#pragma address [(]<変数名>=<絶対アドレス>[,…][)]

 

指定した変数を指定したアドレスに割り付けます。その際、コンパイラが指定した変数ごとにセクションを設定し、リンク時に指定した絶対アドレスに割り付けます。連続したアドレスに変数を指定した場合、それらの変数は同一セクションにします。

Cソース

#pragma address X=0x7f00
int X;
main(){
X=0;
}

 

出力コード

_main:
MOV.L       #0,R5
MOV.L       #7F00H,R14	;
MOV.L       R5,[R14]
RTS
.SECTION    $ADDR_B_7F00,DATA
.ORG        7F00H
.glb        _X
_X:                              ; static: X
.blkl       1

 

[備考]

- #pragma address指定は、変数の宣言前に行ってください。

- 構造体/共用体のメンバ、もしくは変数以外を指定した場合はエラーとなります。

- #pragma addressを同一の変数に対して複数回指定した場合はエラーとなります。

- #pragma address が有効でも、ソースファイル内で参照のないstatic変数は、最適化により削除される場合があります。

- 初期値を持ち、かつconst修飾のない変数に対して#pragma addressを適用することは推奨されません。もし該当する変数がある場合は、次の制限事項に注意してください。

・この変数に対応するセクションに、最適化リンカ(rlink)の-romオプション(ROM領域のRAM化)を
適用することはできません。

・この変数に書き込みを行ってもエラーや警告などのメッセージは表示されません。

・この変数に対応するセクションはRAMに配置し、スタートアップの実行またはその前に、すべての
初期値を該当するRAM領域に書き込んでおく必要があります。

 

(10)

初期値のエンディアン指定

#pragma endian [{big | little}]

 

静的オブジェクトを格納する領域のエンディアンを指定します。

#pragma endianを記述した行から、ファイルの末尾か、または次の#pragma endianを記述した行の手前までに定義したものが対象となります。

bigを指定した場合はbig endianになります。オプション指定がendian=littleである場合は、セクション名の後に_Bをつけたセクションに配置されます。

littleを指定した場合はlittle endianになります。オプション指定がendian=bigである場合は、セクション名の後に_Lをつけたセクションに配置されます。

big | little を省略すると、以降はオプションに従います。

endian=littleオプション指定時(デフォルト)

Cソース:

#pragma endian big
int A=100;/* D_Bセクション*/
#pragma endian
int B=200;/* Dセクション */
 

 

出力コード:

   .glb   _A
   .glb   _B
   .SECTION   D,ROMDATA,ALIGN=4
_B:
   .lword   200
   .SECTION  D_B,ROMDATA,ALIGN=4
   .ENDIAN  BIG
_A:
   .lword   100

 

endianオプションと異なる#pragma endian対象のオブジェクトに、long long型、double型(dbl_size=8オプション指定時)およびlong double型(同)の領域を含む場合は、これらの領域に対するアドレスやポインタを用いた間接的なアクセスはしないでください。この場合の動作は保証しません。

次の項目のエンディアンは変更できません。endianオプションを使用してください。

(1) 文字列リテラルおよび集成体の動的初期化で用いる初期化子

(2) switch文の分岐テーブル

(3) 外部参照宣言されたオブジェクト(初期化式なくextern宣言されたオブジェクト)

(4) #pragma address により指定されたオブジェクト

 

(11)

分岐先の命令実行向け整合を行う関数の指定

#pragma instalign4 [(]<関数名>[(<分岐先の種類>)][,…][)]
#pragma instalign8 [(]<関数名>[(<分岐先の種類>)][,…][)]
#pragma noinstalign [(]<関数名>[,…][)]

 

分岐先の命令実行向け整合を行う関数を指定します。

指定された関数に対し、#pragma instalign4の場合は4バイト、#pragma instalign8の場合は8バイトでそれぞれ配置アドレスを命令実行向け整合します。

#pragma noinstalignが指定された関数は、整合は行いません。

分岐先の種類は、以下から選択します(*1)。

指定なし:関数先頭、switch文のcaseおよびdefaultラベル

inmostloop:各最内周ループの先頭、関数先頭、switch文のcaseおよびdefaultラベル

loop:各ループの先頭、関数先頭、switch文のcaseおよびdefaultラベル

注 1.

ここに挙げたもの以外の分岐先は、命令実行向け整合の対象ではありません。たとえば、loopを選択した場合はループの先頭は対象ですが、ループ内にあるループを構成しないif文などの分岐先は対象ではありません。

 

それぞれ、指定した関数ごとに有効になることを除き、instalign4, instalign8, noinstalignオプションと機能は同じです。これらのオプションと同時に指定した場合は、#pragmaの指定が優先されます。instalign4またはinstalign8を選択した関数が属するコードセクションは、そのアライメント数は4(instalign4の場合)または8(instalign8の場合)に変わります。同じコードセクション内に、instalign4とinstalign8が指定された関数が混在する場合、そのコードセクションのアライメント数は8になります。
その他の内容については、instalign4, instalign8, noinstalignオプションと同様ですので、これらのオプションの項目を参照ください。

 

(12)

スタック破壊検出コードを生成する関数の指定 【Professional版のみ】 【V2.04.00以降】

#pragma stack_protector[(] 関数名 [(num=<整数値>)] [,...] [)]
#pragma no_stack_protector[(] 関数名 [,...] [)]

 

関数の入口・出口にスタック破壊検出コードを生成します。スタック破壊検出コードとは次に示す3つの処理を実行するための命令列を指します。

(1) 関数の入口で、ローカル変数領域の直前(0xFFFFFFFF番地に向かう方向)に4バイトの領域を確保し、その領域に<数値>で指定した値を格納します。

(2) 関数の出口で、<数値>を格納した4バイトの領域が書き換わっていないことをチェックします。

(3) (2)で書き換わっている場合には、スタックが破壊されたとして __stack_chk_fail 関数を呼び出します。

<数値>には 0 から 4294967295までの10進数または16進数の整数値を指定します。<数値>の指定を省略した場合には、コンパイラが自動的に数値を指定します。

__stack_chk_fail関数はユーザが定義する必要があり、スタックの破壊検出時に実行する処理を記述します。

__stack_chk_fail関数を定義する際には、次の項目に注意してください。

-

返却値の型はvoid型のみであり、仮引数を持たない関数です。

-

通常の関数のように呼び出すことは禁止します。

-

__stack_chk_fail関数は、-stack_protectorオプション、-stack_protector_allオプション、#pragma stack_protectorに関わらず、スタック破壊検出コードを生成しません。

-

C++プログラム内で__stack_chk_fail関数を定義する場合は「extern "C"」を付加してください。

-

関数内ではabort()を呼び出してプログラムを終了させるなど、呼び出し元であるスタックの破壊を検出した関数にリターンしないようにしてください。

-

__stack_chk_fail関数を定義する場合は、staticを指定しないでください。

#pragma no_stack_protector が指定された関数は-stack_protector オプション、-stack_protector_all オプションに関わらず、スタック破壊検出コードを生成しません。

#pragma stack_protector と-stack_protector オプション、-stack_protector_all オプションが同時に使用された場合は、#pragma 指定が有効になります。

同一翻訳単位内で、同じ関数に対して #pragma stack_protector と no_stack_protector を同時に指定する場合はエラーとします。

#pragma stack_protectorで指定した関数が次のいずれかの関数として指定されている場合、エラーメッセージを出力します。

#pragma inline

#pragma inline_asm

#pragma entry