Skip to content

Commit b405af1

Browse files
committed
github action to compare with the output of readelf -h
1 parent 73037e9 commit b405af1

File tree

6 files changed

+198
-32
lines changed

6 files changed

+198
-32
lines changed

.github/workflows/tools.yml

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ on:
99
branches: [ "master" ]
1010

1111
jobs:
12-
examples:
12+
macos:
1313
runs-on: ${{ matrix.os }}
1414
strategy:
1515
fail-fast: false
@@ -26,3 +26,21 @@ jobs:
2626
run: |
2727
export PYTHONPATH=$PYTHONPATH:$(pwd)
2828
zsh ./tests/examples_macos.sh
29+
linux:
30+
runs-on: ${{ matrix.os }}
31+
strategy:
32+
fail-fast: false
33+
matrix:
34+
os: ["ubuntu-latest", "ubuntu-22.04", "ubuntu-20.04"]
35+
python-version: ["3.10"]
36+
steps:
37+
- uses: actions/checkout@v3
38+
- name: Set up Python ${{ matrix.python-version }}
39+
uses: actions/setup-python@v4
40+
with:
41+
python-version: ${{ matrix.python-version }}
42+
- name: Comparison with readelf
43+
run: |
44+
readelf --version
45+
export PYTHONPATH=$PYTHONPATH:$(pwd)
46+
bash ./tests/examples_linux.sh

elfesteem/elf.py

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,24 @@ class Shdr(CStructWithStrTable):
4040
("addralign","ptr"),
4141
("entsize","ptr") ]
4242
strtab = property(lambda _: _.parent.shstrtab)
43-
format = property(lambda _: {
44-
32: " [%(idx)2d] %(name17)-17s %(type_txt)-15s %(addr)08x %(offset)06x %(size)06x %(entsize)02x %(flags_txt)3s %(link)2d %(info)3d %(addralign)2d",
45-
64: " [%(idx)2d] %(name17)-17s %(type_txt)-15s %(addr)016x %(offset)08x %(size)016x %(entsize)016x %(flags_txt)3s %(link)2d %(info)2d %(addralign)2d",
46-
}[_.wsize])
43+
header32 = [" [Nr] Name Type Addr Off Size ES Flg Lk Inf Al"]
44+
format32 = " [%(idx)2d] %(name17)-17s %(type_txt)-15s %(addr)08x %(offset)06x %(size)06x %(entsize)02x %(flags_txt)3s %(link)2d %(info)3d %(addralign)2d"
45+
header64 = [" [Nr] Name Type Address Offset Size EntSize Flags Link Info Align"]
46+
format64 = " [%(idx)2d] %(name17)-17s %(type_txt)-15s %(addr)016x %(offset)08x %(size)016x %(entsize)016x %(flags_txt)3s %(link)2d %(info)2d %(addralign)2d"
47+
footer32 = [
48+
"Key to Flags:",
49+
" W (write), A (alloc), X (execute), M (merge), S (strings)",
50+
" I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)",
51+
" O (extra OS processing required) o (OS specific), p (processor specific)",
52+
]
53+
footer64 = [
54+
"Key to Flags:",
55+
" W (write), A (alloc), X (execute), M (merge), S (strings), l (large)",
56+
" I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)",
57+
" O (extra OS processing required) o (OS specific), p (processor specific)",
58+
]
59+
format = property(lambda _: { 32: Shdr.format32, 64: Shdr.format64 }[_.wsize])
60+
footer = property(lambda _: { 32: Shdr.footer32, 64: Shdr.footer64 }[_.wsize])
4761
name17 = property(lambda _: _.name[:17])
4862
idx = property(lambda _: _.parent.parent.shlist.index(_.parent))
4963
def flags_txt(self):
@@ -353,6 +367,27 @@ class Vernaux64(Vernaux32):
353367
ELFDATA2MSB = 2, # Most significant byte at lowest address
354368
)
355369

