Last active
December 20, 2022 22:59
-
-
Save NightFeather/cb22959977f73b3570f8ac0ceaaa8e40 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class MMapEntry | |
attr_reader :addr_range, :permission, :offset, :devnum, :inode, :path | |
def self.from_procmaps line | |
addr, perm, offset, devnum, inode, path = line.split | |
saddr, eaddr = addr.split("-").map { |a| a.to_i 16 } | |
dev = devnum.split(":").map { |s| s.to_i 16 } | |
path = path[1..-2].to_sym if path =~ /\[.+\]/ | |
MMapEntry.new saddr..eaddr, perm, offset.to_i(16), dev, inode, path | |
end | |
def initialize addr_range, permission, offset, devnum, inode, path | |
@addr_range = addr_range | |
@permission = permission | |
@offset = offset | |
@devnum = devnum | |
@inode = inode | |
@path = path | |
end | |
def annonymous? | |
@path.nil? | |
end | |
def special? | |
@path.is_a? Symbol | |
end | |
def readable? | |
@permission.includes? ?r | |
end | |
def writable? | |
@permission.includes? ?w | |
end | |
def executable? | |
@permission.includes? ?x | |
end | |
def private? | |
@permission.includes? ?p | |
end | |
def inspect | |
"MMapEntry<[0x%x], %s, %d, %s>" % [ | |
@addr_range.begin, | |
@permission, | |
@offset, | |
annonymous? ? 'anon' : @path.to_s | |
] | |
end | |
alias to_s inspect | |
end | |
class PageEntry | |
FIELDS = [ | |
[:pfn, 0, 55], | |
[:swap_type, 0, 5], | |
[:swap_offset, 5, 50], | |
[:soft_dirty, 55, 1], | |
[:exclusive, 56, 1], | |
[:write_protected, 57, 1], | |
[:mapped_or_shared, 61, 1], | |
[:swapped, 62, 1], | |
[:present, 63, 1], | |
] | |
def self.from_vmap uaddr, entry | |
PageEntry.new uaddr, entry.unpack("Q")[0] | |
end | |
PAGE_SHIFT = 12 | |
def initialize uaddr, data | |
@uaddr = uaddr | |
@data = data | |
end | |
FIELDS.each do |(m, o, s)| | |
define_method(m) do | |
r = (@data >> o) & ((1 << s) - 1) | |
if s == 1 | |
not r.zero? | |
else | |
r | |
end | |
end | |
end | |
def inspect | |
m = "PageEntry<#{"0x%x" % @uaddr}, #{pfn}" | |
if swapped | |
m += ", in swap, swap_offset: #{swap_offset}, swap_type: #{swap_type}," | |
else | |
m += ", in ram, paddr: #{"0x%x" % paddr}" | |
end | |
m += ", exclusive" if exclusive | |
m += ", dirty" if soft_dirty | |
m += ", mapped/shared" if mapped_or_shared | |
m + ">" | |
end | |
alias to_s inspect | |
def paddr | |
pfn << PAGE_SHIFT | |
end | |
# /proc/kpagecount | |
def refcount | |
end | |
# /proc/kpageflags | |
def pagetype | |
end | |
end | |
def parse_mmap pid = "self" | |
File.read("/proc/#{pid}/maps") | |
.lines.map(&:chomp).reject(&:empty?) | |
.map { |l| MMapEntry.from_procmaps l } | |
end | |
def parse_page start, stop = nil, pid: "self" | |
File.open("/proc/#{pid}/pagemap", mode: 'rb') do |vm| | |
stop = start + 0x1000 if stop.nil? | |
(start...stop).step(0x1000).map do |addr| | |
PageEntry.from_vmap addr, vm.pread(8, ((addr/0x1000)*8)) | |
end | |
end | |
end | |
pid = ARGV[0] || "self" | |
parse_mmap(pid).each do |entry| | |
if entry.path == :vsyscall | |
puts "#{entry}, no backing page" | |
else | |
page = nil | |
if entry.path == :stack | |
page = parse_page(entry.addr_range.end - 0x1000, pid: ARGV[0] || "self")[0] | |
else | |
page = parse_page(entry.addr_range.begin, pid: pid)[0] | |
end | |
puts "#{entry}, #{entry.addr_range.step(0x1000).count} pages, #{page}" | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment