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
21extern IMetaDataDispenserEx *g_pDisp;
22extern 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.
26static const char g_szCORMETA[] = ".cormeta";
27static 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.
61void 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.
99PBYTE 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.
129char *GetNameOfObj(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
165void 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
253void 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