Linux core source/info

Linux の core (x86, 32-bit) に file しても何の core かまでは教えてくれない。

readelf の出力を足がかりに、core ファイルから少し情報を引き出すスクリプト
作ってみた。


$ cat ci.py
#!/usr/bin/env python

import os, sys, re
from struct import *

def roundup(x,y): return ((((x) + ((y) - 1)) / (y)) * (y))

def termstr(str):
nullidx = -1
idx = 0
for c in str:
if c == "\0":
nullidx = idx
break
idx+=1
if nullidx != -1:
return str[0:nullidx]
return str

class ElfNote:
def __init__(self, fields):
self.namesz = fields[0]
self.descsz = fields[1]
self.type = fields[2]

class Core:
import re
NT_PRSTATUS = 1
NT_PRFPREG = 2
NT_PRPSINFO = 3
NT_TASKSTRUCT = 4
NT_AUXV = 6
NT_PRXFPREG = 0x46e62b7f

# register index, see http://lxr.linux.no/linux/arch/x86_64/ia32/ia32_binfmt.c#L122
IRBX = 0
IRCX = 1
IRDX = 2
IRSI = 3
IRDI = 4
IRBP = 5
IRAX = 6
IDS = 7
IES = 8
IFS = 9
IGS = 10
IORIG_RAX = 11
IRIP = 12
ICS = 13
IEFLAGS = 14
IRSP = 15
ISS = 16

def __init__(self, path):
self.path = path
self.note_offset = self.note_length = -1
self.verbose = False

def parse(self):
if self.isElfFile() == False:
print "ERROR: file %s does not look like an expected linux 32-bit core"
return

relf = os.popen('/usr/bin/readelf -a ' + core)
note_header_ptn = re.compile('Notes at offset (?P0x[0-9a-f]+) with length (?P0x[0-9a-f]*):')
# Notes at offset 0x00000434 with length 0x0000046c:
for l in relf:
mt = re.match(note_header_ptn, l)
if mt != None:
self.note_offset = int(mt.group('noffset'), 16)
self.note_length = int(mt.group('nlength'), 16)
if self.verbose:
print "Note offset %#x, length %#x" % (self.note_offset, self.note_length)
relf.close()

if self.isNoteOffValid() == False:
print "# bad off"

# print "# can proceed"
self.processHeader()

def processHeader(self):
""" see http://lxr.linux.no/linux/fs/proc/kcore.c#L100
"""
try:
fcore = open(core)
elf_note_format = "3I"
fcore.seek(self.note_offset)

processed = 0
while processed < self.note_length:
snote = fcore.read(calcsize(elf_note_format))
notea = unpack(elf_note_format, snote)
en = ElfNote(notea)
nlen = roundup(en.namesz, 4)
dname = termstr(fcore.read(nlen))
if self.verbose:
print "NoteHeader namesz %d, descsz %#x, type %d, name %s" % (en.namesz, en.descsz, en.type, dname)
processed += calcsize(elf_note_format) + nlen
if en.type == self.NT_PRSTATUS:
self.processPrstatus(fcore, en)
elif en.type == self.NT_PRPSINFO:
self.processPrpsinfo(fcore, en)
else:
fcore.read(en.descsz)
processed += en.descsz

fcore.close()
except IOError, (errno, strerror):
print "I/O error(%s): %s: %s" % (errno, strerror, core)

def processPrstatus(self, fcore, note):
""" see http://lxr.linux.no/linux/fs/binfmt_elf.c#L1336
"""
try:
prstatus_format = "3ih2L4i8l17Li"
dprstatus = fcore.read(calcsize(prstatus_format))
prsa = unpack(prstatus_format, dprstatus)
if self.verbose:
print "prstatus"
print " siginfo signo %d, code %d, errno %d" % (prsa[0], prsa[1], prsa[2])
print " cursig %d" % prsa[3],
print " sigpend %d" % prsa[4],
print " sighold %d" % prsa[5]
print " pid %d" % prsa[6],
print " ppid %d" % prsa[7],
print " pgrp %d" % prsa[8],
print " sid %d" % prsa[9]

