# Generate macros that control the symbol versions of the public # library API, 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. # See libcrypt.map.in and gen-vers.awk for explanations of the format # of .map.in files. See crypt-port.h for an explanation of how to use # the macros generated by this program. # # Note: if you change the format of .map.in files you probably need to # update gen-map.awk too. # # Note: we currently don't support compatibility symbols that need # a different definition from the default version. 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 } { if ($2 == "-") { compat_only[$1] = 1 } else { compat_only[$1] = 0 if ($2 in SYMBOLS) { SYMBOLS[$2] = SYMBOLS[$2] SUBSEP $1 } else { SYMBOLS[$2] = $1 } } for (i = 3; i <= NF; i++) { sym=$i 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. # Note: unlike in gen-map.awk, symbols all of whose versions are # below SYMVER_MIN must still be counted in 'allsyms' so their # INCLUDE macros are generated. for (i = 1; 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]) { allsyms[syms[s]] = 1 if (i >= symver_min_idx) { symset[vr, syms[s]] = 1 } } } } } print "/* Generated from " ARGV[1] " by gen-vers.awk. DO NOT EDIT. */" print "" print "#ifndef _CRYPT_SYMBOL_VERS_H" print "#define _CRYPT_SYMBOL_VERS_H 1" print "" print "/* For each public symbol , INCLUDE_ is true if it" print " has any versions above the backward compatibility minimum." print " Compatibility-only symbols are not included in the static" print " library, or in the shared library when configured with" print " --disable-obsolete-api. */" print "#if defined PIC && ENABLE_OBSOLETE_API" print "" for (sym in allsyms) { include = 0 for (i = symver_floor_idx; i <= NVCHAIN; i++) { if ((VCHAIN[i], sym) in symset) { include++ } } includesym[sym] = include printf("#define INCLUDE_%s %d\n", sym, include) } print "" print "#else" print "" for (sym in allsyms) { printf("#define INCLUDE_%s %d\n", sym, (includesym[sym] && !compat_only[sym])) } print "" print "#endif" print "" print "/* For each public symbol that is included, define its" print " highest version as the default, and aliases at each" print " compatibility version. */" for (sym in allsyms) { if (includesym[sym]) { seq = 0 for (i = NVCHAIN; i >= symver_floor_idx; i--) { v = VCHAIN[i] if ((v, sym) in symset) { if (seq == 0) { if (compat_only[sym] || includesym[sym] > 0) { printf("#ifdef PIC\n#define %s _crypt_%s\n#endif\n", sym, sym); } printf("#define SYMVER_%s \\\n", sym) if (compat_only[sym]) { printf(" symver_compat0 (\"%s\", %s, %s)", sym, sym, v) } else if (includesym[sym] > 0) { printf(" symver_default (\"%s\", %s, %s)", sym, sym, v) } else { # Due to what appears to be a bug in GNU ld, # we must not issue symver_default() if there # aren't going to be any other versions. printf(" symver_nop ()") } } else { printf("; \\\n symver_compat (%d, \"%s\", %s, %s, %s)", seq, sym, sym, sym, v) } seq++ } } print "" } else { printf("#define SYMVER_%s symver_nop()\n", sym) } } print "" print "#endif /* crypt-symbol-vers.h */" }