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

Reply via email to