ctf-reader: looks for debug information in out-of-tree modules

Message ID 20220815193209.3631681-1-guillermo.e.martinez@oracle.com
State New
Headers
Series ctf-reader: looks for debug information in out-of-tree modules |

Commit Message

Guillermo E. Martinez Aug. 15, 2022, 7:32 p.m. UTC
  Hello libabigail team,

This patch add support to extract the kABI from out-of-tree
Linux Kernel modules.

Please let know your thoughts.

Thanks!,
guillermo
--

The archive `vmlinux.ctfa' contain CTF debug information for
all the types used by more than one module, CTF for the core
kernel and CTF for each module compiled in Linux tree directory.
CTF information for out-of-tree module is not present in
`vmlinux.ctfa' file, even so, the compiler can emit the `.ctf'
section into the out-of-tree modules and it can be extracted
by the libabigail tools.

	* src/abg-ctf-reader.cc (process_ctf_archive, read_corpus
	slurp_elf_info): Avoid looks for `vmlinux.ctfa' when
	we aren't processing a `cur_corpus_group_'. So CTF
	info is embedded in the `.ko' file.
	* tests/data/Makefile.am: Add test inputs and expected files.
	* tests/data/test-read-ctf/test-linux-module.{ko,c,abi}: Add
	new test input and reference kABI.
	* tests/test-read-ctf.cc: Add new testcase.

Signed-off-by: Guillermo E. Martinez <guillermo.e.martinez@oracle.com>
---
 src/abg-ctf-reader.cc                         |   9 ++-
 tests/data/Makefile.am                        |   3 +
 .../data/test-read-ctf/test-linux-module.abi  |  65 ++++++++++++++++++
 tests/data/test-read-ctf/test-linux-module.c  |  37 ++++++++++
 tests/data/test-read-ctf/test-linux-module.ko | Bin 0 -> 26248 bytes
 tests/test-read-ctf.cc                        |   9 +++
 6 files changed, 120 insertions(+), 3 deletions(-)
 create mode 100644 tests/data/test-read-ctf/test-linux-module.abi
 create mode 100644 tests/data/test-read-ctf/test-linux-module.c
 create mode 100644 tests/data/test-read-ctf/test-linux-module.ko
  

Comments

Dodji Seketeli Aug. 29, 2022, 11:02 a.m. UTC | #1
Hello,

"Guillermo E. Martinez via Libabigail" <libabigail@sourceware.org> a
écrit:

> --
>
> The archive `vmlinux.ctfa' contain CTF debug information for
> all the types used by more than one module, CTF for the core
> kernel and CTF for each module compiled in Linux tree directory.
> CTF information for out-of-tree module is not present in
> `vmlinux.ctfa' file, even so, the compiler can emit the `.ctf'
> section into the out-of-tree modules and it can be extracted
> by the libabigail tools.
>
> 	* src/abg-ctf-reader.cc (process_ctf_archive, read_corpus
> 	slurp_elf_info): Avoid looks for `vmlinux.ctfa' when
> 	we aren't processing a `cur_corpus_group_'. So CTF
> 	info is embedded in the `.ko' file.
> 	* tests/data/Makefile.am: Add test inputs and expected files.
> 	* tests/data/test-read-ctf/test-linux-module.{ko,c,abi}: Add
> 	new test input and reference kABI.
> 	* tests/test-read-ctf.cc: Add new testcase.
>
> Signed-off-by: Guillermo E. Martinez <guillermo.e.martinez@oracle.com>

Applied to master, thanks!

[...]

Cheers,
  

Patch

diff --git a/src/abg-ctf-reader.cc b/src/abg-ctf-reader.cc
index c1ea15aa..71808f9a 100644
--- a/src/abg-ctf-reader.cc
+++ b/src/abg-ctf-reader.cc
@@ -1228,7 +1228,8 @@  process_ctf_archive(read_context *ctxt, corpus_sptr corp)
   filter.set_public_symbols();
   std::string dict_name;
 
-  if (corp->get_origin() & corpus::LINUX_KERNEL_BINARY_ORIGIN)
+  if ((corp->get_origin() & corpus::LINUX_KERNEL_BINARY_ORIGIN)
+      && ctxt->cur_corpus_group_)
     {
       tools_utils::base_name(ctxt->filename, dict_name);
 
@@ -1472,7 +1473,8 @@  slurp_elf_info(read_context *ctxt,
   ABG_ASSERT(ctxt->symtab);
   corp->set_symtab(ctxt->symtab);
 
-  if (corp->get_origin() & corpus::LINUX_KERNEL_BINARY_ORIGIN)
+  if ((corp->get_origin() & corpus::LINUX_KERNEL_BINARY_ORIGIN)
+      && ctxt->cur_corpus_group_)
     {
       status |= elf_reader::STATUS_OK;
       return;
@@ -1613,7 +1615,8 @@  read_corpus(read_context *ctxt, elf_reader::status &status)
    (ctxt->cur_corpus_->get_exported_decls_builder().get());
 
   int errp;
-  if (corp->get_origin() & corpus::LINUX_KERNEL_BINARY_ORIGIN)
+  if ((corp->get_origin() & corpus::LINUX_KERNEL_BINARY_ORIGIN)
+      && ctxt->cur_corpus_group_)
     {
       if (ctxt->ctfa == NULL)
         {
diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
index c7875b01..ebfce623 100644
--- a/tests/data/Makefile.am
+++ b/tests/data/Makefile.am
@@ -683,6 +683,9 @@  test-read-ctf/test-callback2.abi	\
 test-read-ctf/test-anonymous-fields.c	\
 test-read-ctf/test-anonymous-fields.o	\
 test-read-ctf/test-anonymous-fields.o.abi	\
+test-read-ctf/test-linux-module.abi		\
+test-read-ctf/test-linux-module.c		\
+test-read-ctf/test-linux-module.ko		\
 \
 test-annotate/test0.abi			\
 test-annotate/test1.abi			\
diff --git a/tests/data/test-read-ctf/test-linux-module.abi b/tests/data/test-read-ctf/test-linux-module.abi
new file mode 100644
index 00000000..d6bff4e1
--- /dev/null
+++ b/tests/data/test-read-ctf/test-linux-module.abi
@@ -0,0 +1,65 @@ 
+<abi-corpus version='2.1' path='data/test-read-ctf/test-linux-module.ko'>
+  <elf-function-symbols>
+    <elf-symbol name='testexport' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes' crc='0x533ba8e8'/>
+    <elf-symbol name='testexport2' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes' crc='0x65900b2f'/>
+  </elf-function-symbols>
+  <elf-variable-symbols>
+    <elf-symbol name='global_sym' size='4' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes' crc='0x44a6713b'/>
+  </elf-variable-symbols>
+  <abi-instr address-size='64' language='LANG_C'>
+    <type-decl name='int' size-in-bits='32' alignment-in-bits='32' id='type-id-1'/>
+    <class-decl name='__raw_tickets' size-in-bits='32' alignment-in-bits='16' is-struct='yes' visibility='default' id='type-id-2'>
+      <data-member access='public' layout-offset-in-bits='0'>
+        <var-decl name='owner' type-id='type-id-3' visibility='default'/>
+      </data-member>
+      <data-member access='public' layout-offset-in-bits='16'>
+        <var-decl name='next' type-id='type-id-3' visibility='default'/>
+      </data-member>
+    </class-decl>
+    <class-decl name='arch_spinlock_t' size-in-bits='32' alignment-in-bits='32' is-struct='yes' naming-typedef-id='type-id-4' visibility='default' id='type-id-5'>
+      <data-member access='public' layout-offset-in-bits='0'>
+        <var-decl name='' type-id='type-id-6' visibility='default'/>
+      </data-member>
+    </class-decl>
+    <class-decl name='raw_spinlock' size-in-bits='32' alignment-in-bits='32' is-struct='yes' visibility='default' id='type-id-7'>
+      <data-member access='public' layout-offset-in-bits='0'>
+        <var-decl name='raw_lock' type-id='type-id-4' visibility='default'/>
+      </data-member>
+    </class-decl>
+    <class-decl name='spinlock' size-in-bits='32' alignment-in-bits='32' is-struct='yes' visibility='default' id='type-id-8'>
+      <data-member access='public' layout-offset-in-bits='0'>
+        <var-decl name='' type-id='type-id-9' visibility='default'/>
+      </data-member>
+    </class-decl>
+    <union-decl name='' size-in-bits='32' is-anonymous='yes' visibility='default' id='type-id-9'>
+      <data-member access='public'>
+        <var-decl name='rlock' type-id='type-id-7' visibility='default'/>
+      </data-member>
+    </union-decl>
+    <union-decl name='' size-in-bits='32' is-anonymous='yes' visibility='default' id='type-id-6'>
+      <data-member access='public'>
+        <var-decl name='slock' type-id='type-id-10' visibility='default'/>
+      </data-member>
+      <data-member access='public'>
+        <var-decl name='tickets' type-id='type-id-2' visibility='default'/>
+      </data-member>
+    </union-decl>
+    <type-decl name='unsigned int' size-in-bits='32' alignment-in-bits='32' id='type-id-11'/>
+    <type-decl name='unsigned short int' size-in-bits='16' alignment-in-bits='16' id='type-id-12'/>
+    <pointer-type-def type-id='type-id-13' size-in-bits='64' alignment-in-bits='64' id='type-id-14'/>
+    <function-decl name='testexport' visibility='default' binding='global' size-in-bits='64' alignment-in-bits='8' elf-symbol-id='testexport'>
+      <return type-id='type-id-1'/>
+    </function-decl>
+    <function-decl name='testexport2' visibility='default' binding='global' size-in-bits='64' alignment-in-bits='8' elf-symbol-id='testexport2'>
+      <parameter type-id='type-id-14'/>
+      <return type-id='type-id-1'/>
+    </function-decl>
+    <var-decl name='global_sym' type-id='type-id-1' mangled-name='global_sym' visibility='default' elf-symbol-id='global_sym'/>
+    <typedef-decl name='__u16' type-id='type-id-12' id='type-id-15'/>
+    <typedef-decl name='__u32' type-id='type-id-11' id='type-id-16'/>
+    <typedef-decl name='arch_spinlock_t' type-id='type-id-5' id='type-id-4'/>
+    <typedef-decl name='spinlock_t' type-id='type-id-8' id='type-id-13'/>
+    <typedef-decl name='u16' type-id='type-id-15' id='type-id-3'/>
+    <typedef-decl name='u32' type-id='type-id-16' id='type-id-10'/>
+  </abi-instr>
+</abi-corpus>
diff --git a/tests/data/test-read-ctf/test-linux-module.c b/tests/data/test-read-ctf/test-linux-module.c
new file mode 100644
index 00000000..4a647aa7
--- /dev/null
+++ b/tests/data/test-read-ctf/test-linux-module.c
@@ -0,0 +1,37 @@ 
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+int global_sym = 0;
+EXPORT_SYMBOL(global_sym);
+static spinlock_t my_lock;
+
+int testexport(void)
+{
+  printk("in testexport\n");
+  return 0;
+}
+
+EXPORT_SYMBOL(testexport);
+
+int testexport2(spinlock_t *t)
+{
+  printk("in testexport\n");
+  return 0;
+}
+EXPORT_SYMBOL(testexport2);
+
+int hello_init(void)
+{
+  printk(KERN_INFO "Hello World!\n");
+  return 0;
+}
+
+void hello_exit(void)
+{
+  printk(KERN_INFO "Bye World!\n");
+}
+
+module_init(hello_init);
+module_exit(hello_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/tests/data/test-read-ctf/test-linux-module.ko b/tests/data/test-read-ctf/test-linux-module.ko
new file mode 100644
index 0000000000000000000000000000000000000000..c37d6e90511e7c497a5345bfbe4b03f29ed04710
GIT binary patch
literal 26248
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

literal 0
HcmV?d00001

diff --git a/tests/test-read-ctf.cc b/tests/test-read-ctf.cc
index 994610b8..b0ef7de6 100644
--- a/tests/test-read-ctf.cc
+++ b/tests/test-read-ctf.cc
@@ -293,6 +293,15 @@  static InOutSpec in_out_specs[] =
     "data/test-read-ctf/test-callback2.abi",
     "output/test-read-ctf/test-callback2.abi",
   },
+  // out-of-tree kernel module.
+  {
+    "data/test-read-ctf/test-linux-module.ko",
+    "",
+    "",
+    SEQUENCE_TYPE_ID_STYLE,
+    "data/test-read-ctf/test-linux-module.abi",
+    "output/test-read-ctf/test-linux-module.abi",
+  },
   // This should be the last entry.
   {NULL, NULL, NULL, SEQUENCE_TYPE_ID_STYLE, NULL, NULL}
 };