Commit cc3a2ba7 authored by Ximin Luo's avatar Ximin Luo

Add support for reading the symbol table to ArFile

parent 3ba4298d
......@@ -131,6 +131,10 @@ class RequiredToolNotFound(Exception):
'arch': 'gettext',
'FreeBSD': 'gettext-tools',
},
'nm': {
'debian': 'binutils-multiarch',
'arch': 'binutils',
},
'objdump': {
'debian': 'binutils-multiarch',
'arch': 'binutils',
......
......@@ -18,13 +18,15 @@
# You should have received a copy of the GNU General Public License
# along with diffoscope. If not, see <http://www.gnu.org/licenses/>.
import os.path
import re
from diffoscope import tool_required
from diffoscope.difference import Difference
from diffoscope.comparators.binary import File
from diffoscope.comparators.libarchive import LibarchiveContainer, list_libarchive
from diffoscope.comparators.utils import Command
from diffoscope import logger
# TODO: this would also be useful for Go archives. Currently those are handled
# by StaticLibFile, but then readelf complains with "Error: Not an ELF file".
# ArFile gives slightly more reasonable output, e.g. a readable plain diff of
......@@ -34,16 +36,20 @@ class ArContainer(LibarchiveContainer):
def get_members(self):
members = LibarchiveContainer.get_members(self)
cls = members.__class__
# for some reason libarchive outputs ["/", "//"] as member names for
# some archives. for now, let's just filter these out, otherwise they
# cause exceptions later. eventually, we should investigate this in
# more detail and handle it properly.
filtered_out = cls([p for p in members.items() if not os.path.basename(p[0])])
known_ignores = {
"/" : "this is the symbol table, already accounted for in other output",
"//" : "this is the table for GNU long names, already accounted for in the archive filelist",
}
filtered_out = cls([p for p in members.items() if p[0] in known_ignores])
if filtered_out:
logger.debug("ignored directory ar members %s in %s, don't yet know how to handle these",
list(filtered_out.keys()), self.source.path)
return cls([p for p in members.items() if os.path.basename(p[0])])
for k, v in filtered_out.items():
logger.debug("ignored ar member '%s' because %s", k, known_ignores[k])
return cls([p for p in members.items() if p[0] not in known_ignores])
class ArSymbolTableDumper(Command):
@tool_required('nm')
def cmdline(self):
return ['nm', '-s', self.path]
class ArFile(File):
CONTAINER_CLASS = ArContainer
......@@ -54,6 +60,7 @@ class ArFile(File):
return ArFile.RE_FILE_TYPE.search(file.magic_file_type)
def compare_details(self, other, source=None):
return [Difference.from_text_readers(list_libarchive(self.path),
list_libarchive(other.path),
self.path, other.path, source="file list")]
return [Difference.from_command(ArSymbolTableDumper, self.path, other.path),
Difference.from_text_readers(list_libarchive(self.path),
list_libarchive(other.path),
self.path, other.path, source="file list")]
......@@ -52,22 +52,31 @@ def test_no_differences(rlib1):
def differences(rlib1, rlib2):
return rlib1.compare(rlib2).details
def test_item0_elf(differences):
assert differences[0].source1 == 'alloc_system-d16b8f0e.0.o'
assert differences[0].source2 == 'alloc_system-d16b8f0e.0.o'
def test_num_items(differences):
assert len(differences) == 4
def test_item0_armap(differences):
assert differences[0].source1 == 'nm -s {}'
assert differences[0].source2 == 'nm -s {}'
expected_diff = open(os.path.join(os.path.dirname(__file__), '../data/rlib_armap_expected_diff')).read()
assert differences[0].unified_diff == expected_diff
def test_item1_elf(differences):
assert differences[1].source1 == 'alloc_system-d16b8f0e.0.o'
assert differences[1].source2 == 'alloc_system-d16b8f0e.0.o'
expected_diff = open(os.path.join(os.path.dirname(__file__), '../data/rlib_elf_expected_diff')).read()
assert differences[0].details[0].unified_diff == expected_diff
assert differences[1].details[0].unified_diff == expected_diff
def test_item1_rust_metadata_bin(differences):
assert differences[1].source1 == 'rust.metadata.bin'
assert differences[1].source2 == 'rust.metadata.bin'
def test_item2_rust_metadata_bin(differences):
assert differences[2].source1 == 'rust.metadata.bin'
assert differences[2].source2 == 'rust.metadata.bin'
@pytest.mark.skipif(tool_missing('llvm-dis'), reason='missing llvm-dis')
def test_item2_deflate_llvm_bitcode(differences):
assert differences[2].source1 == 'alloc_system-d16b8f0e.0.bytecode.deflate'
assert differences[2].source2 == 'alloc_system-d16b8f0e.0.bytecode.deflate'
def test_item3_deflate_llvm_bitcode(differences):
assert differences[3].source1 == 'alloc_system-d16b8f0e.0.bytecode.deflate'
assert differences[3].source2 == 'alloc_system-d16b8f0e.0.bytecode.deflate'
expected_diff = open(os.path.join(os.path.dirname(__file__), '../data/rlib_llvm_dis_expected_diff')).read()
actual_diff = differences[2].details[0].details[1].unified_diff
actual_diff = differences[3].details[0].details[1].unified_diff
assert diff_ignore_line_numbers(actual_diff) == diff_ignore_line_numbers(expected_diff)
def test_compare_non_existing(monkeypatch, rlib1):
......
@@ -1,10 +1,10 @@
Archive index:
-__rust_allocate in alloc_system-d16b8f0e.0.o
+__rust_all0cate in alloc_system-d16b8f0e.0.o
__rust_deallocate in alloc_system-d16b8f0e.0.o
__rust_reallocate in alloc_system-d16b8f0e.0.o
__rust_reallocate_inplace in alloc_system-d16b8f0e.0.o
__rust_usable_size in alloc_system-d16b8f0e.0.o
alloc_system-d16b8f0e.0.o:
0000000000000000 T __rust_allocate
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment