1 /*
2  * Copyright (C) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include <cstddef>
16 #include <string.h>
17 #include <inttypes.h>
18 #include <algorithm>
19 #include <string>
20 #include <string_view>
21 #include <vector>
22 #include <set>
23 #include "dir.h"
24 #include "coff.h"
25 #include "elf32.h"
26 #include "elf64.h"
27 
28 #ifdef __APPLE__
29 #include <mach-o/loader.h>
30 #include <mach-o/nlist.h>
31 #include <mach-o/fat.h>
32 #endif
33 
34 struct fs_entry {
35     char fname[256];
36     uint64_t offset;
37     uint64_t size;
38 };
39 
40 std::vector<fs_entry> directory;
41 std::vector<uint8_t> bin;
42 std::vector<std::string_view> valid_exts;
43 
append_file(const std::string& filename, const std::string& storename)44 void append_file(const std::string& filename, const std::string& storename)
45 {
46     std::string_view ext;
47     auto pos = filename.find_last_of(".");
48     if (pos != std::string::npos) {
49         // found it.
50         ext = std::string_view(filename).substr(pos);
51     }
52     bool valid = false;
53     for (const auto& e : valid_exts) {
54         if (ext.compare(e) == 0) {
55             valid = true;
56             break;
57         }
58     }
59     if (!valid) {
60         printf("Skipped %s\n", storename.c_str());
61         return;
62     }
63     if (storename.size() > 255) {
64         printf("Filename too long [%s]\n", storename.c_str());
65         exit(-1);
66     }
67     fs_entry tmp{};
68     strcpy(tmp.fname, storename.c_str());
69 #if _WIN32
70     struct _stat64 fileStat;
71     if (-1 == _stat64(filename.c_str(), &fileStat)) {
72 #else
73     struct stat fileStat;
74     if (-1 == stat(filename.c_str(), &fileStat)) {
75 #endif
76         printf("File [%s] not found\n", tmp.fname);
77         exit(-1);
78     }
79     tmp.size = fileStat.st_size;
80     auto padding = (8 - (bin.size() & 7)) & 7;
81     tmp.offset = bin.size() + padding;
82     directory.push_back(tmp);
83     FILE* f = fopen(filename.c_str(), "rb");
84     if (f == NULL) {
85         printf("Could not open %s.\n", filename.c_str());
86         exit(-1);
87     }
88     bin.resize((size_t)(bin.size() + padding + tmp.size));
89     fread(bin.data() + tmp.offset, 1, (size_t)tmp.size, f);
90     fclose(f);
91     printf("Stored: %s [%" PRIu64 " , %" PRIu64 "]\n", tmp.fname, tmp.offset, tmp.size);
92 }
93 
94 void add_directory(const std::string& path, const std::string& outpath)
95 {
96     struct dirent* pDirent = nullptr;
97     DIR* pDir = opendir(path.c_str());
98     if (pDir == NULL) {
99         printf("Cannot open directory '%s'\n", path.c_str());
100         exit(1);
101     }
102     std::string p, op;
103     p = path;
104     if (!p.empty()) {
105         if (p.back() != '/') {
106             p += "/";
107         }
108     }
109     op = outpath;
110     if (!op.empty()) {
111         if (op.back() != '/') {
112             op += "/";
113         }
114     }
115 
116     // sort the readdir result
117     auto alphaSort = [](dirent* x, dirent* y) { return std::string(x->d_name) < std::string(y->d_name); };
118     auto dirSet = std::set<dirent*, decltype(alphaSort)>(alphaSort);
119 
120     while ((pDirent = readdir(pDir)) != NULL) {
121         // This structure may be statically allocated
122         dirSet.insert(pDirent);
123     }
124 
125     for (auto &d : dirSet) {
126         if (d->d_type == DT_DIR) {
127             if (d->d_name[0] == '.') {
128                 continue;
129             }
130             add_directory(p + d->d_name, op + d->d_name);
131             continue;
132         }
133         append_file(p + d->d_name, op + d->d_name);
134     }
135 
136     closedir(pDir);
137 }
138 /*
139  *   //Pseudo code for accessing files in blob
140  *   struct fs_entry
141  *   {
142  *       char fname[256];
143  *       uint64_t offset;
144  *       uint64_t size;
145  *   };
146  *   extern "C" uint64_t SizeOfDataForReadOnlyFileSystem;
147  *   extern "C" struct fs_entry BinaryDataForReadOnlyFileSystem[];
148  *   void dump_files()
149  *   {
150  *       for (int i = 0; i < SizeOfDataForReadOnlyFileSystem; i++)
151  *       {
152  *           if (BinaryDataForReadOnlyFileSystem[i].fname[0] == 0) break;
153  *           printf("%s\n", BinaryDataForReadOnlyFileSystem[i].fname);
154  *           char* data = (char*)(BinaryDataForReadOnlyFileSystem[i].offset +
155  * (uintptr_t)BinaryDataForReadOnlyFileSystem); printf("%lld\n", BinaryDataForReadOnlyFileSystem[i].offset);
156  *       }
157  *   }
158  */
159 
160 std::string gDataName = "BinaryDataForReadOnlyFileSystem";
161 std::string gSizeName = "SizeOfDataForReadOnlyFileSystem";
162 
163 void write_obj(const std::string& fname, const std::string& secname, size_t size_of_data, const void* data, bool x64)
164 {
165     size_t size_of_section = sizeof(uint64_t) + size_of_data;
166 #pragma pack(push, 1)
167     // Using headers and defines from winnt.h
168     struct ObjFile {
169         IMAGE_FILE_HEADER coffHead;
170         IMAGE_SECTION_HEADER sections[1];
171         IMAGE_SYMBOL symtab[2];
172     } obj{};
173 #pragma pack(pop)
174 
175     // fill coff header.
176     if (!x64)
177         obj.coffHead.Machine = IMAGE_FILE_MACHINE_I386;
178     else
179         obj.coffHead.Machine = IMAGE_FILE_MACHINE_AMD64;
180     obj.coffHead.NumberOfSections = sizeof(obj.sections) / sizeof(IMAGE_SECTION_HEADER);
181     obj.coffHead.TimeDateStamp = 0; // duh.
182     obj.coffHead.PointerToSymbolTable = offsetof(decltype(obj), symtab);
183     obj.coffHead.NumberOfSymbols = sizeof(obj.symtab) / sizeof(IMAGE_SYMBOL);
184     obj.coffHead.SizeOfOptionalHeader = 0;
185     obj.coffHead.Characteristics = 0; // if x86 use IMAGE_FILE_32BIT_MACHINE ?
186 
187     // create stringtable
188     char stringtable[256]{ 0 };
189     char* dst = stringtable;
190     auto add_string = [&dst, &stringtable](std::string_view a) -> Elf32_Word {
191         const auto offset = dst - stringtable;
192         dst += a.copy(dst, 256u - offset);
193         *dst++ = '\0';
194         return static_cast<uint32_t>(offset + 4);
195     };
196 
197     // obj.symtab[0].N.Name.Long = add_string("?BinaryDataForReadOnlyFileSystem@@3PAUfs_entry@@A");//
198     // ?BinaryDataForReadOnlyFileSystem@@3PADA"); obj.symtab[1].N.Name.Long =
199     // add_string("?SizeOfDataForReadOnlyFileSystem@@3KA");
200     if (!x64) {
201         // in win32 the symbols have extra "_" ?
202         std::string t = "_";
203         t += gDataName;
204         std::string t2 = "_";
205         t2 += gSizeName;
206         obj.symtab[1].N.Name.Long = add_string(t.c_str() /*"BinaryDataForReadOnlyFileSystem"*/);
207         obj.symtab[0].N.Name.Long = add_string(t2.c_str() /*"SizeOfDataForReadOnlyFileSystem"*/);
208     } else {
209         obj.symtab[1].N.Name.Long = add_string(gDataName.c_str() /*"BinaryDataForReadOnlyFileSystem"*/);
210         obj.symtab[0].N.Name.Long = add_string(gSizeName.c_str() /*"SizeOfDataForReadOnlyFileSystem"*/);
211     }
212     uint32_t stringTableSize = (uint32_t)((dst - stringtable) + 4);
213 
214     // fill the section.
215     memcpy(&obj.sections[0].Name[0], secname.c_str(), secname.size());
216     obj.sections[0].Misc.VirtualSize = 0;
217     obj.sections[0].VirtualAddress = 0;
218     obj.sections[0].SizeOfRawData = (uint32_t)size_of_section; // sizeof the data on disk.
219     obj.sections[0].PointerToRawData =
220         ((sizeof(obj) + stringTableSize + 3) / 4) * 4; // DWORD align the data directly after the headers..
221     obj.sections[0].PointerToLinenumbers = 0;
222     obj.sections[0].NumberOfRelocations = 0;
223     obj.sections[0].NumberOfLinenumbers = 0;
224     obj.sections[0].Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_MEM_READ;
225     // fill symbols
226     obj.symtab[1].Value = (uint32_t)sizeof(uint64_t);
227     obj.symtab[1].SectionNumber = 1; // first section.. (one based)
228     obj.symtab[1].Type = IMAGE_SYM_TYPE_CHAR | (IMAGE_SYM_DTYPE_ARRAY << 8);
229     obj.symtab[1].StorageClass = IMAGE_SYM_CLASS_EXTERNAL;
230     obj.symtab[1].NumberOfAuxSymbols = 0;
231 
232     obj.symtab[0].Value = (uint32_t)0;
233     obj.symtab[0].SectionNumber = 1; // first section.. (one based)
234     // obj.symtab[0].Type = IMAGE_SYM_TYPE_UINT;  //(just use IMAGE_SYM_TYPE_NULL like mstools?)
235     obj.symtab[0].StorageClass = IMAGE_SYM_CLASS_EXTERNAL;
236     obj.symtab[0].NumberOfAuxSymbols = 0;
237 
238     FILE* d = fopen(fname.c_str(), "wb");
239     if (d == NULL) {
240         printf("Could not open %s.\n", fname.c_str());
241         exit(-1);
242     }
243     // write headers
244     fwrite(&obj, sizeof(obj), 1, d);
245     // write string table
246     fwrite(&stringTableSize, 1, sizeof(stringTableSize), d);
247     fwrite(stringtable, 1, stringTableSize - 4, d);
248     // write sections..
249     size_t p = ftell(d);
250     uint32_t pad = 0;
251     size_t padcount = obj.sections[0].PointerToRawData - p;
252     fwrite(&pad, padcount, 1, d);
253     fwrite(data, size_of_section, 1, d);
254     fclose(d);
255 #undef add_string
256 }
257 
258 template<class type>
259 void write_elf(
260     type o, uint8_t arch, const std::string& fname, const std::string& secname, size_t size_of_data, const void* data)
261 {
262     size_t size_of_section = size_of_data + sizeof(uint64_t);
263     char stringtable[256];
264     o.head.e_type = ET_REL;
265     o.head.e_machine = arch; // machine id..
266     o.head.e_version = EV_CURRENT;
267     o.head.e_ehsize = sizeof(o.head);
268     o.head.e_shentsize = sizeof(o.sections[0]);
269     o.head.e_shnum = sizeof(o.sections) / sizeof(o.sections[0]);
270     o.head.e_shoff = sizeof(o.head);
271     o.head.e_shstrndx = 1;
272 
273     // initialize stringtable.
274     char* dst = stringtable;
275     *dst = 0;
276     dst++;
277     auto add_string = [&dst, &stringtable](std::string_view a) -> Elf32_Word {
278         const auto offset = dst - stringtable;
279         dst += a.copy(dst, 256u - offset);
280         *dst++ = '\0';
281         return static_cast<Elf32_Word>(offset);
282     };
283 
284     // create symbols
285     o.symbs[2].st_name = add_string(gDataName); // ?BinaryDataForReadOnlyFileSystem@@3PADA");
286     o.symbs[2].st_value = sizeof(uint64_t);
287     o.symbs[2].st_size = static_cast<Elf32_Word>(size_of_data);
288     o.symbs[2].st_info = o.symbs[1].st_info = ELF_ST_INFO(STB_GLOBAL, STT_OBJECT);
289     o.symbs[2].st_other = o.symbs[1].st_other = STV_HIDDEN;
290     o.symbs[2].st_shndx = o.symbs[1].st_shndx = 3;
291 
292     o.symbs[1].st_name = add_string(gSizeName);
293     o.symbs[1].st_value = 0;
294     o.symbs[1].st_size = sizeof(uint64_t);
295 
296     o.sections[2].sh_name = add_string(".symtab");
297     o.sections[2].sh_type = SHT_SYMTAB;
298     o.sections[2].sh_offset = offsetof(decltype(o), symbs); // sizeof(o) + size_of_section + stringtable_size;
299     o.sections[2].sh_addralign = 8;
300     o.sections[2].sh_size = sizeof(o.symbs);
301     o.sections[2].sh_entsize = sizeof(o.symbs[0]);
302     o.sections[2].sh_link = 1;
303     o.sections[2].sh_info = 1; // index of first non-local symbol.
304 
305     std::string tmp = ".rodata.";
306     tmp += secname;
307     const char* sec;
308     sec = tmp.c_str();
309     /*sec = ".rodata.rofs"*/
310     o.sections[3].sh_name = add_string(sec);
311     o.sections[3].sh_type = SHT_PROGBITS;
312     o.sections[3].sh_flags = SHF_ALLOC | SHF_MERGE;
313     o.sections[3].sh_offset = sizeof(o);
314     o.sections[3].sh_addralign = 8;
315     o.sections[3].sh_size = static_cast<Elf32_Word>(size_of_section);
316 
317     o.sections[1].sh_name = add_string(".strtab");
318     o.sections[1].sh_type = SHT_STRTAB;
319     o.sections[1].sh_offset = static_cast<Elf32_Off>(sizeof(o) + size_of_section);
320     o.sections[1].sh_addralign = 1;
321     o.sections[1].sh_size = static_cast<Elf32_Word>(dst - stringtable);
322 
323     FILE* e = fopen(fname.c_str(), "wb");
324     if (e == NULL) {
325         printf("Could not open %s.\n", fname.c_str());
326         exit(-1);
327     }
328     fwrite(&o, sizeof(o), 1, e);
329     fwrite(data, size_of_section, 1, e);
330     fwrite(stringtable, (size_t)o.sections[1].sh_size, 1, e);
331     fclose(e);
332 }
333 
334 void write_macho(const std::string& fname, const std::string& secname, size_t size_of_data_, const void* data)
335 {
336 #ifdef __APPLE__
337     // Write fat header for X64_64 and ARM64 object
338     struct fat_header fathdr;
339     fathdr.magic = FAT_CIGAM;
340     fathdr.nfat_arch = htonl(2); // big-endian values in fat header
341 
342     struct fat_arch archs[2] = {
343         {
344             htonl(CPU_TYPE_X86_64),
345             htonl(CPU_SUBTYPE_X86_64_ALL),
346             0,
347             0, // size of data TBD
348             htonl(3)
349         },
350         {
351             htonl(CPU_TYPE_ARM64),
352             htonl(CPU_SUBTYPE_ARM64_ALL),
353             0, // offset,
354             0, // size of data TBD
355             htonl(3)
356         },
357     };
358 
359     size_t fpos = 0; // "file" offsets are actually relative to the architecture header
360     archs[0].offset = htonl(sizeof(fathdr) + sizeof(archs));
361 
362     size_t size_of_section = size_of_data_ + sizeof(uint64_t);
363 
364     struct mach_header_64 x64_header = {
365         MH_MAGIC_64,
366         CPU_TYPE_X86_64,
367         CPU_SUBTYPE_X86_64_ALL,
368         MH_OBJECT,
369         2, // ncmds
370         sizeof(segment_command_64) + sizeof(section_64) + sizeof(symtab_command), // sizeofcmds
371         0, // flags
372         0  // reserved
373     };
374 
375     struct mach_header_64 arm64_header = {
376         MH_MAGIC_64,
377         CPU_TYPE_ARM64,
378         CPU_SUBTYPE_ARM64_ALL,
379         MH_OBJECT,
380         2, // ncmds
381         sizeof(segment_command_64) + sizeof(section_64) + sizeof(symtab_command), // sizeofcmds
382         0, // flags
383         0  // reserved
384     };
385 
386     struct segment_command_64 data_seg = {
387         LC_SEGMENT_64,
388         sizeof(data_seg) + sizeof(section_64),
389         "", // for object files name is empty
390         0, // vmaddress
391         (size_of_section + 7) & ~7, // vmsize aligned to 8 bytes
392         0, // fileoffset = TBD
393         size_of_section, // filesize = TBD
394         VM_PROT_READ, // maxprot
395         VM_PROT_READ, // initprot
396         1, // nsects
397         SG_NORELOC  // flags
398     };
399 
400     struct section_64 data_sect = {
401         "__const",
402         "__DATA",
403         0, // addr
404         size_of_section, // vmsize aligned to 8 bytes
405         0, // offset = TBD
406         3, // alignment = 2^3 = 8 bytes
407         0, // reloff
408         0, // nreloc
409         S_REGULAR, // flags
410         0, // reserved1
411         0, // reserved2
412     };
413 
414     std::string dataName = "_";
415     dataName += gDataName;
416     std::string sizeName = "_";
417     sizeName += gSizeName;
418 
419     uint32_t string_size = dataName.size() + sizeName.size() + 3; // prepending plus two terminating nulls
420 
421     struct symtab_command symtab = {
422         LC_SYMTAB,
423         sizeof(symtab),
424         0, // symoff = TBD
425         2, // nsyms
426         0, // stroff = TBD
427         string_size, // strsize
428     };
429 
430     fpos += sizeof(x64_header) + sizeof(data_seg) + sizeof(data_sect) + sizeof(symtab);
431 
432     data_seg.fileoff = static_cast<uint32_t>(fpos);
433     data_sect.offset = static_cast<uint32_t>(fpos);
434     fpos += size_of_section;
435 
436     size_t sectionAligned = (fpos + 7) & ~7;
437     uint32_t sectionAlign = sectionAligned - fpos;
438     fpos = sectionAligned;
439 
440     symtab.symoff = static_cast<uint32_t>(fpos);
441 
442     struct nlist_64 syms[2] = {
443     {
444         1, // first string
445         N_EXT | N_SECT,
446         1, // segment
447         REFERENCE_FLAG_DEFINED,
448         0
449     },
450     {
451         static_cast<uint32_t>(sizeName.size() + 2), // second string
452         N_EXT | N_SECT,
453         1, // segment
454         REFERENCE_FLAG_DEFINED,
455         8
456     }
457     };
458 
459     fpos += sizeof(syms);
460 
461     symtab.stroff = static_cast<uint32_t>(fpos);
462     fpos += string_size;
463 
464     archs[0].size = htonl(fpos);
465 
466     size_t aligned = (fpos + 7) & ~7;
467     uint32_t align = aligned - fpos;
468     archs[1].offset = htonl(aligned + sizeof(fathdr) + sizeof(archs));
469     archs[1].size = archs[0].size;
470 
471     FILE* e = fopen(fname.c_str(), "wb");
472     if (e == NULL) {
473         printf("Could not open %s.\n", fname.c_str());
474         exit(-1);
475     }
476 
477     fwrite(&fathdr, sizeof(fathdr), 1, e);
478     fwrite(&archs, sizeof(archs), 1, e);
479     fwrite(&x64_header, sizeof(x64_header), 1, e);
480     fwrite(&data_seg, sizeof(data_seg), 1, e);
481     fwrite(&data_sect, sizeof(data_sect), 1, e);
482     fwrite(&symtab, sizeof(symtab), 1, e);
483     fwrite(data, size_of_section, 1, e);
484     for(int i = 0; i < sectionAlign; i++) {
485         fputc(0, e); // alignment byte to begin string table 8 byte boundary
486     }
487     fwrite(&syms, sizeof(syms), 1, e);
488     fputc(0, e); // filler byte to begin string table as it starts indexing at 1
489     fwrite(sizeName.c_str(), sizeName.size() + 1, 1, e);
490     fwrite(dataName.c_str(), dataName.size() + 1, 1, e);
491 
492     for(int i = 0; i < align; i++) {
493         fputc(0, e); // alignment byte to begin arm64 architecture at 8 byte boundary
494     }
495 
496     fwrite(&arm64_header, sizeof(arm64_header), 1, e);
497     fwrite(&data_seg, sizeof(data_seg), 1, e);
498     fwrite(&data_sect, sizeof(data_sect), 1, e);
499     fwrite(&symtab, sizeof(symtab), 1, e);
500     fwrite(data, size_of_section, 1, e);
501     for(int i = 0; i < sectionAlign; i++) {
502         fputc(0, e); // alignment byte to begin string table 8 byte boundary
503     }
504     fwrite(&syms, sizeof(syms), 1, e);
505     fputc(0, e); // filler byte to begin string table
506     fwrite(sizeName.c_str(), sizeName.size() + 1, 1, e);
507     fwrite(dataName.c_str(), dataName.size() + 1, 1, e);
508     fclose(e);
509 #endif
510 }
511 
512 int main(int argc, char* argv[])
513 {
514     std::string obj32Name = "rofs_32.obj";
515     std::string obj64Name = "rofs_64.obj";
516     std::string o32Name = "rofs_32.o";
517     std::string o64Name = "rofs_64.o";
518     std::string x32Name = "rofs_x86.o";
519     std::string x64Name = "rofs_x64.o";
520     std::string macName = "rofs_mac.o";
521     std::string secName = "rofs";
522 
523     bool buildX86 = true, buildX64 = true, buildV7 = true, buildV8 = true;
524     bool buildWindows = true, buildAndroid = true, buildMac = true;
525 
526     std::string inPath = { "" }, roPath = { "" };
527 
528     int baseArg = 0;
529     if (argc >= 2) {
530         if (argv[1][0] == '-') {
531             buildAndroid = false;
532             buildWindows = false;
533             buildMac = false;
534             buildX86 = buildX64 = buildV7 = buildV8 = false;
535         }
536         bool platset = false;
537         for (;;) {
538             if (baseArg + 1 >= argc) {
539                 printf("Invalid argument!\n");
540                 return 0;
541             }
542             if (argv[baseArg + 1][0] != '-')
543                 break;
544             if (strcmp(argv[baseArg + 1], "-linux") == 0) {
545                 platset = true;
546                 buildAndroid = true;
547                 baseArg++;
548             } else if (strcmp(argv[baseArg + 1], "-android") == 0) {
549                 platset = true;
550                 buildAndroid = true;
551                 baseArg++;
552             } else if (strcmp(argv[baseArg + 1], "-windows") == 0) {
553                 platset = true;
554                 buildWindows = true;
555                 baseArg++;
556             } else if (strcmp(argv[baseArg + 1], "-mac") == 0) {
557                 platset = true;
558                 buildMac = true;
559                 baseArg++;
560             } else if (strcmp(argv[baseArg + 1], "-x86") == 0) {
561                 buildX86 = true;
562                 baseArg++;
563             } else if (strcmp(argv[baseArg + 1], "-x86_64") == 0) {
564                 buildAndroid = true;
565                 buildX64 = true;
566                 baseArg++;
567             } else if (strcmp(argv[baseArg + 1], "-x64") == 0) {
568                 buildX64 = true;
569                 buildWindows = true;
570                 baseArg++;
571             } else if (strcmp(argv[baseArg + 1], "-armeabi-v7a") == 0) {
572                 buildV7 = true;
573                 buildAndroid = true;
574                 platset = true;
575                 baseArg++;
576             } else if (strcmp(argv[baseArg + 1], "-arm64-v8a") == 0) {
577                 buildV8 = true;
578                 buildAndroid = true;
579                 platset = true;
580                 baseArg++;
581             } else if (strcmp(argv[baseArg + 1], "-extensions") == 0) {
582                 baseArg++;
583                 if (baseArg + 1 >= argc) {
584                     printf("Invalid argument!\n");
585                     return 0;
586                 }
587                 auto exts = std::string_view(argv[baseArg + 1]);
588                 while (!exts.empty()) {
589                     auto pos = exts.find(';');
590                     pos = std::min(pos, exts.size());
591                     valid_exts.push_back(exts.substr(0, pos));
592                     exts.remove_prefix(std::min(pos + 1, exts.size()));
593                 }
594                 baseArg++;
595             } else {
596                 printf("Invalid argument!\n");
597                 return 0;
598             }
599         }
600         if (!platset) {
601             buildWindows = true;
602             buildAndroid = true;
603             buildMac = true;
604         }
605 
606         if (argc < baseArg + 3) {
607             printf("invalid args");
608             return 0;
609         }
610 
611         inPath = argv[baseArg + 1];
612     } else {
613         printf("Not enough arguments!\n");
614         return 0;
615     }
616     if (argc >= baseArg + 3) {
617         if (argv[baseArg + 2][0] == '/') {
618             roPath = argv[baseArg + 2] + 1;
619         } else {
620             roPath = argv[baseArg + 2];
621         }
622     }
623     if (argc >= baseArg + 5) {
624         gDataName = argv[baseArg + 3];
625         gSizeName = argv[baseArg + 4];
626     }
627     if (argc == baseArg + 6) {
628         secName = obj32Name = obj64Name = o32Name = o64Name = x32Name = x64Name = macName = argv[baseArg + 5];
629         obj32Name += "_32.obj";
630         obj64Name += "_64.obj";
631         o32Name += "_32.o";
632         o64Name += "_64.o";
633         x32Name += "_x86.o";
634         x64Name += "_x64.o";
635         macName += "_mac.o";
636     }
637     add_directory(inPath, roPath);
638 
639     // fix offsets
640     size_t base_offset = sizeof(fs_entry) * (directory.size() + 1);
641     for (auto& d : directory) {
642         d.offset += base_offset;
643     }
644 
645     // add terminator
646     directory.push_back({ { 0 }, 0, 0 });
647 
648     const size_t size_of_dir = (sizeof(fs_entry) * directory.size());
649     const size_t size_of_data = bin.size() + size_of_dir;
650     uint8_t* data = new uint8_t[size_of_data + sizeof(uint64_t)];
651     *(uint64_t*)(data) = size_of_data;
652     memcpy(data + sizeof(uint64_t), directory.data(), size_of_dir);
653     memcpy(data + sizeof(uint64_t) + size_of_dir, bin.data(), bin.size());
654 
655     // Build obj
656     if (buildWindows) {
657         if (buildX86) {
658             write_obj(obj32Name, secName, size_of_data, data, false);
659         }
660         if (buildX64) {
661             write_obj(obj64Name, secName, size_of_data, data, true);
662         }
663     }
664     // Create .elf (.o)
665     if (buildAndroid) {
666         struct {
667             Elf32_Ehdr head{};
668             Elf32_Shdr sections[4]{};
669             Elf32_Sym symbs[3]{};
670         } o32;
671         struct {
672             Elf64_Ehdr head{};
673             Elf64_Shdr sections[4]{};
674             Elf64_Sym symbs[3]{};
675         } o64;
676         if (buildV7) {
677             write_elf(o32, EM_ARM, o32Name, secName, size_of_data, data);
678         }
679         if (buildV8) {
680             write_elf(o64, EM_AARCH64, o64Name, secName, size_of_data, data);
681         }
682         if (buildX86) {
683             write_elf(o32, EM_386, x32Name, secName, size_of_data, data);
684         }
685         if (buildX64) {
686             write_elf(o64, EM_X86_64, x64Name, secName, size_of_data, data);
687         }
688     }
689     // Create mach-o (.o)
690     if (buildMac) {
691         write_macho(macName, secName, size_of_data, data);
692     }
693 }
694