-
Notifications
You must be signed in to change notification settings - Fork 143
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Test symbol visibility in the static archive and shared objects
The tests check that the static archive librubyparser.a hides all of the YARP functions, to ensure the symbols do not end up externally visible in a dynamic symbol table and therefore not susceptible to being replaced by the dynamic linker. We expect something like: ``` $ objdump --syms build/librubyparser.a | grep " yp_parse$" 000000000001dbf0 g F .text 0000000000000009 .hidden yp_parse ``` The `g` means the symbol is global, but `.hidden` means that the symbol should not be made external if linked into a shared object (like `yarp.so`). The tests also check that the shared object librubyparser.so contains global versions of the public methods like `yp_parse`: ``` $ nm build/librubyparser.so | grep " yp_parse$" 0000000000021a50 T yp_parse ``` Finally, the tests verify that there is exactly one dynamic symbol in `yarp.so`, which is the initializing function `Init_yarp`. NOTE that we add `-fvisibility=hidden` to the C extension compiler flags to ensure this test passes.
- Loading branch information
1 parent
83061ae
commit c87d91a
Showing
2 changed files
with
105 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
# frozen_string_literal: true | ||
|
||
require "yarp_test_helper" | ||
|
||
if RUBY_PLATFORM =~ /linux/ | ||
# | ||
# examine a yarp dll or static archive for expected external symbols. | ||
# these tests only work on a linux system right now. | ||
# | ||
class LibrarySymbolsTest < Test::Unit::TestCase | ||
def setup | ||
super | ||
|
||
@librubyparser_a = File.expand_path(File.join(__dir__, "..", "build", "librubyparser.a")) | ||
@librubyparser_so = File.expand_path(File.join(__dir__, "..", "build", "librubyparser.so")) | ||
@yarp_so = File.expand_path(File.join(__dir__, "..", "lib", "yarp.so")) | ||
end | ||
|
||
# objdump runner and helpers | ||
def objdump(path) | ||
assert_path_exist(path) | ||
%x(objdump --section=.text --syms #{path}).split("\n") | ||
end | ||
|
||
def global_objdump_symbols(path) | ||
objdump(path).select { |line| line[17] == "g" } | ||
end | ||
|
||
def hidden_global_objdump_symbols(path) | ||
global_objdump_symbols(path).select { |line| line =~ / \.hidden / } | ||
end | ||
|
||
def visible_global_objdump_symbols(path) | ||
global_objdump_symbols(path).reject { |line| line =~ / \.hidden / } | ||
end | ||
|
||
# nm runner and helpers | ||
def nm(path) | ||
assert_path_exist(path) | ||
%x(nm #{path}).split("\n") | ||
end | ||
|
||
def global_nm_symbols(path) | ||
nm(path).select { |line| line[17] == "T" } | ||
end | ||
|
||
def local_nm_symbols(path) | ||
nm(path).select { |line| line[17] == "t" } | ||
end | ||
|
||
# dig the symbol name out of each line. works for both `objdump` and `nm` output. | ||
def names(symbol_lines) | ||
symbol_lines.map { |line| line.split(/\s+/).last } | ||
end | ||
|
||
# | ||
# static archive - librubyparser.a | ||
# | ||
def test_librubyparser_a_contains_nothing_globally_visible | ||
omit("librubyparser.a is not built") unless File.exist?(@librubyparser_a) | ||
|
||
assert_empty(names(visible_global_objdump_symbols(@librubyparser_a))) | ||
end | ||
|
||
def test_librubyparser_a_contains_hidden_yp_symbols | ||
omit("librubyparser.a is not built") unless File.exist?(@librubyparser_a) | ||
|
||
names(hidden_global_objdump_symbols(@librubyparser_a)).tap do |symbols| | ||
assert_includes(symbols, "yp_parse") | ||
assert_includes(symbols, "yp_version") | ||
end | ||
end | ||
|
||
# | ||
# shared object - librubyparser.so | ||
# | ||
def test_librubyparser_so_exports_only_the_necessary_functions | ||
omit("librubyparser.so is not built") unless File.exist?(@librubyparser_so) | ||
|
||
names(global_nm_symbols(@librubyparser_so)).tap do |symbols| | ||
assert_includes(symbols, "yp_parse") | ||
assert_includes(symbols, "yp_version") | ||
end | ||
names(local_nm_symbols(@librubyparser_so)).tap do |symbols| | ||
assert_includes(symbols, "yp_encoding_shift_jis_isupper_char") | ||
end | ||
# TODO: someone who uses this library needs to finish this test | ||
end | ||
|
||
# | ||
# shared object - yarp.so | ||
# | ||
def test_yarp_so_exports_only_the_C_extension_init_function | ||
omit("yarp.so is not built") unless File.exist?(@yarp_so) | ||
|
||
names(global_nm_symbols(@yarp_so)).tap do |symbols| | ||
assert_equal(["Init_yarp"], symbols) | ||
end | ||
end | ||
end | ||
end |