1 /*
2     Copyright (c) 2005-2022 Intel Corporation
3 
4     Licensed under the Apache License, Version 2.0 (the "License");
5     you may not use this file except in compliance with the License.
6     You may obtain a copy of the License at
7 
8         http://www.apache.org/licenses/LICENSE-2.0
9 
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
15 */
16 
17 #include "oneapi/tbb/detail/_config.h"
18 #include "oneapi/tbb/detail/_assert.h"
19 #include "../tbb/assert_impl.h"
20 
21 #if !__TBB_WIN8UI_SUPPORT && defined(_WIN32)
22 
23 #ifndef _CRT_SECURE_NO_DEPRECATE
24 #define _CRT_SECURE_NO_DEPRECATE 1
25 #endif
26 
27 // no standard-conforming implementation of snprintf prior to VS 2015
28 #if !defined(_MSC_VER) || _MSC_VER>=1900
29 #define LOG_PRINT(s, n, format, ...) snprintf(s, n, format, __VA_ARGS__)
30 #else
31 #define LOG_PRINT(s, n, format, ...) _snprintf_s(s, n, _TRUNCATE, format, __VA_ARGS__)
32 #endif
33 
34 #include <windows.h>
35 #include <new>
36 #include <stdio.h>
37 #include <string.h>
38 
39 #include "function_replacement.h"
40 
41 // The information about a standard memory allocation function for the replacement log
42 struct FunctionInfo {
43     const char* funcName;
44     const char* dllName;
45 };
46 
47 // Namespace that processes and manages the output of records to the Log journal
48 // that will be provided to user by TBB_malloc_replacement_log()
49 namespace Log {
50     // Value of RECORDS_COUNT is set due to the fact that we maximally
51     // scan 8 modules, and in every module we can swap 6 opcodes. (rounded to 8)
52     static const unsigned RECORDS_COUNT = 8 * 8;
53     static const unsigned RECORD_LENGTH = MAX_PATH;
54 
55     // Need to add 1 to count of records, because last record must be always nullptr
56     static char *records[RECORDS_COUNT + 1];
57     static bool replacement_status = true;
58 
59     // Internal counter that contains number of next string for record
60     static unsigned record_number = 0;
61 
62     // Function that writes info about (not)found opcodes to the Log journal
63     // functionInfo - information about a standard memory allocation function for the replacement log
64     // opcodeString - string, that contain byte code of this function
65     // status - information about function replacement status
record(FunctionInfo functionInfo,const char * opcodeString,bool status)66     static void record(FunctionInfo functionInfo, const char * opcodeString, bool status) {
67         __TBB_ASSERT(functionInfo.dllName, "Empty DLL name value");
68         __TBB_ASSERT(functionInfo.funcName, "Empty function name value");
69         __TBB_ASSERT(opcodeString, "Empty opcode");
70         __TBB_ASSERT(record_number <= RECORDS_COUNT, "Incorrect record number");
71 
72         //If some replacement failed -> set status to false
73         replacement_status &= status;
74 
75         // If we reach the end of the log, write this message to the last line
76         if (record_number == RECORDS_COUNT) {
77             // %s - workaround to fix empty variable argument parsing behavior in GCC
78             LOG_PRINT(records[RECORDS_COUNT - 1], RECORD_LENGTH, "%s", "Log was truncated.");
79             return;
80         }
81 
82         char* entry = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, RECORD_LENGTH);
83         __TBB_ASSERT(entry, "Invalid memory was returned");
84 
85         LOG_PRINT(entry, RECORD_LENGTH, "%s: %s (%s), byte pattern: <%s>",
86             status ? "Success" : "Fail", functionInfo.funcName, functionInfo.dllName, opcodeString);
87 
88         records[record_number++] = entry;
89     }
90 };
91 
Ptr2Addrint(LPVOID ptr)92 inline UINT_PTR Ptr2Addrint(LPVOID ptr)
93 {
94     Int2Ptr i2p;
95     i2p.lpv = ptr;
96     return i2p.uip;
97 }
98 
Addrint2Ptr(UINT_PTR ptr)99 inline LPVOID Addrint2Ptr(UINT_PTR ptr)
100 {
101     Int2Ptr i2p;
102     i2p.uip = ptr;
103     return i2p.lpv;
104 }
105 
106 // Is the distance between addr1 and addr2 smaller than dist
IsInDistance(UINT_PTR addr1,UINT_PTR addr2,__int64 dist)107 inline bool IsInDistance(UINT_PTR addr1, UINT_PTR addr2, __int64 dist)
108 {
109     __int64 diff = addr1>addr2 ? addr1-addr2 : addr2-addr1;
110     return diff<dist;
111 }
112 
113 /*
114  * When inserting a probe in 64 bits process the distance between the insertion
115  * point and the target may be bigger than 2^32. In this case we are using
116  * indirect jump through memory where the offset to this memory location
117  * is smaller than 2^32 and it contains the absolute address (8 bytes).
118  *
119  * This class is used to hold the pages used for the above trampolines.
120  * Since this utility will be used to replace malloc functions this implementation
121  * doesn't allocate memory dynamically.
122  *
123  * The struct MemoryBuffer holds the data about a page in the memory used for
124  * replacing functions in 64-bit code where the target is too far to be replaced
125  * with a short jump. All the calculations of m_base and m_next are in a multiple
126  * of SIZE_OF_ADDRESS (which is 8 in Win64).
127  */
128 class MemoryProvider {
129 private:
130     struct MemoryBuffer {
131         UINT_PTR m_base;    // base address of the buffer
132         UINT_PTR m_next;    // next free location in the buffer
133         DWORD    m_size;    // size of buffer
134 
135         // Default constructor
MemoryBufferMemoryProvider::MemoryBuffer136         MemoryBuffer() : m_base(0), m_next(0), m_size(0) {}
137 
138         // Constructor
MemoryBufferMemoryProvider::MemoryBuffer139         MemoryBuffer(void *base, DWORD size)
140         {
141             m_base = Ptr2Addrint(base);
142             m_next = m_base;
143             m_size = size;
144         }
145     };
146 
CreateBuffer(UINT_PTR addr)147 MemoryBuffer *CreateBuffer(UINT_PTR addr)
148     {
149         // No more room in the pages database
150         if (m_lastBuffer - m_pages == MAX_NUM_BUFFERS)
151             return 0;
152 
153         void *newAddr = Addrint2Ptr(addr);
154         // Get information for the region which the given address belongs to
155         MEMORY_BASIC_INFORMATION memInfo;
156         if (VirtualQuery(newAddr, &memInfo, sizeof(memInfo)) != sizeof(memInfo))
157             return 0;
158 
159         for(;;) {
160             // The new address to check is beyond the current region and aligned to allocation size
161             newAddr = Addrint2Ptr( (Ptr2Addrint(memInfo.BaseAddress) + memInfo.RegionSize + m_allocSize) & ~(UINT_PTR)(m_allocSize-1) );
162 
163             // Check that the address is in the right distance.
164             // VirtualAlloc can only round the address down; so it will remain in the right distance
165             if (!IsInDistance(addr, Ptr2Addrint(newAddr), MAX_DISTANCE))
166                 break;
167 
168             if (VirtualQuery(newAddr, &memInfo, sizeof(memInfo)) != sizeof(memInfo))
169                 break;
170 
171             if (memInfo.State == MEM_FREE && memInfo.RegionSize >= m_allocSize)
172             {
173                 // Found a free region, try to allocate a page in this region
174                 void *newPage = VirtualAlloc(newAddr, m_allocSize, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
175                 if (!newPage)
176                     break;
177 
178                 // Add the new page to the pages database
179                 MemoryBuffer *pBuff = new (m_lastBuffer) MemoryBuffer(newPage, m_allocSize);
180                 ++m_lastBuffer;
181                 return pBuff;
182             }
183         }
184 
185         // Failed to find a buffer in the distance
186         return 0;
187     }
188 
189 public:
MemoryProvider()190     MemoryProvider()
191     {
192         SYSTEM_INFO sysInfo;
193         GetSystemInfo(&sysInfo);
194         m_allocSize = sysInfo.dwAllocationGranularity;
195         m_lastBuffer = &m_pages[0];
196     }
197 
198     // We can't free the pages in the destructor because the trampolines
199     // are using these memory locations and a replaced function might be called
200     // after the destructor was called.
~MemoryProvider()201     ~MemoryProvider()
202     {
203     }
204 
205     // Return a memory location in distance less than 2^31 from input address
GetLocation(UINT_PTR addr)206     UINT_PTR GetLocation(UINT_PTR addr)
207     {
208         MemoryBuffer *pBuff = m_pages;
209         for (; pBuff<m_lastBuffer && IsInDistance(pBuff->m_next, addr, MAX_DISTANCE); ++pBuff)
210         {
211             if (pBuff->m_next < pBuff->m_base + pBuff->m_size)
212             {
213                 UINT_PTR loc = pBuff->m_next;
214                 pBuff->m_next += MAX_PROBE_SIZE;
215                 return loc;
216             }
217         }
218 
219         pBuff = CreateBuffer(addr);
220         if(!pBuff)
221             return 0;
222 
223         UINT_PTR loc = pBuff->m_next;
224         pBuff->m_next += MAX_PROBE_SIZE;
225         return loc;
226     }
227 
228 private:
229     MemoryBuffer m_pages[MAX_NUM_BUFFERS];
230     MemoryBuffer *m_lastBuffer;
231     DWORD m_allocSize;
232 };
233 
234 static MemoryProvider memProvider;
235 
236 // Compare opcodes from dictionary (str1) and opcodes from code (str2)
237 // str1 might contain '*' to mask addresses
238 // RETURN: 0 if opcodes did not match, 1 on success
compareStrings(const char * str1,const char * str2)239 size_t compareStrings( const char *str1, const char *str2 )
240 {
241    for (size_t i=0; str1[i]!=0; i++){
242        if( str1[i]!='*' && str1[i]!='#' && str1[i]!=str2[i] ) return 0;
243    }
244    return 1;
245 }
246 
247 // Check function prologue with known prologues from the dictionary
248 // opcodes - dictionary
249 // inpAddr - pointer to function prologue
250 // Dictionary contains opcodes for several full asm instructions
251 // + one opcode byte for the next asm instruction for safe address processing
252 // RETURN: 1 + the index of the matched pattern, or 0 if no match found.
CheckOpcodes(const char ** opcodes,void * inpAddr,bool abortOnError,const FunctionInfo * functionInfo=nullptr)253 static UINT CheckOpcodes( const char ** opcodes, void *inpAddr, bool abortOnError, const FunctionInfo* functionInfo = nullptr)
254 {
255     static size_t opcodesStringsCount = 0;
256     static size_t maxOpcodesLength = 0;
257     static size_t opcodes_pointer = (size_t)opcodes;
258     char opcodeString[2*MAX_PATTERN_SIZE+1];
259     size_t i;
260     size_t result = 0;
261 
262     // Get the values for static variables
263     // max length and number of patterns
264     if( !opcodesStringsCount || opcodes_pointer != (size_t)opcodes ){
265         while( *(opcodes + opcodesStringsCount)!= nullptr ){
266             if( (i=strlen(*(opcodes + opcodesStringsCount))) > maxOpcodesLength )
267                 maxOpcodesLength = i;
268             opcodesStringsCount++;
269         }
270         opcodes_pointer = (size_t)opcodes;
271         __TBB_ASSERT( maxOpcodesLength/2 <= MAX_PATTERN_SIZE, "Pattern exceeded the limit of 28 opcodes/56 symbols" );
272     }
273 
274     // Translate prologue opcodes to string format to compare
275     for( i=0; i<maxOpcodesLength/2 && i<MAX_PATTERN_SIZE; ++i ){
276         sprintf( opcodeString + 2*i, "%.2X", *((unsigned char*)inpAddr+i) );
277     }
278     opcodeString[2*i] = 0;
279 
280     // Compare translated opcodes with patterns
281     for( UINT idx=0; idx<opcodesStringsCount; ++idx ){
282         result = compareStrings( opcodes[idx],opcodeString );
283         if( result ) {
284             if (functionInfo) {
285                 Log::record(*functionInfo, opcodeString, /*status*/ true);
286             }
287             return idx + 1; // avoid 0 which indicates a failure
288         }
289     }
290     if (functionInfo) {
291         Log::record(*functionInfo, opcodeString, /*status*/ false);
292     }
293     if (abortOnError) {
294         // Impossibility to find opcodes in the dictionary is a serious issue,
295         // as if we unable to call original function, leak or crash is expected result.
296         __TBB_ASSERT_RELEASE( false, "CheckOpcodes failed" );
297     }
298     return 0;
299 }
300 
301 // Modify offsets in original code after moving it to a trampoline.
302 // We do not have more than one offset to correct in existing opcode patterns.
CorrectOffset(UINT_PTR address,const char * pattern,UINT distance)303 static void CorrectOffset( UINT_PTR address, const char* pattern, UINT distance )
304 {
305     const char* pos = strstr(pattern, "#*******");
306     if( pos ) {
307         address += (pos - pattern)/2; // compute the offset position
308         UINT value;
309         // UINT assignment is not used to avoid potential alignment issues
310         memcpy(&value, Addrint2Ptr(address), sizeof(value));
311         value += distance;
312         memcpy(Addrint2Ptr(address), &value, sizeof(value));
313     }
314 }
315 
316 // Insert jump relative instruction to the input address
317 // RETURN: the size of the trampoline or 0 on failure
InsertTrampoline32(void * inpAddr,void * targetAddr,const char * pattern,void ** storedAddr)318 static DWORD InsertTrampoline32(void *inpAddr, void *targetAddr, const char* pattern, void** storedAddr)
319 {
320     size_t bytesToMove = SIZE_OF_RELJUMP;
321     UINT_PTR srcAddr = Ptr2Addrint(inpAddr);
322     UINT_PTR tgtAddr = Ptr2Addrint(targetAddr);
323     // Check that the target fits in 32 bits
324     if (!IsInDistance(srcAddr, tgtAddr, MAX_DISTANCE))
325         return 0;
326 
327     UINT_PTR offset;
328     UINT offset32;
329     UCHAR *codePtr = (UCHAR *)inpAddr;
330 
331     if ( storedAddr ){ // If requested, store original function code
332         bytesToMove = strlen(pattern)/2-1; // The last byte matching the pattern must not be copied
333         __TBB_ASSERT_RELEASE( bytesToMove >= SIZE_OF_RELJUMP, "Incorrect bytecode pattern?" );
334         UINT_PTR trampAddr = memProvider.GetLocation(srcAddr);
335         if (!trampAddr)
336             return 0;
337         *storedAddr = Addrint2Ptr(trampAddr);
338         // Set 'executable' flag for original instructions in the new place
339         DWORD pageFlags = PAGE_EXECUTE_READWRITE;
340         if (!VirtualProtect(*storedAddr, MAX_PROBE_SIZE, pageFlags, &pageFlags)) return 0;
341         // Copy original instructions to the new place
342         memcpy(*storedAddr, codePtr, bytesToMove);
343         offset = srcAddr - trampAddr;
344         offset32 = (UINT)(offset & 0xFFFFFFFF);
345         CorrectOffset( trampAddr, pattern, offset32 );
346         // Set jump to the code after replacement
347         offset32 -= SIZE_OF_RELJUMP;
348         *(UCHAR*)(trampAddr+bytesToMove) = 0xE9;
349         memcpy((UCHAR*)(trampAddr+bytesToMove+1), &offset32, sizeof(offset32));
350     }
351 
352     // The following will work correctly even if srcAddr>tgtAddr, as long as
353     // address difference is less than 2^31, which is guaranteed by IsInDistance.
354     offset = tgtAddr - srcAddr - SIZE_OF_RELJUMP;
355     offset32 = (UINT)(offset & 0xFFFFFFFF);
356     // Insert the jump to the new code
357     *codePtr = 0xE9;
358     memcpy(codePtr+1, &offset32, sizeof(offset32));
359 
360     // Fill the rest with NOPs to correctly see disassembler of old code in debugger.
361     for( unsigned i=SIZE_OF_RELJUMP; i<bytesToMove; i++ ){
362         *(codePtr+i) = 0x90;
363     }
364 
365     return SIZE_OF_RELJUMP;
366 }
367 
368 // This function is called when the offset doesn't fit in 32 bits
369 // 1  Find and allocate a page in the small distance (<2^31) from input address
370 // 2  Put jump RIP relative indirect through the address in the close page
371 // 3  Put the absolute address of the target in the allocated location
372 // RETURN: the size of the trampoline or 0 on failure
InsertTrampoline64(void * inpAddr,void * targetAddr,const char * pattern,void ** storedAddr)373 static DWORD InsertTrampoline64(void *inpAddr, void *targetAddr, const char* pattern, void** storedAddr)
374 {
375     size_t bytesToMove = SIZE_OF_INDJUMP;
376 
377     UINT_PTR srcAddr = Ptr2Addrint(inpAddr);
378     UINT_PTR tgtAddr = Ptr2Addrint(targetAddr);
379 
380     // Get a location close to the source address
381     UINT_PTR location = memProvider.GetLocation(srcAddr);
382     if (!location)
383         return 0;
384 
385     UINT_PTR offset;
386     UINT offset32;
387     UCHAR *codePtr = (UCHAR *)inpAddr;
388 
389     // Fill the location
390     UINT_PTR *locPtr = (UINT_PTR *)Addrint2Ptr(location);
391     *locPtr = tgtAddr;
392 
393     if ( storedAddr ){ // If requested, store original function code
394         bytesToMove = strlen(pattern)/2-1; // The last byte matching the pattern must not be copied
395         __TBB_ASSERT_RELEASE( bytesToMove >= SIZE_OF_INDJUMP, "Incorrect bytecode pattern?" );
396         UINT_PTR trampAddr = memProvider.GetLocation(srcAddr);
397         if (!trampAddr)
398             return 0;
399         *storedAddr = Addrint2Ptr(trampAddr);
400         // Set 'executable' flag for original instructions in the new place
401         DWORD pageFlags = PAGE_EXECUTE_READWRITE;
402         if (!VirtualProtect(*storedAddr, MAX_PROBE_SIZE, pageFlags, &pageFlags)) return 0;
403         // Copy original instructions to the new place
404         memcpy(*storedAddr, codePtr, bytesToMove);
405         offset = srcAddr - trampAddr;
406         offset32 = (UINT)(offset & 0xFFFFFFFF);
407         CorrectOffset( trampAddr, pattern, offset32 );
408         // Set jump to the code after replacement. It is within the distance of relative jump!
409         offset32 -= SIZE_OF_RELJUMP;
410         *(UCHAR*)(trampAddr+bytesToMove) = 0xE9;
411         memcpy((UCHAR*)(trampAddr+bytesToMove+1), &offset32, sizeof(offset32));
412     }
413 
414     // Fill the buffer
415     offset = location - srcAddr - SIZE_OF_INDJUMP;
416     offset32 = (UINT)(offset & 0xFFFFFFFF);
417     *(codePtr) = 0xFF;
418     *(codePtr+1) = 0x25;
419     memcpy(codePtr+2, &offset32, sizeof(offset32));
420 
421     // Fill the rest with NOPs to correctly see disassembler of old code in debugger.
422     for( unsigned i=SIZE_OF_INDJUMP; i<bytesToMove; i++ ){
423         *(codePtr+i) = 0x90;
424     }
425 
426     return SIZE_OF_INDJUMP;
427 }
428 
429 // Insert a jump instruction in the inpAddr to the targetAddr
430 // 1. Get the memory protection of the page containing the input address
431 // 2. Change the memory protection to writable
432 // 3. Call InsertTrampoline32 or InsertTrampoline64
433 // 4. Restore memory protection
434 // RETURN: FALSE on failure, TRUE on success
InsertTrampoline(void * inpAddr,void * targetAddr,const char ** opcodes,void ** origFunc)435 static bool InsertTrampoline(void *inpAddr, void *targetAddr, const char ** opcodes, void** origFunc)
436 {
437     DWORD probeSize;
438     // Change page protection to EXECUTE+WRITE
439     DWORD origProt = 0;
440     if (!VirtualProtect(inpAddr, MAX_PROBE_SIZE, PAGE_EXECUTE_WRITECOPY, &origProt))
441         return FALSE;
442 
443     const char* pattern = nullptr;
444     if ( origFunc ){ // Need to store original function code
445         UCHAR * const codePtr = (UCHAR *)inpAddr;
446         if ( *codePtr == 0xE9 ){ // JMP relative instruction
447             // For the special case when a system function consists of a single near jump,
448             // instead of moving it somewhere we use the target of the jump as the original function.
449             unsigned offsetInJmp = *(unsigned*)(codePtr + 1);
450             *origFunc = (void*)(Ptr2Addrint(inpAddr) + offsetInJmp + SIZE_OF_RELJUMP);
451             origFunc = nullptr; // now it must be ignored by InsertTrampoline32/64
452         } else {
453             // find the right opcode pattern
454             UINT opcodeIdx = CheckOpcodes( opcodes, inpAddr, /*abortOnError=*/true );
455             __TBB_ASSERT( opcodeIdx > 0, "abortOnError ignored in CheckOpcodes?" );
456             pattern = opcodes[opcodeIdx-1];  // -1 compensates for +1 in CheckOpcodes
457         }
458     }
459 
460     probeSize = InsertTrampoline32(inpAddr, targetAddr, pattern, origFunc);
461     if (!probeSize)
462         probeSize = InsertTrampoline64(inpAddr, targetAddr, pattern, origFunc);
463 
464     // Restore original protection
465     VirtualProtect(inpAddr, MAX_PROBE_SIZE, origProt, &origProt);
466 
467     if (!probeSize)
468         return FALSE;
469 
470     FlushInstructionCache(GetCurrentProcess(), inpAddr, probeSize);
471     FlushInstructionCache(GetCurrentProcess(), origFunc, probeSize);
472 
473     return TRUE;
474 }
475 
476 // Routine to replace the functions
477 // TODO: replace opcodesNumber with opcodes and opcodes number to check if we replace right code.
ReplaceFunctionA(const char * dllName,const char * funcName,FUNCPTR newFunc,const char ** opcodes,FUNCPTR * origFunc)478 FRR_TYPE ReplaceFunctionA(const char *dllName, const char *funcName, FUNCPTR newFunc, const char ** opcodes, FUNCPTR* origFunc)
479 {
480     // Cache the results of the last search for the module
481     // Assume that there was no DLL unload between
482     static char cachedName[MAX_PATH+1];
483     static HMODULE cachedHM = 0;
484 
485     if (!dllName || !*dllName)
486         return FRR_NODLL;
487 
488     if (!cachedHM || strncmp(dllName, cachedName, MAX_PATH) != 0)
489     {
490         // Find the module handle for the input dll
491         HMODULE hModule = GetModuleHandleA(dllName);
492         if (hModule == 0)
493         {
494             // Couldn't find the module with the input name
495             cachedHM = 0;
496             return FRR_NODLL;
497         }
498 
499         cachedHM = hModule;
500         strncpy(cachedName, dllName, MAX_PATH);
501     }
502 
503     FARPROC inpFunc = GetProcAddress(cachedHM, funcName);
504     if (inpFunc == 0)
505     {
506         // Function was not found
507         return FRR_NOFUNC;
508     }
509 
510     if (!InsertTrampoline((void*)inpFunc, (void*)newFunc, opcodes, (void**)origFunc)){
511         // Failed to insert the trampoline to the target address
512         return FRR_FAILED;
513     }
514 
515     return FRR_OK;
516 }
517 
ReplaceFunctionW(const wchar_t * dllName,const char * funcName,FUNCPTR newFunc,const char ** opcodes,FUNCPTR * origFunc)518 FRR_TYPE ReplaceFunctionW(const wchar_t *dllName, const char *funcName, FUNCPTR newFunc, const char ** opcodes, FUNCPTR* origFunc)
519 {
520     // Cache the results of the last search for the module
521     // Assume that there was no DLL unload between
522     static wchar_t cachedName[MAX_PATH+1];
523     static HMODULE cachedHM = 0;
524 
525     if (!dllName || !*dllName)
526         return FRR_NODLL;
527 
528     if (!cachedHM || wcsncmp(dllName, cachedName, MAX_PATH) != 0)
529     {
530         // Find the module handle for the input dll
531         HMODULE hModule = GetModuleHandleW(dllName);
532         if (hModule == 0)
533         {
534             // Couldn't find the module with the input name
535             cachedHM = 0;
536             return FRR_NODLL;
537         }
538 
539         cachedHM = hModule;
540         wcsncpy(cachedName, dllName, MAX_PATH);
541     }
542 
543     FARPROC inpFunc = GetProcAddress(cachedHM, funcName);
544     if (inpFunc == 0)
545     {
546         // Function was not found
547         return FRR_NOFUNC;
548     }
549 
550     if (!InsertTrampoline((void*)inpFunc, (void*)newFunc, opcodes, (void**)origFunc)){
551         // Failed to insert the trampoline to the target address
552         return FRR_FAILED;
553     }
554 
555     return FRR_OK;
556 }
557 
IsPrologueKnown(const char * dllName,const char * funcName,const char ** opcodes,HMODULE module)558 bool IsPrologueKnown(const char* dllName, const char *funcName, const char **opcodes, HMODULE module)
559 {
560     FARPROC inpFunc = GetProcAddress(module, funcName);
561     FunctionInfo functionInfo = { funcName, dllName };
562 
563     if (!inpFunc) {
564         Log::record(functionInfo, "unknown", /*status*/ false);
565         return false;
566     }
567 
568     return CheckOpcodes( opcodes, (void*)inpFunc, /*abortOnError=*/false, &functionInfo) != 0;
569 }
570 
571 // Public Windows API
TBB_malloc_replacement_log(char *** function_replacement_log_ptr)572 extern "C" __declspec(dllexport) int TBB_malloc_replacement_log(char *** function_replacement_log_ptr)
573 {
574     if (function_replacement_log_ptr != nullptr) {
575         *function_replacement_log_ptr = Log::records;
576     }
577 
578     // If we have no logs -> return false status
579     return Log::replacement_status && Log::records[0] != nullptr ? 0 : -1;
580 }
581 
582 #endif /* !__TBB_WIN8UI_SUPPORT && defined(_WIN32) */
583