Little late to the party, but I had qemu/efi/gdb debugging working pretty
well a while back to write some luks drive encryption support for efi.
>From my notes the best way to programmatically get all debug information at
once is to iterate the the loaded image protocols, get a list of image
offsets, then read the debug paths from the codeview section from each
image. Comments directly from my scripts:
# Spec ref: UEFI Spec - 17.4 EFI Debug Support Table
# Implementation ref: MdeModulePkg/Core/Dxe/Misc/DebugImageInfo.c
# DxeMain will create EFI_SYSTEM_TABLE_POINTER allocated on 4MB boundary
# starting at top of mem or PcdMaxEfiSystemTablePointerAddress if not 0.
# Debug info lookup process:
# 1) Find EFI_SYSTEM_TABLE_POINTER on 4MB boundary. Currently don't check
# Crc32 although before runtime services is installed it will be 0.
# 2) Find EFI_DEBUG_IMAGE_INFO_TABLE_HEADER from EFI_SYSTEM_TABLE
# 3) Abort if (EFI_DEBUG_IMAGE_INFO_TABLE_HEADER.UpdateStatus |
# EFI_DEBUG_IMAGE_INFO_UPDATE_IN_PROGRESS)
# 4) Iterate EFI_DEBUG_IMAGE_INFO pointers for each image and update gdb
# symbol files as needed.
#
At the time I was developing on Ubuntu x64 (don't remember which version)
and using a mix of tools from the base apt, Linaro's ppa and built locally.
There were also quite a few caveats:
* You need to modify the efi exception handlers to hold and be returnable.
I believe i386 and x64 were fine but arm was broken I think due to some
obscure assumption that trustzone was running. All of that is on an
external hdd I'd have to dig out if anyone is interested.
* Gdb will always resolve the first symbol it finds. This is complicated
by efi when you have a single inferior representing multiple programs and
some symbols are removed by --gc-sections. This means commonly used global
symbols like gMyProtocolGuid will sometimes be unreadable or end up
incorrect. I made some magic linker scripts that would resolve all of
this, again on an external hdd.
* You need to know the limits of memory to search for the debug support
table. I never figured out how to get this from qemu and would just
hardcode it.
* gdb-multiarch from apt does not work for am64 targets from a x86 host.
It's just a problem with the way Ubuntu is configuring gdb so you can build
your own if you need to work around this.
* There's not a good way to wait for the debugger, I ended up just using
int foo = 1; while(foo == 1);, connecting with gdb, loading symbols and
then using gdb to change foo to 0. Worked well enough but a little clunky.
* The debug support table listed above is created when Dxe starts, so this
doesn't help much for Pei.
In the end it should just be as simple as:
$ qemu-system-x86_64 -L . -hda fat:hda-contents -serial pty -serial pty
-monitor stdio -nographic -S
Shell > ./myapp
Start debugger from the qemu console
(gdb) source efi_qemu.py
(gdb) efi set target x64
(gdb) efi symbols
(gdb) bt
I don't have an efi setup anymore so I can't test this, but should be able
to give you some pointers if this doesn't work with newest everything.
On Wed, Apr 2, 2014 at 9:57 AM, Laszlo Ersek <[email protected]> wrote:
> Hi Lethom,
>
> > 2014-04-02 11:20 GMT+02:00 Roger <[email protected]
> > <mailto:[email protected]>>:
> >
> > Anybody tried to use INTEL UEFI Development Kit Debugger Tool ?
> > Can it work in QEMU and OVMF environment?
>
> On 04/02/14 12:56, Lethom Legrand wrote:
> > I managed to do that yesterday, it is not hard to do, but it's not
> > obvious to understand how it works. I am currently writing a short
> > article/how to about it.
>
> I'm very curious.
>
> Just one note: the article you refer to:
>
>
> http://wiki.osdev.org/UEFI#Using_GNU_toolchain_for_compiling_and_debugging_EFI_applications
>
> describes a way to connect host-side gdb to qemu's builting gdbserver.
>
> Whereas Roger asked about the UDK Debugger.
>
> For using the UDK Debugger, the idea is that you'd start it in one VM,
> start the debuggee in another VM, and connect the (emulated) serial
> ports of the VMs. Unfortunately, this doesn't seem to work, because qemu
> (apparently) can't emulate the physical world timings of a serial port
> closely enough.
>
> http://thread.gmane.org/gmane.comp.bios.tianocore.devel/6777
> http://thread.gmane.org/gmane.comp.bios.tianocore.devel/2585/focus=2598
>
> In any case, I don't think users would "insist" on the UDK Debugger, if
> you got gdb + qemu's gdbserver working; so we're awaiting your article
> intently :)
>
> Thanks
> Laszlo
>
>
>
> ------------------------------------------------------------------------------
> _______________________________________________
> edk2-devel mailing list
> [email protected]
> https://lists.sourceforge.net/lists/listinfo/edk2-devel
>
#!/usr/bin/env python
#
import itertools
import math
import os
import sys
try:
import gdb
except:
pass
# It will be simpler to just use struct and recreate the EFI structure
# definitions by hand than use swig. It also allows us to read structures
# before any symbol files have been loaded, such as right after attaching to
# a target or even if the arch configuration is incorrect.
##
EFI_SYSTEM_TABLE_POINTER_DEF = (
('UINT64', 'Signature'),
('UINT64', 'EfiSystemTableBase'),
('UINT32', 'Crc32')
)
EFI_SYSTEM_TABLE_SIG = 'IBI SYST'
EFI_TABLE_HEADER_DEF = (
('UINT64', 'Signature'),
('UINT32', 'Revision'),
('UINT32', 'HeaderSize'),
('UINT32', 'CRC32'),
('UINT32', 'Reserved')
)
EFI_SYSTEM_TABLE_VER = 0x0002001f
EFI_SYSTEM_TABLE_DEF = (
(EFI_TABLE_HEADER_DEF, 'Hdr'),
('PTR', 'FirmwareVendor'),
('UINT32', 'FirmwareRevision'),
('PTR', 'ConsoleInHandle'),
('PTR', 'ConIn'),
('PTR', 'ConsoleOutHandle'),
('PTR', 'ConOut'),
('PTR', 'StandardErrorHandle'),
('PTR', 'StdErr'),
('PTR', 'RuntimeServices'),
('PTR', 'BootServices'),
('UINTN', 'NumberOfTableEntries'),
('PTR', 'ConfigurationTable')
)
EFI_GUID_DEF = (
('UINT32', 'Data1'),
('UINT16', 'Data2'),
('UINT16', 'Data3'),
('UINT8', 'Data4_0'),
('UINT8', 'Data4_1'),
('UINT8', 'Data4_2'),
('UINT8', 'Data4_3'),
('UINT8', 'Data4_4'),
('UINT8', 'Data4_5'),
('UINT8', 'Data4_6'),
('UINT8', 'Data4_7')
)
EFI_CONFIGURATION_TABLE_DEF = (
(EFI_GUID_DEF, 'VendorGuid'),
('PTR', 'VendorTable')
)
EfiDebugImageInfoTableGuid = '49152e77-1ada-4764-b7a2-7afefed95e8b'
EFI_DEBUG_IMAGE_INFO_TABLE_HEADER_DEF = (
('UINT32', 'UpdateStatus'),
('UINT32', 'TableSize'),
('PTR', 'EfiDebugImageInfoTable')
)
EFI_DEBUG_IMAGE_INFO_DEF = (
('UINT32', 'ImageInfoType'),
('PTR', 'LoadedImageProtocolInstance'),
('PTR', 'ImageHandle')
)
EFI_LOADED_IMAGE_PROTOCOL_DEF = (
('UINT32', 'Revision'),
('PTR', 'ParentHandle'),
('PTR', 'SystemTable'),
('PTR', 'DeviceHandle'),
('PTR', 'FilePath'),
('PTR', 'Reserved'),
('UINT32', 'LoadOptionsSize'),
('PTR', 'LoadOptions'),
('PTR', 'ImageBase'),
('UINT64', 'ImageSize'),
('UINT32', 'ImageCodeType'), # enum EFI_MEMORY_TYPE
('UINT32', 'ImageDataType'), # enum EFI_MEMORY_TYPE
('PTR', 'Unload')
)
DOS_IMAGE_SIG = 'MZ'
PE_IMAGE_SIG = 'PE\x00\x00'
TE_IMAGE_SIG = 'VZ'
OPT_NT_HDR32_MAGIC = 0x010b
OPT_NT_HDR64_MAGIC = 0x020b
PE_IMAGE_SUB_EFI_APP = 10
PE_IMAGE_SUB_EFI_BS = 11
PE_IMAGE_SUB_EFI_RT = 12
PE_IMAGE_SUB_SAL_RT = 13
PE_IMAGE_MACHINE_I386 = 0x014c
PE_IMAGE_MACHINE_X64 = 0x8664
PE_IMAGE_MACHINE_ARM = 0x01c2
PE_IMAGE_MACHINE_EBC = 0x0ebc
PE_IMAGE_MACHINE_IA64 = 0x0200
CODEVIEW_TYPE = 0x2
CODEVIEW_NB10_SIG = 'NB10'
CODEVIEW_NB10_OFF = 0x10
CODEVIEW_RSDS_SIG = 'RSDS'
CODEVIEW_RSDS_OFF = 0x18
CODEVIEW_MTOC_SIG = 'MTOC'
CODEVIEW_MTOC_OFF = 0x14
# From BeagleBoardPkg/BeagleBoardPkg.dsc:
# gArmTokenSpaceGuid.PcdSystemMemoryBase|0x80000000
# gArmTokenSpaceGuid.PcdSystemMemorySize|0x08000000
BEAGLE_CONF = {
'ENDIAN': 'LITTLE',
'NAT_SIZE': 4,
'MEM_BASE': 0x80000000,
'MEM_SIZE': 0x08000000
}
# TODO: Need to figure out how to find this.
QEMUIA32_CONF = {
'ENDIAN': 'LITTLE',
'NAT_SIZE': 4,
'MEM_BASE': 0x14000000,
'MEM_SIZE': 0x04000000
}
QEMUX64_CONF = {
'ENDIAN': 'LITTLE',
'NAT_SIZE': 8,
'MEM_BASE': 0x14000000,
'MEM_SIZE': 0x04000000
}
# From MdePkg/Include/*/ProcessorBind.h.
BASE_CTYPE_DEF = {
'UINT64': 'unsigned long long',
'INT64': 'long long',
'UINT32': 'unsigned int',
'INT32': 'int',
'UINT16': 'unsigned short',
'CHAR16': 'unsigned short',
'INT16': 'short',
'BOOLEAN': 'unsigned char',
'UINT8': 'unsigned char',
'CHAR8': 'char',
'INT8': 'signed char'
}
BASE_32_CTYPE_DEF = {
'UINTN': 'unsigned int',
'INTN': 'int',
'PTR': 'unsigned int'
}
BASE_64_CTYPE_DEF = {
'UINTN': 'unsigned long long',
'INTN': 'long long',
'PTR': 'unsigned long long'
}
target_conf = {}
target_ctype_def = {}
target_guids = {}
# Spec ref: UEFI Spec - 17.4 EFI Debug Support Table
# Implementation ref: MdeModulePkg/Core/Dxe/Misc/DebugImageInfo.c
# DxeMain will create EFI_SYSTEM_TABLE_POINTER allocated on 4MB boundary
# starting at top of mem or PcdMaxEfiSystemTablePointerAddress if not 0.
# Debug info lookup process:
# 1) Find EFI_SYSTEM_TABLE_POINTER on 4MB boundary. Currently don't check
# Crc32 although before runtime services is installed it will be 0.
# 2) Find EFI_DEBUG_IMAGE_INFO_TABLE_HEADER from EFI_SYSTEM_TABLE
# 3) Abort if (EFI_DEBUG_IMAGE_INFO_TABLE_HEADER.UpdateStatus |
# EFI_DEBUG_IMAGE_INFO_UPDATE_IN_PROGRESS)
# 4) Iterate EFI_DEBUG_IMAGE_INFO pointers for each image and update gdb
# symbol files as needed.
#
# Once loaded: use a platform specific method, trigger a trap in gdb that will
# automatically update symbol tables as needed.
# Should use this to also update mem base and size.
def set_target(target):
if target == 'ARM':
return
elif target == 'IA32':
return
elif target == 'X64':
target_conf['NAT_SIZE'] = 8
target_ctype_def['UINTN'] = 'unsigned long long'
target_ctype_def['INTN'] = 'long long'
target_ctype_def['PTR'] = 'unsigned long long'
else:
raise(ValueError('Unknown target %s' % target))
# Assumes natural alignment.
def _base_sizeof(ctype):
if isinstance(ctype, str):
if ctype in ('UINT8', 'INT8', 'CHAR8', 'BOOLEAN'): return 1
elif ctype in ('UINT16', 'INT16', 'CHAR16'): return 2
elif ctype in ('UINT32', 'INT32'): return 4
elif ctype in ('UINT64', 'INT64'): return 8
elif ctype in ('UINTN', 'INTN', 'PTR'): return target_conf['NAT_SIZE']
else: raise(ValueError('Unknown C type: %s' % ctype))
else:
raise(TypeError('Invalid type: %s' % type(ctype)))
def _alignto(ctype, addr):
if isinstance(ctype, tuple):
return alignto(ctype[0][0], addr)
elif isinstance(ctype, str):
boundary = _base_sizeof(ctype)
elif isinstance(ctype, int) or isinstance(ctype, long):
boundary = math.log(ctype, 2)
if boundary != int(boundary):
raise(ValueError('%d not a power of two' % ctype))
boundary = int(boundary)
else:
raise(TypeError('Invalid type: %s' % type(ctype)))
return (addr + boundary - 1) & ~(boundary - 1)
def sizeof(ctype, rs=0):
if isinstance(ctype, tuple):
for mtype in zip(*ctype)[0]:
rs = sizeof(mtype, rs)
return rs
elif isinstance(ctype, str):
ts = _base_sizeof(ctype)
return _alignto(ctype, rs + ts)
else:
raise(TypeError('Invalid type: %s' % type(t)))
def offsetof(ctype, member):
i = zip(*ctype)[1].index(member)
return sizeof(ctype[:i + 1]) - sizeof(ctype[i][0])
def read(ctype, addr):
if ctype in target_ctype_def:
cmd = '*(%s *)(%#.8x)' % (target_ctype_def[ctype], addr)
return int(gdb.parse_and_eval(cmd))
else:
raise(ValueError('Unknown C type: %s' % ctype))
def read_member(ctype, member, addr):
rt = [x[0] for x in ctype if x[1] == member][0]
if isinstance(rt, str) == False:
raise(ValueError('Member %s not a base type' % member))
addr += offsetof(ctype, member)
return read(rt, addr)
def read_ascii(addr, size):
buf = gdb.selected_inferior().read_memory(addr, size)
# Trim null characters while we're at it.
return ''.join([buf[x] for x in range(size) if buf[x] != '\x00'])
def guid_to_str(addr):
last_blob = 0
for x in xrange(2,8):
last_blob <<= 8
last_blob += read_member(EFI_GUID_DEF, ('Data4_%d' % x), addr)
return ('%.8x-%.4x-%.4x-%.4x-%.12x' %
(read_member(EFI_GUID_DEF, 'Data1', addr),
read_member(EFI_GUID_DEF, 'Data2', addr),
read_member(EFI_GUID_DEF, 'Data3', addr),
(read_member(EFI_GUID_DEF, 'Data4_0', addr) << 8) +
read_member(EFI_GUID_DEF, 'Data4_1', addr),
last_blob
))
# Can use this to detect if gdb has the correct endian set.
def sig_to_val(sig, rev=False):
ret = 0
sigv = [ord(x) for x in sig]
# EFI signature macros set lsb first
if rev == False:
sigv.reverse()
for x in sigv:
ret <<= 8
ret += x
return ret
def find_system_table_by_pointer(mem_base=None, mem_size=None):
boundary = 0x400000 # 4MB
if mem_base == None:
mem_base = target_conf['MEM_BASE']
if mem_size == None:
mem_size = target_conf['MEM_SIZE']
offset = mem_size - boundary
sigv = sig_to_val(EFI_SYSTEM_TABLE_SIG)
while offset >= 0:
if sigv == read('UINT64', mem_base + offset):
table_addr = read_member(EFI_SYSTEM_TABLE_POINTER_DEF, \
'EfiSystemTableBase', mem_base + offset)
if sigv == read('UINT64', table_addr):
return table_addr
offset -= boundary
return None
def find_debug_image_info_hdr(syst):
table_count = read_member(EFI_SYSTEM_TABLE_DEF, 'NumberOfTableEntries', syst)
table_base = read_member(EFI_SYSTEM_TABLE_DEF, 'ConfigurationTable', syst)
for x in xrange(table_count):
# Do not need to align this.
config_table = table_base + (sizeof(EFI_CONFIGURATION_TABLE_DEF) * x)
vendor_table = read_member(EFI_CONFIGURATION_TABLE_DEF, 'VendorTable', config_table)
if guid_to_str(config_table) == EfiDebugImageInfoTableGuid:
return vendor_table
return None
def get_image_base_list(info_hdr):
base_list = []
status = read_member(EFI_DEBUG_IMAGE_INFO_TABLE_HEADER_DEF, 'UpdateStatus', info_hdr)
table_size = read_member(EFI_DEBUG_IMAGE_INFO_TABLE_HEADER_DEF, 'TableSize', info_hdr)
if table_size == 0:
return base_list
ptr_base = read_member(EFI_DEBUG_IMAGE_INFO_TABLE_HEADER_DEF, 'EfiDebugImageInfoTable', info_hdr)
for x in xrange(table_size):
info_normal = read('PTR', ptr_base + (sizeof('PTR') * x))
if info_normal == 0:
continue
image_proto = read_member(EFI_DEBUG_IMAGE_INFO_DEF, 'LoadedImageProtocolInstance', info_normal)
image_base = read_member(EFI_LOADED_IMAGE_PROTOCOL_DEF, 'ImageBase', image_proto)
base_list.append(image_base)
return base_list
# Not using DEFs for image/pe structures since offsets will be fixed
# regardless of target architecture and will always be aligned.
def find_pe_base(image_base):
pe_base = image_base
if read('UINT16', image_base) == sig_to_val(DOS_IMAGE_SIG):
pe_base += (read('UINT32', image_base + 0x3c) & 0xffff)
# TODO: Also look check for TE images.
if read('UINT32', pe_base) == sig_to_val(PE_IMAGE_SIG):
return pe_base
return None
def get_opt_hdr_type(pe_base):
# Comment in: MdePkg/Library/BasePeCoffGetEntryPointLib/PeCoffGetEntryPoint.c
# Use machine type to determine opt header type for compatibility with some
# tools. If machine type is unknown fallback on opt header magic.
opt_type = None
machine_type = read('UINT16', pe_base + 0x4)
if machine_type in (PE_IMAGE_MACHINE_I386, PE_IMAGE_MACHINE_ARM):
opt_type = 32
elif machine_type in (PE_IMAGE_MACHINE_X64, PE_IMAGE_MACHINE_IA64, PE_IMAGE_MACHINE_EBC):
opt_type = 64
else:
opt_magic = read('UINT16', pe_base + 0x18)
if opt_magic == OPT_NT_HDR32_MAGIC:
opt_type = 32
elif opt_magic == OPT_NT_HDR32_MAGIC:
opt_type = 64
return opt_type
def read_section_table(image_base):
sections = {}
pe_base = find_pe_base(image_base)
if pe_base != None:
opt_type = get_opt_hdr_type(pe_base)
if opt_type == 32:
sec_offset = pe_base + 0xf8
elif opt_type == 64:
sec_offset = pe_base + 0x108
soh = read('UINT32', pe_base + 0x54)
sec_size = image_base + soh - sec_offset
for index in xrange(sec_size / 0x28):
name = read_ascii(sec_offset + (0x28 * index), 0x8)
if name == '':
continue
vs = read('UINT32', sec_offset + (0x28 * index) + 0x8)
va = read('UINT32', sec_offset + (0x28 * index) + 0xc)
rs = read('UINT32', sec_offset + (0x28 * index) + 0x10)
rp = read('UINT32', sec_offset + (0x28 * index) + 0x14)
sections[name] = (vs, va, rs, rp)
return sections
def read_dbg_path(image_base):
pe_base = find_pe_base(image_base)
if pe_base == None:
return ''
dir_cnt = 0
dbg_va = 0
dbg_path_off = 0
opt_type = get_opt_hdr_type(pe_base)
if opt_type == 32:
dir_cnt = read('UINT32', pe_base + 0x74)
dbg_dir_va = read('UINT32', pe_base + 0xa8)
dbg_dir_size = read('UINT32', pe_base + 0xac)
elif opt_type == 64:
dir_cnt = read('UINT32', pe_base + 0x84)
dbg_dir_va = read('UINT32', pe_base + 0xb8)
dbg_dir_size = read('UINT32', pe_base + 0xbc)
# Checks there is a debug section to locate.
if dir_cnt < 5:
return ''
dbg_dir_type = read('UINT32', image_base + dbg_dir_va + 0xc)
if dbg_dir_type == CODEVIEW_TYPE:
dbg_dir_data_size = read('UINT32', image_base + dbg_dir_va + 0x10)
dbg_dir_data_va = read('UINT32', image_base + dbg_dir_va + 0x14)
dbg_sig = read('UINT32', image_base + dbg_dir_data_va)
if dbg_sig == sig_to_val(CODEVIEW_NB10_SIG):
dbg_offset = CODEVIEW_NB10_OFF
elif dbg_sig == sig_to_val(CODEVIEW_RSDS_SIG):
dbg_offset = CODEVIEW_RSDS_OFF
elif dbg_sig == sig_to_val(CODEVIEW_MTOC_SIG):
dbg_offset = CODEVIEW_MTOC_OFF
else:
return ''
dbg_path = read_ascii(image_base + dbg_dir_data_va + dbg_offset, dbg_dir_data_size - dbg_offset)
return dbg_path
def get_debug_info(image_base):
sections = read_section_table(image_base)
text_base = sections.get('.text', None)
data_base = sections.get('.data', None)
# If the section was found apply image loaded address to the VA.
if text_base != None: text_base = text_base[1] + image_base
if data_base != None: data_base = data_base[1] + image_base
dbg_path = read_dbg_path(image_base)
return text_base, data_base, dbg_path
def stdout_wf(msg):
# sys.stdout is directed to gdb.stdout
sys.stdout.write(msg)
sys.stdout.flush()
def set_params(params):
old_params = []
for param, val in params:
old_params.append((param, gdb.parameter(param)))
# gdb.paramter(...) will try to convert it's internal representation into a
# python type. Python None can map to "auto", INT_MAX/UINT_MAX (-1) or 0.
# Other returned types are long, bool, str.
# See gdb/python/python.c::gdbpy_parameter_value
if val == True: val = 'on'
elif val == False: val = 'off'
if val == None:
# Try all possible arguments that may return None.
for new_val in [-1, 0, 'auto']:
try:
gdb.execute('set %s %s' % (param, new_val))
except:
continue
if gdb.parameter(param) == None:
break
else:
gdb.execute('set %s %s' % (param, val))
return old_params
# Almost the same as guid_to_str(...) but hopefully faster if values are
# cached by gdb.
def guid_to_str_by_val(gdb_value):
if gdb_value.type != gdb.lookup_type('EFI_GUID'):
return ''
last_blob = 0
for x in xrange(2,8):
last_blob <<= 8
last_blob += int(gdb_value['Data4'][x])
return ('%.8x-%.4x-%.4x-%.4x-%.12x' %
(int(gdb_value['Data1']),
int(gdb_value['Data2']),
int(gdb_value['Data3']),
(int(gdb_value['Data4'][0]) << 8) + int(gdb_value['Data4'][1]),
last_blob
))
def load_guids(quiet=False):
if quiet == False:
print 'Loading guids'
syms = gdb.execute('info variables ^g.*Guid', False, True).split('\n')
guid_names = [x[9:-1] for x in syms if x.startswith('EFI_GUID ')]
guid_names = list(set(guid_names))
guid_names = [(x, guid_to_str_by_val(gdb.parse_and_eval(x))) for x in guid_names]
# These are generated at link time and do not have an associated type. The
# file guids optimized out will have a *abs* 0 value and already be filtered
# by GDB.
mod_names = [x.split(' ')[-1] for x in syms if x.startswith('0x') and x.endswith('FileGuid')]
mod_names = [(x, guid_to_str_by_val(gdb.parse_and_eval('(EFI_GUID)(%s)' % x))) for x in mod_names]
return dict(guid_names + mod_names)
def name_to_guid(guid_name):
global target_guids
# Check if it has been loaded but don't refresh all guids if possible.
if target_guids.has_key(guid_name):
return target_guids[guid_name]
# Lookup manually. Should only happen if load_guids has not been called
# since symbols were refreshed.
try:
guid = gdb.parse_and_eval(guid_name)
return guid_to_str_by_val(guid)
except:
return None
def guid_to_name(guid_str):
global target_guids
if len(target_guids) == 0:
target_guids = load_guids(True)
# A guid may have many names (gEfiDriverConfigurationProtocolGuid and
# gEfiDriverConfiguration2ProtocolGuid) so just return the first found.
matches = [x[0] for x in target_guids.items() if x[1] == guid_str]
if len(matches) != 0:
return matches[0]
return None
def walk_efi_list(head):
# head is considered to not have data attached to it.
ptrs = []
if head.type != gdb.lookup_type('LIST_ENTRY'):
return ptrs
list_entry = head['ForwardLink'].dereference()
while list_entry.address not in [head.address, 0]:
ptrs.append(long(list_entry.address))
list_entry = list_entry['ForwardLink'].dereference()
return ptrs
# MdePkg/Include/Base.h::BASE_CR(Record, TYPE, Field)
def base_cr(r, t, f):
#CR(Link, PROTOCOL_ENTRY, AllEntries
offset = gdb.lookup_type(t)[f].bitpos
if (offset % 8) != 0:
raise(ValueError('offset of t.f must be int bytes'))
offset /= 8
return gdb.parse_and_eval('*(%s *)(%d)' % (t, r - offset))
# For now all of efi commands are currently set as COMMAND_SUPPORT and will
# show up in '(gdb) help support'.
class EfiCommand(gdb.Command):
"""Commands specific for EFI targets in DXE"""
def __init__(self):
super(EfiCommand, self).__init__('efi', gdb.COMMAND_SUPPORT, gdb.COMPLETE_NONE, True)
def invoke(self, args, from_tty):
gdb.execute('help efi')
class EfiSetCommand(gdb.Command):
"""Set efi specific stuff"""
def __init__(self):
super(EfiSetCommand, self).__init__('efi set', gdb.COMMAND_SUPPORT, gdb.COMPLETE_NONE, True)
def invoke(self, args, from_tty):
gdb.execute('help efi set')
class EfiSetTargetCommand(gdb.Command):
"""Set internal and gdb parameters for EFI target
usage: efi set target <preset>|<custom arch mem_base mem_size>"""
def __init__(self):
super(EfiSetTargetCommand, self).__init__('efi set target', gdb.COMMAND_SUPPORT)
self.arch_cmds = {
'arm': ('set arch arm', 'set arm force-mode thumb'),
'ia32': ('set arch i386',),
'x64': ('set arch i386:x86-64',)
}
self.arch_ctypes = {
'arm': (BASE_CTYPE_DEF, BASE_32_CTYPE_DEF),
'ia32': (BASE_CTYPE_DEF, BASE_32_CTYPE_DEF),
'x64': (BASE_CTYPE_DEF, BASE_64_CTYPE_DEF)
}
self.presets = {
'beagle': ('arm', BEAGLE_CONF),
'qemuia32': ('ia32', QEMUIA32_CONF),
'qemux64': ('x64', QEMUX64_CONF)
}
def _help(self):
print self.__doc__
print 'presets are: %s' % ' '.join(self.presets.keys())
def complete(self, text, word):
params = self.presets.keys() + ['custom',]
params = [x for x in params if x.startswith(text)]
return params
def invoke(self, args, from_tty):
global target_conf
global target_ctype_def
args = [x for x in args.split(' ') if x != '']
arch = ''
if len(args) != 0:
if args[0] == 'custom':
print 'custom stuff'
elif args[0] in self.presets:
arch = self.presets[args[0]][0]
target_conf = dict(self.presets[args[0]][1].items())
target_conf['NAME'] = args[0]
else:
print 'invalid argument %s' % args[0]
if arch == '':
self._help()
return
target_ctype_def = dict(itertools.chain(*[x.iteritems() for x in self.arch_ctypes[arch]]))
for cmd in self.arch_cmds[arch]:
gdb.execute(cmd)
class EfiShowCommand(gdb.Command):
"""Show efi specific settings"""
def __init__(self):
super(EfiShowCommand, self).__init__('efi show', gdb.COMMAND_SUPPORT, gdb.COMPLETE_NONE, True)
def invoke(self, args, from_tty):
gdb.execute('help efi show')
class EfiShowTargetCommand(gdb.Command):
"""Show efi target settings"""
def __init__(self):
super(EfiShowTargetCommand, self).__init__('efi show target', gdb.COMMAND_SUPPORT, gdb.COMPLETE_NONE)
def invoke(self, args, from_tty):
name = target_conf.get('NAME', 'none')
print 'efi target is "%s"' % name
if name != 'none':
msg = ''
for key in target_conf:
val = target_conf[key]
if isinstance(val, int) or isinstance(val, long):
val = hex(val)
msg += ' %s: %s' % (str(key), str(val))
print 'efi target conf:%s' % msg
class EfiSymbolsCommand(gdb.Command):
"""Searches for codeview pointers in loaded images and loads symbol files"""
def __init__(self):
super(EfiSymbolsCommand, self).__init__('efi symbols', gdb.COMMAND_SUPPORT, gdb.COMPLETE_NONE)
def invoke(self, args, from_tty):
gdb.execute('symbol-file')
stdout_wf('Locating images')
syst = find_system_table_by_pointer()
if syst == None:
print 'Can not find system table pointer'
return
stdout_wf('.')
info_hdr = find_debug_image_info_hdr(syst)
stdout_wf('.')
img_list = get_image_base_list(info_hdr)
print 'Done'
stdout_wf('Loading symbols.')
err_list = []
for x in img_list:
debug_info = get_debug_info(x)
if debug_info[2] == '' or debug_info[0] == None:
err_list.append('No information on image at %#x' % x)
continue
cmd = 'add-symbol-file %s %#x' % (debug_info[2], debug_info[0])
if debug_info[1] != None:
cmd += ' -s .data %#x' % debug_info[1]
try:
gdb.execute(cmd, False, True)
stdout_wf('.')
except:
err_list.append('%s @ %#x' % (debug_info[2], debug_info[0]))
stdout_wf('!')
print 'Done'
if len(err_list) != 0:
print 'Could not load:'
for x in err_list:
print x
class EfiInfoCommand(gdb.Command):
"""Show stuff specific to EFI"""
def __init__(self):
super(EfiInfoCommand, self).__init__('efi info', gdb.COMMAND_SUPPORT, gdb.COMPLETE_NONE, True)
def invoke(self, args, from_tty):
gdb.execute('help efi info')
class EfiInfoImagesCommand(gdb.Command):
"""Show loaded images and codeview pointer if available
usage: efi info images [image index|verbose]"""
def __init__(self):
super(EfiInfoImagesCommand, self).__init__('efi info images', gdb.COMMAND_SUPPORT, gdb.COMPLETE_NONE)
def _help(self):
print self.__doc__
def invoke(self, args, from_tty):
verbose = False
index = None
if args == 'verbose':
verbose = True
elif args.isdigit() == True:
verbose = True
index = int(args)
elif args != '':
self._help()
return
syst = find_system_table_by_pointer()
if syst == None:
print 'Can not find system table pointer'
return
info_hdr = find_debug_image_info_hdr(syst)
img_list = enumerate(get_image_base_list(info_hdr))
img_list = [x for x in img_list]
if index != None:
img_list = [x for x in img_list if x[0] == index]
pad = target_conf['NAT_SIZE'] * 2
print ' idx %s debug path' % 'base'.ljust(pad + 2)
if verbose == True:
print ' {:<10s} {:<10s} {:<10s} {:<10s} {:<10s}'.format(
'sec name', 'v_size', 'v_addr', 'r_size', 'r_addr')
for index, image_base in img_list:
base = '0x%s' % ('%x' % image_base).zfill(pad)
dbg_path = read_dbg_path(image_base)
if dbg_path == '': dbg_path = '?'
print '%4d %s %s' % (index, base, dbg_path)
if verbose == True:
sections = read_section_table(image_base).items()
# Sort by VA.
sections.sort(key= lambda x: x[1][1])
for sec in sections:
print ' {:<10s} {:#010x} {:#010x} {:#010x} {:#010x}'.format(sec[0], *sec[1])
class EfiInfoTableCommand(gdb.Command):
"""Show various EFI tables"""
def __init__(self):
super(EfiInfoTableCommand, self).__init__('efi info table', gdb.COMMAND_SUPPORT)
self._pt_tag = '_print_table_'
def complete(self, text, word):
tables = [x[len(self._pt_tag):] for x in dir(self) if x.startswith(self._pt_tag)]
tables = [x for x in tables if x.startswith(text)]
return tables
def _set_print_params(self, params=None):
if params == None:
params = [
('print pretty', 'on'),
('print union', 'on'),
('print symbol-filename', 'on')
]
return set_params(params)
def _print_table_system(self):
old_pp = self._set_print_params()
gdb.execute('p *gST')
self._set_print_params(old_pp)
def _print_table_rt_services(self):
old_pp = self._set_print_params()
gdb.execute('p *gRT')
self._set_print_params(old_pp)
def _print_table_boot_services(self):
old_pp = self._set_print_params()
gdb.execute('p *gBS')
self._set_print_params(old_pp)
def _print_table_dxe_services(self):
old_pp = self._set_print_params()
gdb.execute('p mDxeServices')
self._set_print_params(old_pp)
def invoke(self, args, from_tty):
try:
method = getattr(self, self._pt_tag + args)
except:
print 'invalid table: "%s"' % args
return
method()
class EfiInfoGuidsCommand(gdb.Command):
"""Show guids that have a symbol matching EFI_GUID g.*Guid
usage: efi info guids [refresh]"""
def __init__(self):
super(EfiInfoGuidsCommand, self).__init__('efi info guids', gdb.COMMAND_SUPPORT)
def complete(self, text, word):
params = ['refresh',]
params = [x for x in params if x.startswith(text)]
return params
def _help(self):
print self.__doc__
def invoke(self, args, from_tty):
global target_guids
if args == 'refresh':
refresh = True
elif args == '':
refresh = False
else:
self._help()
return
if len(target_guids) == 0 or refresh == True:
target_guids = load_guids()
for name, guid in sorted(target_guids.items()):
print '%s %s' % ((name + ':').ljust(50), guid)
class EfiInfoProtocolsCommand(gdb.Command):
"""Show information in the protocol database."""
def __init__(self):
super(EfiInfoProtocolsCommand, self).__init__('efi info protocols', gdb.COMMAND_SUPPORT)
def invoke(self, args, from_tty):
head = gdb.parse_and_eval('mProtocolDatabase')
entry_links = walk_efi_list(head)
for link in entry_links:
entry = base_cr(link, 'PROTOCOL_ENTRY', 'AllEntries')
guid_str = guid_to_str_by_val(entry['ProtocolID'])
guid_name = guid_to_name(guid_str)
if guid_name == None:
guid_name = guid_str
print '%#x: %s' % (long(entry.address), guid_name)
def gdb_load_commands():
# Load everything in this module name Efi*Command
me = sys.modules[__name__]
efi_cmds = dir(me)
efi_cmds = [x for x in efi_cmds if x.startswith('Efi') and x.endswith('Command')]
efi_cmds = [getattr(me, x) for x in efi_cmds]
# Pass if the constructor requires arguments.
for cls in efi_cmds:
try:
cls()
except:
pass
if __name__ == '__main__':
gdb_load_commands()
------------------------------------------------------------------------------
_______________________________________________
edk2-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/edk2-devel