Shared admin folder
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

nmcheck 8.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. #!/usr/bin/perl -w
  2. # Check namespace cleanness of a library.
  3. # Allowed symbols are passed as arguments.
  4. # They may have trailing * = wildcard.
  5. # Wildcards may be also specified as *::* (e.g. K*::* for all KDE classes)
  6. # Symbols are listed as full function unmangled names without arguments,
  7. # e.g. 'foo bar* nspace::*' allows foo(), foo(int), bar(), barbar()
  8. # and all symbols in namespace/class nspace.
  9. # If an argument has comma in it, it's a filename of a file containing
  10. # allowed symbols, one per line.
  11. $thisProg = "$0"; # This programs name
  12. $library = "";
  13. $allowed_symbols = "";
  14. $debug = 0;
  15. $allowed_weak = "";
  16. $weak_specified = 0;
  17. while( defined( $ARGV[ 0 ] ))
  18. {
  19. $_ = shift;
  20. if( /^--verbose$|^-v$/ )
  21. {
  22. $debug = 1;
  23. }
  24. elsif( /^--help$|^-h$/ )
  25. {
  26. print STDOUT "Usage $thisProg [OPTION] ... library [allowed symbols] ...\n",
  27. "\n",
  28. "Check if the given library has only allowed public symbols.\n",
  29. "\n",
  30. " --allowweak=[symbol] allow only these weak symbols\n",
  31. " -v, --verbose verbosely list files processed\n",
  32. " -h, --help print this help, then exit\n";
  33. exit 0;
  34. }
  35. elsif( /^--allowweak=(.*)$/ )
  36. {
  37. $allowed_weak .= " " . $1;
  38. $weak_specified = 1;
  39. }
  40. elsif( /^--allowweak$/ ) # simply list all weak
  41. {
  42. $allowed_weak .= " ";
  43. $weak_specified = 1;
  44. }
  45. elsif( /^--*/ )
  46. {
  47. die "Invalid argument!\n";
  48. }
  49. else
  50. {
  51. if( ! $library )
  52. {
  53. $library = $_;
  54. }
  55. else
  56. {
  57. $allowed_symbols .= " " . $_;
  58. }
  59. }
  60. }
  61. if( ! $weak_specified )
  62. {
  63. $allowed_weak = "*";
  64. # allow all weak symbols by default
  65. # instances of templates and similar stuff - unfortunately includes also things from other libraries,
  66. # so it cannot be on by default
  67. }
  68. print STDERR "library:" . $library . "\n" if $debug;
  69. print STDERR "allowed_symbols:" . $allowed_symbols . "\n" if $debug;
  70. print STDERR "allowed_weak:" . $allowed_weak . "\n" if $debug;
  71. $default_symbols = "_fini _init"; # system symbols
  72. # on my system, every .so has :
  73. # A _DYNAMIC
  74. # A _GLOBAL_OFFSET_TABLE_
  75. # A __bss_start
  76. # A _edata
  77. # A _end
  78. # T _fini
  79. # T _init
  80. # no need to list A symbols in $default_symbols
  81. print STDERR "default_symbols: " . $default_symbols . "\n" if $debug;
  82. print STDOUT "Namespace cleanness check for " . $library . " :\n";
  83. $lib_file = "";
  84. if( $library =~ /\.la$/ )
  85. {
  86. # get the real library file from .la
  87. open( FILEIN, $library ) || die "Couldn't open $! !\n";
  88. while( $line = <FILEIN> )
  89. {
  90. if( $line =~ /library_names=\'([^ ]*).*/o )
  91. {
  92. $lib_file = $1;
  93. }
  94. }
  95. close( FILEIN );
  96. if( ! $lib_file )
  97. {
  98. print STDERR "Library file not found in .la file!\n";
  99. exit 1;
  100. }
  101. my $libpath = $library;
  102. $libpath =~ s%[^/]*$%%;
  103. if( -e $libpath . ".libs/" . $lib_file )
  104. {
  105. $lib_file = $libpath . ".libs/" . $lib_file;
  106. }
  107. else
  108. {
  109. $lib_file = $libpath . $lib_file;
  110. }
  111. }
  112. else
  113. {
  114. $lib_file = $library;
  115. }
  116. print STDERR "libfile: ". $lib_file . "\n" if $debug;
  117. $allowed_symbols .= " " . $default_symbols;
  118. sub process_symbols($\@\%\@);
  119. @wildcards = ();
  120. %exacts = ();
  121. @regwildcards = ();
  122. process_symbols( $allowed_symbols, @wildcards, %exacts, @regwildcards );
  123. @weak_wildcards = ();
  124. %weak_exacts = ();
  125. @weak_regwildcards = ();
  126. process_symbols( $allowed_weak, @weak_wildcards, %weak_exacts, @weak_regwildcards );
  127. # grep is for stripping not exported symbols, which don't have address (=first column)
  128. $nm_command = "nm -BDCg " . $lib_file . " | grep -v '^ ' |";
  129. # TODO how portable is this nmcheck stuff?
  130. print STDERR "nm command:" . $nm_command . "\n" if $debug;
  131. open( FILEIN, $nm_command ) || die "nm command failed\n";
  132. my $exit_code = 0;
  133. while( $line = <FILEIN> )
  134. {
  135. my $type;
  136. my $symbol;
  137. if( $line =~ /^[^ ]* (.) (.*)$/o )
  138. {
  139. $type = $1;
  140. $symbol = $2;
  141. }
  142. else
  143. {
  144. die "Invalid line: " . $line . "\n";
  145. }
  146. print STDERR "Type: " . $type . " , symbol: " . $symbol . "\n" if $debug;
  147. if( $type eq "A" )
  148. { # these should be system symbols, so ignore them
  149. next;
  150. }
  151. my $orig_symbol = $symbol;
  152. if( $symbol =~ /\(anonymous namespace\)/o )
  153. { # TODO tell to prefer named namespaces? (shorter symbols)
  154. next;
  155. }
  156. # strip prefixes
  157. # the :: appending is to make "CLASS::*" work also for "vtable for CLASS"
  158. $symbol =~ s/^typeinfo for (.*)$/$1::/o;
  159. $symbol =~ s/^typeinfo fn for (.*)$/$1::/o;
  160. $symbol =~ s/^typeinfo name for (.*)$/$1::/o;
  161. $symbol =~ s/^vtable for (.*)$/$1::/o;
  162. $symbol =~ s/^guard variable for (.*)$/$1::/o;
  163. $symbol =~ s/^reference temporary for (.*)$/$1::/o;
  164. $symbol =~ s/^VTT for (.*)$/$1::/o;
  165. $symbol =~ s/^virtual thunk \[[^\]]*\] to (.*)$/$1::/o;
  166. $symbol =~ s/^non-virtual thunk \[[^\]]*\] to (.*)$/$1::/o;
  167. $symbol =~ s/^covariant return thunk \[[^\]]*\] to (.*)$/$1::/o;
  168. $symbol =~ s/^construction vtable thunk for (.*)$/$1::/o;
  169. $symbol =~ s/^construction vtable for .*-in-(.*) [0-9]*$/$1::/o;
  170. # templates seem to have also return types mangled in their name, and nm prints it too
  171. # they have also template arguments in the symbol
  172. # get rid of both of those
  173. while( $symbol =~ /<.*>/o )
  174. {
  175. $symbol =~ s/<[^<>]*>//o; # strip innermost <>
  176. }
  177. if( $symbol !~ /operator\(\)/o )
  178. {
  179. $symbol =~ s/ ?\(.*\).*$//o; # strip () and all after it
  180. }
  181. else
  182. {
  183. $symbol =~ s/(^|:| )operator\(\) ?\(.*\).*$//o; # strip () and all after it
  184. }
  185. $symbol =~ s/\[.*\] *$//o; # strip [in-charge] etc.
  186. if( $symbol =~ /(^|:| )operator /o )
  187. {
  188. $symbol =~ s/.* ([^\s]*)operator /$1/o; # strip everything before 'X::operator blah'
  189. }
  190. else
  191. {
  192. $symbol =~ s/.* ([^\s]+) *$/$1/o; # get last word (strip return type)
  193. }
  194. # print STDERR "Processed symbol: " . $symbol . "\n" if $debug;
  195. my $found = 0;
  196. if( $exacts{ $symbol } )
  197. {
  198. $found = 1;
  199. }
  200. if( ! $found )
  201. {
  202. for my $wild ( @wildcards )
  203. {
  204. if( index( $symbol, $wild ) == 0 )
  205. {
  206. $found = 1;
  207. last;
  208. }
  209. }
  210. }
  211. if( ! $found )
  212. {
  213. for my $wild ( @regwildcards )
  214. {
  215. if( $symbol =~ /^$wild$/ )
  216. {
  217. $found = 1;
  218. last;
  219. }
  220. }
  221. }
  222. if( ( ! $found ) && ( $type eq "W" || $type eq "V" ))
  223. {
  224. if( $weak_exacts{ $symbol } )
  225. {
  226. $found = 1;
  227. }
  228. if( ! $found )
  229. {
  230. for my $wild ( @weak_wildcards )
  231. {
  232. if( index( $symbol, $wild ) == 0 )
  233. {
  234. $found = 1;
  235. last;
  236. }
  237. }
  238. }
  239. if( ! $found )
  240. {
  241. for my $wild ( @weak_regwildcards )
  242. {
  243. if( $symbol =~ /^$wild$/ )
  244. {
  245. $found = 1;
  246. last;
  247. }
  248. }
  249. }
  250. }
  251. if( ! $found )
  252. {
  253. print STDERR "Public symbol " . $orig_symbol . " is not allowed!\n";
  254. $exit_code = 1;
  255. }
  256. }
  257. close( FILEIN );
  258. print STDOUT $exit_code == 0 ? "OK\n" : "FAILED\n";
  259. exit $exit_code;
  260. sub process_symbols($\@\%\@)
  261. {
  262. my $allowed_symbols = $_[ 0 ];
  263. my $wildcards_ref = $_[ 1 ];
  264. my $exacts_ref = $_[ 2 ];
  265. my $regwildcards_ref = $_[ 3 ];
  266. $allowed_symbols =~ s/^ *//o; # strip whitespace
  267. $allowed_symbols =~ s/ *$//o;
  268. if( $allowed_symbols eq "NONE" )
  269. {
  270. $allowed_symbols = "";
  271. }
  272. my @symbols1 = split( ' ', $allowed_symbols );
  273. my $i = 0;
  274. my @symbols2 = ();
  275. while( defined( $symbols1[ $i ] ))
  276. {
  277. my $symbol = $symbols1[ $i ];
  278. if( $symbol =~ /\./ ) # dot in name -> file
  279. {
  280. open( SYMIN, $symbol ) || die ( "Cannot open file " . $symbol . "!" );
  281. while( $line = <SYMIN> )
  282. {
  283. $line =~ s/^\s*//o; # strip whitespace
  284. $line =~ s/\s*$//o;
  285. if( $line !~ /^$/o # empty line
  286. && $line !~ /^\s*#/ ) # comment line starting with #
  287. {
  288. $symbols2[ $#symbols2 + 1 ] = $line;
  289. }
  290. }
  291. close( SYMIN );
  292. }
  293. else
  294. {
  295. $symbols2[ $#symbols2 + 1 ] = $symbol;
  296. }
  297. $i++;
  298. }
  299. $i = 0;
  300. while( defined( $symbols2[ $i ] ))
  301. {
  302. my $symbol = $symbols2[ $i ];
  303. if( $symbol =~ /__/
  304. || $symbol =~ /^_[A-Z]/ )
  305. { # ISO C++ 2.10.2
  306. die "Symbols containing a double underscore or beginning with an underscore and an upper-case letter are reserved!\n";
  307. }
  308. elsif( $symbol eq "main"
  309. || $symbol eq "main*" )
  310. {
  311. die "Symbol main is not allowed!\n";
  312. }
  313. if( $symbol =~ /^([^\*]*)\*$/o # trailing * without any * before it
  314. && $symbol !~ /operator\*$/o )
  315. {
  316. print STDERR "wildcard:" . $symbol . "\n" if $debug;
  317. $wildcards_ref->[ $#{$wildcards_ref} + 1 ] = $1;
  318. }
  319. elsif( $symbol =~ /\*$/o
  320. && ( $symbol =~ /\*::/o || $symbol =~ /::\*/o )
  321. && $symbol !~ /^\*/o
  322. && $symbol !~ /operator\*$/o )
  323. {
  324. print STDERR "regwildcard:" . $symbol . "\n" if $debug;
  325. $symbol =~ s/\*/\.\*/go; # change * to .* (regexp)
  326. $regwildcards_ref->[ $#{$regwildcards_ref} + 1 ] = $symbol;
  327. }
  328. else
  329. {
  330. print STDERR "exact:" . $symbol . "\n" if $debug;
  331. $exacts_ref->{ $symbol } = 1;
  332. }
  333. $i++;
  334. }
  335. }