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)) for i in range(0, 6)) 20 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, int(frame_size / 16 - 4)) * 16 27 global func_size_low, func_size_high 28 func_size = random.randint(func_size_low, func_size_high) * 0x10 29 func_size_high += 1 30 if func_size_high % 0x10 == 0: 31 func_size_low += 1 32 33 print( 34 """\ 35### %s frame=%d lsda=%s size=%d 36 .section __TEXT,__text,regular,pure_instructions 37 .p2align 4, 0x90 38 .globl %s 39%s: 40 .cfi_startproc""" 41 % (name, frame_size, have_lsda, func_size, name, name) 42 ) 43 if have_lsda: 44 global lsda_n 45 lsda_n += 1 46 print( 47 """\ 48 .cfi_personality 155, ___gxx_personality_v0 49 .cfi_lsda 16, Lexception%d""" 50 % lsda_n 51 ) 52 print( 53 """\ 54 pushq %%rbp 55 .cfi_def_cfa_offset %d 56 .cfi_offset %%rbp, %d 57 movq %%rsp, %%rbp 58 .cfi_def_cfa_register %%rbp""" 59 % (frame_size, frame_offset + 6 * 8) 60 ) 61 print( 62 """\ 63 .fill %d 64 popq %%rbp 65 retq 66 .cfi_endproc 67""" 68 % (func_size - 6) 69 ) 70 71 if have_lsda: 72 print( 73 """\ 74 .section __TEXT,__gcc_except_tab 75 .p2align 2 76Lexception%d: 77 .space 0x10 78""" 79 % lsda_n 80 ) 81 return func_size 82 83 84def random_seed(): 85 """Generate a seed that can easily be passed back in via --seed=STRING""" 86 return "".join(random.choice(string.ascii_lowercase) for i in range(10)) 87 88 89def main(): 90 parser = argparse.ArgumentParser( 91 description=__doc__, 92 epilog="""\ 93Function sizes begin small then monotonically increase. The goal is 94to produce early pages that are full and later pages that are less 95than full, in order to test handling for both cases. Full pages 96contain the maximum of 1021 compact unwind entries for a total page 97size = 4 KiB. 98 99Use --pages=N or --functions=N to control the size of the output. 100Default is --pages=2, meaning produce at least two full pages of 101compact unwind entries, plus some more. The calculation is sloppy. 102""", 103 ) 104 parser.add_argument( 105 "--seed", 106 type=str, 107 default=random_seed(), 108 help="Seed the random number generator", 109 ) 110 parser.add_argument( 111 "--pages", type=int, default=2, help="Number of compact-unwind pages" 112 ) 113 parser.add_argument( 114 "--functions", type=int, default=None, help="Number of functions to generate" 115 ) 116 parser.add_argument( 117 "--encodings", 118 type=int, 119 default=127, 120 help="Maximum number of unique unwind encodings (default = 127)", 121 ) 122 parser.add_argument( 123 "--lsda", 124 type=int, 125 default=0, 126 help="Percentage of functions with personality & LSDA (default = 10", 127 ) 128 args = parser.parse_args() 129 random.seed(args.seed) 130 p2align = 14 131 global lsda_odds 132 lsda_odds = args.lsda / 100.0 133 134 print( 135 """\ 136### seed=%s lsda=%f p2align=%d 137 .section __TEXT,__text,regular,pure_instructions 138 .p2align %d, 0x90 139""" 140 % (args.seed, lsda_odds, p2align, p2align) 141 ) 142 143 size = 0 144 base = 1 << p2align 145 if args.functions: 146 for n in range(args.functions): 147 size += print_function("x%08x" % (size + base)) 148 else: 149 while size < (args.pages << 24): 150 size += print_function("x%08x" % (size + base)) 151 152 print( 153 """\ 154 .section __TEXT,__text,regular,pure_instructions 155 .globl _main 156 .p2align 4, 0x90 157_main: 158 retq 159 160 .p2align 4, 0x90 161___gxx_personality_v0: 162 retq 163""" 164 ) 165 166 167if __name__ == "__main__": 168 main() 169