OSDN Git Service

096128188bec7db251648627f6e76c6c9e529a35
[trx-305dsp/dsp.git] / trx305 / kernel / cfg / base / fc_binutils.cpp
1 /*
2  *  TOPPERS/JSP Kernel
3  *      Toyohashi Open Platform for Embedded Real-Time Systems/
4  *      Just Standard Profile Kernel
5  * 
6  *  Copyright (C) 2003 by Embedded and Real-Time Systems Laboratory
7  *                              Toyohashi Univ. of Technology, JAPAN
8  * 
9  *  上記著作権者は,以下の (1)〜(4) の条件か,Free Software Foundation 
10  *  によって公表されている GNU General Public License の Version 2 に記
11  *  述されている条件を満たす場合に限り,本ソフトウェア(本ソフトウェア
12  *  を改変したものを含む.以下同じ)を使用・複製・改変・再配布(以下,
13  *  利用と呼ぶ)することを無償で許諾する.
14  *  (1) 本ソフトウェアをソースコードの形で利用する場合には,上記の著作
15  *      権表示,この利用条件および下記の無保証規定が,そのままの形でソー
16  *      スコード中に含まれていること.
17  *  (2) 本ソフトウェアを,ライブラリ形式など,他のソフトウェア開発に使
18  *      用できる形で再配布する場合には,再配布に伴うドキュメント(利用
19  *      者マニュアルなど)に,上記の著作権表示,この利用条件および下記
20  *      の無保証規定を掲載すること.
21  *  (3) 本ソフトウェアを,機器に組み込むなど,他のソフトウェア開発に使
22  *      用できない形で再配布する場合には,次のいずれかの条件を満たすこ
23  *      と.
24  *    (a) 再配布に伴うドキュメント(利用者マニュアルなど)に,上記の著
25  *        作権表示,この利用条件および下記の無保証規定を掲載すること.
26  *    (b) 再配布の形態を,別に定める方法によって,TOPPERSプロジェクトに
27  *        報告すること.
28  *  (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損
29  *      害からも,上記著作権者およびTOPPERSプロジェクトを免責すること.
30  * 
31  *  本ソフトウェアは,無保証で提供されているものである.上記著作権者お
32  *  よびTOPPERSプロジェクトは,本ソフトウェアに関して,その適用可能性も
33  *  含めて,いかなる保証も行わない.また,本ソフトウェアの利用により直
34  *  接的または間接的に生じたいかなる損害に関しても,その責任を負わない.
35  * 
36  *  @(#) $Id: fc_binutils.cpp,v 1.2 2009/06/12 13:30:29 suikan Exp $
37  */
38
39 #if defined(FILECONTAINER_BINUTILS) || defined(TESTSUITE)
40
41 #ifdef _MSC_VER
42 #pragma warning(disable:4786) //デバッグ文字列を255文字に切り詰めた
43 #endif
44
45 #include "base/filecontainer.h"
46 #include <string>
47 #include <map>
48 #include <fstream>
49 #include <cassert>
50 #include <cctype>
51 #include <cstdio>
52 #include <cstdlib>
53 #include <cstring>
54
55 #define _isspace(x)  isspace(x)
56 #define _isprint(x)  isprint(x)
57
58 #define SIZE_LOADPAGE 65536         //バイナリデータを格納するページ単位
59
60 #define SIZE_TO_CONFIRM_BINARYFILE 128  //ファイルがバイナリを含むかどうかを確認するのに読み出すデータの長さ (バッファを取るのであまり大きくしないこと)
61
62 #define MAGIC_SYMBOL "_checker_magic_number"
63 #define MAGIC_NUMBER 0x01234567                         //4バイトの整数
64
65 #define CMD_GNUNM      "nm"
66 #define CMD_GNUOBJCOPY "objcopy"
67
68 #define MAKE_BASEADDRESS(x)   ((x) & ~(SIZE_LOADPAGE-1))
69 #define MAKE_OFFSETADDRESS(x) ((x) &  (SIZE_LOADPAGE-1))
70
71 using namespace std;
72
73 namespace {
74
75     class FileContainerBinutilsImpl : public FileContainer
76     {
77     public:
78         typedef void (interceptor_func_t)(fstream &, const string &);      //不意に訪れたバイナリファイルの襲撃に対応する関数の型
79
80     protected:
81         string                 symbol_prefix;
82         map<string, address_t> symbol_table;
83         map<address_t, char *> contents;
84
85         address_t last_address;     //キャッシュもどき
86         char *    last_page;
87
88             //データ取り込み
89         void loadSymbols(fstream & file)  throw(Exception);
90         void loadDataContents(fstream & file) throw(Exception);
91
92             //contentsへ1バイト書き込み
93         void writeByte(address_t address, unsigned int) throw(Exception);
94
95             //自動処理
96         void searchSymbolPrefix(void) throw();
97         void searchByteOrder(void)    throw();
98
99     public:
100         FileContainerBinutilsImpl(void) throw();
101         virtual ~FileContainerBinutilsImpl(void) throw();
102
103             /* インタフェース部 */
104         virtual void        attachModule(const string & filename) throw(Exception);
105         virtual void        loadContents(void * dest, address_t address, size_t size) throw(Exception);
106         virtual address_t   getSymbolAddress(const string & symbol) throw(Exception);
107         virtual std::string getArchitecture(void) throw();
108
109         TESTSUITE_PROTOTYPE(main)
110     };
111
112     namespace {
113         FileContainerBinutilsImpl instance_of_FileContainerBinutilsImpl;
114     }
115
116         /* コンストラクタ */
117     FileContainerBinutilsImpl::FileContainerBinutilsImpl(void) throw()
118         : symbol_prefix(""), symbol_table(), contents(), last_address(0), last_page(0)
119     {}
120
121         /* デストラクタ : データバッファの解放 */
122     FileContainerBinutilsImpl::~FileContainerBinutilsImpl(void) throw()
123     {
124         map<address_t, char *>::iterator scope;
125
126         scope = contents.begin();
127         while(scope != contents.end()) {
128             delete [] scope->second;
129             ++ scope;
130         }
131         symbol_table.clear();
132         contents.clear();
133     }
134
135         /* ファイル名をカンマで二つに分ける */
136     void splitFilename(const string & src, string & first, string & second) throw(Exception)
137     {
138         if(!src.empty()) {
139             string::size_type pos;
140
141             pos = src.find_first_of(',');
142             if(pos != string::npos) {
143                 first  = src.substr(0, pos);
144                 second = src.substr(pos + 1);
145             }
146             else {
147                     //ファイル名が一つしか指定されていない
148                 first  = src;
149                 second = src;
150             }
151         }
152         else
153             ExceptionMessage("[FCBI] Empty filename could not be accepted.","[FCBI] ファイル名がありません").throwException();
154     }
155
156         /* ファイルがバイナリデータを持っているかどうかを判定 */
157     bool hasBinaryContents(fstream & file) throw()
158     {
159         assert(file.is_open());
160
161         bool        result = false;
162         char        buffer[SIZE_TO_CONFIRM_BINARYFILE];
163         streamsize  length;
164
165         file.read(buffer, SIZE_TO_CONFIRM_BINARYFILE);
166         length = file.gcount();
167
168         for(streamsize i = 0; i < length; ++ i) {
169             if(buffer[i] < 0 || !(_isprint(buffer[i]) || _isspace(buffer[i]))){
170                 result = true;
171                 break;
172             }
173         }
174
175         if(!result) {
176             file.clear();
177             file.seekg(0, ios::beg);    //先頭に戻しておく
178         }
179
180         return result;
181     }
182
183         /* テキストファイルを開く (バイナリだった場合には対処) */
184     void openTextFile(fstream & file, const string & filename, FileContainerBinutilsImpl::interceptor_func_t * interceptor) throw(Exception)
185     {
186         assert(!filename.empty());
187         assert(!file.is_open());
188
189         file.open(filename.c_str(), ios::in|ios::binary);
190         if(!file.is_open()) {
191             ExceptionMessage("File '%' could not be opened.","ファイル '%' は開けません") << filename << throwException;
192             return;
193         }
194
195             /* バイナリファイルだったら... */
196         while(hasBinaryContents(file)) {
197             file.close();
198             
199             if(interceptor != 0) {
200                 (*interceptor)(file, filename);
201                 interceptor = 0;    //対処は一回のみ
202             }
203
204             if(!file.is_open()) {
205                 break;
206             }
207         }
208
209             /* ファイルが開けなかったら例外 */
210         if(!file.is_open())
211             ExceptionMessage("Program failed to convert the binary '%' into suitable style. Please specify a suitable TEXT file.",
212                             "プログラムはバイナリファイル'%'の変換に失敗しました。正しいテキストファイルを指定し直してください。")
213                                 << filename << throwException;
214     }
215
216         /* 一時的なファイル名の生成 */
217     const char * makeTemporaryFilename(void) throw()
218     {
219         static char filename[10];
220
221         sprintf(filename, "cfg%06x", (int)(rand() & 0xffffffl));
222
223         return filename;
224     }
225
226
227         /* バイナリをGNU-NMを使って変換する */
228     void interceptWithGnuNM(fstream & file, const string & filename) throw(Exception)
229     {
230         assert(!file.is_open());
231
232         string cmdline;
233         string symfile;
234
235         symfile.assign(makeTemporaryFilename());
236         cmdline = string(CMD_GNUNM) + " " + filename + " > " + symfile;
237         VerboseMessage("[EXEC] %\n") << cmdline;
238
239         system(cmdline.c_str());
240
241             /* 正しく開けたらファイルを削除 */
242         file.open(symfile.c_str(), ios::in);
243         if(file.is_open()) {
244             remove(symfile.c_str());
245         }
246     }
247
248         /* バイナリをGNU-OBJCOPYを使って変換する */
249     void interceptWithGnuObjcopy(fstream & file, const string & filename) throw(Exception)
250     {
251         assert(!file.is_open());
252
253         string cmdline;
254         string srecfile;
255
256         srecfile.assign(makeTemporaryFilename());
257         cmdline = string(CMD_GNUOBJCOPY) + " -F srec " + filename + " " + srecfile;
258         VerboseMessage("[EXEC] %\n") << cmdline;
259
260         system(cmdline.c_str());
261
262             /* 正しく開けたらファイルを削除 */
263         file.open(srecfile.c_str(), ios::in);
264         if(file.is_open()) {
265             remove(srecfile.c_str());
266         }
267     }
268
269         /* 16進から10進への変換 (ポインタ移動, 長さ指定付き) */
270     unsigned int hextodec(const char * & src, size_t length) throw()
271     {
272         assert(length <= sizeof(unsigned int) * 2);
273
274         unsigned int result = 0;
275         unsigned int digit;
276
277         while(length-- > 0) {
278             if(*src >= '0' && *src <= '9')
279                 digit = *src - '0';
280             else if(*src >= 'A' && *src <='F')
281                 digit = *src - 'A' + 10;
282             else if(*src >= 'a' && *src <='f')
283                 digit = *src - 'a' + 10;
284             else
285                 break;
286
287             ++ src;
288             result = (result << 4) | (digit & 0xf);
289         }
290
291         return result;
292     }
293
294         /* NMが出力した行をパース */
295     bool readGnuNmLine(fstream & file, FileContainer::address_t & address, string & attribute, string & symbolname) throw()
296     {
297         assert(file.is_open());
298
299         string src;
300         string addr;
301         string::size_type pos1;
302         string::size_type pos2;
303
304             /* 中身を空にしておく */
305         address = 0;
306         if(!attribute.empty())
307             attribute.erase();
308         if(!symbolname.empty())
309             symbolname.erase();
310
311             //次の行を取得 (空行, 未定義シンボルは読み飛ばす)
312         do {
313             if(file.eof())
314                 return false;
315
316             getline(file, src, '\n');
317         } while(src.empty() || src.at(0) == ' ');
318
319             //行を分解
320         pos1 = src.find_first_of(' ');
321         addr = src.substr(0, pos1);
322
323         pos2 = src.find_first_of(' ', pos1 + 1);
324         attribute  = src.substr(pos1 + 1, pos2 - pos1 - 1);
325         symbolname = src.substr(pos2 + 1);
326
327             //アドレスのパース  (注 : なんでこんなちまちまやってるかというと、アドレスが32bitを超えるターゲットがいるから)
328         while(!addr.empty()) {
329             size_t length = addr.size();
330             const char * src = addr.c_str();
331             if(length > sizeof(unsigned int) * 2)
332                 length = sizeof(unsigned int) * 2;
333             address = (address << (length * 2)) | (hextodec(src, length));
334             addr.erase(0, length);
335         }
336
337         return true;
338     }
339
340         /* シンボルの読み込み */
341     void FileContainerBinutilsImpl::loadSymbols(fstream & file) throw(Exception)
342     {
343         assert(file.is_open());
344
345         address_t address;
346         string    attribute;
347         string    symbolname;
348
349         while(readGnuNmLine(file, address, attribute, symbolname)) {
350             symbol_table.insert(map<string, address_t>::value_type(symbolname, address));
351         }
352
353         VerboseMessage("% symbols loaded\n") << symbol_table.size() << &throwException;
354
355         file.close();
356     }
357
358         /* contentsに1バイト書き込み */
359     void FileContainerBinutilsImpl::writeByte(address_t address, unsigned int value) throw(Exception)
360     {
361         address_t & base = last_address;
362         char *    & page = last_page;
363
364             /* キャッシュもどきが使えないなら、ページを探す */
365         if(MAKE_BASEADDRESS(address) != last_address || last_page == 0) {
366             map<address_t, char *>::iterator scope;
367
368             base  = MAKE_BASEADDRESS(address);
369             scope = contents.find(base);
370             if(scope == contents.end()) {
371                 page = new(nothrow) char [SIZE_LOADPAGE];
372                 if(page == 0) {
373                     ExceptionMessage("Not enough memory available to store the contents","空きメモリ不足のため、データの格納に失敗しました").throwException();
374                     return;
375                 }
376                 contents.insert(map<address_t,char*>::value_type(base, page));
377             }
378             else
379                 page = scope->second;
380         }
381
382         *(page + (address - base)) = static_cast<char>(value & 0xff);
383     }
384
385         /* 末尾の空白文字を切り取る */
386     void trimString(string & src) throw()
387     {
388         string::size_type pos;
389
390         pos = src.find_last_not_of(" \t\r\n");
391         if(pos != string::npos && pos != src.size())
392             src.erase(pos + 1);
393     }
394
395         /* モトローラSレコードを一行読み込む */
396         /*
397                 The general format of an S-record follows: 
398                 +-------------------//------------------//-----------------------+
399                 | type | count | address  |            data           | checksum |
400                 +-------------------//------------------//-----------------------+
401         */
402     bool readRecord(fstream & file, string & dest) throw(Exception)
403     {
404         unsigned int sum;
405         unsigned int count;
406         unsigned int i;
407         const char * pos;
408
409             /* 次の行を読み込む */
410         do {
411                 //getlineがReadFileを呼んでブロックするので、確実にEOFを反応させるためにこうする
412             int ch = file.get();
413             if(ch == EOF)
414                 return false;
415
416             file.putback(static_cast<char>(ch));
417             getline(file, dest);
418         } while(dest.empty());
419
420         trimString(dest);
421
422             /* 正当性の判定 */
423
424         if(dest[0] != 'S')      //行頭が'S'で始まらない
425             ExceptionMessage("The file is not a Motorola S-Record file.","モトローラSフォーマットで無い行が見つかりました") << throwException;
426
427         pos = dest.c_str() + 2;
428         count = hextodec(pos, 2);
429         if(dest.size() != (count + 2)*2)
430             ExceptionMessage("Illegal S-Record found (count unmatched).","不正なSレコードがあります (サイズ不一致)") << throwException;
431
432         sum = count;
433         for(i = 0; i < count; ++ i)
434             sum += hextodec(pos, 2);
435
436         if((sum & 0xff) != 0xff)
437             ExceptionMessage("Illegal S-Record found (check-sum unmatched).","不正なSレコードがあります (チェックサム不一致)") << throwException;
438
439         return true;
440     }
441
442         /* 次の開始アドレスを得る */
443     FileContainer::address_t parseRecordAddress(const string & src, FileContainer::address_t base) throw()
444     {
445         const char * record = src.c_str();
446         FileContainer::address_t result = 0;
447
448         record += 4;
449         switch(*(record - 3)) {
450             case '1':
451                 result = hextodec(record, 4);
452                 break;
453             case '2':
454                 result = hextodec(record, 6);
455                 break;
456             case '3':
457                 result = hextodec(record, 8);
458                 break;
459             case '5':
460                 result = base;
461                 break;
462             default:
463                 break;
464         }
465
466         return result;
467     }
468
469         /* データ部分だけを残してチョップ */
470     void chopRecord(string & src) throw()
471     {
472         string::size_type start;
473
474         switch(src[1]) {
475             case '1':  start = 4 + 4;  break;
476             case '2':  start = 4 + 6;  break;
477             case '3':  start = 4 + 8;  break;
478             default:   start = 4;      break;
479         }
480
481             //先頭4バイト + アドレス部 + 最後のサムを取り除く
482         src = src.substr(start, src.size() - start - 2);
483     }
484
485         /* プログラムデータの読み込み */
486     void FileContainerBinutilsImpl::loadDataContents(fstream & file) throw(Exception)
487     {
488         assert(file.is_open());
489
490         address_t address;
491         string    line;
492
493         address = 0;
494         while(readRecord(file, line)) {
495
496             address = parseRecordAddress(line, address);
497
498             chopRecord(line);
499
500                 /* データの格納 */
501             const char * pos = line.c_str();
502             while(*pos != '\x0') {
503                 unsigned int data = hextodec(pos, 2);
504                 writeByte(address, data);
505                 ++ address;
506             }
507         }
508
509         file.close();
510     }
511
512         /* シンボルプレフィクスの自動判定 */
513     void FileContainerBinutilsImpl::searchSymbolPrefix(void) throw()
514     {
515         const char *  candidate_list[] = {"", "_", NULL};
516         const char ** candidate;
517
518         for(candidate = candidate_list; *candidate != NULL; ++ candidate) {
519             map<string, address_t>::const_iterator scope;
520             string symbol;
521             
522             symbol = string(*candidate) + MAGIC_SYMBOL;
523             scope  = symbol_table.find(symbol);
524
525             if(scope != symbol_table.end())
526                 break;
527         }
528
529         if(*candidate != NULL)
530             symbol_prefix.assign(*candidate);
531     }
532
533         /* エンディアンの自動判定 */
534     void FileContainerBinutilsImpl::searchByteOrder(void) throw()
535     {
536         address_t address;
537         union {
538             char         buffer[4];
539             unsigned int value;
540         };
541
542         try {
543             value   = 0;
544             address = getSymbolAddress(MAGIC_SYMBOL);
545             loadContents(buffer, address, 4);
546
547             if(value == MAGIC_NUMBER) {
548                 byteorder = HOSTORDER;
549             }
550             else {
551                 buffer[0] ^= buffer[3], buffer[3] ^= buffer[0], buffer[0] ^= buffer[3]; // swap(buffer[0], buffer[3])
552                 buffer[1] ^= buffer[2], buffer[2] ^= buffer[1], buffer[1] ^= buffer[2]; // swap(buffer[1], buffer[2])
553
554                 if(value == MAGIC_NUMBER)
555                     byteorder = HOSTORDER == LITTLE ? BIG : LITTLE;
556                 else
557                     throw false;
558             }
559         }
560         catch(...) {}
561     }
562
563         /* モジュールのアタッチ -> シンボル読出し, データ格納 */
564     void FileContainerBinutilsImpl::attachModule(const string & filename) throw(Exception)
565     {
566         fstream file;
567         string  symbol_filename;
568         string  contents_filename;
569
570         splitFilename(filename, symbol_filename, contents_filename);
571
572         openTextFile(file, symbol_filename, interceptWithGnuNM);
573         loadSymbols(file);
574
575         openTextFile(file, contents_filename, interceptWithGnuObjcopy);
576         loadDataContents(file);
577
578         searchSymbolPrefix();
579         searchByteOrder();
580     }
581
582         /* 格納している内容の取得 */
583     void FileContainerBinutilsImpl::loadContents(void * _dest, address_t address, size_t size) throw(Exception)
584     {
585         char * dest = static_cast<char *>(_dest);
586
587         while(size > 0) {
588             map<address_t, char *>::const_iterator scope;
589
590             address_t  base   = MAKE_BASEADDRESS(address);
591             address_t  offset = MAKE_OFFSETADDRESS(address);
592             size_t     transfer_size = size;
593
594             if(transfer_size > SIZE_LOADPAGE - offset)
595                 transfer_size = SIZE_LOADPAGE - offset;
596
597             scope = contents.find(base);
598             if(scope == contents.end())
599                 ExceptionMessage("[Internel error] Memory read with unmapped address","[内部エラー] マップされてないアドレスを使ってメモリリードが行われました").throwException();
600
601             memcpy(dest, scope->second + offset, transfer_size);
602
603             dest += transfer_size;
604             size -= transfer_size;
605         }
606     }
607
608         /* シンボルのアドレスの取得 */
609     FileContainer::address_t FileContainerBinutilsImpl::getSymbolAddress(const string & symbol) throw(Exception)
610     {
611         string symbolname;
612         map<string, address_t>::const_iterator scope;
613
614         symbolname = symbol_prefix + symbol;
615
616         scope = symbol_table.find(symbolname);
617         if(scope == symbol_table.end())
618             ExceptionMessage("Unknown symbol '%'","不明なシンボル名 '%'") << symbol << throwException;
619
620         return scope->second;
621     }
622
623         /* アーキテクチャ名の取得 */
624     string FileContainerBinutilsImpl::getArchitecture(void) throw()
625     {
626         if(byteorder == LITTLE)
627             return "Little endian target (with GNU/Binutils)";
628         else
629             return "Big endian target (with GNU/Binutils)";
630     }
631
632 }
633
634 //---------------------------------------------
635
636 #ifdef TESTSUITE
637 #include "base/coverage_undefs.h"
638
639 namespace {
640     fstream * interceptor_file;
641     string    interceptor_filename;
642     void interceptor(fstream & file, const string & filename)
643     {
644         CHECKPOINT("interceptor");
645         interceptor_file = &file;
646         interceptor_filename = filename;
647
648         if(filename.compare("textfile") == 0) {
649             remove(filename.c_str());
650             file.open(filename.c_str(), ios::out);
651             file << "text";
652             file.close();
653
654             file.open(filename.c_str(), ios::in|ios::binary);
655         }
656         else if(filename.compare("binaryfile") == 0) {
657             remove(filename.c_str());
658             file.open(filename.c_str(), ios::out|ios::binary);
659             file.write("\x1",1);
660             file.close();
661
662             file.open(filename.c_str(), ios::in|ios::binary);
663         }
664     }
665 }
666
667 TESTSUITE(main, FileContainerBinutilsImpl)
668 {
669     PREDECESSOR("TFileContainer");
670
671     SingletonBase::ContextChain chain;
672     chain.saveContext<RuntimeObjectTable>();
673
674     BEGIN_CASE("splitFilename","splitFilename") {
675         BEGIN_CASE("1","カンマの前後で切れる") {
676             string first, second;
677             
678             splitFilename("a,b", first, second);
679             TEST_CASE("1","firstの中身は正しい", first.compare("a") == 0);
680             TEST_CASE("2","secondの中身は正しい", second.compare("b") == 0);
681         } END_CASE;
682
683         BEGIN_CASE("2","カンマの無い引数を与えると、両方に同じ中身が入る") {
684             string first, second;
685
686             splitFilename("abc", first, second);
687             TEST_CASE("1","firstの中身は正しい", first.compare("abc") == 0);
688             TEST_CASE("2","secondの中身は正しい", second.compare("abc") == 0);
689         } END_CASE;
690
691         BEGIN_CASE("3","空文字を与えると例外") {
692             bool result = false;
693             string first, second;
694             try { splitFilename("", first, second); } catch(Exception &) { result = true; }
695             if(!result)
696                 TEST_FAIL;
697         } END_CASE;
698     } END_CASE;
699
700     BEGIN_CASE("hasBinaryContents","hasBinaryContents") {
701         BEGIN_CASE("1","テキストファイルを食わせる") {
702             fstream file("test", ios::out);
703             file << "This is a sample text file.";
704             file.close();
705
706             file.open("test",ios::in|ios::binary);
707             TEST_CASE("1","関数はfalseを返す", !hasBinaryContents(file));
708             TEST_CASE("2","fileはeofに達していない", !file.eof());
709             file.close();
710
711             remove("test");
712         } END_CASE;
713
714         BEGIN_CASE("2","バイナリデータを食わせる") {
715             fstream file("test", ios::out|ios::binary);
716             file << "This is a sample text file.";
717             file.write("\x0\x1\x2\x3", 4);
718             file.close();
719
720             file.open("test",ios::in|ios::binary);
721             TEST_CASE("1","関数はtrueを返す", hasBinaryContents(file));
722             TEST_CASE("2","fileはeofに達していない", !file.eof());
723             file.close();
724
725             remove("test");
726         } END_CASE;
727     } END_CASE;
728
729     BEGIN_CASE("openTextFile","openTextFile") {
730         BEGIN_CASE("1","テキストファイルを指定する") {
731             TestSuite::clearCheckpoints();
732             fstream file("test", ios::out);
733             file << "This is a sample text file.";
734             file.close();
735
736             bool result = true;
737             try { openTextFile(file, "test", interceptor); } catch(Exception &) { result = false; }
738
739             TEST_CASE("1","例外は起きない", result);
740             TEST_CASE("2","ファイルが開かれている", file.is_open());
741             TEST_CASE("3","interceptorはコールされていない", !TestSuite::isReached("interceptor"));
742
743             string work;
744             getline(file, work, '\n');
745             TEST_CASE("4","読み出された内容が正しい", work.compare("This is a sample text file.") == 0);
746
747             file.close();
748             remove("test");
749         } END_CASE;
750
751         BEGIN_CASE("2","バイナリファイルを指定する (interceptorはファイルを開かない)") {
752             TestSuite::clearCheckpoints();
753             fstream file("test", ios::out|ios::binary);
754             file.write("\x1", 1);
755             file.close();
756
757             bool result = false;
758             try { openTextFile(file, "test", interceptor); } catch(Exception &) { result = true; }
759
760             TEST_CASE("1","例外が起きる", result);
761             TEST_CASE("2","ファイルが開かれている", !file.is_open());
762             TEST_CASE("3","interceptorがコールされている", TestSuite::isReached("interceptor"));
763             TEST_CASE("4","interceptorの引数が正しい (file)", interceptor_file == &file);
764             TEST_CASE("5","interceptorの引数が正しい", interceptor_filename.compare("test") == 0);
765
766             file.close();
767             remove("test");
768         } END_CASE;
769
770         BEGIN_CASE("3","存在しないファイルを指定する") {
771             TestSuite::clearCheckpoints();
772             fstream file;
773             bool result = false;
774             try { openTextFile(file, "___unknown___", interceptor); } catch(Exception &) { result = true; }
775
776             TEST_CASE("1","例外が起きる", result);
777             TEST_CASE("2","ファイルが開かれていない", !file.is_open());
778             TEST_CASE("3","interceptorがコールされていない", !TestSuite::isReached("interceptor"));
779         } END_CASE;
780
781         BEGIN_CASE("4","interceptorがテキストファイルを生成する") {
782             TestSuite::clearCheckpoints();
783             fstream file("textfile", ios::out|ios::binary);
784             file.write("\x1", 1);
785             file.close();
786
787             bool result = true;
788             try { openTextFile(file, "textfile", interceptor); } catch(Exception &) { result = false; }
789
790             TEST_CASE("1","例外は起きない", result);
791             TEST_CASE("2","ファイルが開かれている", file.is_open());
792             TEST_CASE("3","interceptorがコールされている", TestSuite::isReached("interceptor"));
793             TEST_CASE("4","interceptorの引数が正しい (file)", interceptor_file == &file);
794             TEST_CASE("5","interceptorの引数が正しい", interceptor_filename.compare("textfile") == 0);
795
796             string work;
797             getline(file, work, '\n');
798             TEST_CASE("4","読み出された内容が正しい", work.compare("text") == 0);
799
800             file.close();
801             remove("textfile");
802         } END_CASE;
803
804         BEGIN_CASE("5","interceptorがバイナリファイルを生成する") {
805             TestSuite::clearCheckpoints();
806             fstream file("binaryfile", ios::out|ios::binary);
807             file.write("\x1", 1);
808             file.close();
809
810             bool result = false;
811             try { openTextFile(file, "binaryfile", interceptor); } catch(Exception &) { result = true; }
812
813             TEST_CASE("1","例外は起きる", result);
814             TEST_CASE("2","ファイルが開かれていない", !file.is_open());
815             TEST_CASE("3","interceptorがコールされている", TestSuite::isReached("interceptor"));
816             TEST_CASE("4","interceptorの引数が正しい (file)", interceptor_file == &file);
817             TEST_CASE("5","interceptorの引数が正しい", interceptor_filename.compare("binaryfile") == 0);
818
819             file.close();
820             remove("binaryfile");
821         } END_CASE;
822     } END_CASE;
823
824     BEGIN_CASE("hextodec","hextodec") {
825         const char * letter = "0123456789abcdEFg";
826         const char * work = letter;
827
828         TEST_CASE("1", "切り出された値が正しい", hextodec(work, 2) == 1);
829         TEST_CASE("2", "workが進んでいる", work == letter + 2);
830         TEST_CASE("3", "切り出された値が正しい", hextodec(work, 4) == 0x2345);
831         TEST_CASE("4", "workが進んでいる", work == letter + 6);
832         TEST_CASE("5", "切り出された値が正しい", hextodec(work, 8) == 0x6789abcd);
833         TEST_CASE("6", "workが進んでいる", work == letter + 14);
834         TEST_CASE("7", "切り出された値が正しい", hextodec(work, 8) == 0xef);
835         TEST_CASE("8", "workが'g'の位置でとまる", *work == 'g');
836         TEST_CASE("9", "切り出された値が正しい", hextodec(work, 8) == 0);
837         TEST_CASE("10", "workが'g'の位置でとまる", *work == 'g');
838     } END_CASE;
839
840     BEGIN_CASE("readGnuNmLine","readGnuNmLine") {
841         fstream file("test", ios::out);
842         file << "0804aab0 T _kernel_task_initialize\n0805d8a0 B _kernel_tcb_table\n\n0804e560 R _kernel_tinib_table\n         U getpid@@GLIBC_2.0\n";
843         file.close();
844
845         address_t address;
846         string    attribute;
847         string    symbolname;
848
849         file.open("test", ios::in);
850         BEGIN_CASE("1","普通のエントリが読める") {
851             TEST_CASE("1","関数は成功する", readGnuNmLine(file, address, attribute, symbolname));
852             TEST_CASE("2","addressは正しい", address == 0x0804aab0);
853             TEST_CASE("3","attributeは正しい", attribute.compare("T") == 0);
854             TEST_CASE("4","symbolnameは正しい", symbolname.compare("_kernel_task_initialize") == 0);
855         } END_CASE;
856
857         BEGIN_CASE("2","普通のエントリが読める (2)") {
858             TEST_CASE("1","関数は成功する", readGnuNmLine(file, address, attribute, symbolname));
859             TEST_CASE("2","addressは正しい", address == 0x0805d8a0);
860             TEST_CASE("3","attributeは正しい", attribute.compare("B") == 0);
861             TEST_CASE("4","symbolnameは正しい", symbolname.compare("_kernel_tcb_table") == 0);
862         } END_CASE;
863
864         BEGIN_CASE("3","空行を読み飛ばして次が読める") {
865             TEST_CASE("1","関数は成功する", readGnuNmLine(file, address, attribute, symbolname));
866             TEST_CASE("2","addressは正しい", address == 0x0804e560);
867             TEST_CASE("3","attributeは正しい", attribute.compare("R") == 0);
868             TEST_CASE("4","symbolnameは正しい", symbolname.compare("_kernel_tinib_table") == 0);
869         } END_CASE;
870
871         BEGIN_CASE("4","アドレスの無いエントリは無視する") {
872             TEST_CASE("1","関数は失敗する", !readGnuNmLine(file, address, attribute, symbolname));
873         } END_CASE;
874
875         file.close();
876         remove("test");
877     } END_CASE;
878
879     BEGIN_CASE("loadSymbols","loadSymbols") {
880         fstream file("test", ios::out);
881         file << "0804aab0 T _kernel_task_initialize\n0805d8a0 B _kernel_tcb_table\n\n0804e560 R _kernel_tinib_table\n         U getpid@@GLIBC_2.0\n";
882         file.close();
883
884         file.open("test",ios::in);
885
886         FileContainerBinutilsImpl fcbi;
887         fcbi.loadSymbols(file);
888
889         TEST_CASE("1","読み込まれたエントリの数が正しい", fcbi.symbol_table.size() == 3);
890         TEST_CASE("2","ファイルは閉じられている", !file.is_open());
891
892         remove("test");
893     } END_CASE;
894
895     BEGIN_CASE("writeByte","writeByte") {
896         FileContainerBinutilsImpl fcbi;
897
898         BEGIN_CASE("1","存在しないページへの書き込み") {
899             TEST_CASE("0", "[前提] contentsの要素数は0", fcbi.contents.size() == 0);
900             fcbi.writeByte(0x100, 0);
901
902             TEST_CASE("1","contentsの要素が増えている", fcbi.contents.size() == 1);
903
904             const char * scope = fcbi.contents.find(MAKE_BASEADDRESS(0x100))->second + MAKE_OFFSETADDRESS(0x100);
905             TEST_CASE("2","書き込まれている内容が正しい", *scope == 0);
906         } END_CASE;
907
908         BEGIN_CASE("2","存在するページへの書き込み (連続アクセス)") {
909             fcbi.writeByte(0x100, 0xff);
910
911             TEST_CASE("1","contentsの要素が増えていない", fcbi.contents.size() == 1);
912
913             const char * scope = fcbi.contents.find(MAKE_BASEADDRESS(0x100))->second + MAKE_OFFSETADDRESS(0x100);
914             TEST_CASE("2","書き込まれている内容が正しい", *scope == 0xff);
915         } END_CASE;
916
917         BEGIN_CASE("3","新しいページへの書き込み") {
918             fcbi.writeByte(0x10000000, 0xff);
919
920             TEST_CASE("1","contentsの要素が増えている", fcbi.contents.size() == 2);
921
922             const char * scope = fcbi.contents.find(MAKE_BASEADDRESS(0x10000000))->second + MAKE_OFFSETADDRESS(0x10000000);
923             TEST_CASE("2","書き込まれている内容が正しい", *scope == 0xff);
924         } END_CASE;
925
926         BEGIN_CASE("4","既存のページへのアクセス") {
927             fcbi.writeByte(0x100, 0x0);
928
929             TEST_CASE("1","contentsの要素が増えていない", fcbi.contents.size() == 2);
930
931             const char * scope = fcbi.contents.find(MAKE_BASEADDRESS(0x100))->second + MAKE_OFFSETADDRESS(0x100);
932             TEST_CASE("2","書き込まれている内容が正しい", *scope == 0x0);
933         } END_CASE;
934     } END_CASE;
935
936     BEGIN_CASE("readRecord","readRecord") {
937         fstream file("test",ios::out);
938         file << "S315080480F42F6C69622F6C642D6C696E75782E736F98\n\nS315080480F42F6C69622F6C642D6C696E75782E736F98\nS308080481042E320005\nDUMMY\nS31808048108040000001000000001000000474E550056\nS31008048108040000001000000001000000474E550056\n";
939         file.close();
940
941         file.open("test",ios::in);
942         BEGIN_CASE("1","正常ケース") {
943             string work;
944             bool result;
945             bool exception = false;
946             try { result = readRecord(file, work); } catch(Exception &) { exception = true; }
947             TEST_CASE("1","例外は起こらない", !exception);
948             TEST_CASE("2","関数はtrueを返す", result);
949             TEST_CASE("3","読み出された内容が正しい", work.compare("S315080480F42F6C69622F6C642D6C696E75782E736F98") == 0);
950         } END_CASE;
951
952         BEGIN_CASE("2","正常ケース (空行の読み飛ばし)") {
953             string work;
954             bool result;
955             bool exception = false;
956             try { result = readRecord(file, work); } catch(Exception &) { exception = true; }
957             TEST_CASE("1","例外は起こらない", !exception);
958             TEST_CASE("2","関数はtrueを返す", result);
959             TEST_CASE("3","読み出された内容が正しい", work.compare("S315080480F42F6C69622F6C642D6C696E75782E736F98") == 0);
960         } END_CASE;
961
962         BEGIN_CASE("3","チェックサムが誤っているエントリ") {
963             string work;
964             bool result;
965             bool exception = false;
966             try { result = readRecord(file, work); } catch(Exception &) { exception = true; }
967             TEST_CASE("1","例外をおこす", exception);
968         } END_CASE;
969
970         BEGIN_CASE("4","先頭がSで始まらないエントリ") {
971             string work;
972             bool result;
973             bool exception = false;
974             try { result = readRecord(file, work); } catch(Exception &) { exception = true; }
975             TEST_CASE("1","例外をおこす", exception);
976         } END_CASE;
977
978         BEGIN_CASE("5","指定された長さよりも長いエントリ") {
979             string work;
980             bool result;
981             bool exception = false;
982             try { result = readRecord(file, work); } catch(Exception &) { exception = true; }
983             TEST_CASE("1","例外をおこす", exception);
984         } END_CASE;
985
986         BEGIN_CASE("6","指定された長さよりも短いエントリ") {
987             string work;
988             bool result;
989             bool exception = false;
990             try { result = readRecord(file, work); } catch(Exception &) { exception = true; }
991             TEST_CASE("1","例外をおこす", exception);
992         } END_CASE;
993
994         BEGIN_CASE("7","ファイル終端") {
995             string work;
996             bool result;
997             bool exception = false;
998             try { result = readRecord(file, work); } catch(Exception &) { exception = true; }
999             TEST_CASE("1","例外をおこさない", !exception);
1000             TEST_CASE("2","関数はfalseを返す", !result);
1001         } END_CASE;
1002
1003         remove("test");
1004     } END_CASE;
1005
1006     BEGIN_CASE("chopRecord","chopRecord") {
1007         BEGIN_CASE("1","S1レコード") {
1008             string src("S106080480F42F4A");
1009
1010             chopRecord(src);
1011             TEST_CASE("1","値が正しい", src.compare("80F42F") == 0);
1012         } END_CASE;
1013
1014         BEGIN_CASE("2","S2レコード") {
1015             string src("S206080480F42F4A");
1016
1017             chopRecord(src);
1018             TEST_CASE("1","値が正しい", src.compare("F42F") == 0);
1019         } END_CASE;
1020
1021         BEGIN_CASE("3","S3レコード") {
1022             string src("S306080480F42F4A");
1023
1024             chopRecord(src);
1025             TEST_CASE("1","値が正しい", src.compare("2F") == 0);
1026         } END_CASE;
1027
1028         BEGIN_CASE("4","S4レコード") {
1029             string src("S406080480F42F4A");
1030
1031             chopRecord(src);
1032             TEST_CASE("1","値が正しい", src.compare("080480F42F") == 0);
1033         } END_CASE;
1034
1035         BEGIN_CASE("5","S5レコード") {
1036             string src("S506080480F42F4A");
1037
1038             chopRecord(src);
1039             TEST_CASE("1","値が正しい", src.compare("080480F42F") == 0);
1040         } END_CASE;
1041
1042     } END_CASE;
1043
1044     BEGIN_CASE("loadDataContents/loadContents","loadDataContents/loadContents") {
1045         fstream file("test", ios::out);
1046         file << "S30D000000000123456789ABCDEF32\nS509FEDCBA9876543210BE";
1047         file.close();
1048
1049         BEGIN_CASE("1","正常ケース") {
1050             FileContainerBinutilsImpl fcbi;
1051
1052             file.open("test",ios::in);
1053             bool exception = false;
1054             try { fcbi.loadDataContents(file); } catch(...) { exception = true; }
1055
1056             TEST_CASE("1","例外は起こらない", !exception);
1057             TEST_CASE("2","データが確保されている", fcbi.contents.size() == 1);
1058             TEST_CASE("3","ファイルは閉じられている", !file.is_open());
1059             BEGIN_CASE("4","格納した値が正しく読める") {
1060                 unsigned int i;
1061
1062                 assert(sizeof(unsigned int) >= 4);
1063                 i = 0;
1064                 
1065                 fcbi.loadContents(&i, 0, 4);
1066                 TEST_CASE("1","1-4バイト目", i == 0x67452301);
1067                 fcbi.loadContents(&i, 4, 4);
1068                 TEST_CASE("1","5-8バイト目", i == 0xefcdab89);
1069                 fcbi.loadContents(&i, 8, 4);
1070                 TEST_CASE("1","9-12バイト目", i == 0x98badcfe);
1071                 fcbi.loadContents(&i,12, 4);
1072                 TEST_CASE("1","13-16バイト目", i == 0x10325476);
1073             } END_CASE;
1074         } END_CASE;
1075         remove("test");
1076
1077         BEGIN_CASE("2","loadContentsで一度にページサイズを超える量を要求する") {
1078             FileContainerBinutilsImpl fcbi;
1079             unsigned int i;
1080
1081             for(i=0;i<SIZE_LOADPAGE*2;++i)
1082                 fcbi.writeByte(i, i);
1083
1084             unsigned char * buffer = new unsigned char [SIZE_LOADPAGE * 2];
1085             fcbi.loadContents(buffer, 0, SIZE_LOADPAGE * 2);
1086
1087             for(i=0;i<SIZE_LOADPAGE*2;++i)
1088                 if(*(buffer + i) != (i & 0xff))
1089                     TEST_FAIL;
1090
1091             delete [] buffer;
1092         } END_CASE;
1093
1094     } END_CASE;
1095
1096     BEGIN_CASE("searchSymbolPrefix","searchSymbolPrefix") {
1097
1098         BEGIN_CASE("1","プレフィクスがない") {
1099             FileContainerBinutilsImpl fcbi;
1100             fcbi.symbol_table.insert(pair<string, address_t>(MAGIC_SYMBOL, 0x100));
1101
1102             fcbi.searchSymbolPrefix();
1103             if(!fcbi.symbol_prefix.empty())
1104                 TEST_FAIL;
1105         } END_CASE;
1106
1107         BEGIN_CASE("2","変数名に\"_\"がつくタイプ") {
1108             FileContainerBinutilsImpl fcbi;
1109             fcbi.symbol_table.insert(pair<string, address_t>("_" MAGIC_SYMBOL, 0x100));
1110
1111             fcbi.searchSymbolPrefix();
1112             if(fcbi.symbol_prefix.compare("_") != 0)
1113                 TEST_FAIL;
1114         } END_CASE;
1115
1116         BEGIN_CASE("3","\"__\"には反応しない") {
1117             FileContainerBinutilsImpl fcbi;
1118             fcbi.symbol_table.insert(pair<string, address_t>("__" MAGIC_SYMBOL, 0x100));
1119
1120             fcbi.searchSymbolPrefix();
1121             if(!fcbi.symbol_prefix.empty())
1122                 TEST_FAIL;
1123         } END_CASE;
1124             
1125         BEGIN_CASE("4","発見できない場合、変更しない") {
1126             FileContainerBinutilsImpl fcbi;
1127             fcbi.symbol_prefix.assign("test");
1128
1129             fcbi.searchSymbolPrefix();
1130             if(fcbi.symbol_prefix.compare("test") != 0)
1131                 TEST_FAIL;
1132         } END_CASE;
1133     } END_CASE;
1134
1135     BEGIN_CASE("searchByteOrder","searchByteOrder") {
1136         BEGIN_CASE("1","ホストと同じエンディアン") {
1137             FileContainerBinutilsImpl fcbi;
1138             unsigned int value = MAGIC_NUMBER;
1139             fcbi.symbol_table.insert(pair<string, address_t>(MAGIC_SYMBOL, 0x100));
1140
1141             for(int i=0;i<4;i++)
1142                 fcbi.writeByte(0x100 + i, *((char *)&value + i));
1143
1144             fcbi.byteorder = UNKNOWN;
1145             fcbi.searchByteOrder();
1146
1147             if(fcbi.byteorder != HOSTORDER)
1148                 TEST_FAIL;
1149         } END_CASE;
1150
1151         BEGIN_CASE("2","ホストと違うエンディアン") {
1152             FileContainerBinutilsImpl fcbi;
1153             unsigned int value = MAGIC_NUMBER;
1154             fcbi.symbol_table.insert(pair<string, address_t>(MAGIC_SYMBOL, 0x100));
1155
1156             for(int i=0;i<4;i++)
1157                 fcbi.writeByte(0x100 + i, *((char *)&value + (3 - i)));
1158
1159             fcbi.byteorder = UNKNOWN;
1160             fcbi.searchByteOrder();
1161
1162             if(fcbi.byteorder == HOSTORDER)
1163                 TEST_FAIL;
1164         } END_CASE;
1165
1166         BEGIN_CASE("3","シンボルが無い") {
1167             FileContainerBinutilsImpl fcbi;
1168             unsigned int value = MAGIC_NUMBER;
1169
1170             for(int i=0;i<4;i++)
1171                 fcbi.writeByte(0x100 + i, *((char *)&value + (3 - i)));
1172
1173             fcbi.byteorder = UNKNOWN;
1174             fcbi.searchByteOrder();
1175
1176             if(fcbi.byteorder != UNKNOWN)
1177                 TEST_FAIL;
1178         } END_CASE;
1179
1180         BEGIN_CASE("4","データが無い") {
1181             FileContainerBinutilsImpl fcbi;
1182             unsigned int value = MAGIC_NUMBER;
1183             fcbi.symbol_table.insert(pair<string, address_t>(MAGIC_SYMBOL, 0x100));
1184
1185             fcbi.byteorder = UNKNOWN;
1186             fcbi.searchByteOrder();
1187
1188             if(fcbi.byteorder != UNKNOWN)
1189                 TEST_FAIL;
1190         } END_CASE;
1191     } END_CASE;
1192
1193     chain.restoreContext();
1194 }
1195
1196 #endif /* TESTSUITE */
1197
1198 #endif /* FILECONTAINER_BINUTILS || TESTSUITE */
1199