# Generate a version map file from a .map.in file. # # Written by Zack Weinberg in 2017. # To the extent possible under law, Zack Weinberg has waived all # copyright and related or neighboring rights to this work. # # See https://creativecommons.org/publicdomain/zero/1.0/ for further # details. # The .map.in file is the first input file, and we expect the Makefile # to have set the variables SYMVER_MIN, SYMVER_FLOOR, and COMPAT_ABI. # All compat symbol versions that do not match COMPAT_ABI are ignored. # All symbol versions lower than SYMVER_MIN are discarded from the output. # All symbol versions lower than SYMVER_FLOOR are replaced with SYMVER_FLOOR. # SYMVER_FLOOR must be greater than or equal to SYMVER_MIN. # # The ordering of symbol versions is entirely controlled by the %chain # directive, which must therefore list both all of the versions # actually used for symbols, and all of the versions that might be # used as SYMVER_MIN or SYMVER_FLOOR. # # Note: if you change the format of .map.in files you probably need to # update gen-vers.awk too. BEGIN { split("", SYMBOLS) # ensure SYMBOLS is an array split("", VCHAIN) # ditto VCHAIN NVCHAIN = 0 # This arranges for sorted output if gawk is in use, and is # harmless otherwise. PROCINFO["sorted_in"] = "@ind_str_asc" } NF == 0 { next } # blank line, discard $1 == "#" { next } # comment, discard $1 == "%chain" { for (i = 2; i <= NF; i++) { VCHAIN[++NVCHAIN] = $i } next } { for (i = 2; i <= NF; i++) { sym=$i if (sym != "-") { n=split(sym, a, ":") if (n > 1) { sym="-" for (j = 2; j <= n; j++) { if (COMPAT_ABI == "yes" || COMPAT_ABI == a[j]) { sym=a[1] } } } } if (sym != "-") { if (sym in SYMBOLS) { SYMBOLS[sym] = SYMBOLS[sym] SUBSEP $1 } else { SYMBOLS[sym] = $1 } } } } END { if (NVCHAIN == 0) { print ARGV[1] ": error: missing %chain directive" > "/dev/stderr" close("/dev/stderr") exit 1 } symver_min_idx = 0 symver_floor_idx = 0 for (i = 1; i <= NVCHAIN; i++) { if (VCHAIN[i] == SYMVER_MIN) { symver_min_idx = i } if (VCHAIN[i] == SYMVER_FLOOR) { symver_floor_idx = i } } if (symver_min_idx == 0) { print ARGV[1] ": error: SYMVER_MIN (" SYMVER_MIN ") " \ "not found in %chain directives" > "/dev/stderr" close("/dev/stderr") exit 1 } if (symver_floor_idx == 0) { print ARGV[1] ": error: SYMVER_FLOOR (" SYMVER_FLOOR ") " \ "not found in %chain directives" > "/dev/stderr" close("/dev/stderr") exit 1 } if (symver_floor_idx < symver_min_idx) { print ARGV[1] ": error: SYMVER_FLOOR (" SYMVER_FLOOR ") " \ "is lower than SYMVER_MIN (" SYMVER_MIN ")" > "/dev/stderr" close("/dev/stderr") exit 1 } # Construct a pruned set of symbols and versions, including only # versions with symbols, discarding all symbols associated with # versions below SYMVER_MIN, raising symbols below SYMVER_FLOOR to # SYMVER_FLOOR, and removing duplicates. for (i = symver_min_idx; i <= NVCHAIN; i++) { v = VCHAIN[i] if (v in SYMBOLS) { nsyms = split(SYMBOLS[v], syms, SUBSEP) j = i; if (j < symver_floor_idx) j = symver_floor_idx; vr = VCHAIN[j] for (s = 1; s <= nsyms; s++) { if (syms[s]) { symset[vr, syms[s]] = 1 allsyms[syms[s]] = 1 } } } } vp = "" for (i = symver_floor_idx; i <= NVCHAIN; i++) { v = VCHAIN[i] split("", osyms) j = 0 for (sym in allsyms) { if ((v, sym) in symset) { osyms[++j] = sym } } if (j > 0) { printf("%s {\n global:\n", v); for (s = 1; s <= j; s++) { printf(" %s;\n", osyms[s]); } if (vp == "") { vp = v printf(" local:\n *;\n};\n"); } else { printf("} %s;\n", vp); } } } }