INFINITY

GNU Tools Cauldron 2016

Gary Benson <gbenson@redhat.com>

https://infinitynotes.org/c16/

Note

  • Gary Benson, gbenson
  • Red Hat, GDB
  • Infinity is Tool-agnostic / "software development tool"

Motivation

Note

Before I talk about what Infinity is, I want to talk about the motivation for creating it.

Some libraries need to be "understood" by software development tools.

Motivation (2)

_images/moti-4boxes.svg

Note

  • Application is linked to libpthread
  • Tool is accessing the application somehow (red line)
  • Tool has loaded libthread_db to do what it's doing
  • Already fair amount of baked-in knowledge in tool. Now, even at this stage we've already got this situation where tools need a fair amount of baked-in knowledge to function.

Motivation (3)

There's already a fair amount of baked-in knowledge here:

Note

  • BULLET 1: libpthread.so filename baked into tool.
  • BULLET 3 (a): Non-static can derive from path.
  • BULLET 3 (b): Version check is just a string, e.g. "2.12"
  • BULLET 4: Inefficient. Every tool implements. For every library.
  • All these issues can be/are handled.

Motivation (4)

_images/moti-4boxes.svg

Note

This is the graphic we just looked at.

Motivation (5)

_images/moti-5boxes.svg

Note

  • All this stuff is linked to the C library
  • Fine if homogenous environment

Motivation (6)

_images/moti-6boxes.svg

Note

  • Does not work in heterogenous environment * Different architectures (cross-core) * Containers (same hardware, different OS, security)
  • Can make special builds of debug libraries * MxN matrix build/organize * Poor version checks

What does libthread_db do?

libthread_db.so exports functions to:

  • Get information about a specific thread
  • Iterate over all threads

libthread_db.so imports functions to:

  • Get the numeric ID of the master process
  • Access memory of process X
  • Access registers of thread Y

Note

  • High-level / low-level
  • Tool hands over steering wheel to libthread_db.so
  • libthread_db.so drives debugger/tool

Solution

Platform-independent introspection functions stored in the libraries they apply to.

Note

So what's the solution?

  • Encode introspection functions in some platform-independent way
  • Put them in the same library the regular code is in * Don't need version checks, all built at same time

Solution (2)

_images/infinity-paradigm.svg

Note

And here's that as a graphic.

What is Infinity?

Note

So now we're at the slide which probably should have been the first slide. What is Infinity?

Components

Note

  • Flow: note functions developed in parallel with their testcases.
  • Unit tests
  • Late error discovery: * libpthread operates just fine with broken notes. * Failure only shows when you try to debug.

Potential Uses

Note

  • Solaris has librtld_db for e.g. dlmopen.
  • OpenMP may or may not have OMPD, the OpenMP debug library
  • Clang's Address Sanitizer and Thread Sanitizer passes currently use hacks to calculate things they need that are not exported. Frank asked me if it's the sort of thing Infinity could solve.
  • Pretty printers: * Something else from Frank. * Could you call a function like printf from Infinity? * Not currently / I've a good idea what's needed / on the roadmap

libthread_db Function

td_err_e
td_ta_map_lwp2thr (td_thragent_t *ta, lwpid_t lwpid, td_thrhandle_t *th)
{
  psaddr_t list;
  td_err_e err = DB_GET_SYMBOL (list, ta, __stack_user);
  if (err != TD_OK)
    return err;

  err = DB_GET_FIELD (list, ta, list, list_t, next, 0);
  if (err != TD_OK)
    return err;

  if (list == 0)
    {
      if (ps_getpid (ta->ph) != lwpid)
	return TD_ERR;
      th->th_ta_p = ta;
      th->th_unique = 0;
      return TD_OK;
    }

  return __td_ta_lookup_th_unique (ta_arg, lwpid, th);
}

Note

  • Comments stripped / not ideal
    • It's getting the address of the symbol __stack_user
    • It's getting the address of the next field
    • And it's doing something special if next is NULL.
    • Otherwise it's deferring to __td_ta_lookup_th_unique.
  • __stack_user->next == NULL
    • pthreads still initializing
    • thread register may not have
    • should only be one thread
    • returns NULL, other stuff handles

Infinity Function

define thread::from_lwpid returns td_err_e, pthread_t
	argument lwpid_t lwpid

	deref LIST_T_NEXT_OFFSET(__stack_user), ptr
	beq NULL, libpthread_uninitialized
	call libpthread::__lookup_th_unique
	return

libpthread_uninitialized:
	load NULL
	swap

	call procservice::getpid
	beq is_main_thread

not_main_thread:
	load TD_ERR
	return

is_main_thread:
	load TD_OK
	return

Note

  • Again comments**+preamble** stripped
  • Compiled to DWARF bytecode
  • Included with metadata
  • Name with namespace
  • One argument pushed
  • Check __stack_user->next etc
  • I8C checks but does not lay out stack

Another Infinity Function

define thread::get_tls_addr returns td_err_e, ptr
	argument pthread_t descr
	argument link_map_t map_address
	argument size_t offset
	extern func td_err_e, ptr (pthread_t, size_t) get_tlsbase

  /* Get the TLS module ID from the inferior's struct link_map.  */
	rot
	call rtld::__link_map_tls_modid

  /* Get the base address for the module.  */
	call get_tlsbase

  /* Add the offset (irrelevant if get_tlsbase fails).  */
	rot
	add
	swap

Note

  • This is for testcase example
  • More stack manipulation here
  • Makes two calls
  • One is to runtime linker

Testcase

class TestThrGetTLSAddr(TestCase):
    TESTFUNC = "thread::get_tls_addr(ppi)ip"

    def do_test(self, descr, lm, offset, modid, code, base):
        # Stub out the functions thread::get_tls_addr calls.
        self.implement("rtld::__lm_tls_modid", (lm,), (modid,))
        self.implement("thread::get_tlsbase", (descr, modid), (code, base))
        # Run the test.
        result = self.i8ctx.call(self.TESTFUNC, descr, lm, offset)
        self.assertEqual(len(result), 2)
        self.assertEqual(result[0], code)
        # This next is strictly only necessary if code == TD_OK,
        # but our implementation always calculates it.
        self.assertEqual(result[1], base + offset)

    def test_get_tls_addr(self):
        """Check thread::get_tls_addr works"""
        self.do_test(1019, 2371, 3259, 4507, 5981, 6427)

Note

  • No flow / one check
  • self.implement lines stub out other Infinity functions
  • Call with these arguments, returns these values, checked
  • Can handle more complex stubs
  • Can build memory

Current Status

Note

  • Nice to have:
  • Python API for the client library
  • Replace Python interpreter

Demo time!

Further Information

https://infinitynotes.org/