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(§.data_seg, sizeof(sect.data_seg), 1, e);
584 fwrite(§.data_sect, sizeof(sect.data_sect), 1, e);
585 fwrite(§.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(§.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(§.fathdr, sizeof(sect.fathdr), 1, e);
608 fwrite(§.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