Bug Summary

File:Source/Core/Core/Src/PowerPC/JitCommon/JitCache.cpp
Location:line 360, column 3
Description:Array access (via field 'blocks') results in a null pointer dereference

Annotated Source Code

1// Copyright 2013 Dolphin Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5// Enable define below to enable oprofile integration. For this to work,
6// it requires at least oprofile version 0.9.4, and changing the build
7// system to link the Dolphin executable against libopagent. Since the
8// dependency is a little inconvenient and this is possibly a slight
9// performance hit, it's not enabled by default, but it's useful for
10// locating performance issues.
11
12#include "Common.h"
13
14#ifdef _WIN32
15#include <windows.h>
16#endif
17
18#include "../../Core.h"
19#include "MemoryUtil.h"
20
21#include "../../HW/Memmap.h"
22#include "../JitInterface.h"
23#include "../../CoreTiming.h"
24
25#include "../PowerPC.h"
26#include "../PPCTables.h"
27#include "../PPCAnalyst.h"
28
29#include "JitCache.h"
30#include "JitBase.h"
31
32#include "disasm.h"
33
34#if defined USE_OPROFILE && USE_OPROFILE
35#include <opagent.h>
36
37op_agent_t agent;
38#endif
39
40#if defined USE_VTUNE
41#include <jitprofiling.h>
42#pragma comment(lib, "libittnotify.lib")
43#pragma comment(lib, "jitprofiling.lib")
44#endif
45
46using namespace Gen;
47
48#define INVALID_EXIT0xFFFFFFFF 0xFFFFFFFF
49
50bool JitBlock::ContainsAddress(u32 em_address)
51{
52 // WARNING - THIS DOES NOT WORK WITH INLINING ENABLED.
53 return (em_address >= originalAddress && em_address < originalAddress + 4 * originalSize);
54}
55
56 bool JitBaseBlockCache::IsFull() const
57 {
58 return GetNumBlocks() >= MAX_NUM_BLOCKS - 1;
59 }
60
61 void JitBaseBlockCache::Init()
62 {
63 MAX_NUM_BLOCKS = 65536*2;
64
65#if defined USE_OPROFILE && USE_OPROFILE
66 agent = op_open_agent();
67#endif
68 blocks = new JitBlock[MAX_NUM_BLOCKS];
69 blockCodePointers = new const u8*[MAX_NUM_BLOCKS];
70#ifdef JIT_UNLIMITED_ICACHE
71 if (iCache == 0 && iCacheEx == 0 && iCacheVMEM == 0)
72 {
73 iCache = new u8[JIT_ICACHE_SIZE0x2000000];
74 iCacheEx = new u8[JIT_ICACHEEX_SIZE0x4000000];
75 iCacheVMEM = new u8[JIT_ICACHE_SIZE0x2000000];
76 }
77 else
78 {
79 PanicAlert("JitBaseBlockCache::Init() - iCache is already initialized")MsgAlert(false, WARNING, "JitBaseBlockCache::Init() - iCache is already initialized"
)
;
80 }
81 if (iCache == 0 || iCacheEx == 0 || iCacheVMEM == 0)
82 {
83 PanicAlert("JitBaseBlockCache::Init() - unable to allocate iCache")MsgAlert(false, WARNING, "JitBaseBlockCache::Init() - unable to allocate iCache"
)
;
84 }
85 memset(iCache, JIT_ICACHE_INVALID_BYTE0x14, JIT_ICACHE_SIZE0x2000000);
86 memset(iCacheEx, JIT_ICACHE_INVALID_BYTE0x14, JIT_ICACHEEX_SIZE0x4000000);
87 memset(iCacheVMEM, JIT_ICACHE_INVALID_BYTE0x14, JIT_ICACHE_SIZE0x2000000);
88#endif
89 Clear();
90 }
91
92 void JitBaseBlockCache::Shutdown()
93 {
94 delete[] blocks;
95 delete[] blockCodePointers;
96#ifdef JIT_UNLIMITED_ICACHE
97 if (iCache != 0)
98 delete[] iCache;
99 iCache = 0;
100 if (iCacheEx != 0)
101 delete[] iCacheEx;
102 iCacheEx = 0;
103 if (iCacheVMEM != 0)
104 delete[] iCacheVMEM;
105 iCacheVMEM = 0;
106#endif
107 blocks = 0;
108 blockCodePointers = 0;
109 num_blocks = 0;
110#if defined USE_OPROFILE && USE_OPROFILE
111 op_close_agent(agent);
112#endif
113
114#ifdef USE_VTUNE
115 iJIT_NotifyEvent(iJVM_EVENT_TYPE_SHUTDOWN, NULL__null);
116#endif
117 }
118
119 // This clears the JIT cache. It's called from JitCache.cpp when the JIT cache
120 // is full and when saving and loading states.
121 void JitBaseBlockCache::Clear()
122 {
123 if (IsFull())
124 Core::DisplayMessage("Clearing block cache.", 3000);
125 else
126 Core::DisplayMessage("Clearing code cache.", 3000);
127
128 for (int i = 0; i < num_blocks; i++)
129 {
130 DestroyBlock(i, false);
131 }
132 links_to.clear();
133 block_map.clear();
134 valid_block.reset();
135 num_blocks = 0;
136 memset(blockCodePointers, 0, sizeof(u8*)*MAX_NUM_BLOCKS);
137 }
138
139 void JitBaseBlockCache::ClearSafe()
140 {
141#ifdef JIT_UNLIMITED_ICACHE
142 memset(iCache, JIT_ICACHE_INVALID_BYTE0x14, JIT_ICACHE_SIZE0x2000000);
143 memset(iCacheEx, JIT_ICACHE_INVALID_BYTE0x14, JIT_ICACHEEX_SIZE0x4000000);
144 memset(iCacheVMEM, JIT_ICACHE_INVALID_BYTE0x14, JIT_ICACHE_SIZE0x2000000);
145#endif
146 }
147
148 /*void JitBaseBlockCache::DestroyBlocksWithFlag(BlockFlag death_flag)
149 {
150 for (int i = 0; i < num_blocks; i++)
151 {
152 if (blocks[i].flags & death_flag)
153 {
154 DestroyBlock(i, false);
155 }
156 }
157 }*/
158
159 void JitBaseBlockCache::Reset()
160 {
161 Shutdown();
162 Init();
163 }
164
165 JitBlock *JitBaseBlockCache::GetBlock(int no)
166 {
167 return &blocks[no];
168 }
169
170 int JitBaseBlockCache::GetNumBlocks() const
171 {
172 return num_blocks;
173 }
174
175 bool JitBaseBlockCache::RangeIntersect(int s1, int e1, int s2, int e2) const
176 {
177 // check if any endpoint is inside the other range
178 if ((s1 >= s2 && s1 <= e2) ||
179 (e1 >= s2 && e1 <= e2) ||
180 (s2 >= s1 && s2 <= e1) ||
181 (e2 >= s1 && e2 <= e1))
182 return true;
183 else
184 return false;
185 }
186
187 int JitBaseBlockCache::AllocateBlock(u32 em_address)
188 {
189 JitBlock &b = blocks[num_blocks];
190 b.invalid = false;
191 b.originalAddress = em_address;
192 b.exitAddress[0] = INVALID_EXIT0xFFFFFFFF;
193 b.exitAddress[1] = INVALID_EXIT0xFFFFFFFF;
194 b.exitPtrs[0] = 0;
195 b.exitPtrs[1] = 0;
196 b.linkStatus[0] = false;
197 b.linkStatus[1] = false;
198 b.blockNum = num_blocks;
199 num_blocks++; //commit the current block
200 return num_blocks - 1;
201 }
202
203 void JitBaseBlockCache::FinalizeBlock(int block_num, bool block_link, const u8 *code_ptr)
204 {
205 blockCodePointers[block_num] = code_ptr;
206 JitBlock &b = blocks[block_num];
207 b.originalFirstOpcode = JitInterface::Read_Opcode_JIT(b.originalAddress);
208 JitInterface::Write_Opcode_JIT(b.originalAddress, (JIT_OPCODE0 << 26) | block_num);
209
210 // Convert the logical address to a physical address for the block map
211 u32 pAddr = b.originalAddress & 0x1FFFFFFF;
212
213 for (u32 i = 0; i < (b.originalSize + 7) / 8; ++i)
214 valid_block[pAddr / 32 + i] = true;
215
216 block_map[std::make_pair(pAddr + 4 * b.originalSize - 1, pAddr)] = block_num;
217 if (block_link)
218 {
219 for (int i = 0; i < 2; i++)
220 {
221 if (b.exitAddress[i] != INVALID_EXIT0xFFFFFFFF)
222 links_to.insert(std::pair<u32, int>(b.exitAddress[i], block_num));
223 }
224
225 LinkBlock(block_num);
226 LinkBlockExits(block_num);
227 }
228
229#if defined USE_OPROFILE && USE_OPROFILE
230 char buf[100];
231 sprintf(buf, "EmuCode%x", b.originalAddress);
232 const u8* blockStart = blockCodePointers[block_num];
233 op_write_native_code(agent, buf, (uint64_t)blockStart,
234 blockStart, b.codeSize);
235#endif
236
237#ifdef USE_VTUNE
238 sprintf(b.blockName, "EmuCode_0x%08x", b.originalAddress);
239
240 iJIT_Method_Load jmethod = {0};
241 jmethod.method_id = iJIT_GetNewMethodID();
242 jmethod.class_file_name = "";
243 jmethod.source_file_name = __FILE__"/home/anal/dolphin-emu/Source/Core/Core/Src/PowerPC/JitCommon/JitCache.cpp";
244 jmethod.method_load_address = (void*)blockCodePointers[block_num];
245 jmethod.method_size = b.codeSize;
246 jmethod.line_number_size = 0;
247 jmethod.method_name = b.blockName;
248 iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, (void*)&jmethod);
249#endif
250 }
251
252 const u8 **JitBaseBlockCache::GetCodePointers()
253 {
254 return blockCodePointers;
255 }
256
257#ifdef JIT_UNLIMITED_ICACHE
258 u8* JitBaseBlockCache::GetICache()
259 {
260 return iCache;
261 }
262
263 u8* JitBaseBlockCache::GetICacheEx()
264 {
265 return iCacheEx;
266 }
267
268 u8* JitBaseBlockCache::GetICacheVMEM()
269 {
270 return iCacheVMEM;
271 }
272#endif
273
274 int JitBaseBlockCache::GetBlockNumberFromStartAddress(u32 addr)
275 {
276 if (!blocks)
277 return -1;
278#ifdef JIT_UNLIMITED_ICACHE
279 u32 inst;
280 if (addr & JIT_ICACHE_VMEM_BIT0x20000000)
281 {
282 inst = *(u32*)(iCacheVMEM + (addr & JIT_ICACHE_MASK0x1ffffff));
283 }
284 else if (addr & JIT_ICACHE_EXRAM_BIT0x10000000)
285 {
286 inst = *(u32*)(iCacheEx + (addr & JIT_ICACHEEX_MASK0x3ffffff));
287 }
288 else
289 {
290 inst = *(u32*)(iCache + (addr & JIT_ICACHE_MASK0x1ffffff));
291 }
292 inst = Common::swap32(inst);
293#else
294 u32 inst = Memory::ReadFast32(addr);
295#endif
296 if (inst & 0xfc000000) // definitely not a JIT block
297 return -1;
298 if ((int)inst >= num_blocks)
299 return -1;
300 if (blocks[inst].originalAddress != addr)
301 return -1;
302 return inst;
303 }
304
305 void JitBaseBlockCache::GetBlockNumbersFromAddress(u32 em_address, std::vector<int> *block_numbers)
306 {
307 for (int i = 0; i < num_blocks; i++)
308 if (blocks[i].ContainsAddress(em_address))
309 block_numbers->push_back(i);
310 }
311
312 u32 JitBaseBlockCache::GetOriginalFirstOp(int block_num)
313 {
314 if (block_num >= num_blocks)
315 {
316 //PanicAlert("JitBaseBlockCache::GetOriginalFirstOp - block_num = %u is out of range", block_num);
317 return block_num;
318 }
319 return blocks[block_num].originalFirstOpcode;
320 }
321
322 CompiledCode JitBaseBlockCache::GetCompiledCodeFromBlock(int block_num)
323 {
324 return (CompiledCode)blockCodePointers[block_num];
325 }
326
327 //Block linker
328 //Make sure to have as many blocks as possible compiled before calling this
329 //It's O(N), so it's fast :)
330 //Can be faster by doing a queue for blocks to link up, and only process those
331 //Should probably be done
332
333 void JitBaseBlockCache::LinkBlockExits(int i)
334 {
335 JitBlock &b = blocks[i];
336 if (b.invalid)
337 {
338 // This block is dead. Don't relink it.
339 return;
340 }
341 for (int e = 0; e < 2; e++)
342 {
343 if (b.exitAddress[e] != INVALID_EXIT0xFFFFFFFF && !b.linkStatus[e])
344 {
345 int destinationBlock = GetBlockNumberFromStartAddress(b.exitAddress[e]);
346 if (destinationBlock != -1)
347 {
348 WriteLinkBlock(b.exitPtrs[e], blocks[destinationBlock].checkedEntry);
349 b.linkStatus[e] = true;
350 }
351 }
352 }
353 }
354
355 using namespace std;
356
357 void JitBaseBlockCache::LinkBlock(int i)
358 {
359 LinkBlockExits(i);
360 JitBlock &b = blocks[i];
Array access (via field 'blocks') results in a null pointer dereference
361 pair<multimap<u32, int>::iterator, multimap<u32, int>::iterator> ppp;
362 // equal_range(b) returns pair<iterator,iterator> representing the range
363 // of element with key b
364 ppp = links_to.equal_range(b.originalAddress);
365 if (ppp.first == ppp.second)
366 return;
367 for (multimap<u32, int>::iterator iter = ppp.first; iter != ppp.second; ++iter) {
368 // PanicAlert("Linking block %i to block %i", iter->second, i);
369 LinkBlockExits(iter->second);
370 }
371 }
372
373 void JitBaseBlockCache::UnlinkBlock(int i)
374 {
375 JitBlock &b = blocks[i];
376 pair<multimap<u32, int>::iterator, multimap<u32, int>::iterator> ppp;
377 ppp = links_to.equal_range(b.originalAddress);
378 if (ppp.first == ppp.second)
379 return;
380 for (multimap<u32, int>::iterator iter = ppp.first; iter != ppp.second; ++iter) {
381 JitBlock &sourceBlock = blocks[iter->second];
382 for (int e = 0; e < 2; e++)
383 {
384 if (sourceBlock.exitAddress[e] == b.originalAddress)
385 sourceBlock.linkStatus[e] = false;
386 }
387 }
388 }
389
390 void JitBaseBlockCache::DestroyBlock(int block_num, bool invalidate)
391 {
392 if (block_num < 0 || block_num >= num_blocks)
393 {
394 PanicAlert("DestroyBlock: Invalid block number %d", block_num)MsgAlert(false, WARNING, "DestroyBlock: Invalid block number %d"
, block_num)
;
395 return;
396 }
397 JitBlock &b = blocks[block_num];
398 if (b.invalid)
399 {
400 if (invalidate)
401 PanicAlert("Invalidating invalid block %d", block_num)MsgAlert(false, WARNING, "Invalidating invalid block %d", block_num
)
;
402 return;
403 }
404 b.invalid = true;
405#ifdef JIT_UNLIMITED_ICACHE
406 JitInterface::Write_Opcode_JIT(b.originalAddress, b.originalFirstOpcode?b.originalFirstOpcode:JIT_ICACHE_INVALID_WORD0x14141414);
407#else
408 if (Memory::ReadFast32(b.originalAddress) == block_num)
409 Memory::WriteUnchecked_U32(b.originalFirstOpcode, b.originalAddress);
410#endif
411
412 UnlinkBlock(block_num);
413
414 // Send anyone who tries to run this block back to the dispatcher.
415 // Not entirely ideal, but .. pretty good.
416 // Spurious entrances from previously linked blocks can only come through checkedEntry
417 WriteDestroyBlock(b.checkedEntry, b.originalAddress);
418 }
419
420 void JitBaseBlockCache::InvalidateICache(u32 address, const u32 length)
421 {
422 // Convert the logical address to a physical address for the block map
423 u32 pAddr = address & 0x1FFFFFFF;
424
425 // Optimize the common case of length == 32 which is used by Interpreter::dcb*
426 bool destroy_block = true;
427 if (length == 32)
428 {
429 if (!valid_block[pAddr / 32])
430 destroy_block = false;
431 else
432 valid_block[pAddr / 32] = false;
433 }
434
435 // destroy JIT blocks
436 // !! this works correctly under assumption that any two overlapping blocks end at the same address
437 if (destroy_block)
438 {
439 std::map<pair<u32,u32>, u32>::iterator it1 = block_map.lower_bound(std::make_pair(pAddr, 0)), it2 = it1;
440 while (it2 != block_map.end() && it2->first.second < pAddr + length)
441 {
442#ifdef JIT_UNLIMITED_ICACHE
443 JitBlock &b = blocks[it2->second];
444 if (b.originalAddress & JIT_ICACHE_VMEM_BIT0x20000000)
445 {
446 u32 cacheaddr = b.originalAddress & JIT_ICACHE_MASK0x1ffffff;
447 memset(iCacheVMEM + cacheaddr, JIT_ICACHE_INVALID_BYTE0x14, 4);
448 }
449 else if (b.originalAddress & JIT_ICACHE_EXRAM_BIT0x10000000)
450 {
451 u32 cacheaddr = b.originalAddress & JIT_ICACHEEX_MASK0x3ffffff;
452 memset(iCacheEx + cacheaddr, JIT_ICACHE_INVALID_BYTE0x14, 4);
453 }
454 else
455 {
456 u32 cacheaddr = b.originalAddress & JIT_ICACHE_MASK0x1ffffff;
457 memset(iCache + cacheaddr, JIT_ICACHE_INVALID_BYTE0x14, 4);
458 }
459#endif
460 DestroyBlock(it2->second, true);
461 it2++;
462 }
463 if (it1 != it2)
464 {
465 block_map.erase(it1, it2);
466 }
467 }
468
469#ifdef JIT_UNLIMITED_ICACHE
470 // invalidate iCache.
471 // icbi can be called with any address, so we should check
472 if ((address & ~JIT_ICACHE_MASK0x1ffffff) != 0x80000000 && (address & ~JIT_ICACHE_MASK0x1ffffff) != 0x00000000 &&
473 (address & ~JIT_ICACHE_MASK0x1ffffff) != 0x7e000000 && // TLB area
474 (address & ~JIT_ICACHEEX_MASK0x3ffffff) != 0x90000000 && (address & ~JIT_ICACHEEX_MASK0x3ffffff) != 0x10000000)
475 {
476 return;
477 }
478 if (address & JIT_ICACHE_VMEM_BIT0x20000000)
479 {
480 u32 cacheaddr = address & JIT_ICACHE_MASK0x1ffffff;
481 memset(iCacheVMEM + cacheaddr, JIT_ICACHE_INVALID_BYTE0x14, length);
482 }
483 else if (address & JIT_ICACHE_EXRAM_BIT0x10000000)
484 {
485 u32 cacheaddr = address & JIT_ICACHEEX_MASK0x3ffffff;
486 memset(iCacheEx + cacheaddr, JIT_ICACHE_INVALID_BYTE0x14, length);
487 }
488 else
489 {
490 u32 cacheaddr = address & JIT_ICACHE_MASK0x1ffffff;
491 memset(iCache + cacheaddr, JIT_ICACHE_INVALID_BYTE0x14, length);
492 }
493#endif
494 }
495 void JitBlockCache::WriteLinkBlock(u8* location, const u8* address)
496 {
497 XEmitter emit(location);
498 emit.JMP(address, true);
499 }
500 void JitBlockCache::WriteDestroyBlock(const u8* location, u32 address)
501 {
502 XEmitter emit((u8 *)location);
503 emit.MOV(32, M(&PCPowerPC::ppcState.pc), Imm32(address));
504 emit.JMP(jit->GetAsmRoutines()->dispatcher, true);
505 }