いつもお世話になっております。
far領域(00000H~017DFFH)のRomデータを取得して加算するプログラムをC言語で作成したのですが、010000H以上のアクセスができませんでした。逆アセンブリをみたところ、ESレジスタの記述が見つからず、そこで、データ取得処理をアセンブラで記述してみました。
アセンブラ関数は、
16ビットアドレスのfar領域アクセス用関数(ES=0x00)と、20ビットアドレスのfar領域アクセス用関数(ES=0x01)を
2つ用意して、共に引数をunsigned short アドレスとして、戻り値をunsigned charでデータとしました。
Cソースファイルからアセンブラ関数を呼び出して、指定アドレスのデータを取得したいのですが、取得出来ません。
ステップ実行で確認すると、アセンブラ関数では指定のアドレスデータを取得できて、戻り値としてAレジスタにセットしてリターンしているのですが、C側に戻ると取得データは格納されておらず、逆アセンブリを見てみると、別のアドレスを指していました。
C言語からアセンブラ関数を呼び出す際の注意点などありましたら、教えていただきたく、宜しくお願いいたします。
【環境】
マイコン :RL78/G13(S2コア)
開発環境 :e2studio ver 2022-04 (22.4.0)
コンパイラ :GCC for Renesas RL78 ver 4.9.2.202201 最適化 -o1
プロジェクト:C++、C混在
const __farで修飾された変数定義で__attribute__((section("セクション名")))でセクション名を変更するのは諦め、先日別スレッドで知ったリンカスクリプトの小技を使ってみました。(もっとも、そこは瑣末の部分で、本質的な部分はconstでは無くconst __farで修飾するように変更したこと、ですけれども。) そして、sum_eの値を確認すると期待した通りの0x017dfeになりました。まだ続きます。関連リンクGCCプロジェクトで変数や関数を指定のセクションに配置する方法japan.renesasrulz.com/cafe_rene/forums-groups/mcu-mpu/rx/f/forum5/7937/gccソース
const unsigned short __far psum_rom = 0x1234; /* This variable is placed at the end of the ROM by linker script. */unsigned short _RomSum( void ){ const unsigned char __far *prom; unsigned long count; unsigned long sum_s = 0x00000L; unsigned long sum_e = (unsigned long)&psum_rom; ← 期待した通りの0x017dfeになる(ちなみに強制的キャストを1つ減らせました) unsigned char rom_h, rom_l; unsigned short sum = 0; // 1Word単位で加算 prom = (const unsigned char __far *)sum_s; for ( count = sum_s ; count < sum_e ; count += 2 ) { rom_h = *prom; ++prom; rom_l = *prom; ++prom; sum += (unsigned short)(((unsigned short)rom_h << 8) | rom_l); } return( sum );}
リンカスクリプト
…略… .frodata : { . = ALIGN(2); *(.frodata) *(EXCLUDE_FILE(*/hashi_gnurl78_far.o) .frodata.*) _efrodata = .; } > ROM …略… .psum 0x017DFE: AT(0x017DFE) { */hashi_gnurl78_far.o(.frodata.*) } > ROM …略…
マップファイル
…略….lowtext 0x00000000000000d8 0x0 *(.plt) .plt 0x00000000000000d8 0x0 ./src/hashi_gnurl78_far.o ← 今回は中身は何もありません *(.lowtext) 0x00000000000000d8 . = ALIGN (0x2)…略….frodata 0x00000000000004d2 0x0 0x00000000000004d2 . = ALIGN (0x2) *(.frodata) *(EXCLUDE_FILE(*/hashi_gnurl78_far.o) .frodata.*) 0x00000000000004d2 _efrodata = .…略….psum 0x0000000000017dfe 0x2 */hashi_gnurl78_far.o(.frodata.*) .frodata.psum_rom 0x0000000000017dfe 0x2 ./src/hashi_gnurl78_far.o ← ちなみに0x1234が入っています 0x0000000000017dfe psum_rom…略…
ワーニングに気を付けていれば気が付けるだろうかなぁ、と考えを巡らせていたのですけれども、当方でソースを整理した以下のソースでも、const __farで修飾された変数定義をconstだけの修飾にしても(e2 studioで設定出来る限りのワーニング検出を有効にしていても)何もワーニングは出ることもなく誤動作するようになってしまいますので(PLTのスタブのアドレスが値として返されてしまってsum_eが期待した値になりませんので)、何と言うか、気を付けましょう、の精神論ぐらいしか思い浮かびませんでした、、、
#include <stdint.h>const uint16_t __far psum_rom = 0x1234; /* This variable is placed at the end of the ROM by linker script. */uint16_t _RomSum(void);uint16_t _RomSum(void){ // FIXME: If one of -O2, -O3, -Os is used, the generated code calls `abort()` immediately. // FIXME: In case of using the immediate value in stead of `&psum_rom`, if other than -O0, -Og is // used, the generated code has no ES register handling. (Note: If one of -O2, -O3, -Os is used, // the generated code calls `abort()` immediately. Therefore `other than -O0, -Og` means only -O1.) const uint16_t __far *prom, *sum_s, *sum_e; uint16_t sum = 0; sum_s = 0; sum_e = &psum_rom; // (const uint16_t __far *)0x017DFEUL; --> No ES register handling. for( prom = sum_s; prom < sum_e; prom++ ) { sum += *prom; } return sum;}
続く。
少し脱線しますが、最適化オプションで-O2, -O3, -Osを指定したところ、ソースのプログラムがNULL領域をアクセスすることをGNURL78がコンパイル時に検出して、ソッコーでabort()を呼び出す以下のコードが生成されましたね、、、(Linux/Windows/MacOS/その他もろもろ、といった汎用OS向けのGCCの機能として存在することは耳にしていましたが、いざ自分が遭遇すると、組み込み向けでは勘弁して欲しいなぁ、といったところですかね、、、(少なくとも0番地側にフラッシュメモリを配置するタイプのマイコンでは、、、))
32 .text 34 .global _abort 35 .section .text._RomSum,"ax",@progbits 36 .global __RomSum 38 __RomSum: 44 0000 11 AF 00 00 movw ax, es:!0 45 0004 FC 00 00 00 call !!_abort
また少し脱線しますが、ソースを以下の通りに変更するとESレジスタを操作していないコードが生成されますね、、、ソース変更箇所
sum_e = (const uint16_t __far *)0x017DFEUL;
リストファイル(最適化オプションは-O1です)
32 .text 34 .section .text._RomSum,"ax",@progbits 35 .global __RomSum 37 __RomSum: 41 ; start of function 42 ; push 2: r16 43 ; uses ES register 44 0000 61 EF sel rb2 45 0002 C1 push ax ; r16 47 0003 61 CF sel rb0 50 0005 C9 F4 00 00 movw r12, #0 51 0009 C9 F6 00 00 movw r14, #0 53 000d C9 F0 00 00 movw r8, #0 55 .L2: 57 0011 AD F4 movw ax, r12 58 0013 BD F2 movw r10, ax 59 0015 FA F2 movw hl, r10 60 0017 AB movw ax, [hl] 61 0018 06 F0 addw ax, r8 62 001a BD F0 movw r8, ax *** 001c AD F4 movw ax, r12 65 001e 04 02 00 addw ax, #2 66 0021 BD F4 movw r12, ax 67 0023 AD F6 movw ax, r14 68 0025 61 D8 sknc 69 0027 A1 incw ax 70 0028 BD F6 movw r14,ax 73 002a AD F6 movw ax, r14 74 002c 44 01 00 cmpw ax, #1 75 002f AD F4 movw ax, r12 76 0031 61 F8 sknz 77 0033 44 FE 7D cmpw ax, #32254 78 0036 61 E8 skz 79 0038 EC 11 00 00 br !!.L2 81 003c 61 EF sel rb2 82 003e C0 pop ax ; r16 83 003f 61 CF sel rb0 84 0041 D7 ret
> 最適化オプションで-O2, -O3, -Osを指定したところ、ソースのプログラムがNULL領域をアクセスすることをGNURL78がコンパイル時に検出して、ソッコーでabort()を呼び出す以下のコードが生成されましたGCCのドキュメントの最適化オプションの項を読んだところ、以下のようにソースを記述すれば対処出来ることに気付きました。(ベストなやり方では無いかも知れませんけれども。) ただ、e2 studioでは、エディタ上で文法エラーとされてしまって、関数全体に渡ってエラーの波線が表示されるようになって、ちょっとうっとうしくなってしまいますけれども。(GCCの拡張機能のごく普通に使われるものを使っただけだったのですが、e2 studioでそうなってしまったことがちょっと意外でしたけれども。)
#include <stdint.h>const uint16_t __far psum_rom = 0x1234; /* This variable is placed at the end of the ROM by linker script. */uint16_t _RomSum(void);uint16_t __attribute__((noinline, optimize("no-isolate-erroneous-paths-dereference"))) _RomSum(void){ // FIXME: In case of using the immediate value in stead of `&psum_rom`, if other than -O0 is used, // the generated code has no ES register handling. It seems to be a bug of GNURL78 (ex. 4.9.2.202201). const uint16_t __far *prom, *sum_s, *sum_e; uint16_t sum = 0; sum_s = 0; sum_e = &psum_rom; // (const uint16_t __far *)0x017DFEUL; --> No ES register handling. for( prom = sum_s; prom < sum_e; prom++ ) { sum += *prom; } return sum;}
続く。[関連リンク]-fisolate-erroneous-paths-dereference-fno-isolate-erroneous-paths-dereferencegcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-fisolate-erroneous-paths-dereference__attribute__ ((optimize (string, …)))gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-optimize-function-attribute__attribute__ ((noinline))gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-noinline-function-attribute
はし さん、こんにちは。NoMaYです。ふと最初の投稿の質問/相談を見返して思ったのですけれども、以下の生成コード異常に気付いた時点で、かふぇルネに投稿されるのもひとつの手だったのかも知れないなぁ、という気もしました。すみません、五月雨式にとりとめもなくリプライを続けていましたが、ここでひと区切りにしたいと思います。> 逆アセンブリをみたところ、ESレジスタの記述が見つからず、はしさんの手元での時系列は分からないですけれども、あるいは、以下の時点というのも別のひとつの手だったのかも知れないなぁ、とも思いました。> 変数をunsigned shortとしていることが原因だと思うのですが、unsigned longにするとよく分からない値が格納されてしまい、
NoMaYさま
ご対応いただき誠にありがとうございます。
一昨日ご投稿いただいた内容で
> 推測ですが、このアドレスが以下の文面の「よく分からない値」の正体ではないかと思われます。(なお、今回、リンカスクリプトは頂かなかったので当方の手元のものとはアドレスが微妙に違うかも知れません。)
まさに、この".plt 0x00000000000000d8"の値が入っていました。
> 推測ですが、上の赤文字の話はコンパイル時のワーニングを無視していた?ことも関係すると思いますが、ワーニングを取って以下のようなソースにしても、それでもsum_eの値は意図したものになりません。続く。
はい。ワーニングが出ても仕方がないという感じで無視しておりました。
> ふと最初の投稿の質問/相談を見返して思ったのですけれども、以下の生成コード異常に気付いた時点で、かふぇルネに投稿されるのもひとつの手だったのかも知れないなぁ、という気もしました。すみません、五月雨式にとりとめもなくリプライを続けていましたが、ここでひと区切りにしたいと思います。
元々が一応動作していたソフトだったので、自分が何かやらかしていると思い、ガチャガチャやってしまったのがまずかったです。
少しずつ整理して検討して、分からないと思ったところで、かふぇルネに投稿すればよかったと思っております。
はしさん中の人です。既に解決済みでしたらすみません。
GCCの開発関係者に確認してみました。以下のようにソースを修正することで正しく動くはずという意見をもらえました。
const unsigned short __far psum_rom __attribute__ ((section (".psum"))) = 0x1234;
unsigned long sum_e = (void __far *)&psum_rom;
ご確認頂ければと思います。
なお、別件のセクション名の問題は不具合と思われるとのことです。
adu様
ご回答いただきありがとうございます。
上記内容で試してみたのですが、psum_romが0xDE番地(PLTに関するセクション)に配置されてしまい、forの繰り返し数が222回となってしまいました。
上記対策は、NoMaYさまに教えていただいた、PLTに配置しない方法をやったうえでの対策ということでしょうか??
いえ、その方法とは関係なく提示させていただきました。
しかし流れをすべて追い切れていないかもしれず、混乱してきました。zipファイルが2種類あるのに気づいておらず、現状のはしさんの困りごとが何なのかもわからなくなってきてしまいました。
現状すでに解決済みでしたら是非無視してください。