import gdb
import traceback
from freebsd import *
class vnet(gdb.Function):
"""
Register a function to look up VNET variables by name.
To look at the value of a VNET variable V_foo, print $V("foo"). The
currently selected thread's VNET is used by default, but can be optionally
specified as a second parameter, e.g., $V("foo", <vnet>), where <vnet> is a
pointer to a struct vnet (e.g., vnet0 or allprison.tqh_first->pr_vnet) or a
string naming a jail.
"""
def __init__(self):
super(vnet, self).__init__("V")
def invoke(self, sym, vnet=None):
sym = sym.string()
if sym.startswith("V_"):
sym = sym[len("V_"):]
if gdb.lookup_symbol("sysctl___kern_features_vimage")[0] is None:
return symval(sym)
if vnet is None:
vnet = tdfind(gdb.selected_thread().ptid[2])['td_vnet']
if not vnet:
vnet = symval("vnet0")
elif vnet.type.is_string_like:
vnet = vnet.string()
for prison in tailq_foreach(symval("allprison"), "pr_list"):
if prison['pr_name'].string() == vnet:
vnet = prison['pr_vnet']
break
else:
raise gdb.error(f"No prison named {vnet}")
def uintptr_t(val):
return val.cast(gdb.lookup_type("uintptr_t"))
vnet_data_base = vnet['vnet_data_base']
vnet_entry = symval("vnet_entry_" + sym)
vnet_entry_addr = uintptr_t(vnet_entry.address)
for lf in linker_file_foreach():
block = gdb.block_for_pc(lf['ops']['cls']['methods'][0]['func'])
elf_file_t = gdb.lookup_type("elf_file_t", block).target()
ef = lf.cast(elf_file_t)
file_type = lf['ops']['cls']['name'].string()
if file_type == "elf64":
start = uintptr_t(ef['vnet_start'])
if start == 0:
continue
end = uintptr_t(ef['vnet_stop'])
base = uintptr_t(ef['vnet_base'])
elif file_type == "elf64_obj":
for i in range(ef['nprogtab']):
pe = ef['progtab'][i]
if pe['name'].string() == "set_vnet":
start = uintptr_t(pe['origaddr'])
end = start + uintptr_t(pe['size'])
base = uintptr_t(pe['addr'])
break
else:
continue
else:
path = lf['pathname'].string()
raise gdb.error(f"{path} has unexpected linker file type {file_type}")
if vnet_entry_addr >= start and vnet_entry_addr < end:
obj = gdb.Value(vnet_data_base + vnet_entry_addr - start + base)
return obj.cast(vnet_entry.type.pointer()).dereference()
vnet()