• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 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 
16 #include <cstring>
17 #include <cinttypes>
18 #include <cstddef>
19 #include <algorithm>
20 #include <array>
21 #include <memory>
22 #include <string>
23 #include <string_view>
24 #include <vector>
25 #include "app.h"
26 #include "dir.h"
27 #include "coff.h"
28 #include "elf32.h"
29 #include "elf64.h"
30 #include "platform.h"
31 #include "toarray.h"
32 
33 #ifdef __APPLE__
34 #include <mach-o/loader.h>
35 #include <mach-o/nlist.h>
36 #include <mach-o/fat.h>
37 #else
38 #include "maco.h"
39 #endif
40 
41 namespace {
42 struct FsEntry {
43     char fname[256];
44     uint64_t offset;
45     uint64_t size;
46 };
47 
48 std::vector<FsEntry> g_directory;
49 std::vector<uint8_t> g_bin;
50 std::vector<std::string_view> g_validExts;
51 
AddFile(const std::string & filename,const std::string & storename)52 bool AddFile(const std::string& filename, const std::string& storename)
53 {
54     std::string_view ext;
55     auto pos = filename.find_last_of('.');
56     if (pos != std::string::npos) {
57         // found it.
58         ext = std::string_view(filename).substr(pos);
59     }
60     bool valid = false;
61     for (const auto& e : g_validExts) {
62         if (ext.compare(e) == 0) {
63             valid = true;
64             break;
65         }
66     }
67     if (!valid) {
68         printf("Skipped %s\n", storename.c_str());
69         return true;
70     }
71     if (storename.size() > (sizeof(FsEntry::fname) - 1u)) {
72         printf("Filename too long [%s]\n", storename.c_str());
73         return false;
74     }
75     FsEntry tmp{};
76     tmp.fname[storename.copy(tmp.fname, sizeof(tmp.fname) - 1U)] = '\0';
77 #if defined(_WIN32) && (_WIN32)
78     struct _stat64 fileStat;
79     if (_stat64(filename.c_str(), &fileStat) == -1) {
80 #else
81     struct stat fileStat;
82     if (stat(filename.c_str(), &fileStat) == -1) {
83 #endif
84         printf("File [%s] not found\n", tmp.fname);
85         return false;
86     }
87     tmp.size = fileStat.st_size;
88     auto padding = (8 - (g_bin.size() & 7)) & 7;
89     tmp.offset = g_bin.size() + padding;
90     g_directory.push_back(tmp);
91     FILE* f = fopen(filename.c_str(), "rb");
92     if (f == nullptr) {
93         printf("Could not open %s.\n", filename.c_str());
94         return false;
95     }
96     g_bin.resize(size_t(g_bin.size() + padding + tmp.size));
97     fread(g_bin.data() + tmp.offset, 1, size_t(tmp.size), f);
98     fclose(f);
99     printf("Stored: %s [%" PRIu64 " , %" PRIu64 "]\n", tmp.fname, tmp.offset, tmp.size);
100     return true;
101 }
102 
103 bool AddDirectory(const std::string& path, const std::string& outpath)
104 {
105     struct dirent* pDirent = nullptr;
106     DIR* pDir = opendir(path.c_str());
107     if (pDir == nullptr) {
108         printf("Cannot open directory '%s'\n", path.c_str());
109         return false;
110     }
111     std::string p = path;
112     if (!p.empty()) {
113         if (p.back() != '/') {
114             p += '/';
115         }
116     }
117     std::string op = outpath;
118     if (!op.empty()) {
119         if (op.back() != '/') {
120             op += '/';
121         }
122     }
123     std::vector<dirent> entries;
124     while ((pDirent = readdir(pDir)) != nullptr) {
125         if (pDirent->d_type == DT_DIR) {
126             if (pDirent->d_name[0] == '.') {
127                 continue;
128             }
129         }
130         entries.push_back(*pDirent);
131     }
132     std::sort(entries.begin(), entries.end(), [](const dirent& lhs, const dirent& rhs) {
133         if (lhs.d_type < rhs.d_type) {
134             return true;
135         }
136         if (lhs.d_type > rhs.d_type) {
137             return false;
138         }
139         return strcmp(lhs.d_name, rhs.d_name) < 0;
140     });
141     for (auto& pDirent1 : entries) {
142         if (pDirent1.d_type == DT_DIR) {
143             AddDirectory(p + pDirent1.d_name, op + pDirent1.d_name);
144 
145         } else if (!AddFile(p + pDirent1.d_name, op + pDirent1.d_name)) {
146             return false;
147         }
148     }
149     closedir(pDir);
150     return true;
151 }
152 
153 /*
154  *   //Pseudo code for accessing files in blob
155  *   struct FsEntry
156  *   {
157  *       char fname[256];
158  *       uint64_t offset;
159  *       uint64_t size;
160  *   };
161  *   extern "C" uint64_t SizeOfDataForReadOnlyFileSystem;
162  *   extern "C" struct FsEntry BinaryDataForReadOnlyFileSystem[];
163  *   void dump_files()
164  *   {
165  *       for (int i = 0; i < SizeOfDataForReadOnlyFileSystem; i++)
166  *       {
167  *           if (BinaryDataForReadOnlyFileSystem[i].fname[0] == 0) break;
168  *           printf("%s\n", BinaryDataForReadOnlyFileSystem[i].fname);
169  *           char* data = (char*)(BinaryDataForReadOnlyFileSystem[i].offset +
170  * (uintptr_t)BinaryDataForReadOnlyFileSystem); printf("%lld\n", BinaryDataForReadOnlyFileSystem[i].offset);
171  *       }
172  *   }
173  */
174 
175 std::string g_dataName = "BinaryDataForReadOnlyFileSystem";
176 std::string g_sizeName = "SizeOfDataForReadOnlyFileSystem";
177 
178 #pragma pack(push, 1)
179 // Using headers and defines from winnt.h
180 struct ObjFile {
181     IMAGE_FILE_HEADER coffHead;
182     IMAGE_SECTION_HEADER sections[1];
183     IMAGE_SYMBOL symtab[2];
184 };
185 #pragma pack(pop)
186 
187 struct StringTable {
188     char table[256u]{};
189     char* dst = table;
190     StringTable() : dst(table) {}
191     uint32_t addString(std::string_view a)
192     {
193         const auto offset = dst - table;
194         dst += a.copy(dst, sizeof(table) - offset);
195         *dst++ = '\0';
196         return static_cast<uint32_t>(offset);
197     }
198     ptrdiff_t GetOffset() const
199     {
200         return dst - table;
201     }
202 };
203 
204 template<typename T>
205 void FillCoffHead(T& obj, bool x64)
206 {
207     // fill coff header.
208     obj.coffHead.Machine = (!x64) ? IMAGE_FILE_MACHINE_I386 : IMAGE_FILE_MACHINE_AMD64;
209     obj.coffHead.NumberOfSections = sizeof(obj.sections) / sizeof(IMAGE_SECTION_HEADER);
210     obj.coffHead.TimeDateStamp = 0; // duh.
211     obj.coffHead.PointerToSymbolTable = offsetof(T, symtab);
212     obj.coffHead.NumberOfSymbols = sizeof(obj.symtab) / sizeof(IMAGE_SYMBOL);
213     obj.coffHead.SizeOfOptionalHeader = 0;
214     obj.coffHead.Characteristics = 0; // if x86 use IMAGE_FILE_32BIT_MACHINE ?
215 }
216 
217 template<typename T>
218 size_t FillCoffSymbtable(T& obj, StringTable& stringtable, bool x64)
219 {
220     if (!x64) {
221         // in win32 the symbols have extra "_" ?
222         std::string t = "_";
223         t += g_dataName;
224         std::string t2 = "_";
225         t2 += g_sizeName;
226         obj.symtab[1].N.Name.Long = stringtable.addString(t);
227         obj.symtab[0].N.Name.Long = stringtable.addString(t2);
228     } else {
229         obj.symtab[1].N.Name.Long = stringtable.addString(g_dataName);
230         obj.symtab[0].N.Name.Long = stringtable.addString(g_sizeName);
231     }
232 
233     const uint32_t stringTableSize = uint32_t(stringtable.GetOffset());
234     *reinterpret_cast<uint32_t*>(stringtable.table) = stringTableSize;
235     return stringTableSize;
236 }
237 
238 template<typename T>
239 void FillCoffSectionAndSymtable(
240     T& obj, size_t stringTableSize, const size_t sizeOfSection, const std::string& secname, bool x64)
241 {
242     // fill the section.
243     secname.copy(reinterpret_cast<char*>(obj.sections[0].Name), sizeof(obj.sections[0].Name));
244     obj.sections[0].Misc.VirtualSize = 0;
245     obj.sections[0].VirtualAddress = 0;
246     obj.sections[0].SizeOfRawData = uint32_t(sizeOfSection); // sizeof the data on disk.
247     obj.sections[0].PointerToRawData = static_cast<uint32_t>(
248         ((sizeof(T) + stringTableSize + 3) / 4) * 4); // DWORD align the data directly after the headers..
249     obj.sections[0].PointerToLinenumbers = 0;
250     obj.sections[0].NumberOfRelocations = 0;
251     obj.sections[0].NumberOfLinenumbers = 0;
252     obj.sections[0].Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_MEM_READ;
253     // fill symbols
254     obj.symtab[1].Value = uint32_t(sizeof(uint64_t));
255     obj.symtab[1].SectionNumber = 1; // first section.. (one based)
256     obj.symtab[1].Type = IMAGE_SYM_TYPE_CHAR | (IMAGE_SYM_DTYPE_ARRAY << 8);
257     obj.symtab[1].StorageClass = IMAGE_SYM_CLASS_EXTERNAL;
258     obj.symtab[1].NumberOfAuxSymbols = 0;
259 
260     obj.symtab[0].Value = uint32_t(0u);
261     obj.symtab[0].SectionNumber = 1; // first section.. (one based)
262     // obj.symtab[0].Type = IMAGE_SYM_TYPE_UINT;  //(just use IMAGE_SYM_TYPE_NULL like mstools?)
263     obj.symtab[0].StorageClass = IMAGE_SYM_CLASS_EXTERNAL;
264     obj.symtab[0].NumberOfAuxSymbols = 0;
265 }
266 
267 bool WriteObj(const std::string& fname, const std::string& secname, size_t sizeOfData, const void* data, bool x64)
268 {
269     const size_t sizeOfSection = sizeof(uint64_t) + sizeOfData;
270     ObjFile obj{};
271     StringTable stringtable;
272     stringtable.dst += sizeof(uint32_t); // leave space for the size of the table as uint32
273 
274     FillCoffHead(obj, x64);
275     size_t stringTableSize = FillCoffSymbtable(obj, stringtable, x64);
276     FillCoffSectionAndSymtable(obj, static_cast<size_t>(stringTableSize), sizeOfSection, secname, x64);
277 
278     FILE* d = fopen(fname.c_str(), "wb");
279     if (d == nullptr) {
280         printf("Could not open %s.\n", fname.c_str());
281         return false;
282     }
283     // write headers
284     fwrite(&obj, sizeof(obj), 1u, d);
285     // write string table
286     fwrite(stringtable.table, stringTableSize, 1u, d);
287     // write sections..
288     size_t p = ftell(d);
289     uint32_t pad = 0;
290     size_t padcount = obj.sections[0].PointerToRawData - p;
291     fwrite(&pad, padcount, 1u, d);
292     fwrite(data, sizeOfSection, 1u, d);
293     fclose(d);
294 
295     return true;
296 }
297 
298 struct Elf32Bit {
299     Elf32_Ehdr head;
300     Elf32_Shdr sections[4];
301     Elf32_Sym symbs[3];
302 };
303 
304 struct Elf64Bit {
305     Elf64_Ehdr head;
306     Elf64_Shdr sections[4];
307     Elf64_Sym symbs[3];
308 };
309 
310 template<class Type>
311 void FillElfHead(Type& o, uint8_t arch)
312 {
313     o.head.e_type = ET_REL;
314     o.head.e_machine = arch; // machine id..
315     o.head.e_version = EV_CURRENT;
316     o.head.e_ehsize = sizeof(o.head);
317     o.head.e_shentsize = sizeof(o.sections[0]);
318     o.head.e_shnum = sizeof(o.sections) / sizeof(o.sections[0]);
319     o.head.e_shoff = sizeof(o.head);
320     o.head.e_shstrndx = 1;
321 }
322 
323 template<class Type>
324 void FillElfSectionAndSymbtable(Type& o, uint8_t arch, StringTable& stringtable, size_t sizeOfData)
325 {
326     // create symbols
327     o.symbs[2].st_name = stringtable.addString(g_dataName); // ?BinaryDataForReadOnlyFileSystem@@3PADA");
328     o.symbs[2].st_value = sizeof(uint64_t);
329     o.symbs[2].st_size = static_cast<Elf32_Word>(sizeOfData);
330     o.symbs[2].st_info = o.symbs[1].st_info = ELF_ST_INFO(STB_GLOBAL, STT_OBJECT);
331     o.symbs[2].st_other = o.symbs[1].st_other = STV_HIDDEN;
332     o.symbs[2].st_shndx = o.symbs[1].st_shndx = 3;
333 
334     o.symbs[1].st_name = stringtable.addString(g_sizeName);
335     o.symbs[1].st_value = 0;
336     o.symbs[1].st_size = sizeof(uint64_t);
337 
338     o.sections[2].sh_name = stringtable.addString(".symtab");
339     o.sections[2].sh_type = SHT_SYMTAB;
340     o.sections[2].sh_offset = offsetof(Type, symbs); // sizeof(o) + sizeOfSection + stringtable_size;
341     o.sections[2].sh_addralign = 8;
342     o.sections[2].sh_size = sizeof(o.symbs);
343     o.sections[2].sh_entsize = sizeof(o.symbs[0]);
344     o.sections[2].sh_link = 1;
345     o.sections[2].sh_info = 1; // index of first non-local symbol.
346 }
347 
348 template<class Type>
349 void FillElfSection(Type& o, uint8_t arch, const std::string& secname, StringTable& stringtable, size_t sizeOfSection)
350 {
351     std::string tmp = ".rodata.";
352     tmp += secname;
353 
354     o.sections[3].sh_name = stringtable.addString(tmp.data());
355     o.sections[3].sh_type = SHT_PROGBITS;
356     o.sections[3].sh_flags = SHF_ALLOC | SHF_MERGE;
357     o.sections[3].sh_offset = sizeof(o);
358     o.sections[3].sh_addralign = 8;
359     o.sections[3].sh_size = static_cast<Elf32_Word>(sizeOfSection);
360 
361     o.sections[1].sh_name = stringtable.addString(".strtab");
362     o.sections[1].sh_type = SHT_STRTAB;
363     o.sections[1].sh_offset = static_cast<Elf32_Off>(sizeof(o) + sizeOfSection);
364     o.sections[1].sh_addralign = 1;
365     o.sections[1].sh_size = static_cast<Elf32_Word>(stringtable.GetOffset());
366 }
367 
368 template<class Type>
369 bool WriteElf(uint8_t arch, const std::string& fname, const std::string& secname, size_t sizeOfData, const void* data)
370 {
371     const size_t sizeOfSection = sizeOfData + sizeof(uint64_t);
372     Type o{};
373     FillElfHead(o, arch);
374 
375     // initialize stringtable.
376     StringTable stringtable;
377     *(stringtable.dst) = '\0';
378     ++(stringtable.dst);
379 
380     FillElfSectionAndSymbtable(o, arch, stringtable, sizeOfData);
381 
382     FillElfSection(o, arch, secname, stringtable, sizeOfSection);
383 
384     FILE* e = fopen(fname.c_str(), "wb");
385     if (e == nullptr) {
386         printf("Could not open %s.\n", fname.c_str());
387         return false;
388     }
389     fwrite(&o, sizeof(o), 1, e);
390     fwrite(data, sizeOfSection, 1, e);
391     fwrite(stringtable.table, size_t(o.sections[1].sh_size), 1, e);
392     fclose(e);
393     return true;
394 }
395 
396 // Note: Assumes power of two alignment.
397 size_t GetAligned(size_t offset, size_t alignment)
398 {
399     return (offset + (alignment - 1)) & ~(alignment - 1);
400 }
401 
402 auto MachoHeader()
403 {
404     fat_header fathdr;
405     fathdr.magic = FAT_CIGAM;
406     fathdr.nfat_arch = platform_htonl(2); // big-endian values in fat header
407     return fathdr;
408 }
409 
410 auto MachoArchs(uint32_t fatAlignmentPowerOfTwo)
411 {
412     using namespace std;
413     fat_arch archs[]{
414         {
415             static_cast<cpu_type_t>(platform_htonl(CPU_TYPE_X86_64)),
416             static_cast<cpu_subtype_t>(platform_htonl(CPU_SUBTYPE_X86_64_ALL)),
417             0, // offset
418             0, // size of data
419             platform_htonl(fatAlignmentPowerOfTwo),
420         },
421         {
422             static_cast<cpu_type_t>(platform_htonl(CPU_TYPE_ARM64)),
423             static_cast<cpu_subtype_t>(platform_htonl(CPU_SUBTYPE_ARM64_ALL)),
424             0, // offset,
425             0, // size of data
426             platform_htonl(fatAlignmentPowerOfTwo),
427         },
428     };
429 
430     return to_array(archs);
431 }
432 
433 auto MachoX64Header()
434 {
435     mach_header_64 x64_header = {
436         MH_MAGIC_64, static_cast<cpu_type_t>(CPU_TYPE_X86_64), static_cast<cpu_subtype_t>(CPU_SUBTYPE_X86_64_ALL),
437         MH_OBJECT,
438         2,                                                                        // ncmds
439         sizeof(segment_command_64) + sizeof(section_64) + sizeof(symtab_command), // sizeofcmds
440         0,                                                                        // flags
441         0                                                                         // reserved
442     };
443 
444     return x64_header;
445 }
446 
447 auto MachoARM64Header()
448 {
449     mach_header_64 arm64_header = {
450         MH_MAGIC_64, static_cast<cpu_type_t>(CPU_TYPE_ARM64), static_cast<cpu_subtype_t>(CPU_SUBTYPE_ARM64_ALL),
451         MH_OBJECT,
452         2,                                                                        // ncmds
453         sizeof(segment_command_64) + sizeof(section_64) + sizeof(symtab_command), // sizeofcmds
454         0,                                                                        // flags
455         0                                                                         // reserved
456     };
457 
458     return arm64_header;
459 }
460 
461 auto MachoSegmentCommand64(size_t& sizeOfSection)
462 {
463     segment_command_64 data_seg = {
464         LC_SEGMENT_64, sizeof(data_seg) + sizeof(section_64),
465         "",                       // for object files name is empty
466         0,                        // vmaddress
467         (sizeOfSection + 7) & ~7, // vmsize aligned to 8 bytes
468         0,                        // fileoffset
469         sizeOfSection,            // filesize
470         VM_PROT_READ,             // maxprot
471         VM_PROT_READ,             // initprot
472         1,                        // nsects
473         SG_NORELOC                // flags
474     };
475 
476     return data_seg;
477 }
478 
479 auto MachoDataSection(size_t& sizeOfSection)
480 {
481     section_64 data_sect = {
482         "__const", "__DATA",
483         0,             // addr
484         sizeOfSection, // vmsize aligned to 8 bytes
485         0,             // offset
486         3,             // alignment = 2^3 = 8 bytes
487         0,             // reloff
488         0,             // nreloc
489         S_REGULAR,     // flags
490         0,             // reserved1
491         0,             // reserved2
492     };
493 
494     return data_sect;
495 }
496 
497 auto MachoSymbolTable(uint32_t& string_size)
498 {
499     symtab_command symtab = {
500         LC_SYMTAB, sizeof(symtab),
501         0,           // symoff
502         2,           // nsyms
503         0,           // stroff
504         string_size, // strsize
505     };
506 
507     return symtab;
508 }
509 
510 auto MachoList(std::string& sizeName)
511 {
512     using namespace std;
513     nlist_64 ret[]{
514         {
515             1, // first string
516             N_EXT | N_SECT,
517             1, // segment
518             REFERENCE_FLAG_DEFINED,
519             0,
520         },
521         {
522             static_cast<uint32_t>(sizeName.size() + 2), // second string
523             N_EXT | N_SECT,
524             1, // segment
525             REFERENCE_FLAG_DEFINED,
526             8,
527         },
528     };
529 
530     return to_array(ret);
531 }
532 
533 struct MachoPaddingInfo {
534     uint32_t offset0;
535     uint32_t offsetAligned0;
536     uint32_t padding0;
537 
538     uint32_t offset1;
539     uint32_t offsetAligned1;
540     uint32_t padding1;
541 };
542 
543 auto MachoUpdate(fat_header& fathdr, fat_arch* archs, uint32_t fatAlignment, size_t fpos)
544 {
545     const uint32_t sliceOffset0 = static_cast<uint32_t>(sizeof(fathdr) + sizeof(archs)); // initial headers
546     const uint32_t sliceOffsetAligned0 = static_cast<uint32_t>(GetAligned(sliceOffset0, fatAlignment));
547     const uint32_t slicePadding0 = sliceOffsetAligned0 - sliceOffset0;
548 
549     archs[0].offset = platform_htonl(sliceOffsetAligned0);
550     archs[0].size = platform_htonl(static_cast<uint32_t>(fpos));
551 
552     const uint32_t sliceOffset1 = static_cast<uint32_t>(sliceOffsetAligned0 + fpos);
553     const uint32_t sliceOffsetAligned1 = static_cast<uint32_t>(GetAligned(sliceOffset1, fatAlignment));
554     const uint32_t slicePadding1 = sliceOffsetAligned1 - sliceOffset1;
555 
556     archs[1].offset = platform_htonl(sliceOffsetAligned1);
557     archs[1].size = platform_htonl(static_cast<uint32_t>(fpos));
558 
559     return MachoPaddingInfo{ sliceOffset0, sliceOffsetAligned0, slicePadding0, sliceOffset1, sliceOffsetAligned1,
560         slicePadding1 };
561 }
562 
563 struct Macho {
564     fat_header fathdr;
565     std::array<fat_arch, 2> archs; // 2: size
566     mach_header_64 x64_header;
567     mach_header_64 arm64_header;
568     segment_command_64 data_seg;
569     section_64 data_sect;
570     symtab_command symtab;
571     std::array<nlist_64, 2> syms; // 2: size
572 };
573 
574 template<typename T1>
575 void MachoWriteArchitechtureData(size_t padding, size_t sizeOfSection, size_t sectionAlign, const std::string& dataName,
576     const std::string& sizeName, const void* data, const T1& sect, const mach_header_64& header, FILE* e)
577 {
578     for (size_t i = 0; i < padding; i++) {
579         fputc(0, e);
580     }
581 
582     fwrite(&header, sizeof(header), 1, e);
583     fwrite(&sect.data_seg, sizeof(sect.data_seg), 1, e);
584     fwrite(&sect.data_sect, sizeof(sect.data_sect), 1, e);
585     fwrite(&sect.symtab, sizeof(sect.symtab), 1, e);
586     fwrite(data, sizeOfSection, 1, e);
587     for (size_t i = 0; i < sectionAlign; i++) {
588         fputc(0, e); // alignment byte to begin string table 8 byte boundary
589     }
590     fwrite(&sect.syms, sizeof(sect.syms), 1, e);
591     fputc(0, e); // filler byte to begin string table as it starts indexing at 1
592     fwrite(sizeName.c_str(), sizeName.size() + 1, 1, e);
593     fwrite(dataName.c_str(), dataName.size() + 1, 1, e);
594 }
595 
596 template<typename T1>
597 bool MachoWriteFile(const std::string& fname, const T1& sect, const MachoPaddingInfo& slices,
598     const std::string& dataName, const std::string& sizeName, const void* data, size_t endPadding, size_t sectionAlign,
599     size_t sizeOfSection)
600 {
601     FILE* e = fopen(fname.c_str(), "wb");
602     if (e == nullptr) {
603         printf("Could not open %s.\n", fname.c_str());
604         return false;
605     }
606 
607     fwrite(&sect.fathdr, sizeof(sect.fathdr), 1, e);
608     fwrite(&sect.archs, sizeof(sect.archs), 1, e);
609     MachoWriteArchitechtureData(
610         slices.padding0, sizeOfSection, sectionAlign, dataName, sizeName, data, sect, sect.x64_header, e);
611     MachoWriteArchitechtureData(
612         slices.padding0, sizeOfSection, sectionAlign, dataName, sizeName, data, sect, sect.arm64_header, e);
613     for (uint32_t i = 0; i < endPadding; i++) {
614         fputc(0, e);
615     }
616 
617     fclose(e);
618 
619     return true;
620 }
621 
622 std::string PrefixUnderscore(std::string& input)
623 {
624     std::string tmp = "_";
625     tmp += input;
626     return tmp;
627 }
628 
629 size_t CalculateAlignment(std::size_t& fpos, uint32_t aligment)
630 {
631     size_t sectionAligned = GetAligned(static_cast<uint32_t>(fpos), aligment);
632     size_t sectionAlign = sectionAligned - fpos;
633     fpos = sectionAligned;
634     return sectionAlign;
635 }
636 
637 bool WriteMacho(const std::string& fname, const std::string& secname, size_t sizeOfData, const void* data)
638 {
639     // The fat slices need to be page aligned (to 16k pages), 16k alignment = 2^14
640     constexpr uint32_t fatAlignmentPowerOfTwo = 14, fatAlignment = 1 << fatAlignmentPowerOfTwo;
641 
642     Macho sect;
643     sect.fathdr = MachoHeader();
644     sect.archs = MachoArchs(fatAlignmentPowerOfTwo);
645 
646     // "file" offsets are actually relative to the architecture header
647     size_t fpos = 0, sizeOfSection = sizeOfData + sizeof(uint64_t);
648 
649     sect.x64_header = MachoX64Header(), sect.arm64_header = MachoARM64Header();
650     sect.data_seg = MachoSegmentCommand64(sizeOfSection);
651     sect.data_sect = MachoDataSection(sizeOfSection);
652 
653     std::string dataName = PrefixUnderscore(g_dataName), sizeName = PrefixUnderscore(g_sizeName);
654 
655     uint32_t string_size =
656         static_cast<uint32_t>(dataName.size() + sizeName.size() + 3); // prepending plus two terminating nulls
657 
658     sect.symtab = MachoSymbolTable(string_size);
659 
660     fpos += sizeof(sect.x64_header) + sizeof(sect.data_seg) + sizeof(sect.data_sect) + sizeof(sect.symtab);
661 
662     sect.data_seg.fileoff = sect.data_sect.offset = static_cast<uint32_t>(fpos);
663     fpos += sizeOfSection;
664 
665     size_t sectionAlign = CalculateAlignment(fpos, 8);
666     sect.symtab.symoff = static_cast<uint32_t>(fpos);
667     auto syms = MachoList(sizeName);
668     fpos += sizeof(syms);
669 
670     sect.symtab.stroff = static_cast<uint32_t>(fpos);
671     fpos += string_size;
672 
673     auto slices = MachoUpdate(sect.fathdr, sect.archs.data(), fatAlignment, fpos);
674     const size_t endPadding = GetAligned(slices.offsetAligned1 + fpos, fatAlignment);
675 
676     return MachoWriteFile(fname, sect, slices, dataName, sizeName, data, endPadding, sectionAlign, sizeOfSection);
677 }
678 
679 enum Arguments {
680     EXECUTABLE,
681     SOURCE_DIR = 1,
682     TARGET_DIR = 2,
683     ROFS_DATA_NAME = 3,
684     ROFS_SIZE_NAME = 4,
685     FILE_NAME = 5,
686 };
687 enum Arch : uint32_t {
688     BUILD_X86 = (1 << 0),
689     BUILD_X64 = (1 << 1),
690     BUILD_V7 = (1 << 2),
691     BUILD_V8 = (1 << 3),
692 };
693 enum Plat : uint32_t {
694     WINDOWS = (1 << 4),
695     PLATFORM_AD = (1 << 5),
696     MAC = (1 << 6),
697 };
698 
699 constexpr uint32_t PlatformSet()
700 {
701     return 1U << 31U;
702 }
703 
704 constexpr std::pair<std::string_view, uint32_t> PLATFORMS[] = {
705     { "-linux", PLATFORM_AD | PlatformSet() },
706     { "-windows", WINDOWS | PlatformSet() },
707     { "-mac", MAC | PlatformSet() },
708     { "-x86", BUILD_X86 },
709     { "-x86_64", PLATFORM_AD | BUILD_X64 },
710     { "-x64", WINDOWS | BUILD_X64 },
711     { "-armeabi-v7a", PLATFORM_AD | BUILD_V7 | PlatformSet() },
712     { "-arm64-v8a", PLATFORM_AD | BUILD_V8 | PlatformSet() },
713 };
714 
715 void ParseExtensions(std::string_view exts, std::vector<std::string_view>& extensions)
716 {
717     while (!exts.empty()) {
718         auto pos = exts.find(';');
719         pos = std::min(pos, exts.size());
720         extensions.push_back(exts.substr(0, pos));
721         exts.remove_prefix(std::min(pos + 1, exts.size()));
722     }
723 }
724 
725 int ParseArcAndPlat(uint32_t& arcAndPlat, const int argc, char* argv[])
726 {
727     arcAndPlat = argv[1][0] == '-' ? 0u : arcAndPlat;
728 
729     int baseArg = 0;
730     bool platset = false;
731     for (;;) {
732         if (baseArg + 1 >= argc) {
733             printf("Invalid argument!\n");
734             return -1;
735         }
736         if (argv[baseArg + 1][0] != '-') {
737             break;
738         }
739 
740         if (auto pos = std::find_if(std::begin(PLATFORMS), std::end(PLATFORMS),
741                 [argument = argv[baseArg + 1]](
742                     const std::pair<std::string_view, uint32_t>& item) { return item.first == argument; });
743             pos != std::end(PLATFORMS)) {
744             arcAndPlat |= pos->second & (~PlatformSet());
745             platset |= (pos->second & PlatformSet()) != 0;
746             baseArg++;
747         } else if (strcmp(argv[baseArg + 1], "-extensions") == 0) {
748             baseArg++;
749             if (baseArg + 1 >= argc) {
750                 printf("Invalid argument!\n");
751                 return -1;
752             }
753             auto exts = std::string_view(argv[baseArg + 1]);
754             ParseExtensions(exts, g_validExts);
755             baseArg++;
756         } else {
757             printf("Invalid argument!\n");
758             return -1;
759         }
760     }
761 
762     arcAndPlat |= (platset ? 0 : (WINDOWS | PLATFORM_AD | MAC));
763     return baseArg;
764 }
765 } // namespace
766 
app_main(int argc,char * argv[])767 int app_main(int argc, char* argv[])
768 {
769     if (argc <= 1) {
770         printf("Not enough arguments!\n");
771         return -1;
772     }
773 
774     uint32_t arcAndPlat = (BUILD_X86 | BUILD_X64 | BUILD_V7 | BUILD_V8) | (WINDOWS | PLATFORM_AD | MAC);
775     int baseArg = ParseArcAndPlat(arcAndPlat, argc, argv);
776     if (baseArg < 0) {
777         return -1;
778     }
779     if (argc < (baseArg + ROFS_DATA_NAME)) {
780         printf("invalid args");
781         return -1;
782     }
783     std::string inPath = argv[baseArg + SOURCE_DIR];
784 
785     std::string roPath;
786     if (argv[baseArg + TARGET_DIR][0] == '/') {
787         roPath = argv[baseArg + TARGET_DIR] + 1;
788     } else {
789         roPath = argv[baseArg + TARGET_DIR];
790     }
791 
792     if (argc > (baseArg + ROFS_SIZE_NAME)) {
793         g_dataName = argv[baseArg + ROFS_DATA_NAME];
794         g_sizeName = argv[baseArg + ROFS_SIZE_NAME];
795     }
796 
797     std::string obj32Name = "rofs_32.obj";
798     std::string obj64Name = "rofs_64.obj";
799     std::string o32Name = "rofs_32.o";
800     std::string o64Name = "rofs_64.o";
801     std::string x32Name = "rofs_x86.o";
802     std::string x64Name = "rofs_x64.o";
803     std::string macName = "rofs_mac.o";
804     std::string secName = "rofs";
805 
806     if (argc > (baseArg + FILE_NAME)) {
807         secName = obj32Name = obj64Name = o32Name = o64Name = x32Name = x64Name = macName = argv[baseArg + FILE_NAME];
808         obj32Name += "_32.obj";
809         obj64Name += "_64.obj";
810         o32Name += "_32.o";
811         o64Name += "_64.o";
812         x32Name += "_x86.o";
813         x64Name += "_x64.o";
814         macName += "_mac.o";
815     }
816     if (!AddDirectory(inPath, roPath)) {
817         return -1;
818     }
819 
820     // fix offsets
821     size_t baseOffset = sizeof(FsEntry) * (g_directory.size() + 1);
822     for (auto& d : g_directory) {
823         d.offset += baseOffset;
824     }
825 
826     // add terminator
827     g_directory.push_back({ { 0 }, 0, 0 });
828 
829     const size_t sizeOfDir = (sizeof(FsEntry) * g_directory.size());
830     const size_t sizeOfData = g_bin.size() + sizeOfDir;
831     auto data = std::make_unique<uint8_t[]>(sizeOfData + sizeof(uint64_t));
832     *reinterpret_cast<uint64_t*>(data.get()) = sizeOfData;
833     std::copy(g_directory.cbegin(), g_directory.cend(), reinterpret_cast<FsEntry*>(data.get() + sizeof(uint64_t)));
834     std::copy(g_bin.cbegin(), g_bin.cend(), data.get() + sizeof(uint64_t) + sizeOfDir);
835 
836     // Build obj
837     if (arcAndPlat & WINDOWS) {
838         if (arcAndPlat & BUILD_X86) {
839             if (!WriteObj(obj32Name, secName, sizeOfData, data.get(), false)) {
840                 return -1;
841             }
842         }
843         if (arcAndPlat & BUILD_X64) {
844             if (!WriteObj(obj64Name, secName, sizeOfData, data.get(), true)) {
845                 return -1;
846             }
847         }
848     }
849     // Create .elf (.o)
850     if (arcAndPlat & PLATFORM_AD) {
851         if (arcAndPlat & BUILD_V7) {
852             if (!WriteElf<Elf32Bit>(EM_ARM, o32Name, secName, sizeOfData, data.get())) {
853                 return -1;
854             }
855         }
856         if (arcAndPlat & BUILD_V8) {
857             if (!WriteElf<Elf64Bit>(EM_AARCH64, o64Name, secName, sizeOfData, data.get())) {
858                 return -1;
859             }
860         }
861         if (arcAndPlat & BUILD_X86) {
862             if (!WriteElf<Elf32Bit>(EM_386, x32Name, secName, sizeOfData, data.get())) {
863                 return -1;
864             }
865         }
866         if (arcAndPlat & BUILD_X64) {
867             if (!WriteElf<Elf64Bit>(EM_X86_64, x64Name, secName, sizeOfData, data.get())) {
868                 return -1;
869             }
870         }
871     }
872     // Create mach-o (.o)
873     if (arcAndPlat & MAC) {
874         if (!WriteMacho(macName, secName, sizeOfData, data.get())) {
875             return -1;
876         }
877     }
878     return 0;
879 }
880