print " utime %d.%06d" % (prsa[10], prsa[11]),
print " stime %d.%06d" % (prsa[12], prsa[13])
print " cutime %d.%06d" % (prsa[14], prsa[15]),
print " cstime %d.%06d" % (prsa[16], prsa[17])

regs = prsa[18:35]

# see http://lxr.linux.no/linux/arch/x86_64/ia32/ia32_binfmt.c#L122
print " EAX=0x%08x, EBX=0x%08x, ECX=0x%08x, EDX=0x%08x" % (regs[self.IRAX], regs[self.IRBX], regs[self.IRCX], regs[self.IRDX])
print " ESP=0x%08x, EBP=0x%08x, ESI=0x%08x, EDI=0x%08x" % (regs[self.IRSP], regs[self.IRBP], regs[self.IRSI], regs[self.IRDI])
print " EIP=0x%08x, EFLAGS=0x%08x" % (regs[self.IRIP], regs[self.IEFLAGS])


except IOError, (errno, strerror):
print "I/O error(%s): %s: %s" % (errno, strerror, core)

def processPrpsinfo(self, fcore, note):
try:
prpsinfo_format = "4bL2H4i16c80c"
dprpsinfo = fcore.read(calcsize(prpsinfo_format))
prpsa = unpack(prpsinfo_format, dprpsinfo)

fname = termstr("".join(prpsa[11:27]))
arg = termstr("".join(prpsa[27:107]))

if self.verbose:
print "prpsinfo"
print " state %d sname %s zomb %s nice %d flag %#x" % (prpsa[0], dprpsinfo[1], prpsa[2], prpsa[3], prpsa[4])
print " uid %d gid %d pid %d ppid %d pgrp %d sid %d" % (prpsa[5], prpsa[6], prpsa[7], prpsa[8], prpsa[9], prpsa[10])

print " fname: %s, args: %s" % (fname, arg)
except IOError, (errno, strerror):
print "I/O error(%s): %s: %s" % (errno, strerror, core)

def isNoteOffValid(self):
if self.note_offset < 0: return False
if self.note_length < 0: return False
return True

def isElfFile(self):
ITYPE = 16
IMACHINE = 17
EI_CLASS = 4
EI_DATA = 5

ET_CORE = 4
EM_386 = 3
ELFCLASS32 = 1
ELFDATA2LSB = 1

valid = True
try:
fcore = open(core)
elf32_ehdr_format = "16B2H5I6H"
deh = fcore.read(calcsize(elf32_ehdr_format))
if deh.startswith('\x7fELF') == False:
print "ERROR: bad magic number"
valid = False
deha = unpack(elf32_ehdr_format, deh)

if deha[ITYPE] != ET_CORE:
print "ERROR: e_type is not ET_CORE"
valid = False

if deha[IMACHINE] != EM_386:
print "ERROR: e_machine is not EM_386"
valid = False

if deha[EI_CLASS] != ELFCLASS32:
print "ERROR: Not a 32-bit object"
valid = False

if deha[EI_DATA] != ELFDATA2LSB:
print "ERROR: Not little endian"
valid = False

fcore.close()
except IOError, (errno, strerror):
print "I/O error(%s): %s: %s" % (errno, strerror, core)

return valid

def printnote(self):
print "note implemented yet."

def setVerbose(self, f):
self.verbose = f
#print "verbose was set to " + str(f)

def printCoreNote(core):
c = Core(core)
c.parse()
c.printnote()

def showHelp():
msg = """help: ci.py [-v]
ci.py -h
flags: -h: show this help
-v: verbose, show prstatus
"""
print msg

if __name__ == "__main__":
import sys, getopt

optlist, args = getopt.getopt(sys.argv[1:], 'hv')

verbose = False

for otup in optlist:
if otup[0] == '-h':
showHelp()
sys.exit(0)
elif otup[0] == '-v':
verbose = True

if len(args) == 0:
showHelp()
sys.exit(0)
else:
for core in args:
c = Core(core)
if verbose: c.setVerbose(verbose)
c.parse()
#c.printnote()