7
7
anyhow:: { anyhow, Context , Result } ,
8
8
clap:: ArgMatches ,
9
9
object:: {
10
- elf:: { FileHeader32 , FileHeader64 } ,
10
+ elf:: {
11
+ FileHeader32 , FileHeader64 , ET_DYN , ET_EXEC , STB_GLOBAL , STB_WEAK , STV_DEFAULT ,
12
+ STV_HIDDEN ,
13
+ } ,
11
14
macho:: { MachHeader32 , MachHeader64 } ,
12
15
read:: {
13
16
elf:: { Dyn , FileHeader , SectionHeader , Sym } ,
@@ -440,6 +443,75 @@ const MACHO_ALLOWED_WEAK_SYMBOLS_38_NON_AARCH64: &[&str] = &[
440
443
"_statvfs" ,
441
444
] ;
442
445
446
+ /// Symbols defined in dependency packages.
447
+ ///
448
+ /// We use this list to spot test behavior of symbols belonging to dependency packages.
449
+ /// The list is obviously not complete.
450
+ const DEPENDENCY_PACKAGE_SYMBOLS : & [ & str ] = & [
451
+ // libX11
452
+ "XClearWindow" ,
453
+ "XFlush" ,
454
+ // OpenSSL
455
+ "BIO_ADDR_new" ,
456
+ "BN_new" ,
457
+ "DH_new" ,
458
+ "SSL_extension_supported" ,
459
+ "SSL_read" ,
460
+ "CRYPTO_memcmp" ,
461
+ "ecp_nistz256_neg" ,
462
+ "OPENSSL_instrument_bus" ,
463
+ "x25519_fe64_add" ,
464
+ // libdb
465
+ "__txn_begin" ,
466
+ // libedit / readline
467
+ "rl_prompt" ,
468
+ "readline" ,
469
+ "current_history" ,
470
+ "history_expand" ,
471
+ // libffi
472
+ "ffi_call" ,
473
+ "ffi_type_void" ,
474
+ // ncurses
475
+ "new_field" ,
476
+ "set_field_term" ,
477
+ "set_menu_init" ,
478
+ "winstr" ,
479
+ // gdbm
480
+ "gdbm_close" ,
481
+ "gdbm_import" ,
482
+ // sqlite3
483
+ "sqlite3_initialize" ,
484
+ "sqlite3_close" ,
485
+ // libxcb
486
+ "xcb_create_window" ,
487
+ "xcb_get_property" ,
488
+ // libz
489
+ "deflateEnd" ,
490
+ "gzclose" ,
491
+ "inflate" ,
492
+ // tix
493
+ "Tix_DItemCreate" ,
494
+ "Tix_GrFormat" ,
495
+ // liblzma
496
+ "lzma_index_init" ,
497
+ "lzma_stream_encoder" ,
498
+ // tcl
499
+ "Tcl_Alloc" ,
500
+ "Tcl_ChannelName" ,
501
+ "Tcl_CreateInterp" ,
502
+ // tk
503
+ "TkBindInit" ,
504
+ "TkCreateFrame" ,
505
+ "Tk_FreeGC" ,
506
+ ] ;
507
+
508
+ const PYTHON_EXPORTED_SYMBOLS : & [ & str ] = & [
509
+ "Py_Initialize" ,
510
+ "PyList_New" ,
511
+ // From limited API.
512
+ "Py_CompileString" ,
513
+ ] ;
514
+
443
515
static WANTED_WINDOWS_STATIC_PATHS : Lazy < BTreeSet < PathBuf > > = Lazy :: new ( || {
444
516
[
445
517
PathBuf :: from ( "python/build/lib/libffi.lib" ) ,
@@ -478,6 +550,9 @@ pub struct ValidationContext {
478
550
/// Dynamic libraries required to be loaded.
479
551
pub seen_dylibs : BTreeSet < String > ,
480
552
553
+ /// Symbols exported from dynamic libpython library.
554
+ pub libpython_exported_symbols : BTreeSet < String > ,
555
+
481
556
/// Undefined Mach-O symbols that are required / non-weak.
482
557
pub macho_undefined_symbols_strong : RequiredSymbols ,
483
558
@@ -490,6 +565,8 @@ impl ValidationContext {
490
565
pub fn merge ( & mut self , other : Self ) {
491
566
self . errors . extend ( other. errors ) ;
492
567
self . seen_dylibs . extend ( other. seen_dylibs ) ;
568
+ self . libpython_exported_symbols
569
+ . extend ( other. libpython_exported_symbols ) ;
493
570
self . macho_undefined_symbols_strong
494
571
. merge ( other. macho_undefined_symbols_strong ) ;
495
572
self . macho_undefined_symbols_weak
@@ -699,6 +776,34 @@ fn validate_elf<'data, Elf: FileHeader<Endian = Endianness>>(
699
776
}
700
777
}
701
778
}
779
+
780
+ // Ensure specific symbols in dynamic binaries have proper visibility.
781
+ if matches ! ( elf. e_type( endian) , ET_EXEC | ET_DYN ) {
782
+ // Non-local symbols belonging to dependencies should have hidden visibility
783
+ // to prevent them from being exported.
784
+ if DEPENDENCY_PACKAGE_SYMBOLS . contains ( & name. as_ref ( ) )
785
+ && matches ! ( symbol. st_bind( ) , STB_GLOBAL | STB_WEAK )
786
+ && symbol. st_visibility ( ) != STV_HIDDEN
787
+ {
788
+ context. errors . push ( format ! (
789
+ "{} contains non-hidden dependency symbol {}" ,
790
+ path. display( ) ,
791
+ name
792
+ ) ) ;
793
+ }
794
+
795
+ if let Some ( filename) = path. file_name ( ) {
796
+ let filename = filename. to_string_lossy ( ) ;
797
+
798
+ if filename. starts_with ( "libpython" ) && filename. ends_with ( ".so.1.0" ) {
799
+ if matches ! ( symbol. st_bind( ) , STB_GLOBAL | STB_WEAK )
800
+ && symbol. st_visibility ( ) == STV_DEFAULT
801
+ {
802
+ context. libpython_exported_symbols . insert ( name. to_string ( ) ) ;
803
+ }
804
+ }
805
+ }
806
+ }
702
807
}
703
808
}
704
809
}
@@ -1121,7 +1226,7 @@ fn validate_distribution(
1121
1226
}
1122
1227
}
1123
1228
1124
- // We've now read the contents of the archive. Move on to analyizing the results.
1229
+ // We've now read the contents of the archive. Move on to analyzing the results.
1125
1230
1126
1231
for path in seen_symlink_targets {
1127
1232
if !seen_paths. contains ( & path) {
@@ -1160,6 +1265,21 @@ fn validate_distribution(
1160
1265
}
1161
1266
}
1162
1267
1268
+ // If we've collected symbols exported from libpython, ensure the Python symbols are
1269
+ // in the set.
1270
+ if !context. libpython_exported_symbols . is_empty ( ) {
1271
+ for symbol in PYTHON_EXPORTED_SYMBOLS {
1272
+ if !context
1273
+ . libpython_exported_symbols
1274
+ . contains ( & symbol. to_string ( ) )
1275
+ {
1276
+ context
1277
+ . errors
1278
+ . push ( format ! ( "libpython does not export {}" , symbol) ) ;
1279
+ }
1280
+ }
1281
+ }
1282
+
1163
1283
// On Apple Python 3.8 we need to ban most weak symbol references because 3.8 doesn't have
1164
1284
// the proper runtime guards in place to prevent them from being resolved at runtime,
1165
1285
// which would lead to a crash. See
0 commit comments