hqlres.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. /*##############################################################################
  2. Copyright (C) 2011 HPCC Systems.
  3. All rights reserved. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ############################################################################## */
  14. #include "jliball.hpp"
  15. #include "hql.hpp"
  16. #include "jlib.hpp"
  17. #include "hqlres.hpp"
  18. #include "jmisc.hpp"
  19. #include "jexcept.hpp"
  20. #include "hqlcerrors.hpp"
  21. #include "thorplugin.hpp"
  22. #ifndef _WIN32
  23. #include "bfd.h"
  24. #endif
  25. #define BIGSTRING_BASE 101
  26. #define MANIFEST_BASE 1000
  27. class ResourceItem : public CInterface
  28. {
  29. public:
  30. ResourceItem(const char * _type, unsigned _id, size32_t _len, const void * _ptr)
  31. : data(_len, _ptr), type(_type), id(_id) {}
  32. public:
  33. MemoryAttr data;
  34. StringAttr type;
  35. unsigned id;
  36. };
  37. ResourceManager::ResourceManager()
  38. {
  39. nextmfid = 1;
  40. nextbsid = 0;
  41. totalbytes = 0;
  42. finalized=false;
  43. }
  44. unsigned ResourceManager::count()
  45. {
  46. return resources.ordinality();
  47. }
  48. unsigned ResourceManager::addString(unsigned len, const char *data)
  49. {
  50. unsigned id = BIGSTRING_BASE + nextbsid++;
  51. resources.append(*new ResourceItem("BIGSTRING", id, len, data));
  52. return id;
  53. }
  54. void ResourceManager::addNamed(const char * type, unsigned len, const void * data, IPropertyTree *entryEx, unsigned id, bool addToManifest, bool compressed)
  55. {
  56. if (id==(unsigned)-1)
  57. id = MANIFEST_BASE + nextmfid++;
  58. if (addToManifest && !finalized)
  59. {
  60. Owned<IPropertyTree> entry=createPTree("Resource");
  61. entry->setProp("@type", type);
  62. entry->setPropInt("@id", id);
  63. if (compressed)
  64. entry->setPropBool("compressed", true);
  65. if (entryEx)
  66. mergePTree(entry, entryEx);
  67. ensureManifestInfo()->addPropTree("Resource", entry.getClear());
  68. }
  69. resources.append(*new ResourceItem(type, id, len, data));
  70. }
  71. bool ResourceManager::addCompress(const char * type, unsigned len, const void * data, IPropertyTree *entryEx, unsigned id, bool addToManifest)
  72. {
  73. bool isCompressed=false;
  74. if (len>=32) //lzw assert if too small
  75. {
  76. isCompressed = true;
  77. MemoryBuffer compressed;
  78. compressResource(compressed, len, data);
  79. addNamed(type, compressed.length(), compressed.toByteArray(), entryEx, id, addToManifest, isCompressed);
  80. }
  81. else
  82. addNamed(type, len, data, entryEx, id, addToManifest, isCompressed);
  83. return isCompressed;
  84. }
  85. static void loadResource(const char *filepath, MemoryBuffer &content)
  86. {
  87. Owned <IFile> f = createIFile(filepath);
  88. Owned <IFileIO> fio = f->open(IFOread);
  89. read(fio, 0, (size32_t) f->size(), content);
  90. }
  91. void ResourceManager::addManifest(const char *filename)
  92. {
  93. Owned<IPropertyTree> manifestSrc = createPTreeFromXMLFile(filename);
  94. StringBuffer dir;
  95. splitDirTail(filename, dir);
  96. Owned<IPropertyTreeIterator> itres = manifestSrc->getElements("Resource[@filename]");
  97. ForEach(*itres)
  98. {
  99. unsigned id = MANIFEST_BASE + nextmfid++;
  100. IPropertyTree &resource = itres->query();
  101. resource.setPropInt("@id", id);
  102. if (!resource.hasProp("@type"))
  103. resource.setProp("@type", "UNKOWN");
  104. StringBuffer fullpath;
  105. const char *respath = resource.queryProp("@filename");
  106. if (!isAbsolutePath(respath))
  107. fullpath.append(dir);
  108. fullpath.append(respath);
  109. MemoryBuffer content;
  110. loadResource(fullpath.str(), content);
  111. if (addCompress(resource.queryProp("@type"), content.length(), content.toByteArray(), NULL, id, false))
  112. resource.setPropBool("@compressed", true);
  113. }
  114. mergePTree(ensureManifestInfo(), manifestSrc);
  115. }
  116. void ResourceManager::addManifestFromArchive(IPropertyTree *archive)
  117. {
  118. if (archive)
  119. {
  120. Owned<IPropertyTreeIterator> manifests = archive->getElements("AdditionalFiles/Manifest");
  121. ForEach(*manifests)
  122. {
  123. const char *xml = manifests->query().queryProp(NULL);
  124. Owned<IPropertyTree> manifestSrc = createPTreeFromXMLString(xml);
  125. Owned<IPropertyTreeIterator> itres = manifestSrc->getElements("Resource[@filename]");
  126. ForEach(*itres)
  127. {
  128. unsigned id = MANIFEST_BASE + nextmfid++;
  129. IPropertyTree &resource = itres->query();
  130. resource.setPropInt("@id", id);
  131. if (!resource.hasProp("@type"))
  132. resource.setProp("@type", "UNKOWN");
  133. const char *filename = resource.queryProp("@filename");
  134. VStringBuffer xpath("AdditionalFiles/Resource[@originalFilename=\"%s\"]", filename);
  135. MemoryBuffer content;
  136. archive->getPropBin(xpath.str(), content);
  137. if (content.length())
  138. {
  139. if (addCompress(resource.queryProp("@type"), content.length(), content.toByteArray(), NULL, id, false))
  140. resource.setPropBool("@compressed", true);
  141. }
  142. }
  143. mergePTree(ensureManifestInfo(), manifestSrc);
  144. }
  145. }
  146. }
  147. void ResourceManager::finalize()
  148. {
  149. if (!finalized)
  150. {
  151. if (manifest)
  152. {
  153. StringBuffer content;
  154. toXML(manifest, content);
  155. addCompress("MANIFEST", content.length()+1, content.str(), NULL, MANIFEST_BASE, false);
  156. }
  157. finalized=true;
  158. }
  159. }
  160. void ResourceManager::putbytes(int h, const void *b, unsigned len)
  161. {
  162. int written = _write(h, b, len);
  163. assertex(written == len);
  164. totalbytes += len;
  165. }
  166. void ResourceManager::flushAsText(const char *filename)
  167. {
  168. finalize();
  169. StringBuffer name;
  170. int len = strlen(filename);
  171. name.append(filename,0,len-4).append(".txt");
  172. FILE* f = fopen(name.str(), "wb");
  173. if (f==NULL)
  174. {
  175. PrintLog("Create resource text file %s failed", name.str());
  176. return; // error is ignorable.
  177. }
  178. ForEachItemIn(idx, resources)
  179. {
  180. ResourceItem&s = (ResourceItem&)resources.item(idx);
  181. fwrite(s.data.get(),1,s.data.length(),f);
  182. }
  183. fclose(f);
  184. }
  185. void ResourceManager::flush(const char *filename, bool flushText, bool target64bit)
  186. {
  187. finalize();
  188. // Use "resources" for strings that are a bit large to generate in the c++ (some compilers had limits at 64k)
  189. // or that we want to access without having to run the dll/so
  190. // In linux there is no .res concept but we can achieve the same effect by generating an object file with a specially-named section
  191. // bintils tools can be used to extract the data externally (internally we just have a named symbol for it)
  192. #ifdef _WIN32
  193. int h = _open(filename, _O_WRONLY|_O_CREAT|_O_TRUNC|_O_BINARY|_O_SEQUENTIAL, _S_IREAD | _S_IWRITE | _S_IEXEC);
  194. //assertex(h != HFILE_ERROR);
  195. if (h == HFILE_ERROR) // error can not be ignored!
  196. throwError1(HQLERR_ResourceCreateFailed, filename);
  197. totalbytes = 0;
  198. putbytes(h, "\x00\x00\x00\x00\x20\x00\x00\x00\xff\xff\x00\x00\xff\xff\x00\x00"
  199. "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x20);
  200. MemoryBuffer temp;
  201. ForEachItemIn(idx, resources)
  202. {
  203. ResourceItem&s = static_cast<ResourceItem&>(resources.item(idx));
  204. __int32 len = s.data.length();
  205. unsigned lenType = strlen(s.type);
  206. unsigned sizeType = (lenType+1)*2;
  207. unsigned sizeTypeName = (sizeType + 4);
  208. unsigned packedSizeTypeName = ((sizeTypeName + 2) & ~3);
  209. __int32 lenHeader = 4 + 4 + packedSizeTypeName + 4 + 2 + 2 + 4 + 4;
  210. unsigned short id = s.id;
  211. temp.clear();
  212. temp.append(sizeof(len), &len);
  213. temp.append(sizeof(lenHeader), &lenHeader);
  214. for (unsigned i=0; i < lenType; i++)
  215. temp.append((byte)s.type[i]).append((byte)0);
  216. temp.append((byte)0).append((byte)0);
  217. temp.append((byte)0xff).append((byte)0xff);
  218. temp.append(sizeof(id), &id);
  219. if (temp.length() & 2)
  220. temp.append((byte)0).append((byte)0);
  221. temp.append(4, "\x00\x00\x00\x00"); // version
  222. temp.append(12, "\x30\x10\x09\x04\x00\x00\x00\x00\x00\x00\x00\x00"); // 0x1030 memory 0x0409 language
  223. assertex(lenHeader == temp.length());
  224. putbytes(h, temp.bufferBase(), lenHeader);
  225. putbytes(h, s.data.get(), len);
  226. if (totalbytes & 3)
  227. putbytes(h, "\x00\x00\x00",4-(totalbytes & 3));
  228. }
  229. _close(h);
  230. #else
  231. asymbol **syms = NULL;
  232. bfd *file = NULL;
  233. StringArray names; // need to make sure that the strings we use in symbol table have appropriate lifetime
  234. try
  235. {
  236. bfd_init ();
  237. bfd_set_default_target(target64bit ? "x86_64-unknown-linux-gnu" : "x86_32-unknown-linux-gnu");
  238. const bfd_arch_info_type *temp_arch_info = bfd_scan_arch ("i386");
  239. file = bfd_openw(filename, target64bit ? "elf64-x86-64" : NULL);//MORE: Test on 64 bit to see if we can always pass NULL
  240. verifyex(file);
  241. verifyex(bfd_set_arch_mach(file, temp_arch_info->arch, temp_arch_info->mach));
  242. verifyex(bfd_set_start_address(file, 0));
  243. verifyex(bfd_set_format(file, bfd_object));
  244. syms = new asymbol *[resources.length()*2+1];
  245. ForEachItemIn(idx, resources)
  246. {
  247. ResourceItem&s = (ResourceItem&)resources.item(idx);
  248. unsigned len = s.data.length();
  249. unsigned id = s.id;
  250. StringBuffer baseName;
  251. baseName.append(s.type).append("_").append(id);
  252. StringBuffer str;
  253. str.clear().append(baseName).append(".data");
  254. names.append(str);
  255. sec_ptr osection = bfd_make_section_anyway_with_flags (file, names.tos(), SEC_HAS_CONTENTS|SEC_ALLOC|SEC_LOAD|SEC_DATA|SEC_READONLY);
  256. verifyex(osection);
  257. verifyex(bfd_set_section_size(file, osection, len));
  258. verifyex(bfd_set_section_vma(file, osection, 0));
  259. bfd_set_reloc (file, osection, NULL, 0);
  260. osection->lma=0;
  261. osection->entsize=0;
  262. syms[idx*2] = bfd_make_empty_symbol(file);
  263. syms[idx*2]->flags = BSF_GLOBAL;
  264. syms[idx*2]->section = osection;
  265. names.append(str.clear().append(baseName).append("_txt_start"));
  266. syms[idx*2]->name = names.tos();
  267. syms[idx*2]->value = 0;
  268. syms[idx*2+1] = bfd_make_empty_symbol(file);
  269. syms[idx*2+1]->flags = BSF_GLOBAL;
  270. syms[idx*2+1]->section = bfd_abs_section_ptr;
  271. names.append(str.clear().append(baseName).append("_txt_size"));
  272. syms[idx*2+1]->name = names.tos();
  273. syms[idx*2+1]->value = len;
  274. }
  275. syms[resources.length()*2] = NULL;
  276. bfd_set_symtab (file, syms, resources.length()*2);
  277. // experience suggests symtab need to be in place before setting contents
  278. ForEachItemIn(idx2, resources)
  279. {
  280. ResourceItem &s = (ResourceItem&)resources.item(idx2);
  281. verifyex(bfd_set_section_contents(file, syms[idx2*2]->section, s.data.get(), 0, s.data.length()));
  282. }
  283. verifyex(bfd_close(file));
  284. delete [] syms;
  285. }
  286. catch (IException *E)
  287. {
  288. E->Release();
  289. //translate the assert exceptions into something else...
  290. StringBuffer msg;
  291. msg.appendf("%s: %s", filename, bfd_errmsg(bfd_get_error()));
  292. delete syms;
  293. if (file)
  294. bfd_close_all_done(file); // allow bfd to clean up memory
  295. throwError1(HQLERR_ResourceCreateFailed, msg.str());
  296. }
  297. #endif
  298. if (flushText)
  299. flushAsText(filename);
  300. }
  301. bool ResourceManager::queryWriteText(StringBuffer & resTextName, const char * filename)
  302. {
  303. int len = strlen(filename);
  304. resTextName.append(filename,0,len-4).append(".txt");
  305. return true;
  306. }
  307. #if 0
  308. int test()
  309. {
  310. ResourceManager r;
  311. r.add("Hello there!2");
  312. r.add("Hello again");
  313. r.flush("c:\\t2.res");
  314. return 6;
  315. }
  316. static int dummy = test();
  317. #endif