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