2 * versioninfo.c: Version information support
5 * Dick Porter (dick@ximian.com)
7 * (C) 2007 Novell, Inc.
14 #include <sys/types.h>
20 #include <mono/io-layer/wapi.h>
21 #include <mono/io-layer/wapi-private.h>
22 #include <mono/io-layer/versioninfo.h>
23 #include <mono/io-layer/io-portability.h>
24 #include <mono/io-layer/error.h>
25 #include <mono/io-layer/io-trace.h>
26 #include <mono/utils/strenc.h>
27 #include <mono/utils/mono-mmap.h>
28 #include <mono/utils/mono-logger-internals.h>
30 #define ALIGN32(ptr) ptr = (gpointer)((char *)ptr + 3); ptr = (gpointer)((char *)ptr - ((gsize)ptr & 3));
32 static WapiImageSectionHeader *
33 get_enclosing_section_header (guint32 rva, WapiImageNTHeaders32 *nt_headers)
35 WapiImageSectionHeader *section = _WAPI_IMAGE_FIRST_SECTION32 (nt_headers);
38 for (i = 0; i < GUINT16_FROM_LE (nt_headers->FileHeader.NumberOfSections); i++, section++) {
39 guint32 size = GUINT32_FROM_LE (section->Misc.VirtualSize);
41 size = GUINT32_FROM_LE (section->SizeOfRawData);
44 if ((rva >= GUINT32_FROM_LE (section->VirtualAddress)) &&
45 (rva < (GUINT32_FROM_LE (section->VirtualAddress) + size))) {
53 /* This works for both 32bit and 64bit files, as the differences are
54 * all after the section header block
57 get_ptr_from_rva (guint32 rva, WapiImageNTHeaders32 *ntheaders, gpointer file_map)
59 WapiImageSectionHeader *section_header;
62 section_header = get_enclosing_section_header (rva, ntheaders);
63 if (section_header == NULL) {
67 delta = (guint32)(GUINT32_FROM_LE (section_header->VirtualAddress) -
68 GUINT32_FROM_LE (section_header->PointerToRawData));
70 return((guint8 *)file_map + rva - delta);
74 scan_resource_dir (WapiImageResourceDirectory *root,
75 WapiImageNTHeaders32 *nt_headers,
77 WapiImageResourceDirectoryEntry *entry,
78 int level, guint32 res_id, guint32 lang_id,
81 WapiImageResourceDirectoryEntry swapped_entry;
82 gboolean is_string, is_dir;
83 guint32 name_offset, dir_offset, data_offset;
85 swapped_entry.Name = GUINT32_FROM_LE (entry->Name);
86 swapped_entry.OffsetToData = GUINT32_FROM_LE (entry->OffsetToData);
88 is_string = swapped_entry.NameIsString;
89 is_dir = swapped_entry.DataIsDirectory;
90 name_offset = swapped_entry.NameOffset;
91 dir_offset = swapped_entry.OffsetToDirectory;
92 data_offset = swapped_entry.OffsetToData;
95 /* Normally holds a directory entry for each type of
98 if ((is_string == FALSE &&
99 name_offset != res_id) ||
100 (is_string == TRUE)) {
103 } else if (level == 1) {
104 /* Normally holds a directory entry for each resource
107 } else if (level == 2) {
108 /* Normally holds a directory entry for each language
110 if ((is_string == FALSE &&
111 name_offset != lang_id &&
113 (is_string == TRUE)) {
117 g_assert_not_reached ();
120 if (is_dir == TRUE) {
121 WapiImageResourceDirectory *res_dir = (WapiImageResourceDirectory *)((guint8 *)root + dir_offset);
122 WapiImageResourceDirectoryEntry *sub_entries = (WapiImageResourceDirectoryEntry *)(res_dir + 1);
125 entries = GUINT16_FROM_LE (res_dir->NumberOfNamedEntries) + GUINT16_FROM_LE (res_dir->NumberOfIdEntries);
127 for (i = 0; i < entries; i++) {
128 WapiImageResourceDirectoryEntry *sub_entry = &sub_entries[i];
131 ret = scan_resource_dir (root, nt_headers, file_map,
132 sub_entry, level + 1, res_id,
141 WapiImageResourceDataEntry *data_entry = (WapiImageResourceDataEntry *)((guint8 *)root + data_offset);
142 *size = GUINT32_FROM_LE (data_entry->Size);
144 return(get_ptr_from_rva (GUINT32_FROM_LE (data_entry->OffsetToData), nt_headers, file_map));
149 find_pe_file_resources32 (gpointer file_map, guint32 map_size,
150 guint32 res_id, guint32 lang_id,
153 WapiImageDosHeader *dos_header;
154 WapiImageNTHeaders32 *nt_headers;
155 WapiImageResourceDirectory *resource_dir;
156 WapiImageResourceDirectoryEntry *resource_dir_entry;
157 guint32 resource_rva, entries, i;
160 dos_header = (WapiImageDosHeader *)file_map;
161 if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
162 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Bad dos signature 0x%x", __func__, dos_header->e_magic);
164 SetLastError (ERROR_INVALID_DATA);
168 if (map_size < sizeof(WapiImageNTHeaders32) + GUINT32_FROM_LE (dos_header->e_lfanew)) {
169 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File is too small: %d", __func__, map_size);
171 SetLastError (ERROR_BAD_LENGTH);
175 nt_headers = (WapiImageNTHeaders32 *)((guint8 *)file_map + GUINT32_FROM_LE (dos_header->e_lfanew));
176 if (nt_headers->Signature != IMAGE_NT_SIGNATURE) {
177 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Bad NT signature 0x%x", __func__, nt_headers->Signature);
179 SetLastError (ERROR_INVALID_DATA);
183 if (nt_headers->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
184 /* Do 64-bit stuff */
185 resource_rva = GUINT32_FROM_LE (((WapiImageNTHeaders64 *)nt_headers)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
187 resource_rva = GUINT32_FROM_LE (nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
190 if (resource_rva == 0) {
191 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: No resources in file!", __func__);
193 SetLastError (ERROR_INVALID_DATA);
197 resource_dir = (WapiImageResourceDirectory *)get_ptr_from_rva (resource_rva, (WapiImageNTHeaders32 *)nt_headers, file_map);
198 if (resource_dir == NULL) {
199 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find resource directory", __func__);
201 SetLastError (ERROR_INVALID_DATA);
205 entries = GUINT16_FROM_LE (resource_dir->NumberOfNamedEntries) + GUINT16_FROM_LE (resource_dir->NumberOfIdEntries);
206 resource_dir_entry = (WapiImageResourceDirectoryEntry *)(resource_dir + 1);
208 for (i = 0; i < entries; i++) {
209 WapiImageResourceDirectoryEntry *direntry = &resource_dir_entry[i];
210 ret = scan_resource_dir (resource_dir,
211 (WapiImageNTHeaders32 *)nt_headers,
212 file_map, direntry, 0, res_id,
223 find_pe_file_resources64 (gpointer file_map, guint32 map_size,
224 guint32 res_id, guint32 lang_id,
227 WapiImageDosHeader *dos_header;
228 WapiImageNTHeaders64 *nt_headers;
229 WapiImageResourceDirectory *resource_dir;
230 WapiImageResourceDirectoryEntry *resource_dir_entry;
231 guint32 resource_rva, entries, i;
234 dos_header = (WapiImageDosHeader *)file_map;
235 if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
236 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Bad dos signature 0x%x", __func__, dos_header->e_magic);
238 SetLastError (ERROR_INVALID_DATA);
242 if (map_size < sizeof(WapiImageNTHeaders64) + GUINT32_FROM_LE (dos_header->e_lfanew)) {
243 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File is too small: %d", __func__, map_size);
245 SetLastError (ERROR_BAD_LENGTH);
249 nt_headers = (WapiImageNTHeaders64 *)((guint8 *)file_map + GUINT32_FROM_LE (dos_header->e_lfanew));
250 if (nt_headers->Signature != IMAGE_NT_SIGNATURE) {
251 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Bad NT signature 0x%x", __func__,
252 nt_headers->Signature);
254 SetLastError (ERROR_INVALID_DATA);
258 if (nt_headers->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
259 /* Do 64-bit stuff */
260 resource_rva = GUINT32_FROM_LE (((WapiImageNTHeaders64 *)nt_headers)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
262 resource_rva = GUINT32_FROM_LE (nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
265 if (resource_rva == 0) {
266 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: No resources in file!", __func__);
268 SetLastError (ERROR_INVALID_DATA);
272 resource_dir = (WapiImageResourceDirectory *)get_ptr_from_rva (resource_rva, (WapiImageNTHeaders32 *)nt_headers, file_map);
273 if (resource_dir == NULL) {
274 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find resource directory", __func__);
276 SetLastError (ERROR_INVALID_DATA);
280 entries = GUINT16_FROM_LE (resource_dir->NumberOfNamedEntries) + GUINT16_FROM_LE (resource_dir->NumberOfIdEntries);
281 resource_dir_entry = (WapiImageResourceDirectoryEntry *)(resource_dir + 1);
283 for (i = 0; i < entries; i++) {
284 WapiImageResourceDirectoryEntry *direntry = &resource_dir_entry[i];
285 ret = scan_resource_dir (resource_dir,
286 (WapiImageNTHeaders32 *)nt_headers,
287 file_map, direntry, 0, res_id,
298 find_pe_file_resources (gpointer file_map, guint32 map_size,
299 guint32 res_id, guint32 lang_id,
302 /* Figure this out when we support 64bit PE files */
304 return find_pe_file_resources32 (file_map, map_size, res_id,
307 return find_pe_file_resources64 (file_map, map_size, res_id,
313 map_pe_file (gunichar2 *filename, gint32 *map_size, void **handle)
320 /* According to the MSDN docs, a search path is applied to
321 * filename. FIXME: implement this, for now just pass it
325 filename_ext = mono_unicode_to_external (filename);
326 if (filename_ext == NULL) {
327 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
329 SetLastError (ERROR_INVALID_NAME);
333 fd = _wapi_open (filename_ext, O_RDONLY, 0);
335 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Error opening file %s: %s", __func__, filename_ext, strerror (errno));
337 SetLastError (_wapi_get_win32_file_error (errno));
338 g_free (filename_ext);
343 if (fstat (fd, &statbuf) == -1) {
344 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Error stat()ing file %s: %s", __func__, filename_ext, strerror (errno));
346 SetLastError (_wapi_get_win32_file_error (errno));
347 g_free (filename_ext);
351 *map_size = statbuf.st_size;
353 /* Check basic file size */
354 if (statbuf.st_size < sizeof(WapiImageDosHeader)) {
355 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File %s is too small: %lld", __func__, filename_ext, statbuf.st_size);
357 SetLastError (ERROR_BAD_LENGTH);
358 g_free (filename_ext);
363 file_map = mono_file_map (statbuf.st_size, MONO_MMAP_READ | MONO_MMAP_PRIVATE, fd, 0, handle);
364 if (file_map == NULL) {
365 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Error mmap()int file %s: %s", __func__, filename_ext, strerror (errno));
367 SetLastError (_wapi_get_win32_file_error (errno));
368 g_free (filename_ext);
373 /* Don't need the fd any more */
375 g_free (filename_ext);
381 unmap_pe_file (gpointer file_map, void *handle)
383 mono_file_unmap (file_map, handle);
387 unicode_chars (const gunichar2 *str)
392 if (str[len] == '\0') {
400 unicode_compare (const gunichar2 *str1, const gunichar2 *str2)
402 while (*str1 && *str2) {
403 if (*str1 != *str2) {
410 return(*str1 == *str2);
413 /* compare a little-endian null-terminated utf16 string and a normal string.
414 * Can be used only for ascii or latin1 chars.
417 unicode_string_equals (const gunichar2 *str1, const gchar *str2)
419 while (*str1 && *str2) {
420 if (GUINT16_TO_LE (*str1) != *str2) {
427 return(*str1 == *str2);
438 /* Returns a pointer to the value data, because there's no way to know
439 * how big that data is (value_len is set to zero for most blocks :-( )
442 get_versioninfo_block (gconstpointer data, version_data *block)
444 block->data_len = GUINT16_FROM_LE (*((guint16 *)data));
445 data = (char *)data + sizeof(guint16);
446 block->value_len = GUINT16_FROM_LE (*((guint16 *)data));
447 data = (char *)data + sizeof(guint16);
449 /* No idea what the type is supposed to indicate */
450 block->type = GUINT16_FROM_LE (*((guint16 *)data));
451 data = (char *)data + sizeof(guint16);
452 block->key = ((gunichar2 *)data);
454 /* Skip over the key (including the terminator) */
455 data = ((gunichar2 *)data) + (unicode_chars (block->key) + 1);
457 /* align on a 32-bit boundary */
464 get_fixedfileinfo_block (gconstpointer data, version_data *block)
466 gconstpointer data_ptr;
467 WapiFixedFileInfo *ffi;
469 data_ptr = get_versioninfo_block (data, block);
471 if (block->value_len != sizeof(WapiFixedFileInfo)) {
472 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: FIXEDFILEINFO size mismatch", __func__);
476 if (!unicode_string_equals (block->key, "VS_VERSION_INFO")) {
477 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: VS_VERSION_INFO mismatch", __func__);
482 ffi = ((WapiFixedFileInfo *)data_ptr);
483 if ((ffi->dwSignature != VS_FFI_SIGNATURE) ||
484 (ffi->dwStrucVersion != VS_FFI_STRUCVERSION)) {
485 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: FIXEDFILEINFO bad signature", __func__);
494 get_varfileinfo_block (gconstpointer data_ptr, version_data *block)
496 /* data is pointing at a Var block
498 data_ptr = get_versioninfo_block (data_ptr, block);
504 get_string_block (gconstpointer data_ptr,
505 const gunichar2 *string_key,
506 gpointer *string_value,
507 guint32 *string_value_len,
510 guint16 data_len = block->data_len;
511 guint16 string_len = 28; /* Length of the StringTable block */
512 char *orig_data_ptr = (char *)data_ptr - 28;
514 /* data_ptr is pointing at an array of one or more String blocks
515 * with total length (not including alignment padding) of
518 while (((char *)data_ptr - (char *)orig_data_ptr) < data_len) {
519 /* align on a 32-bit boundary */
522 data_ptr = get_versioninfo_block (data_ptr, block);
523 if (block->data_len == 0) {
524 /* We must have hit padding, so give up
527 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Hit 0-length block, giving up", __func__);
532 string_len = string_len + block->data_len;
534 if (string_key != NULL &&
535 string_value != NULL &&
536 string_value_len != NULL &&
537 unicode_compare (string_key, block->key) == TRUE) {
538 *string_value = (gpointer)data_ptr;
539 *string_value_len = block->value_len;
542 /* Skip over the value */
543 data_ptr = ((gunichar2 *)data_ptr) + block->value_len;
549 /* Returns a pointer to the byte following the Stringtable block, or
550 * NULL if the data read hits padding. We can't recover from this
551 * because the data length does not include padding bytes, so it's not
552 * possible to just return the start position + length
554 * If lang == NULL it means we're just stepping through this block
557 get_stringtable_block (gconstpointer data_ptr,
559 const gunichar2 *string_key,
560 gpointer *string_value,
561 guint32 *string_value_len,
564 guint16 data_len = block->data_len;
565 guint16 string_len = 36; /* length of the StringFileInfo block */
567 gchar *lowercase_lang;
569 /* data_ptr is pointing at an array of StringTable blocks,
570 * with total length (not including alignment padding) of
574 while(string_len < data_len) {
575 /* align on a 32-bit boundary */
578 data_ptr = get_versioninfo_block (data_ptr, block);
579 if (block->data_len == 0) {
580 /* We must have hit padding, so give up
583 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Hit 0-length block, giving up", __func__);
587 string_len = string_len + block->data_len;
589 found_lang = g_utf16_to_utf8 (block->key, 8, NULL, NULL, NULL);
590 if (found_lang == NULL) {
591 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Didn't find a valid language key, giving up", __func__);
595 lowercase_lang = g_utf8_strdown (found_lang, -1);
597 found_lang = lowercase_lang;
598 lowercase_lang = NULL;
600 if (lang != NULL && !strcmp (found_lang, lang)) {
601 /* Got the one we're interested in */
602 data_ptr = get_string_block (data_ptr, string_key,
604 string_value_len, block);
606 data_ptr = get_string_block (data_ptr, NULL, NULL,
612 if (data_ptr == NULL) {
613 /* Child block hit padding */
614 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Child block hit 0-length block, giving up", __func__);
622 #if G_BYTE_ORDER == G_BIG_ENDIAN
624 big_up_string_block (gconstpointer data_ptr, version_data *block)
626 guint16 data_len = block->data_len;
627 guint16 string_len = 28; /* Length of the StringTable block */
629 char *orig_data_ptr = (char *)data_ptr - 28;
631 /* data_ptr is pointing at an array of one or more String
632 * blocks with total length (not including alignment padding)
635 while (((char *)data_ptr - (char *)orig_data_ptr) < data_len) {
636 /* align on a 32-bit boundary */
639 data_ptr = get_versioninfo_block (data_ptr, block);
640 if (block->data_len == 0) {
641 /* We must have hit padding, so give up
644 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Hit 0-length block, giving up", __func__);
648 string_len = string_len + block->data_len;
650 big_value = g_convert ((gchar *)block->key,
651 unicode_chars (block->key) * 2,
652 "UTF-16BE", "UTF-16LE", NULL, NULL,
654 if (big_value == NULL) {
655 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Didn't find a valid string, giving up", __func__);
659 /* The swapped string should be exactly the same
660 * length as the original little-endian one, but only
661 * copy the number of original chars just to be on the
664 memcpy (block->key, big_value, unicode_chars (block->key) * 2);
667 big_value = g_convert ((gchar *)data_ptr,
668 unicode_chars (data_ptr) * 2,
669 "UTF-16BE", "UTF-16LE", NULL, NULL,
671 if (big_value == NULL) {
672 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Didn't find a valid data string, giving up", __func__);
675 memcpy ((gpointer)data_ptr, big_value,
676 unicode_chars (data_ptr) * 2);
679 data_ptr = ((gunichar2 *)data_ptr) + block->value_len;
685 /* Returns a pointer to the byte following the Stringtable block, or
686 * NULL if the data read hits padding. We can't recover from this
687 * because the data length does not include padding bytes, so it's not
688 * possible to just return the start position + length
691 big_up_stringtable_block (gconstpointer data_ptr, version_data *block)
693 guint16 data_len = block->data_len;
694 guint16 string_len = 36; /* length of the StringFileInfo block */
697 /* data_ptr is pointing at an array of StringTable blocks,
698 * with total length (not including alignment padding) of
702 while(string_len < data_len) {
703 /* align on a 32-bit boundary */
706 data_ptr = get_versioninfo_block (data_ptr, block);
707 if (block->data_len == 0) {
708 /* We must have hit padding, so give up
711 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Hit 0-length block, giving up", __func__);
715 string_len = string_len + block->data_len;
717 big_value = g_convert ((gchar *)block->key, 16, "UTF-16BE",
718 "UTF-16LE", NULL, NULL, NULL);
719 if (big_value == NULL) {
720 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Didn't find a valid string, giving up", __func__);
724 memcpy (block->key, big_value, 16);
727 data_ptr = big_up_string_block (data_ptr, block);
729 if (data_ptr == NULL) {
730 /* Child block hit padding */
731 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Child block hit 0-length block, giving up", __func__);
739 /* Follows the data structures and turns all UTF-16 strings from the
740 * LE found in the resource section into UTF-16BE
743 big_up (gconstpointer datablock, guint32 size)
745 gconstpointer data_ptr;
746 gint32 data_len; /* signed to guard against underflow */
749 data_ptr = get_fixedfileinfo_block (datablock, &block);
750 if (data_ptr != NULL) {
751 WapiFixedFileInfo *ffi = (WapiFixedFileInfo *)data_ptr;
753 /* Byteswap all the fields */
754 ffi->dwFileVersionMS = GUINT32_SWAP_LE_BE (ffi->dwFileVersionMS);
755 ffi->dwFileVersionLS = GUINT32_SWAP_LE_BE (ffi->dwFileVersionLS);
756 ffi->dwProductVersionMS = GUINT32_SWAP_LE_BE (ffi->dwProductVersionMS);
757 ffi->dwProductVersionLS = GUINT32_SWAP_LE_BE (ffi->dwProductVersionLS);
758 ffi->dwFileFlagsMask = GUINT32_SWAP_LE_BE (ffi->dwFileFlagsMask);
759 ffi->dwFileFlags = GUINT32_SWAP_LE_BE (ffi->dwFileFlags);
760 ffi->dwFileOS = GUINT32_SWAP_LE_BE (ffi->dwFileOS);
761 ffi->dwFileType = GUINT32_SWAP_LE_BE (ffi->dwFileType);
762 ffi->dwFileSubtype = GUINT32_SWAP_LE_BE (ffi->dwFileSubtype);
763 ffi->dwFileDateMS = GUINT32_SWAP_LE_BE (ffi->dwFileDateMS);
764 ffi->dwFileDateLS = GUINT32_SWAP_LE_BE (ffi->dwFileDateLS);
766 /* The FFI and header occupies the first 92 bytes
768 data_ptr = (char *)data_ptr + sizeof(WapiFixedFileInfo);
769 data_len = block.data_len - 92;
771 /* There now follow zero or one StringFileInfo blocks
772 * and zero or one VarFileInfo blocks
774 while (data_len > 0) {
775 /* align on a 32-bit boundary */
778 data_ptr = get_versioninfo_block (data_ptr, &block);
779 if (block.data_len == 0) {
780 /* We must have hit padding, so give
783 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Hit 0-length block, giving up", __func__);
787 data_len = data_len - block.data_len;
789 if (unicode_string_equals (block.key, "VarFileInfo")) {
790 data_ptr = get_varfileinfo_block (data_ptr,
792 data_ptr = ((guchar *)data_ptr) + block.value_len;
793 } else if (unicode_string_equals (block.key,
795 data_ptr = big_up_stringtable_block (data_ptr,
799 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Not a valid VERSIONINFO child block", __func__);
803 if (data_ptr == NULL) {
804 /* Child block hit padding */
805 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Child block hit 0-length block, giving up", __func__);
814 VerQueryValue (gconstpointer datablock, const gunichar2 *subblock, gpointer *buffer, guint32 *len)
816 gchar *subblock_utf8, *lang_utf8 = NULL;
817 gboolean ret = FALSE;
819 gconstpointer data_ptr;
820 gint32 data_len; /* signed to guard against underflow */
821 gboolean want_var = FALSE;
822 gboolean want_string = FALSE;
824 const gunichar2 *string_key = NULL;
825 gpointer string_value = NULL;
826 guint32 string_value_len = 0;
827 gchar *lowercase_lang;
829 subblock_utf8 = g_utf16_to_utf8 (subblock, -1, NULL, NULL, NULL);
830 if (subblock_utf8 == NULL) {
834 if (!strcmp (subblock_utf8, "\\VarFileInfo\\Translation")) {
836 } else if (!strncmp (subblock_utf8, "\\StringFileInfo\\", 16)) {
838 memcpy (lang, subblock + 16, 8 * sizeof(gunichar2));
839 lang_utf8 = g_utf16_to_utf8 (lang, 8, NULL, NULL, NULL);
840 lowercase_lang = g_utf8_strdown (lang_utf8, -1);
842 lang_utf8 = lowercase_lang;
843 lowercase_lang = NULL;
844 string_key = subblock + 25;
847 if (!strcmp (subblock_utf8, "\\")) {
848 data_ptr = get_fixedfileinfo_block (datablock, &block);
849 if (data_ptr != NULL) {
850 *buffer = (gpointer)data_ptr;
851 *len = block.value_len;
855 } else if (want_var || want_string) {
856 data_ptr = get_fixedfileinfo_block (datablock, &block);
857 if (data_ptr != NULL) {
858 /* The FFI and header occupies the first 92
861 data_ptr = (char *)data_ptr + sizeof(WapiFixedFileInfo);
862 data_len = block.data_len - 92;
864 /* There now follow zero or one StringFileInfo
865 * blocks and zero or one VarFileInfo blocks
867 while (data_len > 0) {
868 /* align on a 32-bit boundary */
871 data_ptr = get_versioninfo_block (data_ptr,
873 if (block.data_len == 0) {
874 /* We must have hit padding,
875 * so give up processing now
877 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Hit 0-length block, giving up", __func__);
881 data_len = data_len - block.data_len;
883 if (unicode_string_equals (block.key, "VarFileInfo")) {
884 data_ptr = get_varfileinfo_block (data_ptr, &block);
886 *buffer = (gpointer)data_ptr;
887 *len = block.value_len;
891 /* Skip over the Var block */
892 data_ptr = ((guchar *)data_ptr) + block.value_len;
894 } else if (unicode_string_equals (block.key, "StringFileInfo")) {
895 data_ptr = get_stringtable_block (data_ptr, lang_utf8, string_key, &string_value, &string_value_len, &block);
897 string_value != NULL &&
898 string_value_len != 0) {
899 *buffer = string_value;
900 *len = unicode_chars ((const gunichar2 *)string_value) + 1; /* Include trailing null */
906 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Not a valid VERSIONINFO child block", __func__);
910 if (data_ptr == NULL) {
911 /* Child block hit padding */
912 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Child block hit 0-length block, giving up", __func__);
924 g_free (subblock_utf8);
929 GetFileVersionInfoSize (gunichar2 *filename, guint32 *handle)
932 gpointer versioninfo;
937 /* This value is unused, but set to zero */
940 file_map = map_pe_file (filename, &map_size, &map_handle);
941 if (file_map == NULL) {
945 versioninfo = find_pe_file_resources (file_map, map_size, RT_VERSION, 0, &size);
946 if (versioninfo == NULL) {
947 /* Didn't find the resource, so set the return value
953 unmap_pe_file (file_map, map_handle);
959 GetFileVersionInfo (gunichar2 *filename, guint32 handle G_GNUC_UNUSED, guint32 len, gpointer data)
962 gpointer versioninfo;
966 gboolean ret = FALSE;
968 file_map = map_pe_file (filename, &map_size, &map_handle);
969 if (file_map == NULL) {
973 versioninfo = find_pe_file_resources (file_map, map_size, RT_VERSION,
975 if (versioninfo != NULL) {
976 /* This could probably process the data so that
977 * VerQueryValue() doesn't have to follow the data
978 * blocks every time. But hey, these functions aren't
979 * likely to appear in many profiles.
981 memcpy (data, versioninfo, len < size?len:size);
984 #if G_BYTE_ORDER == G_BIG_ENDIAN
989 unmap_pe_file (file_map, map_handle);
995 copy_lang (gunichar2 *lang_out, guint32 lang_len, const gchar *text)
998 int chars = strlen (text);
1001 unitext = g_utf8_to_utf16 (text, -1, NULL, NULL, NULL);
1002 g_assert (unitext != NULL);
1004 if (chars < (lang_len - 1)) {
1005 memcpy (lang_out, (gpointer)unitext, chars * 2);
1006 lang_out[chars] = '\0';
1009 memcpy (lang_out, (gpointer)unitext, (lang_len - 1) * 2);
1010 lang_out[lang_len] = '\0';
1020 VerLanguageName (guint32 lang, gunichar2 *lang_out, guint32 lang_len)
1022 int primary, secondary;
1023 const char *name = NULL;
1025 primary = lang & 0x3FF;
1026 secondary = (lang >> 10) & 0x3F;
1032 name = "Process Default Language";
1040 name = "Arabic (Saudi Arabia)";
1043 name = "Arabic (Iraq)";
1046 name = "Arabic (Egypt)";
1049 name = "Arabic (Libya)";
1052 name = "Arabic (Algeria)";
1055 name = "Arabic (Morocco)";
1058 name = "Arabic (Tunisia)";
1061 name = "Arabic (Oman)";
1064 name = "Arabic (Yemen)";
1067 name = "Arabic (Syria)";
1070 name = "Arabic (Jordan)";
1073 name = "Arabic (Lebanon)";
1076 name = "Arabic (Kuwait)";
1079 name = "Arabic (U.A.E.)";
1082 name = "Arabic (Bahrain)";
1085 name = "Arabic (Qatar)";
1092 name = "Bulgarian (Bulgaria)";
1102 name = "Catalan (Spain)";
1113 name = "Chinese (Taiwan)";
1116 name = "Chinese (PRC)";
1119 name = "Chinese (Hong Kong S.A.R.)";
1122 name = "Chinese (Singapore)";
1125 name = "Chinese (Macau S.A.R.)";
1132 name = "Czech (Czech Republic)";
1142 name = "Danish (Denmark)";
1153 name = "German (Germany)";
1156 name = "German (Switzerland)";
1159 name = "German (Austria)";
1162 name = "German (Luxembourg)";
1165 name = "German (Liechtenstein)";
1172 name = "Greek (Greece)";
1183 name = "English (United States)";
1186 name = "English (United Kingdom)";
1189 name = "English (Australia)";
1192 name = "English (Canada)";
1195 name = "English (New Zealand)";
1198 name = "English (Ireland)";
1201 name = "English (South Africa)";
1204 name = "English (Jamaica)";
1207 name = "English (Caribbean)";
1210 name = "English (Belize)";
1213 name = "English (Trinidad and Tobago)";
1216 name = "English (Zimbabwe)";
1219 name = "English (Philippines)";
1222 name = "English (India)";
1225 name = "English (Malaysia)";
1228 name = "English (Singapore)";
1235 name = "Spanish (Spain)";
1238 name = "Spanish (Traditional Sort)";
1241 name = "Spanish (Mexico)";
1244 name = "Spanish (International Sort)";
1247 name = "Spanish (Guatemala)";
1250 name = "Spanish (Costa Rica)";
1253 name = "Spanish (Panama)";
1256 name = "Spanish (Dominican Republic)";
1259 name = "Spanish (Venezuela)";
1262 name = "Spanish (Colombia)";
1265 name = "Spanish (Peru)";
1268 name = "Spanish (Argentina)";
1271 name = "Spanish (Ecuador)";
1274 name = "Spanish (Chile)";
1277 name = "Spanish (Uruguay)";
1280 name = "Spanish (Paraguay)";
1283 name = "Spanish (Bolivia)";
1286 name = "Spanish (El Salvador)";
1289 name = "Spanish (Honduras)";
1292 name = "Spanish (Nicaragua)";
1295 name = "Spanish (Puerto Rico)";
1298 name = "Spanish (United States)";
1305 name = "Finnish (Finland)";
1316 name = "French (France)";
1319 name = "French (Belgium)";
1322 name = "French (Canada)";
1325 name = "French (Switzerland)";
1328 name = "French (Luxembourg)";
1331 name = "French (Monaco)";
1338 name = "Hebrew (Israel)";
1348 name = "Hungarian (Hungary)";
1358 name = "Icelandic (Iceland)";
1369 name = "Italian (Italy)";
1372 name = "Italian (Switzerland)";
1379 name = "Japanese (Japan)";
1389 name = "Korean (Korea)";
1400 name = "Dutch (Netherlands)";
1403 name = "Dutch (Belgium)";
1411 name = "Norwegian (Bokmal)";
1414 name = "Norwegian (Nynorsk)";
1421 name = "Polish (Poland)";
1432 name = "Portuguese (Brazil)";
1435 name = "Portuguese (Portugal)";
1442 name = "Romansh (Switzerland)";
1449 name = "Romanian (Romania)";
1459 name = "Russian (Russia)";
1469 name = "Croatian (Croatia)";
1475 name = "Serbian (Latin)";
1478 name = "Serbian (Cyrillic)";
1481 name = "Croatian (Bosnia and Herzegovina)";
1484 name = "Bosnian (Latin, Bosnia and Herzegovina)";
1487 name = "Serbian (Latin, Bosnia and Herzegovina)";
1490 name = "Serbian (Cyrillic, Bosnia and Herzegovina)";
1493 name = "Bosnian (Cyrillic, Bosnia and Herzegovina)";
1500 name = "Slovak (Slovakia)";
1510 name = "Albanian (Albania)";
1520 name = "Swedish (Sweden)";
1526 name = "Swedish (Finland)";
1533 name = "Thai (Thailand)";
1543 name = "Turkish (Turkey)";
1553 name = "Urdu (Islamic Republic of Pakistan)";
1563 name = "Indonesian (Indonesia)";
1566 name = "Indonesian";
1573 name = "Ukrainian (Ukraine)";
1583 name = "Belarusian (Belarus)";
1586 name = "Belarusian";
1593 name = "Slovenian (Slovenia)";
1603 name = "Estonian (Estonia)";
1613 name = "Latvian (Latvia)";
1623 name = "Lithuanian (Lithuania)";
1626 name = "Lithuanian";
1633 name = "Tajik (Tajikistan)";
1640 name = "Farsi (Iran)";
1650 name = "Vietnamese (Viet Nam)";
1653 name = "Vietnamese";
1660 name = "Armenian (Armenia)";
1670 name = "Azeri (Latin) (Azerbaijan)";
1673 name = "Azeri (Latin)";
1676 name = "Azeri (Cyrillic)";
1683 name = "Basque (Spain)";
1693 name = "Upper Sorbian (Germany)";
1696 name = "Lower Sorbian (Germany)";
1703 name = "FYRO Macedonian (Former Yugoslav Republic of Macedonia)";
1706 name = "FYRO Macedonian";
1713 name = "Tswana (South Africa)";
1723 name = "Xhosa (South Africa)";
1733 name = "Zulu (South Africa)";
1743 name = "Afrikaans (South Africa)";
1753 name = "Georgian (Georgia)";
1763 name = "Faroese (Faroe Islands)";
1773 name = "Hindi (India)";
1783 name = "Maltese (Malta)";
1793 name = "Sami (Northern) (Norway)";
1796 name = "Sami, Northern (Norway)";
1799 name = "Sami, Northern (Sweden)";
1802 name = "Sami, Northern (Finland)";
1805 name = "Sami, Lule (Norway)";
1808 name = "Sami, Lule (Sweden)";
1811 name = "Sami, Southern (Norway)";
1814 name = "Sami, Southern (Sweden)";
1817 name = "Sami, Skolt (Finland)";
1820 name = "Sami, Inari (Finland)";
1827 name = "Irish (Ireland)";
1835 name = "Malay (Malaysia)";
1838 name = "Malay (Brunei Darussalam)";
1845 name = "Kazakh (Kazakhstan)";
1855 name = "Kyrgyz (Kyrgyzstan)";
1858 name = "Kyrgyz (Cyrillic)";
1865 name = "Swahili (Kenya)";
1875 name = "Turkmen (Turkmenistan)";
1882 name = "Uzbek (Latin) (Uzbekistan)";
1885 name = "Uzbek (Latin)";
1888 name = "Uzbek (Cyrillic)";
1895 name = "Tatar (Russia)";
1906 name = "Bengali (India)";
1913 name = "Punjabi (India)";
1923 name = "Gujarati (India)";
1933 name = "Tamil (India)";
1943 name = "Telugu (India)";
1953 name = "Kannada (India)";
1964 name = "Malayalam (India)";
1971 name = "Assamese (India)";
1978 name = "Marathi (India)";
1988 name = "Sanskrit (India)";
1998 name = "Mongolian (Mongolia)";
2001 name = "Mongolian (Cyrillic)";
2004 name = "Mongolian (PRC)";
2011 name = "Tibetan (PRC)";
2014 name = "Tibetan (Bhutan)";
2021 name = "Welsh (United Kingdom)";
2031 name = "Khmer (Cambodia)";
2038 name = "Lao (Lao PDR)";
2045 name = "Galician (Spain)";
2055 name = "Konkani (India)";
2065 name = "Syriac (Syria)";
2075 name = "Sinhala (Sri Lanka)";
2082 name = "Inuktitut (Syllabics, Canada)";
2085 name = "Inuktitut (Latin, Canada)";
2092 name = "Amharic (Ethiopia)";
2099 name = "Tamazight (Algeria, Latin)";
2106 name = "Nepali (Nepal)";
2113 name = "Frisian (Netherlands)";
2120 name = "Pashto (Afghanistan)";
2127 name = "Filipino (Philippines)";
2134 name = "Divehi (Maldives)";
2144 name = "Hausa (Nigeria, Latin)";
2151 name = "Yoruba (Nigeria)";
2159 name = "Quechua (Bolivia)";
2162 name = "Quechua (Ecuador)";
2165 name = "Quechua (Peru)";
2172 name = "Northern Sotho (South Africa)";
2175 name = "Northern Sotho";
2182 name = "Bashkir (Russia)";
2189 name = "Luxembourgish (Luxembourg)";
2196 name = "Greenlandic (Greenland)";
2210 name = "Mapudungun (Chile)";
2217 name = "Mohawk (Mohawk)";
2224 name = "Breton (France)";
2231 name = "Invariant Language (Invariant Country)";
2238 name = "Uighur (PRC)";
2245 name = "Maori (New Zealand)";
2255 name = "Corsican (France)";
2262 name = "Alsatian (France)";
2269 name = "Yakut (Russia)";
2276 name = "K'iche (Guatemala)";
2283 name = "Kinyarwanda (Rwanda)";
2290 name = "Wolof (Senegal)";
2297 name = "Dari (Afghanistan)";
2303 name = "Language Neutral";
2308 name = "Language Neutral";
2310 return copy_lang (lang_out, lang_len, name);