1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
|
// slang-ir-obfuscate-loc.cpp
#include "slang-ir-obfuscate-loc.h"
#include "../core/slang-castable.h"
#include "../core/slang-char-util.h"
#include "../core/slang-random-generator.h"
#include "../core/slang-stable-hash.h"
#include "slang.h"
namespace Slang
{
namespace
{ // anonymous
struct InstWithLoc
{
typedef InstWithLoc ThisType;
SLANG_FORCE_INLINE bool operator<(const ThisType& rhs) const
{
return loc.getRaw() < rhs.loc.getRaw();
}
IRInst* inst;
SourceLoc loc;
};
struct LocPair
{
SourceLoc originalLoc;
SourceLoc obfuscatedLoc;
};
} // namespace
static void _findInstsRec(IRInst* inst, List<InstWithLoc>& out)
{
if (inst->sourceLoc.isValid())
{
InstWithLoc instWithLoc;
instWithLoc.inst = inst;
instWithLoc.loc = inst->sourceLoc;
out.add(instWithLoc);
}
for (IRInst* child : inst->getModifiableChildren())
{
_findInstsRec(child, out);
}
}
// We assume the root source manager is the core module
static SourceLoc _getCoreModuleLastLoc(SourceManager* sourceManager)
{
auto rootManager = sourceManager;
while (rootManager->getParent())
{
rootManager = rootManager->getParent();
}
return rootManager->getNextRangeStart();
}
SlangResult obfuscateModuleLocs(IRModule* module, SourceManager* sourceManager)
{
// There shouldn't be an obfuscated source map set
SLANG_ASSERT(module->getObfuscatedSourceMap() == nullptr);
List<InstWithLoc> instWithLocs;
// Find all of the instructions with source locs
_findInstsRec(module->getModuleInst(), instWithLocs);
if (instWithLocs.getCount() == 0)
{
// Nothing to do
return SLANG_OK;
}
// Sort them
instWithLocs.sort();
// Lets produce a hash, so we can use as a key for random number generation.
//
// We could base it on time, or some other random seed. But it would be preferable
// if it was stable, and compilations of the same module on different machines
// produce the same hash.
//
// Doing so would mean that we could use the obfuscated location ouput to output
// the origin.
StableHashCode32 hash{0};
List<LocPair> locPairs;
// We want the hash to be stable. One problem is the source locs depend on their order of
// inclusion. To work around this we are going to hash via offsets, not locs.
{
SourceView* sourceView = nullptr;
const SourceLoc endCoreModuleLoc = _getCoreModuleLastLoc(sourceManager);
SourceLoc curLoc;
for (const auto& instWithLoc : instWithLocs)
{
if (instWithLoc.loc != curLoc)
{
LocPair locPair;
locPair.originalLoc = instWithLoc.loc;
locPairs.add(locPair);
// This is the current loc
curLoc = instWithLoc.loc;
// Ignore any core module locs in the hash
if (instWithLoc.loc.getRaw() < endCoreModuleLoc.getRaw())
{
continue;
}
// If the loc isn't in the view, lookup the view it is in
if (sourceView == nullptr || !sourceView->getRange().contains(curLoc))
{
sourceView = sourceManager->findSourceViewRecursively(curLoc);
SLANG_ASSERT(sourceView);
// If there is no source view we can't apply to the hash
if (sourceView == nullptr)
{
continue;
}
const auto pathInfo = sourceView->getViewPathInfo();
const auto name = pathInfo.getName();
const auto nameHash = getStableHashCode32(name.getBuffer(), name.getLength());
// Combine the name
hash = combineStableHash(hash, nameHash);
}
// We *can't* just use the offset to produce the hash, because the source might have
// different line endings on different platforms (in particular linux/unix-like and
// windows). So we hash the line number/line offset to work around
const auto offset = sourceView->getRange().getOffset(curLoc);
const auto sourceFile = sourceView->getSourceFile();
const auto lineIndex = sourceFile->calcLineIndexFromOffset(offset);
const auto lineOffset = sourceFile->calcColumnOffset(lineIndex, offset);
hash = combineStableHash(
hash,
getStableHashCode32(lineIndex),
getStableHashCode32(lineOffset));
}
}
}
const Count uniqueLocCount = locPairs.getCount();
// We need a seed to make this random on each run
const uint32_t randomSeed = uint32_t(hash);
RefPtr<RandomGenerator> rand = RandomGenerator::create(randomSeed);
// We want a random unique name because we could have multiple obfuscated modules
// and we need to identify each
PathInfo obfusctatedPathInfo;
{
// We need a pathInfo to *identify* this modules obfuscated locs.
// We are going to use a random number, seeded from the hash to do this.
// Turning the number as hex as the name.
{
StringBuilder buf;
uint8_t data[4];
rand->nextData(data, sizeof(data));
const Count charsCount = SLANG_COUNT_OF(data) * 2;
char* dst = buf.prepareForAppend(charsCount);
for (Index i = 0; i < SLANG_COUNT_OF(data); ++i)
{
dst[i * 2 + 0] = CharUtil::getHexChar(data[i] & 0xf);
dst[i * 2 + 1] = CharUtil::getHexChar(data[i] >> 4);
}
buf.appendInPlace(dst, charsCount);
// Make it clear this "source" is actually just for obfuscation.
buf << toSlice("-obfuscated");
obfusctatedPathInfo = PathInfo::makePath(buf);
}
}
SourceFile* obfuscatedFile =
sourceManager->createSourceFileWithSize(obfusctatedPathInfo, uniqueLocCount);
// We put each loc on it's own line. We do this rather than using a single line because
// it means the `#line` directives can still do something meaningful, since the best resolution
// they have is a single line.
{
List<uint32_t> offsets;
offsets.setCount(uniqueLocCount + 1);
for (Index i = 0; i < uniqueLocCount + 1; ++i)
{
offsets[i] = uint32_t(i);
}
obfuscatedFile->setLineBreakOffsets(offsets.getBuffer(), offsets.getCount());
}
// Create the view we are going to use from the obfusctated "file".
SourceView* obfuscatedView =
sourceManager->createSourceView(obfuscatedFile, nullptr, SourceLoc());
const auto obfuscatedRange = obfuscatedView->getRange();
// Okay now we want to produce a map from these locs to a new source location
{
// Create a "bag" and put all of the indices in it.
List<SourceLoc> bag;
bag.setCount(uniqueLocCount);
{
SourceLoc* dst = bag.getBuffer();
for (Index i = 0; i < uniqueLocCount; ++i)
{
dst[i] = obfuscatedRange.begin + i;
}
}
// Pull the indices randomly out of the bag to create the map
for (auto& pair : locPairs)
{
// Find an index in the bag
const Index bagIndex = rand->nextInt32InRange(0, int32_t(bag.getCount()));
// Set in the map
pair.obfuscatedLoc = bag[bagIndex];
// Remove from the bag
bag.fastRemoveAt(bagIndex);
}
}
// We can now just set all the new locs in the instructions
if (const LocPair* curPair = locPairs.getBuffer())
{
LocPair pair = *curPair;
for (const auto& instWithLoc : instWithLocs)
{
auto inst = instWithLoc.inst;
if (instWithLoc.loc != pair.originalLoc)
{
SLANG_ASSERT(curPair < locPairs.end());
curPair++;
pair = *curPair;
}
SLANG_ASSERT(pair.originalLoc == instWithLoc.loc);
// Set the loc
inst->sourceLoc = pair.obfuscatedLoc;
}
}
// We can now create a map. The locs are in order in entries, so that should make lookup easier.
// This doesn't "leak" anything as the obfuscated loc map is not distributed.
ComPtr<IBoxValue<SourceMap>> boxedSourceMap(new BoxValue<SourceMap>);
auto sourceMap = boxedSourceMap->getPtr();
sourceMap->m_file = obfusctatedPathInfo.getName();
// Set up entries one per line
List<SourceMap::Entry> entries;
{
entries.setCount(uniqueLocCount);
for (auto& entry : entries)
{
entry.init();
}
}
{
// Current view, with cached "View" based sourceFileIndex
SourceView* curView = nullptr;
Index curViewSourceFileIndex = -1;
// Current handle, and store cached index in curPathSourceFileIndex
StringSlicePool::Handle curPathHandle = StringSlicePool::Handle(0);
Index curPathSourceFileIndex = -1;
for (Index i = 0; i < uniqueLocCount; ++i)
{
const auto& pair = locPairs[i];
// First find the view
if (curView == nullptr || !curView->getRange().contains(pair.originalLoc))
{
curView = sourceManager->findSourceViewRecursively(pair.originalLoc);
SLANG_ASSERT(curView);
// Reset the current view path index, to being unset
curViewSourceFileIndex = -1;
// We have to reset, because the path index is for the source manager
// that holds the view. If the view changes we need to re determine the
// path string, and index.
curPathSourceFileIndex = -1;
}
// Now get the location
const auto handleLoc = curView->getHandleLoc(pair.originalLoc);
Index sourceFileIndex = -1;
if (handleLoc.pathHandle == StringSlicePool::Handle(0))
{
if (curViewSourceFileIndex < 0)
{
const auto pathInfo = curView->getViewPathInfo();
curViewSourceFileIndex =
sourceMap->getSourceFileIndex(pathInfo.getName().getUnownedSlice());
}
sourceFileIndex = curViewSourceFileIndex;
}
else
{
if (curPathSourceFileIndex < 0 || handleLoc.pathHandle != curPathHandle)
{
auto viewSourceManager = curView->getSourceManager();
const auto filePathSlice =
viewSourceManager->getStringSlicePool().getSlice(curPathHandle);
// Set the handle
curPathHandle = handleLoc.pathHandle;
// Get the source file index.
curPathSourceFileIndex = sourceMap->getSourceFileIndex(filePathSlice);
}
sourceFileIndex = curPathSourceFileIndex;
}
// Calculate the line index associated with this loc
const Index generatedLineIndex = Index(obfuscatedRange.getOffset(pair.obfuscatedLoc));
// Set it up
SourceMap::Entry& entry = entries[generatedLineIndex];
entry.sourceFileIndex = sourceFileIndex;
// The generated has a line per loc, so the generated column is always 0
entry.generatedColumn = 0;
// We need to subtract 1, because handleLoc locations are 1 indexed, but SourceMap
// entry is 0 indexed.
entry.sourceColumn = handleLoc.column - 1;
entry.sourceLine = handleLoc.line - 1;
}
}
// Add all of the entries in line order to the source map
for (Index i = 0; i < uniqueLocCount; ++i)
{
// Advance to the current line.
sourceMap->advanceToLine(i);
// Add it to the source map
sourceMap->addEntry(entries[i]);
}
// Associate the sourceMap with the obfuscated file.
obfuscatedFile->setSourceMap(boxedSourceMap, SourceMapKind::Obfuscated);
// Set the obfuscated map onto the module
module->setObfuscatedSourceMap(boxedSourceMap);
return SLANG_OK;
}
} // namespace Slang
|