File tree 8 files changed +130
-0
lines changed
8 files changed +130
-0
lines changed Original file line number Diff line number Diff line change @@ -426,12 +426,29 @@ dln_sym(void *handle, const char *symbol)
426
426
}
427
427
#endif
428
428
429
+ #if RUBY_DLN_CHECK_ABI
430
+ static bool
431
+ abi_check_enabled_p (void )
432
+ {
433
+ const char * val = getenv ("RUBY_ABI_CHECK" );
434
+ return val == NULL || !(val [0 ] == '0' && val [1 ] == '\0' );
435
+ }
436
+ #endif
437
+
429
438
void *
430
439
dln_load (const char * file )
431
440
{
432
441
#if defined(_WIN32 ) || defined(USE_DLN_DLOPEN )
433
442
void * handle = dln_open (file );
434
443
444
+ #if RUBY_DLN_CHECK_ABI
445
+ unsigned long long (* abi_version_fct )(void ) = (unsigned long long (* )(void ))dln_sym (handle , "ruby_abi_version" );
446
+ unsigned long long binary_abi_version = (* abi_version_fct )();
447
+ if (binary_abi_version != ruby_abi_version () && abi_check_enabled_p ()) {
448
+ dln_loaderror ("ABI version of binary is incompatible with this Ruby. Try rebuilding this binary." );
449
+ }
450
+ #endif
451
+
435
452
char * init_fct_name ;
436
453
init_funcname (& init_fct_name , file );
437
454
void (* init_fct )(void ) = (void (* )(void ))dln_sym (handle , init_fct_name );
Original file line number Diff line number Diff line change
1
+ #include <limits.h>
2
+
3
+ unsigned long long
4
+ ruby_abi_version (void )
5
+ {
6
+ return ULONG_MAX ;
7
+ }
8
+
9
+ void
10
+ Init_abi (void )
11
+ {}
Original file line number Diff line number Diff line change
1
+ # frozen_string_literal: false
2
+ require_relative "../auto_ext.rb"
3
+ auto_ext ( inc : true )
Original file line number Diff line number Diff line change
1
+ #ifndef RUBY_ABI_H
2
+ #define RUBY_ABI_H
3
+
4
+ /* This number represents Ruby's ABI version.
5
+ *
6
+ * In development Ruby, it should be bumped every time an ABI incompatible
7
+ * change is introduced. This will force other developers to rebuild extension
8
+ * gems.
9
+ *
10
+ * The following cases are considered as ABI incompatible changes:
11
+ * - Changing any data structures.
12
+ * - Changing macros or inline functions causing a change in behavior.
13
+ * - Deprecating or removing function declarations.
14
+ *
15
+ * The following cases are NOT considered as ABI incompatible changes:
16
+ * - Any changes that does not involve the header files in the `include`
17
+ * directory.
18
+ * - Adding macros, inline functions, or function declarations.
19
+ * - Backwards compatible refactors.
20
+ * - Editing comments.
21
+ *
22
+ * In released versions of Ruby, this number should not be changed since teeny
23
+ * versions of Ruby should guarantee ABI compatibility.
24
+ */
25
+ #define RUBY_ABI_VERSION 0
26
+
27
+ /* Windows does not support weak symbols so ruby_abi_version will not exist
28
+ * in the shared library. */
29
+ #if defined(HAVE_FUNC_WEAK ) && !defined(_WIN32 ) && !defined(__MINGW32__ )
30
+ # define RUBY_DLN_CHECK_ABI 1
31
+ #else
32
+ # define RUBY_DLN_CHECK_ABI 0
33
+ #endif
34
+
35
+ #if RUBY_DLN_CHECK_ABI
36
+
37
+ RUBY_FUNC_EXPORTED unsigned long long __attribute__((weak ))
38
+ ruby_abi_version (void )
39
+ {
40
+ return RUBY_ABI_VERSION ;
41
+ }
42
+
43
+ #endif
44
+
45
+ #endif
Original file line number Diff line number Diff line change 23
23
#include < stdarg.h>
24
24
25
25
#include " defines.h"
26
+ #include " ruby/internal/abi.h"
26
27
#include " ruby/internal/anyargs.h"
27
28
#include " ruby/internal/arithmetic.h"
28
29
#include " ruby/internal/core.h"
Original file line number Diff line number Diff line change
1
+ # frozen_string_literal: true
2
+
3
+ class TestABI < Test ::Unit ::TestCase
4
+ def test_require_lib_with_incorrect_abi_on_dev_ruby
5
+ omit "ABI is not checked" unless abi_checking_supported?
6
+
7
+ assert_separately [ ] , <<~RUBY
8
+ err = assert_raise(LoadError) { require "-test-/abi" }
9
+ assert_match(/ABI version of binary is incompatible with this Ruby/, err.message)
10
+ RUBY
11
+ end
12
+
13
+ def test_disable_abi_check_using_environment_variable
14
+ omit "ABI is not checked" unless abi_checking_supported?
15
+
16
+ assert_separately [ { "RUBY_ABI_CHECK" => "0" } ] , <<~RUBY
17
+ assert_nothing_raised { require "-test-/abi" }
18
+ RUBY
19
+ end
20
+
21
+ def test_enable_abi_check_using_environment_variable
22
+ omit "ABI is not checked" unless abi_checking_supported?
23
+
24
+ assert_separately [ { "RUBY_ABI_CHECK" => "1" } ] , <<~RUBY
25
+ err = assert_raise(LoadError) { require "-test-/abi" }
26
+ assert_match(/ABI version of binary is incompatible with this Ruby/, err.message)
27
+ RUBY
28
+ end
29
+
30
+ def test_require_lib_with_incorrect_abi_on_release_ruby
31
+ omit "ABI is enforced" if abi_checking_supported?
32
+
33
+ assert_separately [ ] , <<~RUBY
34
+ assert_nothing_raised { require "-test-/abi" }
35
+ RUBY
36
+ end
37
+
38
+ private
39
+
40
+ def abi_checking_supported?
41
+ !( RUBY_PLATFORM =~ /mswin|mingw/ )
42
+ end
43
+ end
Original file line number Diff line number Diff line change @@ -229,6 +229,15 @@ module RbConfig
229
229
print " CONFIG[#{ v . dump } ] = #{ ( versions [ v ] ||vars [ v ] ) . dump } \n "
230
230
end
231
231
232
+ # Get the ABI version
233
+ File . foreach ( File . join ( srcdir , "include/ruby/internal/abi.h" ) ) do |l |
234
+ m = /^\s *#\s *define\s +RUBY_ABI_VERSION\s +(\d +)/ . match ( l )
235
+ if m
236
+ print " CONFIG[\" ruby_abi_version\" ] = \" #{ m [ 1 ] } \" \n "
237
+ break
238
+ end
239
+ end
240
+
232
241
dest = drive ? %r'= "(?!\$ [\( \{ ])(?i:[a-z]:)' : %r'= "(?!\$ [\( \{ ])'
233
242
v_disabled = { }
234
243
v_others . collect! do |x |
Original file line number Diff line number Diff line change @@ -41,6 +41,7 @@ module MJITHeader
41
41
IGNORED_FUNCTIONS = [
42
42
'rb_vm_search_method_slowpath' , # This increases the time to compile when inlined. So we use it as external function.
43
43
'rb_equal_opt' , # Not used from VM and not compilable
44
+ 'ruby_abi_version' ,
44
45
]
45
46
46
47
ALWAYS_INLINED_FUNCTIONS = [
You can’t perform that action at this time.
0 commit comments