DccFrontend.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. #include "DccFrontend.h"
  2. #include "dcc.h"
  3. #include "msvc_fixes.h"
  4. #include "project.h"
  5. #include "disassem.h"
  6. #include "CallGraph.h"
  7. #include <QtCore/QFileInfo>
  8. #include <QtCore/QDebug>
  9. #include <cstdio>
  10. class Loader
  11. {
  12. bool loadIntoProject(IProject *);
  13. };
  14. struct PSP { /* PSP structure */
  15. uint16_t int20h; /* interrupt 20h */
  16. uint16_t eof; /* segment, end of allocation block */
  17. uint8_t res1; /* reserved */
  18. uint8_t dosDisp[5]; /* far call to DOS function dispatcher */
  19. uint8_t int22h[4]; /* vector for terminate routine */
  20. uint8_t int23h[4]; /* vector for ctrl+break routine */
  21. uint8_t int24h[4]; /* vector for error routine */
  22. uint8_t res2[22]; /* reserved */
  23. uint16_t segEnv; /* segment address of environment block */
  24. uint8_t res3[34]; /* reserved */
  25. uint8_t int21h[6]; /* opcode for int21h and far return */
  26. uint8_t res4[6]; /* reserved */
  27. uint8_t fcb1[16]; /* default file control block 1 */
  28. uint8_t fcb2[16]; /* default file control block 2 */
  29. uint8_t res5[4]; /* reserved */
  30. uint8_t cmdTail[0x80]; /* command tail and disk transfer area */
  31. };
  32. static struct MZHeader { /* EXE file header */
  33. uint8_t sigLo; /* .EXE signature: 0x4D 0x5A */
  34. uint8_t sigHi;
  35. uint16_t lastPageSize; /* Size of the last page */
  36. uint16_t numPages; /* Number of pages in the file */
  37. uint16_t numReloc; /* Number of relocation items */
  38. uint16_t numParaHeader; /* # of paragraphs in the header */
  39. uint16_t minAlloc; /* Minimum number of paragraphs */
  40. uint16_t maxAlloc; /* Maximum number of paragraphs */
  41. uint16_t initSS; /* Segment displacement of stack */
  42. uint16_t initSP; /* Contents of SP at entry */
  43. uint16_t checkSum; /* Complemented checksum */
  44. uint16_t initIP; /* Contents of IP at entry */
  45. uint16_t initCS; /* Segment displacement of code */
  46. uint16_t relocTabOffset; /* Relocation table offset */
  47. uint16_t overlayNum; /* Overlay number */
  48. } header;
  49. #define EXE_RELOCATION 0x10 /* EXE images rellocated to above PSP */
  50. //static void LoadImage(char *filename);
  51. static void displayMemMap(void);
  52. /****************************************************************************
  53. * displayLoadInfo - Displays low level loader type info.
  54. ***************************************************************************/
  55. void PROG::displayLoadInfo(void)
  56. {
  57. int i;
  58. printf("File type is %s\n", (fCOM)?"COM":"EXE");
  59. if (not fCOM) {
  60. printf("Signature = %02X%02X\n", header.sigLo, header.sigHi);
  61. printf("File size %% 512 = %04X\n", LH(&header.lastPageSize));
  62. printf("File size / 512 = %04X pages\n", LH(&header.numPages));
  63. printf("# relocation items = %04X\n", LH(&header.numReloc));
  64. printf("Offset to load image = %04X paras\n", LH(&header.numParaHeader));
  65. printf("Minimum allocation = %04X paras\n", LH(&header.minAlloc));
  66. printf("Maximum allocation = %04X paras\n", LH(&header.maxAlloc));
  67. }
  68. printf("Load image size = %08lX\n", cbImage - sizeof(PSP));
  69. printf("Initial SS:SP = %04X:%04X\n", initSS, initSP);
  70. printf("Initial CS:IP = %04X:%04X\n", initCS, initIP);
  71. if (option.VeryVerbose and cReloc)
  72. {
  73. printf("\nRelocation Table\n");
  74. for (i = 0; i < cReloc; i++)
  75. {
  76. printf("%06X -> [%04X]\n", relocTable[i],LH(image() + relocTable[i]));
  77. }
  78. }
  79. printf("\n");
  80. }
  81. /*****************************************************************************
  82. * fill - Fills line for displayMemMap()
  83. ****************************************************************************/
  84. static void fill(int ip, char *bf)
  85. {
  86. PROG &prog(Project::get()->prog);
  87. static uint8_t type[4] = {'.', 'd', 'c', 'x'};
  88. uint8_t i;
  89. for (i = 0; i < 16; i++, ip++)
  90. {
  91. *bf++ = ' ';
  92. *bf++ = (ip < prog.cbImage)? type[(prog.map[ip >> 2] >> ((ip & 3) * 2)) & 3]: ' ';
  93. }
  94. *bf = '\0';
  95. }
  96. /*****************************************************************************
  97. * displayMemMap - Displays the memory bitmap
  98. ****************************************************************************/
  99. static void displayMemMap(void)
  100. {
  101. PROG &prog(Project::get()->prog);
  102. char c, b1[33], b2[33], b3[33];
  103. uint8_t i;
  104. int ip = 0;
  105. printf("\nMemory Map\n");
  106. while (ip < prog.cbImage)
  107. {
  108. fill(ip, b1);
  109. printf("%06X %s\n", ip, b1);
  110. ip += 16;
  111. for (i = 3, c = b1[1]; i < 32 and c == b1[i]; i += 2)
  112. ; /* Check if all same */
  113. if (i > 32)
  114. {
  115. fill(ip, b2); /* Skip until next two are not same */
  116. fill(ip+16, b3);
  117. if (not (strcmp(b1, b2) || strcmp(b1, b3)))
  118. {
  119. printf(" :\n");
  120. do
  121. {
  122. ip += 16;
  123. fill(ip+16, b1);
  124. } while (0==strcmp(b1, b2));
  125. }
  126. }
  127. }
  128. printf("\n");
  129. }
  130. DccFrontend::DccFrontend(QObject *parent) :
  131. QObject(parent)
  132. {
  133. }
  134. /*****************************************************************************
  135. * FrontEnd - invokes the loader, parser, disassembler (if asm1), icode
  136. * rewritter, and displays any useful information.
  137. ****************************************************************************/
  138. bool DccFrontend::FrontEnd ()
  139. {
  140. /* Do depth first flow analysis building call graph and procedure list,
  141. * and attaching the I-code to each procedure */
  142. parse (*Project::get());
  143. if (option.asm1)
  144. {
  145. qWarning() << "dcc: writing assembler file "<<asm1_name<<'\n';
  146. }
  147. /* Search through code looking for impure references and flag them */
  148. Disassembler ds(1);
  149. for(Function &f : Project::get()->pProcList)
  150. {
  151. f.markImpure();
  152. if (option.asm1)
  153. {
  154. ds.disassem(&f);
  155. }
  156. }
  157. if (option.Interact)
  158. {
  159. interactDis(&Project::get()->pProcList.front(), 0); /* Interactive disassembler */
  160. }
  161. /* Converts jump target addresses to icode offsets */
  162. for(Function &f : Project::get()->pProcList)
  163. {
  164. f.bindIcodeOff();
  165. }
  166. /* Print memory bitmap */
  167. if (option.Map)
  168. displayMemMap();
  169. return(true); // we no longer own proj !
  170. }
  171. struct DosLoader {
  172. protected:
  173. void prepareImage(PROG &prog,size_t sz,QFile &fp) {
  174. /* Allocate a block of memory for the program. */
  175. prog.cbImage = sz + sizeof(PSP);
  176. prog.Imagez = new uint8_t [prog.cbImage];
  177. prog.Imagez[0] = 0xCD; /* Fill in PSP int 20h location */
  178. prog.Imagez[1] = 0x20; /* for termination checking */
  179. /* Read in the image past where a PSP would go */
  180. if (sz != fp.read((char *)prog.Imagez + sizeof(PSP),sz))
  181. fatalError(CANNOT_READ, fp.fileName().toLocal8Bit().data());
  182. }
  183. };
  184. struct ComLoader : public DosLoader {
  185. bool canLoad(QFile &fp) {
  186. fp.seek(0);
  187. char sig[2];
  188. if(2==fp.read(sig,2)) {
  189. return not (sig[0] == 0x4D and sig[1] == 0x5A);
  190. }
  191. return false;
  192. }
  193. bool load(PROG &prog,QFile &fp) {
  194. fp.seek(0);
  195. /* COM file
  196. * In this case the load module size is just the file length
  197. */
  198. auto cb = fp.size();
  199. /* COM programs start off with an ORG 100H (to leave room for a PSP)
  200. * This is also the implied start address so if we load the image
  201. * at offset 100H addresses should all line up properly again.
  202. */
  203. prog.initCS = 0;
  204. prog.initIP = 0x100;
  205. prog.initSS = 0;
  206. prog.initSP = 0xFFFE;
  207. prog.cReloc = 0;
  208. prepareImage(prog, cb, fp);
  209. /* Set up memory map */
  210. cb = (prog.cbImage + 3) / 4;
  211. prog.map = (uint8_t *)malloc(cb);
  212. memset(prog.map, BM_UNKNOWN, (size_t)cb);
  213. return true;
  214. }
  215. };
  216. #if 0
  217. struct RomLoader {
  218. bool canLoad(QFile &fp) {
  219. fp.seek(0xFFF0);
  220. uint8_t sig[1];
  221. if(fp.read((char *)sig,1) == 1)
  222. {
  223. return (sig[0] == 0xEA);
  224. }
  225. return false;
  226. }
  227. bool load(PROG &prog,QFile &fp) {
  228. printf("Loading ROM...\n");
  229. fp.seek(0);
  230. /* ROM file
  231. * In this case the load module size is just the file length
  232. */
  233. auto cb = fp.size();
  234. fp.seek(cb - 0x10);
  235. uint8_t buf[5];
  236. printf("Going to get CS/IP...\n");
  237. if(fp.read((char *)buf, 5) != 5)
  238. {
  239. return false;
  240. }
  241. fp.seek(0);
  242. /* ROM File, Hard to say where it is suppose to start, so try to trust the
  243. */
  244. prog.initIP = (buf[2] << 8) | buf[1];
  245. //prog.initCS = 0;
  246. prog.initCS = (buf[4] << 8) | buf[3];
  247. prog.initSS = 0;
  248. prog.initSP = 0xFFFE;
  249. prog.cReloc = 0;
  250. prepareImage(prog, cb, fp);
  251. /* Set up memory map */
  252. cb = (prog.cbImage + 3) / 4;
  253. prog.map = (uint8_t *)malloc(cb);
  254. memset(prog.map, BM_UNKNOWN, (size_t)cb);
  255. return true;
  256. }
  257. protected:
  258. void prepareImage(PROG &prog, size_t sz, QFile &fp)
  259. {
  260. int32_t start = 0x100000 - sz;
  261. /* Allocate a block of memory for the program. */
  262. prog.cbImage = 1 * 1024 * 1024; /* Allocate the whole 1MB memory */
  263. //prog.cbImage = 64 * 1024; /* Allocate the whole 1MB memory */
  264. prog.Imagez = new uint8_t [prog.cbImage];
  265. if (fp.read((char *)prog.Imagez + start, sz) != sz)
  266. //if (fp.read((char *)prog.Imagez, sz) != sz)
  267. {
  268. fatalError(CANNOT_READ, fp.fileName().toLocal8Bit().data());
  269. }
  270. }
  271. };
  272. #else
  273. struct RomLoader {
  274. bool canLoad(QFile &fp) {
  275. fp.seek(0xFFF0);
  276. uint8_t sig[1];
  277. if(fp.read((char *)sig,1) == 1)
  278. {
  279. return (sig[0] == 0xEA);
  280. }
  281. return false;
  282. }
  283. bool load(PROG &prog,QFile &fp) {
  284. fp.seek(0);
  285. /* COM file
  286. * In this case the load module size is just the file length
  287. */
  288. auto cb = fp.size();
  289. /* COM programs start off with an ORG 100H (to leave room for a PSP)
  290. * This is also the implied start address so if we load the image
  291. * at offset 100H addresses should all line up properly again.
  292. */
  293. prog.initCS = 0;
  294. prog.initIP = 0x000;
  295. prog.initSS = 0;
  296. prog.initSP = 0xFFFE;
  297. prog.cReloc = 0;
  298. prepareImage(prog, cb, fp);
  299. /* Set up memory map */
  300. cb = (prog.cbImage + 3) / 4;
  301. prog.map = (uint8_t *)malloc(cb);
  302. memset(prog.map, BM_UNKNOWN, (size_t)cb);
  303. return true;
  304. }
  305. protected:
  306. void prepareImage(PROG &prog, size_t sz, QFile &fp)
  307. {
  308. /* Allocate a block of memory for the program. */
  309. prog.cbImage = sz;
  310. prog.Imagez = new uint8_t[prog.cbImage];
  311. if (sz != fp.read((char *)prog.Imagez, sz))
  312. fatalError(CANNOT_READ, fp.fileName().toLocal8Bit().data());
  313. }
  314. };
  315. #endif
  316. struct ExeLoader : public DosLoader {
  317. bool canLoad(QFile &fp) {
  318. if(fp.size()<sizeof(header))
  319. return false;
  320. MZHeader tmp_header;
  321. fp.seek(0);
  322. fp.read((char *)&tmp_header, sizeof(header));
  323. if(not (tmp_header.sigLo == 0x4D and tmp_header.sigHi == 0x5A))
  324. return false;
  325. /* This is a typical DOS kludge! */
  326. if (LH(&header.relocTabOffset) == 0x40)
  327. {
  328. qDebug() << "Don't understand new EXE format";
  329. return false;
  330. }
  331. return true;
  332. }
  333. bool load(PROG &prog,QFile &fp) {
  334. /* Read rest of header */
  335. fp.seek(0);
  336. if (fp.read((char *)&header, sizeof(header)) != sizeof(header))
  337. return false;
  338. /* Calculate the load module size.
  339. * This is the number of pages in the file
  340. * less the length of the header and reloc table
  341. * less the number of bytes unused on last page
  342. */
  343. uint32_t cb = (uint32_t)LH(&header.numPages) * 512 - (uint32_t)LH(&header.numParaHeader) * 16;
  344. if (header.lastPageSize)
  345. {
  346. cb -= 512 - LH(&header.lastPageSize);
  347. }
  348. /* We quietly ignore minAlloc and maxAlloc since for our
  349. * purposes it doesn't really matter where in real memory
  350. * the program would end up. EXE programs can't really rely on
  351. * their load location so setting the PSP segment to 0 is fine.
  352. * Certainly programs that prod around in DOS or BIOS are going
  353. * to have to load DS from a constant so it'll be pretty
  354. * obvious.
  355. */
  356. prog.initCS = (int16_t)LH(&header.initCS) + EXE_RELOCATION;
  357. prog.initIP = (int16_t)LH(&header.initIP);
  358. prog.initSS = (int16_t)LH(&header.initSS) + EXE_RELOCATION;
  359. prog.initSP = (int16_t)LH(&header.initSP);
  360. prog.cReloc = (int16_t)LH(&header.numReloc);
  361. /* Allocate the relocation table */
  362. if (prog.cReloc)
  363. {
  364. prog.relocTable.resize(prog.cReloc);
  365. fp.seek(LH(&header.relocTabOffset));
  366. /* Read in seg:offset pairs and convert to Image ptrs */
  367. uint8_t buf[4];
  368. for (int i = 0; i < prog.cReloc; i++)
  369. {
  370. fp.read((char *)buf,4);
  371. prog.relocTable[i] = LH(buf) + (((int)LH(buf+2) + EXE_RELOCATION)<<4);
  372. }
  373. }
  374. /* Seek to start of image */
  375. uint32_t start_of_image= LH(&header.numParaHeader) * 16;
  376. fp.seek(start_of_image);
  377. /* Allocate a block of memory for the program. */
  378. prepareImage(prog,cb,fp);
  379. /* Set up memory map */
  380. cb = (prog.cbImage + 3) / 4;
  381. prog.map = (uint8_t *)malloc(cb);
  382. memset(prog.map, BM_UNKNOWN, (size_t)cb);
  383. /* Relocate segment constants */
  384. for(uint32_t v : prog.relocTable) {
  385. uint8_t *p = &prog.Imagez[v];
  386. uint16_t w = (uint16_t)LH(p) + EXE_RELOCATION;
  387. *p++ = (uint8_t)(w & 0x00FF);
  388. *p = (uint8_t)((w & 0xFF00) >> 8);
  389. }
  390. return true;
  391. }
  392. };
  393. /*****************************************************************************
  394. * LoadImage
  395. ****************************************************************************/
  396. bool Project::load()
  397. {
  398. // addTask(loaderSelection,PreCond(BinaryImage))
  399. // addTask(applyLoader,PreCond(Loader))
  400. const char *fname = binary_path().toLocal8Bit().data();
  401. QFile finfo(binary_path());
  402. /* Open the input file */
  403. if(not finfo.open(QFile::ReadOnly)) {
  404. fatalError(CANNOT_OPEN, fname);
  405. }
  406. /* Read in first 2 bytes to check EXE signature */
  407. if (finfo.size()<=2)
  408. {
  409. fatalError(CANNOT_READ, fname);
  410. }
  411. RomLoader rom_loader;
  412. ComLoader com_loader;
  413. ExeLoader exe_loader;
  414. if(rom_loader.canLoad(finfo)) {
  415. /* We have no relacation and code should be on 64K only,
  416. * So let's consider it as a COM file
  417. */
  418. prog.fCOM = true;
  419. return rom_loader.load(prog,finfo);
  420. }
  421. if(exe_loader.canLoad(finfo)) {
  422. prog.fCOM = false;
  423. return exe_loader.load(prog,finfo);
  424. }
  425. if(com_loader.canLoad(finfo)) {
  426. prog.fCOM = true;
  427. return com_loader.load(prog,finfo);
  428. }
  429. return false;
  430. }
  431. uint32_t SynthLab;
  432. /* Parses the program, builds the call graph, and returns the list of
  433. * procedures found */
  434. void DccFrontend::parse(Project &proj)
  435. {
  436. PROG &prog(proj.prog);
  437. STATE state;
  438. /* Set initial state */
  439. state.setState(rES, 0); /* PSP segment */
  440. state.setState(rDS, 0);
  441. state.setState(rCS, prog.initCS);
  442. state.setState(rSS, prog.initSS);
  443. state.setState(rSP, prog.initSP);
  444. state.IP = ((uint32_t)prog.initCS << 4) + prog.initIP;
  445. SynthLab = SYNTHESIZED_MIN;
  446. /* Check for special settings of initial state, based on idioms of the
  447. startup code */
  448. state.checkStartup();
  449. ilFunction start_proc;
  450. /* Make a struct for the initial procedure */
  451. if (prog.offMain != -1)
  452. {
  453. start_proc = proj.createFunction(0,"main");
  454. start_proc->retVal.loc = REG_FRAME;
  455. start_proc->retVal.type = TYPE_WORD_SIGN;
  456. start_proc->retVal.id.regi = rAX;
  457. /* We know where main() is. Start the flow of control from there */
  458. start_proc->procEntry = prog.offMain;
  459. /* In medium and large models, the segment of main may (will?) not be
  460. the same as the initial CS segment (of the startup code) */
  461. state.setState(rCS, prog.segMain);
  462. state.IP = prog.offMain;
  463. }
  464. else
  465. {
  466. start_proc = proj.createFunction(0,"start");
  467. /* Create initial procedure at program start address */
  468. start_proc->procEntry = (uint32_t)state.IP;
  469. }
  470. /* The state info is for the first procedure */
  471. start_proc->state = state;
  472. /* Set up call graph initial node */
  473. proj.callGraph = new CALL_GRAPH;
  474. proj.callGraph->proc = start_proc;
  475. /* This proc needs to be called to set things up for LibCheck(), which
  476. checks a proc to see if it is a know C (etc) library */
  477. prog.bSigs = SetupLibCheck();
  478. //BUG: proj and g_proj are 'live' at this point !
  479. /* Recursively build entire procedure list */
  480. start_proc->FollowCtrl(proj.callGraph, &state);
  481. /* This proc needs to be called to clean things up from SetupLibCheck() */
  482. CleanupLibCheck();
  483. }