• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /******************************************************************************
4  *   Copyright (C) 2009-2016, International Business Machines
5  *   Corporation and others.  All Rights Reserved.
6  *******************************************************************************
7  */
8 #include "unicode/utypes.h"
9 
10 #if U_PLATFORM_HAS_WIN32_API
11 #   define VC_EXTRALEAN
12 #   define WIN32_LEAN_AND_MEAN
13 #   define NOUSER
14 #   define NOSERVICE
15 #   define NOIME
16 #   define NOMCX
17 #include <windows.h>
18 #include <time.h>
19 #   if defined(__clang__)
20 #       include <exception>
21 #   endif
22 #   ifdef __GNUC__
23 #       define WINDOWS_WITH_GNUC
24 #   endif
25 #endif
26 
27 #if U_PLATFORM_IS_LINUX_BASED && U_HAVE_ELF_H
28 #   define U_ELF
29 #endif
30 
31 #ifdef U_ELF
32 #   include <elf.h>
33 #   if defined(ELFCLASS64)
34 #       define U_ELF64
35 #   endif
36     /* Old elf.h headers may not have EM_X86_64, or have EM_X8664 instead. */
37 #   ifndef EM_X86_64
38 #       define EM_X86_64 62
39 #   endif
40 #   define ICU_ENTRY_OFFSET 0
41 #endif
42 
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include "unicode/putil.h"
46 #include "cmemory.h"
47 #include "cstring.h"
48 #include "filestrm.h"
49 #include "toolutil.h"
50 #include "unicode/uclean.h"
51 #include "uoptions.h"
52 #include "pkg_genc.h"
53 #include "filetools.h"
54 #include "charstr.h"
55 #include "unicode/errorcode.h"
56 
57 #define MAX_COLUMN ((uint32_t)(0xFFFFFFFFU))
58 
59 #define HEX_0X 0 /*  0x1234 */
60 #define HEX_0H 1 /*  01234h */
61 
62 /* prototypes --------------------------------------------------------------- */
63 static void
64 getOutFilename(
65     const char *inFilename,
66     const char *destdir,
67     char *outFilename,
68     int32_t outFilenameCapacity,
69     char *entryName,
70     int32_t entryNameCapacity,
71     const char *newSuffix,
72     const char *optFilename);
73 
74 static uint32_t
75 write8(FileStream *out, uint8_t byte, uint32_t column);
76 
77 static uint32_t
78 write32(FileStream *out, uint32_t byte, uint32_t column);
79 
80 #if U_PLATFORM == U_PF_OS400
81 static uint32_t
82 write8str(FileStream *out, uint8_t byte, uint32_t column);
83 #endif
84 /* -------------------------------------------------------------------------- */
85 
86 /*
87 Creating Template Files for New Platforms
88 
89 Let the cc compiler help you get started.
90 Compile this program
91     const unsigned int x[5] = {1, 2, 0xdeadbeef, 0xffffffff, 16};
92 with the -S option to produce assembly output.
93 
94 For example, this will generate array.s:
95 gcc -S array.c
96 
97 This will produce a .s file that may look like this:
98 
99     .file   "array.c"
100     .version        "01.01"
101 gcc2_compiled.:
102     .globl x
103     .section        .rodata
104     .align 4
105     .type    x,@object
106     .size    x,20
107 x:
108     .long   1
109     .long   2
110     .long   -559038737
111     .long   -1
112     .long   16
113     .ident  "GCC: (GNU) 2.96 20000731 (Red Hat Linux 7.1 2.96-85)"
114 
115 which gives a starting point that will compile, and can be transformed
116 to become the template, generally with some consulting of as docs and
117 some experimentation.
118 
119 If you want ICU to automatically use this assembly, you should
120 specify "GENCCODE_ASSEMBLY=-a name" in the specific config/mh-* file,
121 where the name is the compiler or platform that you used in this
122 assemblyHeader data structure.
123 */
124 static const struct AssemblyType {
125     const char *name;
126     const char *header;
127     const char *beginLine;
128     const char *footer;
129     int8_t      hexType; /* HEX_0X or HEX_0h */
130 } assemblyHeader[] = {
131     /* For gcc assemblers, the meaning of .align changes depending on the */
132     /* hardware, so we use .balign 16 which always means 16 bytes. */
133     /* https://sourceware.org/binutils/docs/as/Pseudo-Ops.html */
134     {"gcc",
135         ".globl %s\n"
136         "\t.section .note.GNU-stack,\"\",%%progbits\n"
137         "#ifdef __CET__\n"
138         "# include <cet.h>\n"
139         "#endif\n"
140         "\t.section .rodata\n"
141         "\t.balign 16\n"
142         "#ifdef U_HIDE_DATA_SYMBOL\n"
143         "\t.hidden %s\n"
144         "#endif\n"
145         "\t.type %s,%%object\n"
146         "%s:\n\n",
147 
148         ".long ",".size %s, .-%s\n",HEX_0X
149     },
150     {"gcc-darwin",
151         /*"\t.section __TEXT,__text,regular,pure_instructions\n"
152         "\t.section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32\n"*/
153         ".globl _%s\n"
154         "#ifdef U_HIDE_DATA_SYMBOL\n"
155         "\t.private_extern _%s\n"
156         "#endif\n"
157         "\t.data\n"
158         "\t.const\n"
159         "\t.balign 16\n"
160         "_%s:\n\n",
161 
162         ".long ","",HEX_0X
163     },
164     /* macOS PPC should use `.p2align 4` instead `.balign 16` because is
165      * unknown pseudo ops for such legacy system*/
166     {"gcc-darwin-ppc",
167         /*"\t.section __TEXT,__text,regular,pure_instructions\n"
168         "\t.section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32\n"*/
169         ".globl _%s\n"
170         "#ifdef U_HIDE_DATA_SYMBOL\n"
171         "\t.private_extern _%s\n"
172         "#endif\n"
173         "\t.data\n"
174         "\t.const\n"
175         "\t.p2align 4\n"
176         "_%s:\n\n",
177 
178         ".long ","",HEX_0X
179     },
180     {"gcc-cygwin",
181         ".globl _%s\n"
182         "\t.section .rodata\n"
183         "\t.balign 16\n"
184         "_%s:\n\n",
185 
186         ".long ","",HEX_0X
187     },
188     {"gcc-mingw64",
189         ".globl %s\n"
190         "\t.section .rodata\n"
191         "\t.balign 16\n"
192         "%s:\n\n",
193 
194         ".long ","",HEX_0X
195     },
196 /* 16 bytes alignment. */
197 /* http://docs.oracle.com/cd/E19641-01/802-1947/802-1947.pdf */
198     {"sun",
199         "\t.section \".rodata\"\n"
200         "\t.align   16\n"
201         ".globl     %s\n"
202         "%s:\n",
203 
204         ".word ","",HEX_0X
205     },
206 /* 16 bytes alignment for sun-x86. */
207 /* http://docs.oracle.com/cd/E19963-01/html/821-1608/eoiyg.html */
208     {"sun-x86",
209         "Drodata.rodata:\n"
210         "\t.type   Drodata.rodata,@object\n"
211         "\t.size   Drodata.rodata,0\n"
212         "\t.globl  %s\n"
213         "\t.align  16\n"
214         "%s:\n",
215 
216         ".4byte ","",HEX_0X
217     },
218 /* 1<<4 bit alignment for aix. */
219 /* http://pic.dhe.ibm.com/infocenter/aix/v6r1/index.jsp?topic=%2Fcom.ibm.aix.aixassem%2Fdoc%2Falangref%2Fidalangref_csect_pseudoop.htm */
220     {"xlc",
221         ".globl %s{RO}\n"
222         "\t.toc\n"
223         "%s:\n"
224         "\t.csect %s{RO}, 4\n",
225 
226         ".long ","",HEX_0X
227     },
228     {"aCC-ia64",
229         "\t.file   \"%s.s\"\n"
230         "\t.type   %s,@object\n"
231         "\t.global %s\n"
232         "\t.secalias .abe$0.rodata, \".rodata\"\n"
233         "\t.section .abe$0.rodata = \"a\", \"progbits\"\n"
234         "\t.align  16\n"
235         "%s::\t",
236 
237         "data4 ","",HEX_0X
238     },
239     {"aCC-parisc",
240         "\t.SPACE  $TEXT$\n"
241         "\t.SUBSPA $LIT$\n"
242         "%s\n"
243         "\t.EXPORT %s\n"
244         "\t.ALIGN  16\n",
245 
246         ".WORD ","",HEX_0X
247     },
248 /* align 16 bytes */
249 /*  http://msdn.microsoft.com/en-us/library/dwa9fwef.aspx */
250     {"nasm",
251         "global %s\n"
252 #if defined(_WIN32)
253         "section .rdata align=16\n"
254 #else
255         "section .rodata align=16\n"
256 #endif
257         "%s:\n",
258         "  dd ","",HEX_0X
259     },
260     { "masm",
261       "\tTITLE %s\n"
262       "; generated by genccode\n"
263       ".386\n"
264       ".model flat\n"
265       "\tPUBLIC _%s\n"
266       "ICUDATA_%s\tSEGMENT READONLY PARA PUBLIC FLAT 'DATA'\n"
267       "\tALIGN 16\n"
268       "_%s\tLABEL DWORD\n",
269       "\tDWORD ","\nICUDATA_%s\tENDS\n\tEND\n",HEX_0H
270     },
271     { "masm64",
272       "\tTITLE %s\n"
273       "; generated by genccode\n"
274       "\tPUBLIC _%s\n"
275       "ICUDATA_%s\tSEGMENT READONLY 'DATA'\n"
276       "\tALIGN 16\n"
277       "_%s\tLABEL DWORD\n",
278       "\tDWORD ","\nICUDATA_%s\tENDS\n\tEND\n",HEX_0H
279     }
280 };
281 
282 static int32_t assemblyHeaderIndex = -1;
283 static int32_t hexType = HEX_0X;
284 
285 U_CAPI UBool U_EXPORT2
checkAssemblyHeaderName(const char * optAssembly)286 checkAssemblyHeaderName(const char* optAssembly) {
287     int32_t idx;
288     assemblyHeaderIndex = -1;
289     for (idx = 0; idx < UPRV_LENGTHOF(assemblyHeader); idx++) {
290         if (uprv_strcmp(optAssembly, assemblyHeader[idx].name) == 0) {
291             assemblyHeaderIndex = idx;
292             hexType = assemblyHeader[idx].hexType; /* set the hex type */
293             return true;
294         }
295     }
296 
297     return false;
298 }
299 
300 U_CAPI UBool U_EXPORT2
checkCpuArchitecture(const char * optCpuArch)301 checkCpuArchitecture(const char* optCpuArch) {
302     return strcmp(optCpuArch, "x64") == 0 || strcmp(optCpuArch, "x86") == 0 || strcmp(optCpuArch, "arm64") == 0;
303 }
304 
305 
306 U_CAPI void U_EXPORT2
printAssemblyHeadersToStdErr()307 printAssemblyHeadersToStdErr() {
308     int32_t idx;
309     fprintf(stderr, "%s", assemblyHeader[0].name);
310     for (idx = 1; idx < UPRV_LENGTHOF(assemblyHeader); idx++) {
311         fprintf(stderr, ", %s", assemblyHeader[idx].name);
312     }
313     fprintf(stderr,
314         ")\n");
315 }
316 
317 U_CAPI void U_EXPORT2
writeAssemblyCode(const char * filename,const char * destdir,const char * optEntryPoint,const char * optFilename,char * outFilePath,size_t outFilePathCapacity)318 writeAssemblyCode(
319         const char *filename,
320         const char *destdir,
321         const char *optEntryPoint,
322         const char *optFilename,
323         char *outFilePath,
324         size_t outFilePathCapacity) {
325     uint32_t column = MAX_COLUMN;
326     char entry[96];
327     union {
328         uint32_t uint32s[1024];
329         char chars[4096];
330     } buffer;
331     FileStream *in, *out;
332     size_t i, length, count;
333 
334     in=T_FileStream_open(filename, "rb");
335     if(in==nullptr) {
336         fprintf(stderr, "genccode: unable to open input file %s\n", filename);
337         exit(U_FILE_ACCESS_ERROR);
338     }
339 
340     const char* newSuffix = nullptr;
341 
342     if (uprv_strcmp(assemblyHeader[assemblyHeaderIndex].name, "masm") == 0) {
343         newSuffix = ".masm";
344     }
345     else if (uprv_strcmp(assemblyHeader[assemblyHeaderIndex].name, "nasm") == 0) {
346         newSuffix = ".asm";
347     } else {
348         newSuffix = ".S";
349     }
350 
351     getOutFilename(
352         filename,
353         destdir,
354         buffer.chars,
355         sizeof(buffer.chars),
356         entry,
357         sizeof(entry),
358         newSuffix,
359         optFilename);
360     out=T_FileStream_open(buffer.chars, "w");
361     if(out==nullptr) {
362         fprintf(stderr, "genccode: unable to open output file %s\n", buffer.chars);
363         exit(U_FILE_ACCESS_ERROR);
364     }
365 
366     if (outFilePath != nullptr) {
367         if (uprv_strlen(buffer.chars) >= outFilePathCapacity) {
368             fprintf(stderr, "genccode: filename too long\n");
369             exit(U_ILLEGAL_ARGUMENT_ERROR);
370         }
371         uprv_strcpy(outFilePath, buffer.chars);
372 #if defined (WINDOWS_WITH_GNUC) && U_PLATFORM != U_PF_CYGWIN
373         /* Need to fix the file separator character when using MinGW. */
374         swapFileSepChar(outFilePath, U_FILE_SEP_CHAR, '/');
375 #endif
376     }
377 
378     if(optEntryPoint != nullptr) {
379         uprv_strcpy(entry, optEntryPoint);
380         uprv_strcat(entry, "_dat");
381     }
382 
383     /* turn dashes or dots in the entry name into underscores */
384     length=uprv_strlen(entry);
385     for(i=0; i<length; ++i) {
386         if(entry[i]=='-' || entry[i]=='.') {
387             entry[i]='_';
388         }
389     }
390 
391     count = snprintf(
392         buffer.chars, sizeof(buffer.chars),
393         assemblyHeader[assemblyHeaderIndex].header,
394         entry, entry, entry, entry,
395         entry, entry, entry, entry);
396     if (count >= sizeof(buffer.chars)) {
397         fprintf(stderr, "genccode: entry name too long (long filename?)\n");
398         exit(U_ILLEGAL_ARGUMENT_ERROR);
399     }
400     T_FileStream_writeLine(out, buffer.chars);
401     T_FileStream_writeLine(out, assemblyHeader[assemblyHeaderIndex].beginLine);
402 
403     for(;;) {
404         memset(buffer.uint32s, 0, sizeof(buffer.uint32s));
405         length=T_FileStream_read(in, buffer.uint32s, sizeof(buffer.uint32s));
406         if(length==0) {
407             break;
408         }
409         for(i=0; i<(length/sizeof(buffer.uint32s[0])); i++) {
410             // TODO: What if the last read sees length not as a multiple of 4?
411             column = write32(out, buffer.uint32s[i], column);
412         }
413     }
414 
415     T_FileStream_writeLine(out, "\n");
416 
417     count = snprintf(
418         buffer.chars, sizeof(buffer.chars),
419         assemblyHeader[assemblyHeaderIndex].footer,
420         entry, entry, entry, entry,
421         entry, entry, entry, entry);
422     if (count >= sizeof(buffer.chars)) {
423         fprintf(stderr, "genccode: entry name too long (long filename?)\n");
424         exit(U_ILLEGAL_ARGUMENT_ERROR);
425     }
426     T_FileStream_writeLine(out, buffer.chars);
427 
428     if(T_FileStream_error(in)) {
429         fprintf(stderr, "genccode: file read error while generating from file %s\n", filename);
430         exit(U_FILE_ACCESS_ERROR);
431     }
432 
433     if(T_FileStream_error(out)) {
434         fprintf(stderr, "genccode: file write error while generating from file %s\n", filename);
435         exit(U_FILE_ACCESS_ERROR);
436     }
437 
438     T_FileStream_close(out);
439     T_FileStream_close(in);
440 }
441 
442 U_CAPI void U_EXPORT2
writeCCode(const char * filename,const char * destdir,const char * optEntryPoint,const char * optName,const char * optFilename,char * outFilePath,size_t outFilePathCapacity)443 writeCCode(
444         const char *filename,
445         const char *destdir,
446         const char *optEntryPoint,
447         const char *optName,
448         const char *optFilename,
449         char *outFilePath,
450         size_t outFilePathCapacity) {
451     uint32_t column = MAX_COLUMN;
452     char buffer[4096], entry[96];
453     FileStream *in, *out;
454     size_t i, length, count;
455 
456     in=T_FileStream_open(filename, "rb");
457     if(in==nullptr) {
458         fprintf(stderr, "genccode: unable to open input file %s\n", filename);
459         exit(U_FILE_ACCESS_ERROR);
460     }
461 
462     if(optName != nullptr) { /* prepend  'icudt28_' */
463         // +2 includes the _ and the NUL
464         if (uprv_strlen(optName) + 2 > sizeof(entry)) {
465             fprintf(stderr, "genccode: entry name too long (long filename?)\n");
466             exit(U_ILLEGAL_ARGUMENT_ERROR);
467         }
468         strcpy(entry, optName);
469         strcat(entry, "_");
470     } else {
471         entry[0] = 0;
472     }
473 
474     getOutFilename(
475         filename,
476         destdir,
477         buffer,
478         static_cast<int32_t>(sizeof(buffer)),
479         entry + uprv_strlen(entry),
480         static_cast<int32_t>(sizeof(entry) - uprv_strlen(entry)),
481         ".c",
482         optFilename);
483 
484     if (outFilePath != nullptr) {
485         if (uprv_strlen(buffer) >= outFilePathCapacity) {
486             fprintf(stderr, "genccode: filename too long\n");
487             exit(U_ILLEGAL_ARGUMENT_ERROR);
488         }
489         uprv_strcpy(outFilePath, buffer);
490 #if defined (WINDOWS_WITH_GNUC) && U_PLATFORM != U_PF_CYGWIN
491         /* Need to fix the file separator character when using MinGW. */
492         swapFileSepChar(outFilePath, U_FILE_SEP_CHAR, '/');
493 #endif
494     }
495 
496     out=T_FileStream_open(buffer, "w");
497     if(out==nullptr) {
498         fprintf(stderr, "genccode: unable to open output file %s\n", buffer);
499         exit(U_FILE_ACCESS_ERROR);
500     }
501 
502     if(optEntryPoint != nullptr) {
503         uprv_strcpy(entry, optEntryPoint);
504         uprv_strcat(entry, "_dat");
505     }
506 
507     /* turn dashes or dots in the entry name into underscores */
508     length=uprv_strlen(entry);
509     for(i=0; i<length; ++i) {
510         if(entry[i]=='-' || entry[i]=='.') {
511             entry[i]='_';
512         }
513     }
514 
515 #if U_PLATFORM == U_PF_OS400
516     /*
517     TODO: Fix this once the compiler implements this feature. Keep in sync with udatamem.c
518 
519     This is here because this platform can't currently put
520     const data into the read-only pages of an object or
521     shared library (service program). Only strings are allowed in read-only
522     pages, so we use char * strings to store the data.
523 
524     In order to prevent the beginning of the data from ever matching the
525     magic numbers we must still use the initial double.
526     [grhoten 4/24/2003]
527     */
528     count = snprintf(buffer, sizeof(buffer),
529         "#ifndef IN_GENERATED_CCODE\n"
530         "#define IN_GENERATED_CCODE\n"
531         "#define U_DISABLE_RENAMING 1\n"
532         "#include \"unicode/umachine.h\"\n"
533         "#endif\n"
534         "U_CDECL_BEGIN\n"
535         "const struct {\n"
536         "    double bogus;\n"
537         "    const char *bytes; \n"
538         "} %s={ 0.0, \n",
539         entry);
540     if (count >= sizeof(buffer)) {
541         fprintf(stderr, "genccode: entry name too long (long filename?)\n");
542         exit(U_ILLEGAL_ARGUMENT_ERROR);
543     }
544     T_FileStream_writeLine(out, buffer);
545 
546     for(;;) {
547         length=T_FileStream_read(in, buffer, sizeof(buffer));
548         if(length==0) {
549             break;
550         }
551         for(i=0; i<length; ++i) {
552             column = write8str(out, (uint8_t)buffer[i], column);
553         }
554     }
555 
556     T_FileStream_writeLine(out, "\"\n};\nU_CDECL_END\n");
557 #else
558     /* Function renaming shouldn't be done in data */
559     count = snprintf(buffer, sizeof(buffer),
560         "#ifndef IN_GENERATED_CCODE\n"
561         "#define IN_GENERATED_CCODE\n"
562         "#define U_DISABLE_RENAMING 1\n"
563         "#include \"unicode/umachine.h\"\n"
564         "#endif\n"
565         "U_CDECL_BEGIN\n"
566         "const struct {\n"
567         "    double bogus;\n"
568         "    uint8_t bytes[%ld]; \n"
569         "} %s={ 0.0, {\n",
570         (long)T_FileStream_size(in), entry);
571     if (count >= sizeof(buffer)) {
572         fprintf(stderr, "genccode: entry name too long (long filename?)\n");
573         exit(U_ILLEGAL_ARGUMENT_ERROR);
574     }
575     T_FileStream_writeLine(out, buffer);
576 
577     for(;;) {
578         length=T_FileStream_read(in, buffer, sizeof(buffer));
579         if(length==0) {
580             break;
581         }
582         for(i=0; i<length; ++i) {
583             column = write8(out, (uint8_t)buffer[i], column);
584         }
585     }
586 
587     T_FileStream_writeLine(out, "\n}\n};\nU_CDECL_END\n");
588 #endif
589 
590     if(T_FileStream_error(in)) {
591         fprintf(stderr, "genccode: file read error while generating from file %s\n", filename);
592         exit(U_FILE_ACCESS_ERROR);
593     }
594 
595     if(T_FileStream_error(out)) {
596         fprintf(stderr, "genccode: file write error while generating from file %s\n", filename);
597         exit(U_FILE_ACCESS_ERROR);
598     }
599 
600     T_FileStream_close(out);
601     T_FileStream_close(in);
602 }
603 
604 static uint32_t
write32(FileStream * out,uint32_t bitField,uint32_t column)605 write32(FileStream *out, uint32_t bitField, uint32_t column) {
606     int32_t i;
607     char bitFieldStr[64]; /* This is more bits than needed for a 32-bit number */
608     char *s = bitFieldStr;
609     uint8_t* ptrIdx = reinterpret_cast<uint8_t*>(&bitField);
610     static const char hexToStr[16] = {
611         '0','1','2','3',
612         '4','5','6','7',
613         '8','9','A','B',
614         'C','D','E','F'
615     };
616 
617     /* write the value, possibly with comma and newline */
618     if(column==MAX_COLUMN) {
619         /* first byte */
620         column=1;
621     } else if(column<32) {
622         *(s++)=',';
623         ++column;
624     } else {
625         *(s++)='\n';
626         uprv_strcpy(s, assemblyHeader[assemblyHeaderIndex].beginLine);
627         s+=uprv_strlen(s);
628         column=1;
629     }
630 
631     if (bitField < 10) {
632         /* It's a small number. Don't waste the space for 0x */
633         *(s++)=hexToStr[bitField];
634     }
635     else {
636         int seenNonZero = 0; /* This is used to remove leading zeros */
637 
638         if(hexType==HEX_0X) {
639          *(s++)='0';
640          *(s++)='x';
641         } else if(hexType==HEX_0H) {
642          *(s++)='0';
643         }
644 
645         /* This creates a 32-bit field */
646 #if U_IS_BIG_ENDIAN
647         for (i = 0; i < sizeof(uint32_t); i++)
648 #else
649         for (i = sizeof(uint32_t)-1; i >= 0 ; i--)
650 #endif
651         {
652             uint8_t value = ptrIdx[i];
653             if (value || seenNonZero) {
654                 *(s++)=hexToStr[value>>4];
655                 *(s++)=hexToStr[value&0xF];
656                 seenNonZero = 1;
657             }
658         }
659         if(hexType==HEX_0H) {
660          *(s++)='h';
661         }
662     }
663 
664     *(s++)=0;
665     T_FileStream_writeLine(out, bitFieldStr);
666     return column;
667 }
668 
669 static uint32_t
write8(FileStream * out,uint8_t byte,uint32_t column)670 write8(FileStream *out, uint8_t byte, uint32_t column) {
671     char s[4];
672     int i=0;
673 
674     /* convert the byte value to a string */
675     if(byte>=100) {
676         s[i++] = static_cast<char>('0' + byte / 100);
677         byte%=100;
678     }
679     if(i>0 || byte>=10) {
680         s[i++] = static_cast<char>('0' + byte / 10);
681         byte%=10;
682     }
683     s[i++] = static_cast<char>('0' + byte);
684     s[i]=0;
685 
686     /* write the value, possibly with comma and newline */
687     if(column==MAX_COLUMN) {
688         /* first byte */
689         column=1;
690     } else if(column<16) {
691         T_FileStream_writeLine(out, ",");
692         ++column;
693     } else {
694         T_FileStream_writeLine(out, ",\n");
695         column=1;
696     }
697     T_FileStream_writeLine(out, s);
698     return column;
699 }
700 
701 #if U_PLATFORM == U_PF_OS400
702 static uint32_t
write8str(FileStream * out,uint8_t byte,uint32_t column)703 write8str(FileStream *out, uint8_t byte, uint32_t column) {
704     char s[8];
705 
706     if (byte > 7)
707         snprintf(s, sizeof(s), "\\x%X", byte);
708     else
709         snprintf(s, sizeof(s), "\\%X", byte);
710 
711     /* write the value, possibly with comma and newline */
712     if(column==MAX_COLUMN) {
713         /* first byte */
714         column=1;
715         T_FileStream_writeLine(out, "\"");
716     } else if(column<24) {
717         ++column;
718     } else {
719         T_FileStream_writeLine(out, "\"\n\"");
720         column=1;
721     }
722     T_FileStream_writeLine(out, s);
723     return column;
724 }
725 #endif
726 
727 static void
getOutFilename(const char * inFilename,const char * destdir,char * outFilename,int32_t outFilenameCapacity,char * entryName,int32_t entryNameCapacity,const char * newSuffix,const char * optFilename)728 getOutFilename(
729         const char *inFilename,
730         const char *destdir,
731         char *outFilename,
732         int32_t outFilenameCapacity,
733         char *entryName,
734         int32_t entryNameCapacity,
735         const char *newSuffix,
736         const char *optFilename) {
737     const char *basename=findBasename(inFilename), *suffix=uprv_strrchr(basename, '.');
738 
739     icu::CharString outFilenameBuilder;
740     icu::CharString entryNameBuilder;
741     icu::ErrorCode status;
742 
743     /* copy path */
744     if(destdir!=nullptr && *destdir!=0) {
745         outFilenameBuilder.append(destdir, status);
746         outFilenameBuilder.ensureEndsWithFileSeparator(status);
747     } else {
748         outFilenameBuilder.append(inFilename, static_cast<int32_t>(basename - inFilename), status);
749     }
750     inFilename=basename;
751 
752     if(suffix==nullptr) {
753         /* the filename does not have a suffix */
754         entryNameBuilder.append(inFilename, status);
755         if(optFilename != nullptr) {
756             outFilenameBuilder.append(optFilename, status);
757         } else {
758             outFilenameBuilder.append(inFilename, status);
759         }
760         outFilenameBuilder.append(newSuffix, status);
761     } else {
762         int32_t saveOutFilenameLength = outFilenameBuilder.length();
763         /* copy basename */
764         while(inFilename<suffix) {
765             // iSeries cannot have '-' in the .o objects.
766             char c = (*inFilename=='-') ? '_' : *inFilename;
767             outFilenameBuilder.append(c, status);
768             entryNameBuilder.append(c, status);
769             inFilename++;
770         }
771 
772         /* replace '.' by '_' */
773         outFilenameBuilder.append('_', status);
774         entryNameBuilder.append('_', status);
775         ++inFilename;
776 
777         /* copy suffix */
778         outFilenameBuilder.append(inFilename, status);
779         entryNameBuilder.append(inFilename, status);
780 
781         if(optFilename != nullptr) {
782             outFilenameBuilder.truncate(saveOutFilenameLength);
783             outFilenameBuilder.append(optFilename, status);
784         }
785         // add ".c"
786         outFilenameBuilder.append(newSuffix, status);
787     }
788 
789     if (status.isFailure()) {
790         fprintf(stderr, "genccode: error building filename or entrypoint\n");
791         exit(status.get());
792     }
793 
794     if (outFilenameBuilder.length() >= outFilenameCapacity) {
795         fprintf(stderr, "genccode: output filename too long\n");
796         exit(U_ILLEGAL_ARGUMENT_ERROR);
797     }
798 
799     if (entryNameBuilder.length() >= entryNameCapacity) {
800         fprintf(stderr, "genccode: entry name too long (long filename?)\n");
801         exit(U_ILLEGAL_ARGUMENT_ERROR);
802     }
803 
804     outFilenameBuilder.extract(outFilename, outFilenameCapacity, status);
805     entryNameBuilder.extract(entryName, entryNameCapacity, status);
806 }
807 
808 #ifdef CAN_GENERATE_OBJECTS
809 static void
getArchitecture(uint16_t * pCPU,uint16_t * pBits,UBool * pIsBigEndian,const char * optMatchArch,const char * optCpuArch)810 getArchitecture(
811     uint16_t *pCPU,
812     uint16_t *pBits,
813     UBool *pIsBigEndian,
814     const char *optMatchArch,
815     [[maybe_unused]] const char *optCpuArch) {
816     union {
817         char        bytes[2048];
818 #ifdef U_ELF
819         Elf32_Ehdr  header32;
820         /* Elf32_Ehdr and ELF64_Ehdr are identical for the necessary fields. */
821 #elif U_PLATFORM_HAS_WIN32_API
822         IMAGE_FILE_HEADER header;
823 #endif
824     } buffer;
825 
826     const char *filename;
827     FileStream *in;
828     int32_t length;
829 
830 #ifdef U_ELF
831 
832 #elif U_PLATFORM_HAS_WIN32_API
833     const IMAGE_FILE_HEADER *pHeader;
834 #else
835 #   error "Unknown platform for CAN_GENERATE_OBJECTS."
836 #endif
837 
838     if(optMatchArch != nullptr) {
839         filename=optMatchArch;
840     } else {
841         /* set defaults */
842 #ifdef U_ELF
843         /* set EM_386 because elf.h does not provide better defaults */
844         *pCPU=EM_386;
845         *pBits=32;
846         *pIsBigEndian = static_cast<UBool>(U_IS_BIG_ENDIAN ? ELFDATA2MSB : ELFDATA2LSB);
847 #elif U_PLATFORM_HAS_WIN32_API
848         // Windows always runs in little-endian mode.
849         *pIsBigEndian = false;
850 
851         // Note: The various _M_<arch> macros are predefined by the MSVC compiler based
852         // on the target compilation architecture.
853         // https://docs.microsoft.com/cpp/preprocessor/predefined-macros
854 
855         // link.exe will link an IMAGE_FILE_MACHINE_UNKNOWN data-only .obj file
856         // no matter what architecture it is targeting (though other values are
857         // required to match). Unfortunately, the variable name decoration/mangling
858         // is slightly different on x86, which means we can't use the UNKNOWN type
859         // for all architectures though.
860 #   if defined(_M_IX86)
861         *pCPU = IMAGE_FILE_MACHINE_I386;
862 #   else
863         // Linker for ClangCL doesn't handle IMAGE_FILE_MACHINE_UNKNOWN the same as
864         // linker for MSVC. Because of this optCpuArch is used to define the CPU
865         // architecture in that case. While _M_AMD64 and _M_ARM64 could be used,
866         // this would potentially be problematic when cross-compiling as this code
867         // would most likely be ran on host machine to generate the .obj file for
868         // the target architecture.
869 #       if defined(__clang__)
870             if (strcmp(optCpuArch, "x64") == 0) {
871                 *pCPU = IMAGE_FILE_MACHINE_AMD64;
872             } else if (strcmp(optCpuArch, "x86") == 0) {
873                 *pCPU = IMAGE_FILE_MACHINE_I386;
874             } else if (strcmp(optCpuArch, "arm64") == 0) {
875                 *pCPU = IMAGE_FILE_MACHINE_ARM64;
876             } else {
877                 std::terminate(); // Unreachable.
878             }
879 #       else
880             *pCPU = IMAGE_FILE_MACHINE_UNKNOWN;
881 #       endif
882 #   endif
883 #   if defined(_M_IA64) || defined(_M_AMD64) || defined (_M_ARM64)
884         *pBits = 64; // Doesn't seem to be used for anything interesting though?
885 #   elif defined(_M_IX86) || defined(_M_ARM)
886         *pBits = 32;
887 #   else
888 #      error "Unknown platform for CAN_GENERATE_OBJECTS."
889 #   endif
890 #else
891 #   error "Unknown platform for CAN_GENERATE_OBJECTS."
892 #endif
893         return;
894     }
895 
896     in=T_FileStream_open(filename, "rb");
897     if(in==nullptr) {
898         fprintf(stderr, "genccode: unable to open match-arch file %s\n", filename);
899         exit(U_FILE_ACCESS_ERROR);
900     }
901     length=T_FileStream_read(in, buffer.bytes, sizeof(buffer.bytes));
902 
903 #ifdef U_ELF
904     if (length < static_cast<int32_t>(sizeof(Elf32_Ehdr))) {
905         fprintf(stderr, "genccode: match-arch file %s is too short\n", filename);
906         exit(U_UNSUPPORTED_ERROR);
907     }
908     if(
909         buffer.header32.e_ident[0]!=ELFMAG0 ||
910         buffer.header32.e_ident[1]!=ELFMAG1 ||
911         buffer.header32.e_ident[2]!=ELFMAG2 ||
912         buffer.header32.e_ident[3]!=ELFMAG3 ||
913         buffer.header32.e_ident[EI_CLASS]<ELFCLASS32 || buffer.header32.e_ident[EI_CLASS]>ELFCLASS64
914     ) {
915         fprintf(stderr, "genccode: match-arch file %s is not an ELF object file, or not supported\n", filename);
916         exit(U_UNSUPPORTED_ERROR);
917     }
918 
919     *pBits= buffer.header32.e_ident[EI_CLASS]==ELFCLASS32 ? 32 : 64; /* only 32 or 64: see check above */
920 #ifdef U_ELF64
921     if(*pBits!=32 && *pBits!=64) {
922         fprintf(stderr, "genccode: currently only supports 32-bit and 64-bit ELF format\n");
923         exit(U_UNSUPPORTED_ERROR);
924     }
925 #else
926     if(*pBits!=32) {
927         fprintf(stderr, "genccode: built with elf.h missing 64-bit definitions\n");
928         exit(U_UNSUPPORTED_ERROR);
929     }
930 #endif
931 
932     *pIsBigEndian = static_cast<UBool>(buffer.header32.e_ident[EI_DATA] == ELFDATA2MSB);
933     if(*pIsBigEndian!=U_IS_BIG_ENDIAN) {
934         fprintf(stderr, "genccode: currently only same-endianness ELF formats are supported\n");
935         exit(U_UNSUPPORTED_ERROR);
936     }
937     /* TODO: Support byte swapping */
938 
939     *pCPU=buffer.header32.e_machine;
940 #elif U_PLATFORM_HAS_WIN32_API
941     if(length<sizeof(IMAGE_FILE_HEADER)) {
942         fprintf(stderr, "genccode: match-arch file %s is too short\n", filename);
943         exit(U_UNSUPPORTED_ERROR);
944     }
945     /* TODO: Use buffer.header.  Keep aliasing legal.  */
946     pHeader=(const IMAGE_FILE_HEADER *)buffer.bytes;
947     *pCPU=pHeader->Machine;
948     /*
949      * The number of bits is implicit with the Machine value.
950      * *pBits is ignored in the calling code, so this need not be precise.
951      */
952     *pBits= *pCPU==IMAGE_FILE_MACHINE_I386 ? 32 : 64;
953     /* Windows always runs on little-endian CPUs. */
954     *pIsBigEndian=false;
955 #else
956 #   error "Unknown platform for CAN_GENERATE_OBJECTS."
957 #endif
958 
959     T_FileStream_close(in);
960 }
961 
962 U_CAPI void U_EXPORT2
writeObjectCode(const char * filename,const char * destdir,const char * optEntryPoint,const char * optMatchArch,const char * optCpuArch,const char * optFilename,char * outFilePath,size_t outFilePathCapacity,UBool optWinDllExport)963 writeObjectCode(
964         const char *filename,
965         const char *destdir,
966         const char *optEntryPoint,
967         const char *optMatchArch,
968         const char *optCpuArch,
969         const char *optFilename,
970         char *outFilePath,
971         size_t outFilePathCapacity,
972         UBool optWinDllExport) {
973     /* common variables */
974     char buffer[4096], entry[96]={ 0 };
975     FileStream *in, *out;
976     const char *newSuffix;
977     int32_t i, entryLength, length, size, entryOffset=0, entryLengthOffset=0;
978 
979     uint16_t cpu, bits;
980     UBool makeBigEndian;
981 
982     (void)optWinDllExport; /* unused except Windows */
983 
984     /* platform-specific variables and initialization code */
985 #ifdef U_ELF
986     /* 32-bit Elf file header */
987     static Elf32_Ehdr header32={
988         {
989             /* e_ident[] */
990             ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3,
991             ELFCLASS32,
992             U_IS_BIG_ENDIAN ? ELFDATA2MSB : ELFDATA2LSB,
993             EV_CURRENT /* EI_VERSION */
994         },
995         ET_REL,
996         EM_386,
997         EV_CURRENT, /* e_version */
998         0, /* e_entry */
999         0, /* e_phoff */
1000         (Elf32_Off)sizeof(Elf32_Ehdr), /* e_shoff */
1001         0, /* e_flags */
1002         (Elf32_Half)sizeof(Elf32_Ehdr), /* eh_size */
1003         0, /* e_phentsize */
1004         0, /* e_phnum */
1005         (Elf32_Half)sizeof(Elf32_Shdr), /* e_shentsize */
1006         5, /* e_shnum */
1007         2 /* e_shstrndx */
1008     };
1009 
1010     /* 32-bit Elf section header table */
1011     static Elf32_Shdr sectionHeaders32[5]={
1012         { /* SHN_UNDEF */
1013             0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1014         },
1015         { /* .symtab */
1016             1, /* sh_name */
1017             SHT_SYMTAB,
1018             0, /* sh_flags */
1019             0, /* sh_addr */
1020             (Elf32_Off)(sizeof(header32)+sizeof(sectionHeaders32)), /* sh_offset */
1021             (Elf32_Word)(2*sizeof(Elf32_Sym)), /* sh_size */
1022             3, /* sh_link=sect hdr index of .strtab */
1023             1, /* sh_info=One greater than the symbol table index of the last
1024                 * local symbol (with STB_LOCAL). */
1025             4, /* sh_addralign */
1026             (Elf32_Word)(sizeof(Elf32_Sym)) /* sh_entsize */
1027         },
1028         { /* .shstrtab */
1029             9, /* sh_name */
1030             SHT_STRTAB,
1031             0, /* sh_flags */
1032             0, /* sh_addr */
1033             (Elf32_Off)(sizeof(header32)+sizeof(sectionHeaders32)+2*sizeof(Elf32_Sym)), /* sh_offset */
1034             40, /* sh_size */
1035             0, /* sh_link */
1036             0, /* sh_info */
1037             1, /* sh_addralign */
1038             0 /* sh_entsize */
1039         },
1040         { /* .strtab */
1041             19, /* sh_name */
1042             SHT_STRTAB,
1043             0, /* sh_flags */
1044             0, /* sh_addr */
1045             (Elf32_Off)(sizeof(header32)+sizeof(sectionHeaders32)+2*sizeof(Elf32_Sym)+40), /* sh_offset */
1046             (Elf32_Word)sizeof(entry), /* sh_size */
1047             0, /* sh_link */
1048             0, /* sh_info */
1049             1, /* sh_addralign */
1050             0 /* sh_entsize */
1051         },
1052         { /* .rodata */
1053             27, /* sh_name */
1054             SHT_PROGBITS,
1055             SHF_ALLOC, /* sh_flags */
1056             0, /* sh_addr */
1057             (Elf32_Off)(sizeof(header32)+sizeof(sectionHeaders32)+2*sizeof(Elf32_Sym)+40+sizeof(entry)), /* sh_offset */
1058             0, /* sh_size */
1059             0, /* sh_link */
1060             0, /* sh_info */
1061             16, /* sh_addralign */
1062             0 /* sh_entsize */
1063         }
1064     };
1065 
1066     /* symbol table */
1067     static Elf32_Sym symbols32[2]={
1068         { /* STN_UNDEF */
1069             0, 0, 0, 0, 0, 0
1070         },
1071         { /* data entry point */
1072             1, /* st_name */
1073             0, /* st_value */
1074             0, /* st_size */
1075             ELF64_ST_INFO(STB_GLOBAL, STT_OBJECT),
1076             0, /* st_other */
1077             4 /* st_shndx=index of related section table entry */
1078         }
1079     };
1080 
1081     /* section header string table, with decimal string offsets */
1082     static const char sectionStrings[40]=
1083         /*  0 */ "\0"
1084         /*  1 */ ".symtab\0"
1085         /*  9 */ ".shstrtab\0"
1086         /* 19 */ ".strtab\0"
1087         /* 27 */ ".rodata\0"
1088         /* 35 */ "\0\0\0\0"; /* contains terminating NUL */
1089         /* 40: padded to multiple of 8 bytes */
1090 
1091     /*
1092      * Use entry[] for the string table which will contain only the
1093      * entry point name.
1094      * entry[0] must be 0 (NUL)
1095      * The entry point name can be up to 38 characters long (sizeof(entry)-2).
1096      */
1097 
1098     /* 16-align .rodata in the .o file, just in case */
1099     static const char padding[16]={ 0 };
1100     int32_t paddingSize;
1101 
1102 #ifdef U_ELF64
1103     /* 64-bit Elf file header */
1104     static Elf64_Ehdr header64={
1105         {
1106             /* e_ident[] */
1107             ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3,
1108             ELFCLASS64,
1109             U_IS_BIG_ENDIAN ? ELFDATA2MSB : ELFDATA2LSB,
1110             EV_CURRENT /* EI_VERSION */
1111         },
1112         ET_REL,
1113         EM_X86_64,
1114         EV_CURRENT, /* e_version */
1115         0, /* e_entry */
1116         0, /* e_phoff */
1117         (Elf64_Off)sizeof(Elf64_Ehdr), /* e_shoff */
1118         0, /* e_flags */
1119         (Elf64_Half)sizeof(Elf64_Ehdr), /* eh_size */
1120         0, /* e_phentsize */
1121         0, /* e_phnum */
1122         (Elf64_Half)sizeof(Elf64_Shdr), /* e_shentsize */
1123         5, /* e_shnum */
1124         2 /* e_shstrndx */
1125     };
1126 
1127     /* 64-bit Elf section header table */
1128     static Elf64_Shdr sectionHeaders64[5]={
1129         { /* SHN_UNDEF */
1130             0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1131         },
1132         { /* .symtab */
1133             1, /* sh_name */
1134             SHT_SYMTAB,
1135             0, /* sh_flags */
1136             0, /* sh_addr */
1137             (Elf64_Off)(sizeof(header64)+sizeof(sectionHeaders64)), /* sh_offset */
1138             (Elf64_Xword)(2*sizeof(Elf64_Sym)), /* sh_size */
1139             3, /* sh_link=sect hdr index of .strtab */
1140             1, /* sh_info=One greater than the symbol table index of the last
1141                 * local symbol (with STB_LOCAL). */
1142             4, /* sh_addralign */
1143             (Elf64_Xword)(sizeof(Elf64_Sym)) /* sh_entsize */
1144         },
1145         { /* .shstrtab */
1146             9, /* sh_name */
1147             SHT_STRTAB,
1148             0, /* sh_flags */
1149             0, /* sh_addr */
1150             (Elf64_Off)(sizeof(header64)+sizeof(sectionHeaders64)+2*sizeof(Elf64_Sym)), /* sh_offset */
1151             40, /* sh_size */
1152             0, /* sh_link */
1153             0, /* sh_info */
1154             1, /* sh_addralign */
1155             0 /* sh_entsize */
1156         },
1157         { /* .strtab */
1158             19, /* sh_name */
1159             SHT_STRTAB,
1160             0, /* sh_flags */
1161             0, /* sh_addr */
1162             (Elf64_Off)(sizeof(header64)+sizeof(sectionHeaders64)+2*sizeof(Elf64_Sym)+40), /* sh_offset */
1163             (Elf64_Xword)sizeof(entry), /* sh_size */
1164             0, /* sh_link */
1165             0, /* sh_info */
1166             1, /* sh_addralign */
1167             0 /* sh_entsize */
1168         },
1169         { /* .rodata */
1170             27, /* sh_name */
1171             SHT_PROGBITS,
1172             SHF_ALLOC, /* sh_flags */
1173             0, /* sh_addr */
1174             (Elf64_Off)(sizeof(header64)+sizeof(sectionHeaders64)+2*sizeof(Elf64_Sym)+40+sizeof(entry)), /* sh_offset */
1175             0, /* sh_size */
1176             0, /* sh_link */
1177             0, /* sh_info */
1178             16, /* sh_addralign */
1179             0 /* sh_entsize */
1180         }
1181     };
1182 
1183     /*
1184      * 64-bit symbol table
1185      * careful: different order of items compared with Elf32_sym!
1186      */
1187     static Elf64_Sym symbols64[2]={
1188         { /* STN_UNDEF */
1189             0, 0, 0, 0, 0, 0
1190         },
1191         { /* data entry point */
1192             1, /* st_name */
1193             ELF64_ST_INFO(STB_GLOBAL, STT_OBJECT),
1194             0, /* st_other */
1195             4, /* st_shndx=index of related section table entry */
1196             0, /* st_value */
1197             0 /* st_size */
1198         }
1199     };
1200 
1201 #endif /* U_ELF64 */
1202 
1203     /* entry[] have a leading NUL */
1204     entryOffset=1;
1205 
1206     /* in the common code, count entryLength from after the NUL */
1207     entryLengthOffset=1;
1208 
1209     newSuffix=".o";
1210 
1211 #elif U_PLATFORM_HAS_WIN32_API
1212     struct {
1213         IMAGE_FILE_HEADER fileHeader;
1214         IMAGE_SECTION_HEADER sections[2];
1215         char linkerOptions[100];
1216     } objHeader;
1217     IMAGE_SYMBOL symbols[1];
1218     struct {
1219         DWORD sizeofLongNames;
1220         char longNames[100];
1221     } symbolNames;
1222 
1223     /*
1224      * entry sometimes have a leading '_'
1225      * overwritten if entryOffset==0 depending on the target platform
1226      * see check for cpu below
1227      */
1228     entry[0]='_';
1229 
1230     newSuffix=".obj";
1231 #else
1232 #   error "Unknown platform for CAN_GENERATE_OBJECTS."
1233 #endif
1234 
1235     /* deal with options, files and the entry point name */
1236     getArchitecture(&cpu, &bits, &makeBigEndian, optMatchArch, optCpuArch);
1237     if (optMatchArch)
1238     {
1239         printf("genccode: --match-arch cpu=%hu bits=%hu big-endian=%d\n", cpu, bits, makeBigEndian);
1240     }
1241     else
1242     {
1243         printf("genccode: using architecture cpu=%hu bits=%hu big-endian=%d\n", cpu, bits, makeBigEndian);
1244     }
1245 #if U_PLATFORM_HAS_WIN32_API
1246     if(cpu==IMAGE_FILE_MACHINE_I386) {
1247         entryOffset=1;
1248     }
1249 #endif
1250 
1251     in=T_FileStream_open(filename, "rb");
1252     if(in==nullptr) {
1253         fprintf(stderr, "genccode: unable to open input file %s\n", filename);
1254         exit(U_FILE_ACCESS_ERROR);
1255     }
1256     size=T_FileStream_size(in);
1257 
1258     getOutFilename(
1259         filename,
1260         destdir,
1261         buffer,
1262         sizeof(buffer),
1263         entry + entryOffset,
1264         sizeof(entry) - entryOffset,
1265         newSuffix,
1266         optFilename);
1267 
1268     if (outFilePath != nullptr) {
1269         if (uprv_strlen(buffer) >= outFilePathCapacity) {
1270             fprintf(stderr, "genccode: filename too long\n");
1271             exit(U_ILLEGAL_ARGUMENT_ERROR);
1272         }
1273         uprv_strcpy(outFilePath, buffer);
1274     }
1275 
1276     if(optEntryPoint != nullptr) {
1277         uprv_strcpy(entry+entryOffset, optEntryPoint);
1278         uprv_strcat(entry+entryOffset, "_dat");
1279     }
1280     /* turn dashes in the entry name into underscores */
1281     entryLength=(int32_t)uprv_strlen(entry+entryLengthOffset);
1282     for(i=0; i<entryLength; ++i) {
1283         if(entry[entryLengthOffset+i]=='-') {
1284             entry[entryLengthOffset+i]='_';
1285         }
1286     }
1287 
1288     /* open the output file */
1289     out=T_FileStream_open(buffer, "wb");
1290     if(out==nullptr) {
1291         fprintf(stderr, "genccode: unable to open output file %s\n", buffer);
1292         exit(U_FILE_ACCESS_ERROR);
1293     }
1294 
1295 #ifdef U_ELF
1296     if(bits==32) {
1297         header32.e_ident[EI_DATA]= makeBigEndian ? ELFDATA2MSB : ELFDATA2LSB;
1298         header32.e_machine=cpu;
1299 
1300         /* 16-align .rodata in the .o file, just in case */
1301         paddingSize=sectionHeaders32[4].sh_offset & 0xf;
1302         if(paddingSize!=0) {
1303                 paddingSize=0x10-paddingSize;
1304                 sectionHeaders32[4].sh_offset+=paddingSize;
1305         }
1306 
1307         sectionHeaders32[4].sh_size=(Elf32_Word)size;
1308 
1309         symbols32[1].st_size=(Elf32_Word)size;
1310 
1311         /* write .o headers */
1312         T_FileStream_write(out, &header32, (int32_t)sizeof(header32));
1313         T_FileStream_write(out, sectionHeaders32, (int32_t)sizeof(sectionHeaders32));
1314         T_FileStream_write(out, symbols32, (int32_t)sizeof(symbols32));
1315     } else /* bits==64 */ {
1316 #ifdef U_ELF64
1317         header64.e_ident[EI_DATA]= makeBigEndian ? ELFDATA2MSB : ELFDATA2LSB;
1318         header64.e_machine=cpu;
1319 
1320         /* 16-align .rodata in the .o file, just in case */
1321         paddingSize=sectionHeaders64[4].sh_offset & 0xf;
1322         if(paddingSize!=0) {
1323                 paddingSize=0x10-paddingSize;
1324                 sectionHeaders64[4].sh_offset+=paddingSize;
1325         }
1326 
1327         sectionHeaders64[4].sh_size=(Elf64_Xword)size;
1328 
1329         symbols64[1].st_size=(Elf64_Xword)size;
1330 
1331         /* write .o headers */
1332         T_FileStream_write(out, &header64, (int32_t)sizeof(header64));
1333         T_FileStream_write(out, sectionHeaders64, (int32_t)sizeof(sectionHeaders64));
1334         T_FileStream_write(out, symbols64, (int32_t)sizeof(symbols64));
1335 #endif
1336     }
1337 
1338     T_FileStream_write(out, sectionStrings, (int32_t)sizeof(sectionStrings));
1339     T_FileStream_write(out, entry, (int32_t)sizeof(entry));
1340     if(paddingSize!=0) {
1341         T_FileStream_write(out, padding, paddingSize);
1342     }
1343 #elif U_PLATFORM_HAS_WIN32_API
1344     /* populate the .obj headers */
1345     uprv_memset(&objHeader, 0, sizeof(objHeader));
1346     uprv_memset(&symbols, 0, sizeof(symbols));
1347     uprv_memset(&symbolNames, 0, sizeof(symbolNames));
1348 
1349     /* write the linker export directive */
1350     if (optWinDllExport) {
1351         uprv_strcpy(objHeader.linkerOptions, "-export:");
1352         length=8;
1353         uprv_strcpy(objHeader.linkerOptions+length, entry);
1354         length+=entryLength;
1355         uprv_strcpy(objHeader.linkerOptions+length, ",data ");
1356         length+=6;
1357     }
1358     else {
1359         length=0;
1360     }
1361 
1362     /* set the file header */
1363     objHeader.fileHeader.Machine=cpu;
1364     objHeader.fileHeader.NumberOfSections=2;
1365     objHeader.fileHeader.TimeDateStamp=(DWORD)time(nullptr);
1366     objHeader.fileHeader.PointerToSymbolTable=IMAGE_SIZEOF_FILE_HEADER+2*IMAGE_SIZEOF_SECTION_HEADER+length+size; /* start of symbol table */
1367     objHeader.fileHeader.NumberOfSymbols=1;
1368 
1369     /* set the section for the linker options */
1370     uprv_strncpy((char *)objHeader.sections[0].Name, ".drectve", 8);
1371     objHeader.sections[0].SizeOfRawData=length;
1372     objHeader.sections[0].PointerToRawData=IMAGE_SIZEOF_FILE_HEADER+2*IMAGE_SIZEOF_SECTION_HEADER;
1373     objHeader.sections[0].Characteristics=IMAGE_SCN_LNK_INFO|IMAGE_SCN_LNK_REMOVE|IMAGE_SCN_ALIGN_1BYTES;
1374 
1375     /* set the data section */
1376     uprv_strncpy((char *)objHeader.sections[1].Name, ".rdata", 6);
1377     objHeader.sections[1].SizeOfRawData=size;
1378     objHeader.sections[1].PointerToRawData=IMAGE_SIZEOF_FILE_HEADER+2*IMAGE_SIZEOF_SECTION_HEADER+length;
1379     objHeader.sections[1].Characteristics=IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_ALIGN_16BYTES|IMAGE_SCN_MEM_READ;
1380 
1381     /* set the symbol table */
1382     if(entryLength<=8) {
1383         uprv_strncpy((char *)symbols[0].N.ShortName, entry, entryLength);
1384         symbolNames.sizeofLongNames=4;
1385     } else {
1386         symbols[0].N.Name.Short=0;
1387         symbols[0].N.Name.Long=4;
1388         symbolNames.sizeofLongNames=4+entryLength+1;
1389         uprv_strcpy(symbolNames.longNames, entry);
1390     }
1391     symbols[0].SectionNumber=2;
1392     symbols[0].StorageClass=IMAGE_SYM_CLASS_EXTERNAL;
1393 
1394     /* write the file header and the linker options section */
1395     T_FileStream_write(out, &objHeader, objHeader.sections[1].PointerToRawData);
1396 #else
1397 #   error "Unknown platform for CAN_GENERATE_OBJECTS."
1398 #endif
1399 
1400     /* copy the data file into section 2 */
1401     for(;;) {
1402         length=T_FileStream_read(in, buffer, sizeof(buffer));
1403         if(length==0) {
1404             break;
1405         }
1406         T_FileStream_write(out, buffer, length);
1407     }
1408 
1409 #if U_PLATFORM_HAS_WIN32_API
1410     /* write the symbol table */
1411     T_FileStream_write(out, symbols, IMAGE_SIZEOF_SYMBOL);
1412     T_FileStream_write(out, &symbolNames, symbolNames.sizeofLongNames);
1413 #endif
1414 
1415     if(T_FileStream_error(in)) {
1416         fprintf(stderr, "genccode: file read error while generating from file %s\n", filename);
1417         exit(U_FILE_ACCESS_ERROR);
1418     }
1419 
1420     if(T_FileStream_error(out)) {
1421         fprintf(stderr, "genccode: file write error while generating from file %s\n", filename);
1422         exit(U_FILE_ACCESS_ERROR);
1423     }
1424 
1425     T_FileStream_close(out);
1426     T_FileStream_close(in);
1427 }
1428 #endif
1429