1#!/usr/bin/env python
2
3"""Generate skeletal functions with a variety .cfi_ directives.
4The purpose is to produce object-file test inputs to lld with a
5variety of compact unwind encodings.
6"""
7from __future__ import print_function
8import random
9import argparse
10import string
11from math import factorial
12from itertools import permutations
13
14lsda_n = 0
15lsda_odds = 0.0
16func_size_low = 0x10
17func_size_high = 0x100
18saved_regs = ["%r15", "%r14", "%r13", "%r12", "%rbx"]
19saved_regs_combined = list(list(permutations(saved_regs, i))
20                           for i in range(0,6))
21
22def print_function(name):
23  global lsda_odds
24  have_lsda = (random.random() < lsda_odds)
25  frame_size = random.randint(4, 64) * 16
26  frame_offset = -random.randint(0, (frame_size/16 - 4)) * 16
27  reg_count = random.randint(0, 4)
28  reg_combo = random.randint(0, factorial(reg_count) - 1)
29  regs_saved = saved_regs_combined[reg_count][reg_combo]
30  global func_size_low, func_size_high
31  func_size = random.randint(func_size_low, func_size_high) * 0x10
32  func_size_high += 1
33  if func_size_high % 0x10 == 0:
34    func_size_low += 1
35
36  print("""\
37### %s regs=%d frame=%d lsda=%s size=%d
38    .section __TEXT,__text,regular,pure_instructions
39    .p2align 4, 0x90
40    .globl %s
41%s:
42    .cfi_startproc""" % (
43        name, reg_count, frame_size, have_lsda, func_size, name, name))
44  if have_lsda:
45    global lsda_n
46    lsda_n += 1
47    print("""\
48    .cfi_personality 155, ___gxx_personality_v0
49    .cfi_lsda 16, Lexception%d""" % lsda_n)
50  print("""\
51    pushq %%rbp
52    .cfi_def_cfa_offset %d
53    .cfi_offset %%rbp, %d
54    movq %%rsp, %%rbp
55    .cfi_def_cfa_register %%rbp""" % (frame_size, frame_offset + 6*8))
56  for i in range(reg_count):
57    print(".cfi_offset %s, %d" % (regs_saved[i], frame_offset+(i*8)))
58  print("""\
59    .fill %d
60    popq %%rbp
61    retq
62    .cfi_endproc
63""" % (func_size - 6))
64
65  if have_lsda:
66    print("""\
67    .section __TEXT,__gcc_except_tab
68    .p2align 2
69Lexception%d:
70    .space 0x10
71""" % lsda_n)
72  return func_size
73
74def random_seed():
75  """Generate a seed that can easily be passsed back in via --seed=STRING"""
76  return ''.join(random.choice(string.ascii_lowercase) for i in range(10))
77
78def main():
79  parser = argparse.ArgumentParser(
80    description=__doc__,
81    epilog="""\
82Function sizes begin small then monotonically increase.  The goal is
83to produce early pages that are full and later pages that are less
84than full, in order to test handling for both cases.  Full pages
85contain the maximum of 1021 compact unwind entries for a total page
86size = 4 KiB.
87
88Use --pages=N or --functions=N to control the size of the output.
89Default is --pages=2, meaning produce at least two full pages of
90compact unwind entries, plus some more. The calculatation is sloppy.
91""")
92  parser.add_argument('--seed', type=str, default=random_seed(),
93                      help='Seed the random number generator')
94  parser.add_argument('--pages', type=int, default=2,
95                      help='Number of compact-unwind pages')
96  parser.add_argument('--functions', type=int, default=None,
97                      help='Number of functions to generate')
98  parser.add_argument('--encodings', type=int, default=127,
99                      help='Maximum number of unique unwind encodings (default = 127)')
100  parser.add_argument('--lsda', type=int, default=0,
101                      help='Percentage of functions with personality & LSDA (default = 10')
102  args = parser.parse_args()
103  random.seed(args.seed)
104  p2align = 14
105  global lsda_odds
106  lsda_odds = args.lsda / 100.0
107
108  print("""\
109### seed=%s lsda=%f p2align=%d
110    .section __TEXT,__text,regular,pure_instructions
111    .p2align %d, 0x90
112""" % (args.seed, lsda_odds, p2align, p2align))
113
114  size = 0
115  base = (1 << p2align)
116  if args.functions:
117    for n in range(args.functions):
118      size += print_function("x%08x" % (size+base))
119  else:
120    while size < (args.pages << 24):
121      size += print_function("x%08x" % (size+base))
122
123  print("""\
124    .section __TEXT,__text,regular,pure_instructions
125    .globl _main
126    .p2align 4, 0x90
127_main:
128    retq
129
130    .p2align 4, 0x90
131___gxx_personality_v0:
132    retq
133""")
134
135
136if __name__ == '__main__':
137  main()
138