|
| 1 | +-- Scan and dump likely vtable addresses |
1 | 2 | memscan = require('memscan') |
2 | 3 |
|
| 4 | +local osType = dfhack.getOSType() |
| 5 | +if osType ~= 'linux' then |
| 6 | + qerror('unsupported OS: ' .. osType) |
| 7 | +end |
| 8 | + |
3 | 9 | local df_ranges = {} |
4 | 10 | for i,mem in ipairs(dfhack.internal.getMemRanges()) do |
5 | 11 | if mem.read and ( |
@@ -30,22 +36,27 @@ for _, range in ipairs(df_ranges) do |
30 | 36 |
|
31 | 37 | local area = memscan.MemoryArea.new(range.start_addr, range.end_addr) |
32 | 38 | for i = 1, area.uintptr_t.count - 1 do |
| 39 | + -- take every pointer-aligned value in memory mapped to the DF executable, and see if it is a valid vtable |
| 40 | + -- start by following the logic in Process::doReadClassName() and ensure it doesn't crash |
33 | 41 | local vtable = area.uintptr_t:idx2addr(i) |
34 | 42 | local typeinfo = area.uintptr_t[i - 1] |
35 | 43 | if is_df_addr(typeinfo + 8) then |
36 | 44 | local typestring = df.reinterpret_cast('uintptr_t', typeinfo + 8)[0] |
37 | 45 | if is_df_addr(typestring) then |
| 46 | + -- rule out false positives by checking that the vtable points to a table of valid pointers |
| 47 | + -- TODO: check that the pointers are actually function pointers |
38 | 48 | local vlen = 0 |
39 | 49 | while is_df_addr(vtable + (8*vlen)) and is_df_addr(df.reinterpret_cast('uintptr_t', vtable + (8*vlen))[0]) do |
40 | 50 | vlen = vlen + 1 |
41 | | - break -- for now, any vtable with one valid function pointer is valid enough |
| 51 | + break -- for now, any vtable with one valid pointer is valid enough |
42 | 52 | end |
43 | 53 | if vlen > 0 then |
| 54 | + -- some false positives can be ruled out if the string.char() call in read_c_string() throws an error for invalid characters |
44 | 55 | local ok, name = pcall(function() |
45 | | - return memscan.read_c_string(df.reinterpret_cast('char', typestring))--:gsub('^%d+', '') |
| 56 | + return memscan.read_c_string(df.reinterpret_cast('char', typestring)) |
46 | 57 | end) |
47 | | - if not ok then |
48 | | - else |
| 58 | + if ok then |
| 59 | + -- GCC strips the "_Z" prefix from typeinfo names, so add it back |
49 | 60 | local demangled_name = dfhack.internal.cxxDemangle('_Z' .. name) |
50 | 61 | if demangled_name and not demangled_name:match('[<>]') and not demangled_name:match('^std::') then |
51 | 62 | print(("<vtable-address name='%s' value='0x%x'/>"):format(demangled_name, vtable)) |
|
0 commit comments