370+
SetConstants(
371+
# Legal values for e_ident[EI_OSABI]
372+
ELFOSABI_NONE = 0, # UNIX System V ABI
373+
ELFOSABI_SYSV = 0, # Alias
374+
ELFOSABI_HPUX = 1, # HP-UX
375+
ELFOSABI_NETBSD = 2, # NetBSD
376+
ELFOSABI_GNU = 3, # Object uses GNU ELF extensions
377+
ELFOSABI_LINUX = 3, # Compatibility alias
378+
ELFOSABI_SOLARIS = 6, # Sun Solaris
379+
ELFOSABI_AIX = 7, # IBM AIX
380+
ELFOSABI_IRIX = 8, # SGI Irix
381+
ELFOSABI_FREEBSD = 9, # FreeBSD
382+
ELFOSABI_TRU64 = 10, # Compaq TRU64 UNIX
383+
ELFOSABI_MODESTO = 11, # Novell Modesto
384+
ELFOSABI_OPENBSD = 12, # OpenBSD
385+
ELFOSABI_ARM_AEABI = 64, # ARM EABI
386+
ELFOSABI_ARM = 97, # ARM
387+
ELFOSABI_STANDALONE = 255, # Standalone (embedded) application
388+
no_name=('ELFOSABI_SYSV','ELFOSABI_LINUX'),
389+
)
390+
356391
SetConstants(
357392
# Legal values for e_type (object file type).
358393
ET_NONE = 0, # No file type

elfesteem/elf_init.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -736,17 +736,9 @@ def readelf_display(self):
736736
% (len(self.shlist), self.parent.Ehdr.shoff),
737737
"",
738738
"Section Headers:" ]
739-
if self.wsize == 32:
740-
rep.append( " [Nr] Name Type Addr Off Size ES Flg Lk Inf Al" )
741-
elif self.wsize == 64:
742-
rep.extend([" [Nr] Name Type Address Offset Size EntSize Flags Link Info Align"])
739+
rep.extend({32: elf.Shdr.header32, 64: elf.Shdr.header64}[self.wsize])
743740
rep.extend([ _.sh.readelf_display() for _ in self ])
744-
rep.extend([ # Footer
745-
"Key to Flags:",
746-
" W (write), A (alloc), X (execute), M (merge), S (strings)",
747-
" I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)",
748-
" O (extra OS processing required) o (OS specific), p (processor specific)",
749-
])
741+
rep.extend(self[0].sh.footer)
750742
return "\n".join(rep)
751743
def __str__(self):
752744
raise AttributeError("Use pack() instead of str()")

examples/readelf.py

Lines changed: 127 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,26 @@
88
sys.path.insert(1, os.path.abspath(sys.path[0]+'/..'))
99
from elfesteem import elf_init, elf
1010

