if (outFilePath != nullptr) { if (uprv_strlen(buffer.chars) >= outFilePathCapacity) {
fprintf(stderr, "genccode: filename too long\n"); exit(U_ILLEGAL_ARGUMENT_ERROR);
}
uprv_strcpy(outFilePath, buffer.chars); #ifdefined (WINDOWS_WITH_GNUC) && U_PLATFORM != U_PF_CYGWIN /* Need to fix the file separator character when using MinGW. */
swapFileSepChar(outFilePath, U_FILE_SEP_CHAR, '/'); #endif
}
/* turn dashes or dots in the entry name into underscores */
length=uprv_strlen(entry); for(i=0; i<length; ++i) { if(entry[i]=='-' || entry[i]=='.') {
entry[i]='_';
}
}
count = snprintf(
buffer.chars, sizeof(buffer.chars),
assemblyHeader[assemblyHeaderIndex].header,
entry, entry, entry, entry,
entry, entry, entry, entry); if (count >= sizeof(buffer.chars)) {
fprintf(stderr, "genccode: entry name too long (long filename?)\n"); exit(U_ILLEGAL_ARGUMENT_ERROR);
}
T_FileStream_writeLine(out, buffer.chars);
T_FileStream_writeLine(out, assemblyHeader[assemblyHeaderIndex].beginLine);
for(;;) {
memset(buffer.uint32s, 0, sizeof(buffer.uint32s));
length=T_FileStream_read(in, buffer.uint32s, sizeof(buffer.uint32s)); if(length==0) { break;
} for(i=0; i<(length/sizeof(buffer.uint32s[0])); i++) { // TODO: What if the last read sees length not as a multiple of 4?
column = write32(out, buffer.uint32s[i], column);
}
}
T_FileStream_writeLine(out, "\n");
count = snprintf(
buffer.chars, sizeof(buffer.chars),
assemblyHeader[assemblyHeaderIndex].footer,
entry, entry, entry, entry,
entry, entry, entry, entry); if (count >= sizeof(buffer.chars)) {
fprintf(stderr, "genccode: entry name too long (long filename?)\n"); exit(U_ILLEGAL_ARGUMENT_ERROR);
}
T_FileStream_writeLine(out, buffer.chars);
if(T_FileStream_error(in)) {
fprintf(stderr, "genccode: file read error while generating from file %s\n", filename); exit(U_FILE_ACCESS_ERROR);
}
if(T_FileStream_error(out)) {
fprintf(stderr, "genccode: file write error while generating from file %s\n", filename); exit(U_FILE_ACCESS_ERROR);
}
if (outFilePath != nullptr) { if (uprv_strlen(buffer) >= outFilePathCapacity) {
fprintf(stderr, "genccode: filename too long\n"); exit(U_ILLEGAL_ARGUMENT_ERROR);
}
uprv_strcpy(outFilePath, buffer); #ifdefined (WINDOWS_WITH_GNUC) && U_PLATFORM != U_PF_CYGWIN /* Need to fix the file separator character when using MinGW. */
swapFileSepChar(outFilePath, U_FILE_SEP_CHAR, '/'); #endif
}
out=T_FileStream_open(buffer, "w"); if(out==nullptr) {
fprintf(stderr, "genccode: unable to open output file %s\n", buffer); exit(U_FILE_ACCESS_ERROR);
}
/* turn dashes or dots in the entry name into underscores */
length=uprv_strlen(entry); for(i=0; i<length; ++i) { if(entry[i]=='-' || entry[i]=='.') {
entry[i]='_';
}
}
static uint32_t
write32(FileStream *out, uint32_t bitField, uint32_t column) {
int32_t i; char bitFieldStr[64]; /* This is more bits than needed for a 32-bit number */ char *s = bitFieldStr;
uint8_t* ptrIdx = reinterpret_cast<uint8_t*>(&bitField); staticconstchar hexToStr[16] = { '0','1','2','3', '4','5','6','7', '8','9','A','B', 'C','D','E','F'
};
/* write the value, possibly with comma and newline */ if(column==MAX_COLUMN) { /* first byte */
column=1;
} elseif(column<32) {
*(s++)=',';
++column;
} else {
*(s++)='\n';
uprv_strcpy(s, assemblyHeader[assemblyHeaderIndex].beginLine);
s+=uprv_strlen(s);
column=1;
}
if (bitField < 10) { /* It's a small number. Don't waste the space for 0x */
*(s++)=hexToStr[bitField];
} else { int seenNonZero = 0; /* This is used to remove leading zeros */
/* This creates a 32-bit field */ #if U_IS_BIG_ENDIAN for (i = 0; i < sizeof(uint32_t); i++) #else for (i = sizeof(uint32_t)-1; i >= 0 ; i--) #endif
{
uint8_t value = ptrIdx[i]; if (value || seenNonZero) {
*(s++)=hexToStr[value>>4];
*(s++)=hexToStr[value&0xF];
seenNonZero = 1;
}
} if(hexType==HEX_0H) {
*(s++)='h';
}
}
if (status.isFailure()) {
fprintf(stderr, "genccode: error building filename or entrypoint\n"); exit(status.get());
}
if (outFilenameBuilder.length() >= outFilenameCapacity) {
fprintf(stderr, "genccode: output filename too long\n"); exit(U_ILLEGAL_ARGUMENT_ERROR);
}
if (entryNameBuilder.length() >= entryNameCapacity) {
fprintf(stderr, "genccode: entry name too long (long filename?)\n"); exit(U_ILLEGAL_ARGUMENT_ERROR);
}
// link.exe will link an IMAGE_FILE_MACHINE_UNKNOWN data-only .obj file // no matter what architecture it is targeting (though other values are // required to match). Unfortunately, the variable name decoration/mangling // is slightly different on x86, which means we can't use the UNKNOWN type // for all architectures though. # ifdefined(_M_IX86)
*pCPU = IMAGE_FILE_MACHINE_I386; # else // Linker for ClangCL doesn't handle IMAGE_FILE_MACHINE_UNKNOWN the same as // linker for MSVC. Because of this optCpuArch is used to define the CPU // architecture in that case. While _M_AMD64 and _M_ARM64 could be used, // this would potentially be problematic when cross-compiling as this code // would most likely be ran on host machine to generate the .obj file for // the target architecture. # ifdefined(__clang__) if (!optCpuArch) {
fprintf(stderr, "genccode: CPU architecture must be set for Clang-CL\n"); exit(U_ILLEGAL_ARGUMENT_ERROR);
} if (strcmp(optCpuArch, "x64") == 0 || strcmp(optCpuArch, "X64") == 0) {
*pCPU = IMAGE_FILE_MACHINE_AMD64;
} elseif (strcmp(optCpuArch, "x86") == 0 || strcmp(optCpuArch, "X86") == 0) {
*pCPU = IMAGE_FILE_MACHINE_I386;
} elseif (strcmp(optCpuArch, "arm64") == 0 || strcmp(optCpuArch, "ARM64") == 0) {
*pCPU = IMAGE_FILE_MACHINE_ARM64;
} elseif (strcmp(optCpuArch, "arm") == 0 || strcmp(optCpuArch, "ARM") == 0) {
*pCPU = IMAGE_FILE_MACHINE_ARM;
} else {
std::terminate(); // Unreachable.
} # else
*pCPU = IMAGE_FILE_MACHINE_UNKNOWN; # endif # endif # ifdefined(_M_IA64) || defined(_M_AMD64) || defined (_M_ARM64)
*pBits = 64; // Doesn't seem to be used for anything interesting though? # elif defined(_M_IX86) || defined(_M_ARM)
*pBits = 32; # else # error "Unknown platform for CAN_GENERATE_OBJECTS." # endif #else # error "Unknown platform for CAN_GENERATE_OBJECTS." #endif return;
}
in=T_FileStream_open(filename, "rb"); if(in==nullptr) {
fprintf(stderr, "genccode: unable to open match-arch file %s\n", filename); exit(U_FILE_ACCESS_ERROR);
}
length=T_FileStream_read(in, buffer.bytes, sizeof(buffer.bytes));
#ifdef U_ELF if (length < static_cast<int32_t>(sizeof(Elf32_Ehdr))) {
fprintf(stderr, "genccode: match-arch file %s is too short\n", filename); exit(U_UNSUPPORTED_ERROR);
} if(
buffer.header32.e_ident[0]!=ELFMAG0 ||
buffer.header32.e_ident[1]!=ELFMAG1 ||
buffer.header32.e_ident[2]!=ELFMAG2 ||
buffer.header32.e_ident[3]!=ELFMAG3 ||
buffer.header32.e_ident[EI_CLASS]<ELFCLASS32 || buffer.header32.e_ident[EI_CLASS]>ELFCLASS64
) {
fprintf(stderr, "genccode: match-arch file %s is not an ELF object file, or not supported\n", filename); exit(U_UNSUPPORTED_ERROR);
}
*pBits= buffer.header32.e_ident[EI_CLASS]==ELFCLASS32 ? 32 : 64; /* only 32 or 64: see check above */ #ifdef U_ELF64 if(*pBits!=32 && *pBits!=64) {
fprintf(stderr, "genccode: currently only supports 32-bit and 64-bit ELF format\n"); exit(U_UNSUPPORTED_ERROR);
} #else if(*pBits!=32) {
fprintf(stderr, "genccode: built with elf.h missing 64-bit definitions\n"); exit(U_UNSUPPORTED_ERROR);
} #endif
*pIsBigEndian = static_cast<UBool>(buffer.header32.e_ident[EI_DATA] == ELFDATA2MSB); if(*pIsBigEndian!=U_IS_BIG_ENDIAN) {
fprintf(stderr, "genccode: currently only same-endianness ELF formats are supported\n"); exit(U_UNSUPPORTED_ERROR);
} /* TODO: Support byte swapping */
*pCPU=buffer.header32.e_machine; #elif U_PLATFORM_HAS_WIN32_API if(length<sizeof(IMAGE_FILE_HEADER)) {
fprintf(stderr, "genccode: match-arch file %s is too short\n", filename); exit(U_UNSUPPORTED_ERROR);
} /* TODO: Use buffer.header. Keep aliasing legal. */
pHeader=(const IMAGE_FILE_HEADER *)buffer.bytes;
*pCPU=pHeader->Machine; /* *ThenumberofbitsisimplicitwiththeMachinevalue. **pBitsisignoredinthecallingcode,sothisneednotbeprecise.
*/
*pBits= *pCPU==IMAGE_FILE_MACHINE_I386 ? 32 : 64; /* Windows always runs on little-endian CPUs. */
*pIsBigEndian=false; #else # error "Unknown platform for CAN_GENERATE_OBJECTS." #endif
/* 16-align .rodata in the .o file, just in case */
paddingSize=sectionHeaders32[4].sh_offset & 0xf; if(paddingSize!=0) {
paddingSize=0x10-paddingSize;
sectionHeaders32[4].sh_offset+=paddingSize;
}
/* 16-align .rodata in the .o file, just in case */
paddingSize=sectionHeaders64[4].sh_offset & 0xf; if(paddingSize!=0) {
paddingSize=0x10-paddingSize;
sectionHeaders64[4].sh_offset+=paddingSize;
}
/* set the file header */
objHeader.fileHeader.Machine=cpu;
objHeader.fileHeader.NumberOfSections=2;
objHeader.fileHeader.TimeDateStamp=(DWORD)time(nullptr);
objHeader.fileHeader.PointerToSymbolTable=IMAGE_SIZEOF_FILE_HEADER+2*IMAGE_SIZEOF_SECTION_HEADER+length+size; /* start of symbol table */
objHeader.fileHeader.NumberOfSymbols=1;
/* set the section for the linker options */
uprv_strncpy((char *)objHeader.sections[0].Name, ".drectve", 8);
objHeader.sections[0].SizeOfRawData=length;
objHeader.sections[0].PointerToRawData=IMAGE_SIZEOF_FILE_HEADER+2*IMAGE_SIZEOF_SECTION_HEADER;
objHeader.sections[0].Characteristics=IMAGE_SCN_LNK_INFO|IMAGE_SCN_LNK_REMOVE|IMAGE_SCN_ALIGN_1BYTES;
/* set the data section */
uprv_strncpy((char *)objHeader.sections[1].Name, ".rdata", 6);
objHeader.sections[1].SizeOfRawData=size;
objHeader.sections[1].PointerToRawData=IMAGE_SIZEOF_FILE_HEADER+2*IMAGE_SIZEOF_SECTION_HEADER+length;
objHeader.sections[1].Characteristics=IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_ALIGN_16BYTES|IMAGE_SCN_MEM_READ;
/* set the symbol table */ if(entryLength<=8) {
uprv_strncpy((char *)symbols[0].N.ShortName, entry, entryLength);
symbolNames.sizeofLongNames=4;
} else {
symbols[0].N.Name.Short=0;
symbols[0].N.Name.Long=4;
symbolNames.sizeofLongNames=4+entryLength+1;
uprv_strcpy(symbolNames.longNames, entry);
}
symbols[0].SectionNumber=2;
symbols[0].StorageClass=IMAGE_SYM_CLASS_EXTERNAL;
/* write the file header and the linker options section */
T_FileStream_write(out, &objHeader, objHeader.sections[1].PointerToRawData); #else # error "Unknown platform for CAN_GENERATE_OBJECTS." #endif
/* copy the data file into section 2 */ for(;;) {
length=T_FileStream_read(in, buffer, sizeof(buffer)); if(length==0) { break;
}
T_FileStream_write(out, buffer, length);
}
#if U_PLATFORM_HAS_WIN32_API /* write the symbol table */
T_FileStream_write(out, symbols, IMAGE_SIZEOF_SYMBOL);
T_FileStream_write(out, &symbolNames, symbolNames.sizeofLongNames); #endif
if(T_FileStream_error(in)) {
fprintf(stderr, "genccode: file read error while generating from file %s\n", filename); exit(U_FILE_ACCESS_ERROR);
}
if(T_FileStream_error(out)) {
fprintf(stderr, "genccode: file write error while generating from file %s\n", filename); exit(U_FILE_ACCESS_ERROR);
}