| 1 | // Licensed to the .NET Foundation under one or more agreements. |
| 2 | // The .NET Foundation licenses this file to you under the MIT license. |
| 3 | // See the LICENSE file in the project root for more information. |
| 4 | |
| 5 | #include <stdio.h> |
| 6 | #include <ctype.h> |
| 7 | #include <crtdbg.h> |
| 8 | #include "mdinfo.h" |
| 9 | |
| 10 | #ifndef STRING_BUFFER_LEN |
| 11 | #define STRING_BUFFER_LEN 4096 |
| 12 | #endif |
| 13 | |
| 14 | #define OBJ_EXT ".obj" |
| 15 | #define OBJ_EXT_W W(".obj") |
| 16 | #define OBJ_EXT_LEN 4 |
| 17 | #define LIB_EXT ".lib" |
| 18 | #define LIB_EXT_W W(".lib") |
| 19 | #define LIB_EXT_LEN 4 |
| 20 | |
| 21 | extern IMetaDataDispenserEx *g_pDisp; |
| 22 | extern DWORD g_ValModuleType; |
| 23 | |
| 24 | // This function is copied from peparse.c file. Making this static, so we won't end up with |
| 25 | // duplicate definitions causing confusion. |
| 26 | static const char g_szCORMETA[] = ".cormeta" ; |
| 27 | static HRESULT FindObjMetaData(PVOID pImage, PVOID *ppMetaData, long *pcbMetaData) |
| 28 | { |
| 29 | IMAGE_FILE_HEADER *pImageHdr; // Header for the .obj file. |
| 30 | IMAGE_SECTION_HEADER *pSectionHdr; // Section header. |
| 31 | WORD i; // Loop control. |
| 32 | |
| 33 | // Get a pointer to the header and the first section. |
| 34 | pImageHdr = (IMAGE_FILE_HEADER *) pImage; |
| 35 | pSectionHdr = (IMAGE_SECTION_HEADER *)(pImageHdr + 1); |
| 36 | |
| 37 | // Avoid confusion. |
| 38 | *ppMetaData = NULL; |
| 39 | *pcbMetaData = 0; |
| 40 | |
| 41 | // Walk each section looking for .cormeta. |
| 42 | for (i=0; i<VAL16(pImageHdr->NumberOfSections); i++, pSectionHdr++) |
| 43 | { |
| 44 | // Simple comparison to section name. |
| 45 | if (strcmp((const char *) pSectionHdr->Name, g_szCORMETA) == 0) |
| 46 | { |
| 47 | *pcbMetaData = VAL32(pSectionHdr->SizeOfRawData); |
| 48 | *ppMetaData = (void *) ((UINT_PTR)pImage + VAL32(pSectionHdr->PointerToRawData)); |
| 49 | break; |
| 50 | } |
| 51 | } |
| 52 | |
| 53 | // Check for errors. |
| 54 | if (*ppMetaData == NULL || *pcbMetaData == 0) |
| 55 | return (E_FAIL); |
| 56 | return (S_OK); |
| 57 | } |
| 58 | |
| 59 | |
| 60 | // This function returns the address to the MapView of file and file size. |
| 61 | void GetMapViewOfFile(__in wchar_t *szFile, PBYTE *ppbMap, DWORD *pdwFileSize) |
| 62 | { |
| 63 | HANDLE hMapFile; |
| 64 | DWORD dwHighSize; |
| 65 | |
| 66 | HANDLE hFile = WszCreateFile(szFile, |
| 67 | GENERIC_READ, |
| 68 | FILE_SHARE_READ, |
| 69 | NULL, |
| 70 | OPEN_EXISTING, |
| 71 | FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, |
| 72 | NULL); |
| 73 | if (hFile == INVALID_HANDLE_VALUE) |
| 74 | MDInfo::Error("CreateFileA failed!" ); |
| 75 | |
| 76 | *pdwFileSize = GetFileSize(hFile, &dwHighSize); |
| 77 | |
| 78 | if ((*pdwFileSize == 0xFFFFFFFF) && (GetLastError() != NO_ERROR)) |
| 79 | { |
| 80 | CloseHandle(hFile); |
| 81 | MDInfo::Error("GetFileSize failed!" ); |
| 82 | } |
| 83 | _ASSERTE(dwHighSize == 0); |
| 84 | |
| 85 | hMapFile = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, NULL); |
| 86 | CloseHandle(hFile); |
| 87 | if (!hMapFile) |
| 88 | MDInfo::Error("CreateFileMappingA failed!" ); |
| 89 | |
| 90 | *ppbMap = (PBYTE) MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 0); |
| 91 | CloseHandle(hMapFile); |
| 92 | |
| 93 | if (!*ppbMap) |
| 94 | MDInfo::Error("MapViewOfFile failed!" ); |
| 95 | } // void GetMapViewOfFile() |
| 96 | |
| 97 | // This function skips a member given the pointer to the member header |
| 98 | // and returns a pointer to the next header. |
| 99 | PBYTE SkipMember(PBYTE pbMapAddress) |
| 100 | { |
| 101 | PIMAGE_ARCHIVE_MEMBER_HEADER pMemHdr; |
| 102 | ULONG ulMemSize; |
| 103 | int j; |
| 104 | |
| 105 | pMemHdr = (PIMAGE_ARCHIVE_MEMBER_HEADER)pbMapAddress; |
| 106 | |
| 107 | // Get size of the member. |
| 108 | ulMemSize = 0; |
| 109 | for (j = 0; j < 10; j++) |
| 110 | { |
| 111 | if (pMemHdr->Size[j] < '0' || pMemHdr->Size[j] > '9') |
| 112 | break; |
| 113 | else |
| 114 | ulMemSize = ulMemSize * 10 + pMemHdr->Size[j] - '0'; |
| 115 | } |
| 116 | |
| 117 | // Skip past the header. |
| 118 | pbMapAddress += IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR + ulMemSize; |
| 119 | // Find the next even address if the current one is not even. |
| 120 | if ((ULONG_PTR)pbMapAddress % 2) |
| 121 | pbMapAddress++; |
| 122 | |
| 123 | return pbMapAddress; |
| 124 | } // void SkipMember() |
| 125 | |
| 126 | // This function returns the name of the given Obj. If the name fits in the header, |
| 127 | // szBuf will be filled in and returned from the function. Else an offset into the long |
| 128 | // names section will be returned. |
| 129 | char *(PBYTE pbLongNames, PIMAGE_ARCHIVE_MEMBER_HEADER pMemHdr, char szBuf[17]) |
| 130 | { |
| 131 | if (pMemHdr->Name[0] == '/') |
| 132 | { |
| 133 | ULONG ulOffset = 0; |
| 134 | |
| 135 | // Long Names section must exist if the .obj file name starts with '/'. |
| 136 | _ASSERTE(pbLongNames && |
| 137 | "Corrupt archive file - .obj file name in the header starts with " |
| 138 | "'/' but no long names section present in the archive file." ); |
| 139 | |
| 140 | // Calculate the offset into the long names section. |
| 141 | for (int j = 1; j < 16; j++) |
| 142 | { |
| 143 | if (pMemHdr->Name[j] < '0' || pMemHdr->Name[j] > '9') |
| 144 | break; |
| 145 | else |
| 146 | ulOffset = ulOffset * 10 + pMemHdr->Name[j] - '0'; |
| 147 | } |
| 148 | return (char *)(pbLongNames + ulOffset); |
| 149 | } |
| 150 | else |
| 151 | { |
| 152 | int j; |
| 153 | for (j = 0; j < 16; j++) |
| 154 | if ((szBuf[j] = pMemHdr->Name[j]) == '/') |
| 155 | break; |
| 156 | szBuf[j] = '\0'; |
| 157 | return szBuf; |
| 158 | } |
| 159 | } // char *GetNameOfObj() |
| 160 | |
| 161 | // DisplayArchive() function |
| 162 | // |
| 163 | // Opens the .LIB file, and displays the metadata in the specified object files. |
| 164 | |
| 165 | void DisplayArchive(__in_z __in wchar_t* szFile, ULONG DumpFilter, __in_z __in_opt wchar_t* szObjName, strPassBackFn pDisplayString) |
| 166 | { |
| 167 | PBYTE pbMapAddress; |
| 168 | PBYTE pbStartAddress; |
| 169 | PBYTE pbLongNameAddress; |
| 170 | PIMAGE_ARCHIVE_MEMBER_HEADER pMemHdr; |
| 171 | DWORD dwFileSize; |
| 172 | PVOID pvMetaData; |
| 173 | char *szName; |
| 174 | wchar_t wzName[1024]; |
| 175 | char szBuf[17]; |
| 176 | long cbMetaData; |
| 177 | int i; |
| 178 | HRESULT hr; |
| 179 | char szString[1024]; |
| 180 | |
| 181 | GetMapViewOfFile(szFile, &pbMapAddress, &dwFileSize); |
| 182 | pbStartAddress = pbMapAddress; |
| 183 | |
| 184 | // Verify and skip archive signature. |
| 185 | if (dwFileSize < IMAGE_ARCHIVE_START_SIZE || |
| 186 | strncmp((char *)pbMapAddress, IMAGE_ARCHIVE_START, IMAGE_ARCHIVE_START_SIZE)) |
| 187 | { |
| 188 | MDInfo::Error("Bad file format - archive signature mis-match!" ); |
| 189 | } |
| 190 | pbMapAddress += IMAGE_ARCHIVE_START_SIZE; |
| 191 | |
| 192 | // Skip linker member 1, linker member 2. |
| 193 | for (i = 0; i < 2; i++) |
| 194 | pbMapAddress = SkipMember(pbMapAddress); |
| 195 | |
| 196 | // Save address of the long name member and skip it if there exists one. |
| 197 | pMemHdr = (PIMAGE_ARCHIVE_MEMBER_HEADER)pbMapAddress; |
| 198 | if (pMemHdr->Name[0] == '/' && pMemHdr->Name[1] == '/') |
| 199 | { |
| 200 | pbLongNameAddress = pbMapAddress + IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR; |
| 201 | pbMapAddress = SkipMember(pbMapAddress); |
| 202 | } |
| 203 | else |
| 204 | pbLongNameAddress = 0; |
| 205 | |
| 206 | pDisplayString ("\n" ); |
| 207 | // Get the MetaData for each object file and display it. |
| 208 | while (DWORD(pbMapAddress - pbStartAddress) < dwFileSize) |
| 209 | { |
| 210 | if((szName = GetNameOfObj(pbLongNameAddress, (PIMAGE_ARCHIVE_MEMBER_HEADER)pbMapAddress, szBuf))!=NULL) |
| 211 | { |
| 212 | if (Wsz_mbstowcs(wzName, szName, 1024) == -1) |
| 213 | MDInfo::Error("Conversion from Multi-Byte to Wide-Char failed." ); |
| 214 | |
| 215 | // Display metadata only for object files. |
| 216 | // If szObjName is specified, display metadata only for that one object file. |
| 217 | if (!_stricmp(&szName[strlen(szName) - OBJ_EXT_LEN], OBJ_EXT) && |
| 218 | (!szObjName || !_wcsicmp(szObjName, wzName))) |
| 219 | { |
| 220 | // Try to find the MetaData section in the current object file. |
| 221 | hr = FindObjMetaData(pbMapAddress+IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR, &pvMetaData, &cbMetaData); |
| 222 | if (SUCCEEDED(hr)) |
| 223 | { |
| 224 | sprintf_s (szString,1024,"MetaData for object file %s:\n" , szName); |
| 225 | pDisplayString(szString); |
| 226 | MDInfo archiveInfo(g_pDisp, |
| 227 | (PBYTE)pvMetaData, |
| 228 | cbMetaData, |
| 229 | pDisplayString, |
| 230 | DumpFilter); |
| 231 | archiveInfo.DisplayMD(); |
| 232 | } |
| 233 | else |
| 234 | { |
| 235 | sprintf_s(szString,1024,"MetaData not found for object file %s!\n\n" , szName); |
| 236 | pDisplayString(szString); |
| 237 | } |
| 238 | } |
| 239 | } |
| 240 | |
| 241 | // Skip past the object file. |
| 242 | pbMapAddress = SkipMember(pbMapAddress); |
| 243 | } |
| 244 | |
| 245 | UnmapViewOfFile(pbStartAddress); |
| 246 | } // void DisplayArchive() |
| 247 | |
| 248 | // DisplayFile() function |
| 249 | // |
| 250 | // Opens the meta data content of a .EXE, .CLB, .CLASS, .TLB, .DLL or .LIB file, and |
| 251 | // calls RawDisplay() |
| 252 | |
| 253 | void DisplayFile(__in_z __in wchar_t* szFile, BOOL isFile, ULONG DumpFilter, __in_z __in_opt wchar_t* szObjName, strPassBackFn pDisplayString) |
| 254 | { |
| 255 | // Open the emit scope |
| 256 | |
| 257 | // We need to make sure this file isn't too long. Checking _MAX_PATH is probably safe, but since we have a much |
| 258 | // larger buffer, we might as well use it all. |
| 259 | if (wcslen(szFile) > 1000) |
| 260 | return; |
| 261 | |
| 262 | |
| 263 | WCHAR szScope[1024]; |
| 264 | char szString[1024]; |
| 265 | |
| 266 | if (isFile) |
| 267 | { |
| 268 | wcscpy_s(szScope, 1024, W("file:" )); |
| 269 | wcscat_s(szScope, 1024, szFile); |
| 270 | } |
| 271 | else |
| 272 | wcscpy_s(szScope, 1024, szFile); |
| 273 | |
| 274 | // print bar that separates different files |
| 275 | pDisplayString("////////////////////////////////////////////////////////////////\n" ); |
| 276 | wchar_t rcFname[_MAX_FNAME], rcExt[_MAX_EXT]; |
| 277 | |
| 278 | _wsplitpath_s(szFile, NULL, 0, NULL, 0, rcFname, _MAX_FNAME, rcExt, _MAX_EXT); |
| 279 | sprintf_s(szString,1024,"\nFile %S%S: \n" ,rcFname, rcExt); |
| 280 | pDisplayString(szString); |
| 281 | |
| 282 | if (DumpFilter & MDInfo::dumpValidate) |
| 283 | { |
| 284 | if (!_wcsicmp(rcExt, OBJ_EXT_W) || !_wcsicmp(rcExt, LIB_EXT_W)) |
| 285 | g_ValModuleType = ValidatorModuleTypeObj; |
| 286 | else |
| 287 | g_ValModuleType = ValidatorModuleTypePE; |
| 288 | } |
| 289 | |
| 290 | if (!_wcsicmp(rcExt, LIB_EXT_W)) |
| 291 | DisplayArchive(szFile, DumpFilter, szObjName, pDisplayString); |
| 292 | else |
| 293 | { |
| 294 | MDInfo metaDataInfo(g_pDisp, szScope, pDisplayString, DumpFilter); |
| 295 | metaDataInfo.DisplayMD(); |
| 296 | } |
| 297 | } // void DisplayFile() |
| 298 | |
| 299 | |