11+
import subprocess
12+
def popen_read_out_err(cmd):
13+
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
14+
p.wait()
15+
p.stdin.close()
16+
return p.stdout.read() + p.stderr.read()
17+
18+
import re
19+
def get_readelf_version():
20+
readelf_v = popen_read_out_err(["readelf", "--version"])
21+
if type(readelf_v) != str: readelf_v = str(readelf_v, encoding='latin1')
22+
r = re.search(r'GNU readelf .* (\d+\.\d+)', readelf_v)
23+
if r:
24+
sys.stderr.write("readelf version %s\n" % float(r.groups()[0]))
25+
return float(r.groups()[0])
26+
else:
27+
sys.stderr.write("Could not detect readelf version\n")
28+
sys.stderr.write(readelf_v)
29+
return None
30+
1131
et_strings = {
1232
elf.ET_REL: 'REL (Relocatable file)',
1333
elf.ET_EXEC: 'EXEC (Executable file)',
@@ -18,11 +38,26 @@ def expand_code(table, val):
1838
if val in table: return table[val]
1939
return '<unknown>: %#x' % val
2040

41+
def is_pie(e):
42+
# binutils 2.37
43+
# 2021-06-15 https://github.com/bminor/binutils-gdb/commit/93df3340fd5ad32f784214fc125de71811da72ff
44+
for i, sh in enumerate(e.sh):
45+
if sh.sh.type != elf.SHT_DYNAMIC:
46+
continue
47+
if e.wsize == 32:
48+
dyntab = sh.dyntab[:-2]
49+
elif e.wsize == 64:
50+
dyntab = sh.dyntab[:-1]
51+
for d in dyntab:
52+
if d.type == elf.DT_FLAGS_1 and d.name & elf.DF_1_PIE:
53+
return True
54+
return False
55+
2156
def display_headers(e):
2257
print("ELF Header:")
2358
import struct
2459
ident = struct.unpack('16B', e.Ehdr.ident)
25-
print(" Magic: %s"%' '.join(['%02x'%_ for _ in ident]))
60+
print(" Magic: %s "%' '.join(['%02x'%_ for _ in ident]))
2661
print(" Class: %s"%expand_code({
2762
elf.ELFCLASS32: 'ELF32',
2863
elf.ELFCLASS64: 'ELF64',
@@ -38,7 +73,10 @@ def display_headers(e):
3873
0: 'UNIX - System V',
3974
}, ident[elf.EI_OSABI]))
4075
print(" ABI Version: %d"%ident[elf.EI_ABIVERSION])
41-
print(" Type: %s"%expand_code(et_strings, e.Ehdr.type))
76+
elf_file_type = expand_code(et_strings, e.Ehdr.type)
77+
if e.Ehdr.type == elf.ET_DYN and elf.is_pie(e):
78+
elf_file_type = 'DYN (Position-Independent Executable file)'
79+
print(" Type: %s"%elf_file_type)
4280
machine_code = dict(elf.constants['EM'])
4381
# Same textual output as readelf, from readelf.c
4482
machine_code[elf.EM_M32] = 'ME32100'
@@ -72,21 +110,19 @@ def display_headers(e):
72110
machine_code[elf.EM_IA_64] = 'Intel IA-64'
73111
machine_code[elf.EM_MIPS_X] = 'Stanford MIPS-X'
74112
machine_code[elf.EM_COLDFIRE] = 'Motorola Coldfire'
113+
machine_code[elf.EM_X86_64] = 'Advanced Micro Devices X86-64'
75114
print(" Machine: %s"%expand_code(machine_code, e.Ehdr.machine))
76-
"""
77-
TO BE CONTINUED
78-
("version","u32"),
79-
("entry","ptr"),
80-
("phoff","ptr"),
81-
("shoff","ptr"),
82-
("flags","u32"),
83-
("ehsize","u16"),
84-
("phentsize","u16"),
85-
("phnum","u16"),
86-
("shentsize","u16"),
87-
("shnum","u16"),
88-
("shstrndx","u16") ]
89-
"""
115+
print(" Version: %#x"%e.Ehdr.version)
116+
print(" Entry point address: %#x"%e.Ehdr.entry)
117+
print(" Start of program headers: %d (bytes into file)"%e.Ehdr.phoff)
118+
print(" Start of section headers: %d (bytes into file)"%e.Ehdr.shoff)
119+
print(" Flags: %#x"%e.Ehdr.flags)
120+
print(" Size of this header: %d (bytes)"%e.Ehdr.ehsize)
121+
print(" Size of program headers: %d (bytes)"%e.Ehdr.phentsize)
122+
print(" Number of program headers: %d"%e.Ehdr.phnum)
123+
print(" Size of section headers: %d (bytes)"%e.Ehdr.shentsize)
124+
print(" Number of section headers: %d"%e.Ehdr.shnum)
125+
print(" Section header string table index: %d"%e.Ehdr.shstrndx)
90126

91127
def display_program_headers(e):
92128
# Output format similar to readelf -l
@@ -199,11 +235,86 @@ def display_symbols(sections):
199235
parser.add_argument('-d', '--dynamic', dest='options', action='append_const', const='dynamic', help='Display the dynamic section (if present)')
200236
parser.add_argument('-l', '--program-headers', '--segments', dest='options', action='append_const', const='program', help='Display the program headers')
201237
parser.add_argument('-g', '--section-groups', dest='options', action='append_const', const='groups', help='Display the section groups')
238+
parser.add_argument('--readelf', dest='readelf_version', action='append', help='Simulate the output of a given version of readelf')
202239
parser.add_argument('file', nargs='+', help='ELF file(s)')
203240
args = parser.parse_args()
204241
if args.options is None:
205242
args.options = []
206243

244+
elf.is_pie = lambda _: False
245+
if args.readelf_version:
246+
for readelf in args.readelf_version:
247+
if 'native' in readelf:
248+
readelf_version = get_readelf_version()
249+
else:
250+
readelf_version = float(readelf)
251+
if True:
252+
# TODO: readelf has a different output if "do_section_details" or "do_wide"
253+
elf.Shdr.header64 = [" [Nr] Name Type Address Offset",
254+
" Size EntSize Flags Link Info Align"]
255+
elf.Shdr.format64 = (" [%(idx)2d] %(name17)-17s %(type_txt)-15s %(addr)016x %(offset)08x\n"
256+
" %(size)016x %(entsize)016x %(flags_txt)3s %(link)2d %(info)2d %(addralign)d")
257+
if readelf_version >= 2.26:
258+
# 2016-01-20 https://github.com/bminor/binutils-gdb/commit/9fb71ee49fc37163697e4f34e16097928eb83d66
259+
elf.Shdr.footer = property(lambda _: [
260+
"Key to Flags:",
261+
" W (write), A (alloc), X (execute), M (merge), S (strings), I (info),",
262+
" L (link order), O (extra OS processing required), G (group), T (TLS),",
263+
" C (compressed), x (unknown), o (OS specific), E (exclude),",
264+
" %sp (processor specific)" % (
265+
"l (large), " if e.Ehdr.machine in (elf.EM_X86_64, elf.EM_L10M, elf.EM_K10M) else
266+
"y (noread), " if e.Ehdr.machine == elf.EM_ARM else
267+
"" ),
268+
])
269+
if readelf_version >= 2.27:
270+
# 2016-07-05 https://github.com/bminor/binutils-gdb/commit/f0728ee368f217f2473798ad7ccfe9feae4412ce
271+
elf.Shdr.footer = property(lambda _: [
272+
"Key to Flags:",
273+
" W (write), A (alloc), X (execute), M (merge), S (strings), I (info),",
274+
" L (link order), O (extra OS processing required), G (group), T (TLS),",
275+
" C (compressed), x (unknown), o (OS specific), E (exclude),",
276+
" %sp (processor specific)" % (
277+
"l (large), " if e.Ehdr.machine in (elf.EM_X86_64, elf.EM_L10M, elf.EM_K10M) else
278+
"y (purecode), " if e.Ehdr.machine == elf.EM_ARM else
279+
"" ),
280+
])
281+
if readelf_version >= 2.29: # more precisely 2.29.1
282+
# 2017-09-05 https://github.com/bminor/binutils-gdb/commit/83eef883581525d04df3a8e53a82c01d0d12b56a
283+
elf.Shdr.footer = property(lambda _: [
284+
"Key to Flags:",
285+
" W (write), A (alloc), X (execute), M (merge), S (strings), I (info),",
286+
" L (link order), O (extra OS processing required), G (group), T (TLS),",
287+
" C (compressed), x (unknown), o (OS specific), E (exclude),",
288+
" %sp (processor specific)" % (
289+
"l (large), " if e.Ehdr.machine in (elf.EM_X86_64, elf.EM_L10M, elf.EM_K10M) else
290+
"y (purecode), " if e.Ehdr.machine == elf.EM_ARM else
291+
"v (VLE), " if e.Ehdr.machine == elf.EM_PPC else
292+
"" ),
293+
])
294+
if readelf_version >= 2.36: # more precisely 2.36.1
295+
# 2021-02-02 https://github.com/bminor/binutils-gdb/commit/5424d7ed94cf5a7ca24636ab9f4e6d5c353fc0d3
296+
elf.Shdr.footer = property(lambda _: [
297+
"Key to Flags:",
298+
" W (write), A (alloc), X (execute), M (merge), S (strings), I (info),",
299+
" L (link order), O (extra OS processing required), G (group), T (TLS),",
300+
" C (compressed), x (unknown), o (OS specific), E (exclude),",
301+
" %s%sp (processor specific)" % (
302+
"R (retain), D (mbind), " if e.Ehdr.ident[elf.EI_OSABI] in (elf.ELFOSABI_GNU, elf.ELFOSABI_FREEBSD) else
303+
"D (mbind), " if e.Ehdr.ident[elf.EI_OSABI] == elf.ELFOSABI_NONE else
304+
""
305+
,
306+
"l (large), " if e.Ehdr.machine in (elf.EM_X86_64, elf.EM_L10M, elf.EM_K10M) else
307+
"y (noread), " if e.Ehdr.machine == elf.EM_ARM else
308+
"" ),
309+
])
310+
if readelf_version >= 2.35:
311+
# 2020-07-02 https://github.com/bminor/binutils-gdb/commit/0942c7ab94e554657c3e11ab85ae7f15373ee80d
312+
elf.Shdr.name17 = property(lambda _: _.name[:12]+"[...]" if len(_.name) > 17 else _.name)
313+
if readelf_version >= 2.37:
314+
# 2021-06-15 https://github.com/bminor/binutils-gdb/commit/93df3340fd5ad32f784214fc125de71811da72ff
315+
elf.is_pie = is_pie
316+
317+
207318
for file in args.file:
208319
if len(args.file) > 1:
209320
print("\nFile: %s" % file)

tests/examples_linux.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#! /bin/bash
2+
3+
options="-h -S -r -s --dyn-syms -d -l -g"
4+
options="-h -S"
5+
for option in $options; do
6+
for file in /bin/sh; do
7+
echo "=== readelf $option $file ==="
8+
diff -c <(readelf $option $file) <(python ./examples/readelf.py $option --readelf=native $file 2>/dev/null)
9+
done
10+
done

tests/test_elf_manipulation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ def test_ELF_small64(assertion):
161161
'Packing after reading elf64_small.out')
162162
# Packed file is identical :-)
163163
d = e.sh.readelf_display().encode('latin1')
164-
assertion('c497dd6a4e9aa54fb5f65ee3b8f9de3e',
164+
assertion('0454c8b5354b3eda58fce252d5d48621',
165165
hashlib.md5(d).hexdigest(),
166166
'Display Section Headers (readelf, 64bit)')
167167
d = e.getsectionbyname('.symtab').readelf_display().encode('latin1')

0 commit comments

Comments
 (0)