defining object in inline assembly, referencing it from C code

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi

[Sorry for the long mail, I think I have to give some background on what
I'm trying to do. If you decide to read on please ensure you have a
bucket handy.]

I'm trying to reduce the static memory footprint of a data structure in
the linux kernel, using a trick that is already used elsewhere in the
kernel, namely replacing pointers by a 32-bit relative offset from the
containing structure. The existing example is

struct bug_entry {
	signed int	bug_addr_disp;
	signed int	file_disp;
	unsigned short	line;
	unsigned short	flags;
};

which is created using

#define _BUG_FLAGS(ins, flags)				\
do {							\
	asm volatile("1:\t" ins "\n"			\
		     ".pushsection __bug_table,\"aw\"\n"	\
		     "2:\t 1b - 2b \t# bug_entry::bug_addr_disp\n" \
		     "\t %c0 - 2b \t# bug_entry::file_disp\n"	\
		     "\t.word %c1"        "\t# bug_entry::line\n" \
		     "\t.word %c2"        "\t# bug_entry::flags\n" \
		     "\t.org 2b+%c3\n"				\
		     ".popsection"				\
		     : : "i" (__FILE__), "i" (__LINE__),	\
			 "i" (flags),				\
			 "i" (sizeof(struct bug_entry)));	\
} while (0)

so for a struct bug_entry *be, one get's the file where it was defined
via (const char *)be + be->file_disp.

The trouble is, in my case I need to reference the object directly from
the surrounding C code (bug_entrys are only referenced after being
looked up by bug_addr in the __bug_table ELF section).

What I came up with was simply (ab)using "extern" for telling the
compiler "it's ok, you can refer to this symbol, and someone else
provides it" - and then at assembly time, it really does exist. There
are a few problems (e.g., lack of scope in assembly), but with some ugly
hacks, I ended up with this monstrosity, which mostly works, even across
architectures (x86-64, ppc64, arm64):

/*
 * There's a bit of magic involved here.
 *
 * First, unlike the bug table entries, we need to define an object in
 * assembly which we can reference from C code (for use by the
 * DYNAMIC_DEBUG_BRANCH macro), but we don't want 'name' to have
 * external linkage (that would require use of globally unique
 * identifiers, which we can't guarantee). Fortunately, the "extern"
 * keyword just tells the compiler that _somebody_ provides that
 * symbol - usually that somebody is the linker, but in this case it's
 * the assembler, and since we do not do .globl name, the symbol gets
 * internal linkage.
 *
 * So far so good. The next problem is that there's no scope in
 * assembly, so the identifier 'name' has to be unique within each
 * translation unit - otherwise all uses of that identifier end up
 * referring to the same struct _ddebug instance. pr_debug and friends
 * do this by use of indirection and __UNIQUE_ID(), and new users of
 * DEFINE_DYNAMIC_DEBUG_METADATA() should do something similar. We
 * need to catch cases where this is not done at build time.
 *
 * With assembly-level .ifndef we can ensure that we only define a
 * given identifier once, preventing "symbol 'foo' already defined"
 * errors. But we still need to detect and fail on multiple uses of
 * the same identifer. The simplest, and wrong, solution to that is to
 * add an .else .error branch to the .ifndef. The problem is that just
 * because the DEFINE_DYNAMIC_DEBUG_METADATA() macro is only expanded
 * once with a given identifier, the compiler may emit the assembly
 * code multiple times, e.g. if the macro appears in an inline
 * function. Now, in a normal case like
 *
 *   static inline get_next_id(void) { static int v; return ++v; }
 *
 * all inlined copies of get_next_id are _supposed_ to refer to the
 * same object 'v'. So we do need to allow this chunk of assembly to
 * appear multiple times with the same 'name', as long as they all
 * came from the same DEFINE_DYNAMIC_DEBUG_METADATA() instance. To do
 * that, we pass __COUNTER__ to the asm(), and set an assembler symbol
 * name.ddebug.once to that value when we first define 'name'. When we
 * meet a second attempt at defining 'name', we compare
 * name.ddebug.once to %6 and error out if they are different.
 */

#define DEFINE_DYNAMIC_DEBUG_METADATA(name, fmt)		\
	extern struct _ddebug name;				\
	asm volatile(".ifndef " __stringify(name) "\n"		\
		     ".pushsection __verbose,\"aw\"\n"		\
		     ".type "__stringify(name)", STT_OBJECT\n"	\
		     ".size "__stringify(name)", %c5\n"		\
		     "1:\n"					\
		     __stringify(name) ":\n"			\
		     "\t.int %c0 - 1b /* _ddebug::modname_disp */\n"\
		     "\t.int %c1 - 1b /* _ddebug::function_disp */\n"\
		     "\t.int %c2 - 1b /* _ddebug::filename_disp */\n"\
		     "\t.int %c3 - 1b /* _ddebug::format_disp */\n"\
		     "\t.int %c4      /* _ddebug::flags_lineno */\n"\
		     _DPRINTK_ASM_KEY_INIT			\
		     "\t.org 1b+%c5\n"				\
		     ".popsection\n"				\
		     ".set "__stringify(name)".ddebug.once, %c6\n"\
		     ".elseif "__stringify(name)".ddebug.once - %c6\n"\
		     ".line "__stringify(__LINE__) " - 1\n"            \
		     ".error \"'"__stringify(name)"' used as _ddebug identifier more
than once\"\n" \
		     ".endif\n"					\
		     : : "i" (KBUILD_MODNAME), "i" (__func__),	\
		       "i" (__FILE__), "i" (fmt),		\
		       "i" (_DPRINTK_FLAGS_LINENO_INIT),	\
		       "i" (sizeof(struct _ddebug)), "i" (__COUNTER__))

This is then used as in (a macro that expands to)

  do {
    DEFINE_DYNAMIC_DEBUG_METADATA(id1234, fmt);
    if (DYNAMIC_DEBUG_BRANCH(&id1234)) { /* [@] */
       do_something_with(&id1234, fmt, ...);
    }
  while (0)

This works fine, at least for gcc >= 5. In a corner case, however, gcc
4.8 generates code that makes this fail: There's a static inline function

foo(..., int what)
{
  if (what == CONST1) { ... }
  else if (what == CONST2) { ... }
  else { pr_debug("illegal value of what"); }
}

All users of foo pass either CONST1 or CONST2, yet gcc still emits the
body of the above if [@] as its own foo.part.55 function (not the test,
just the do_something_with call). Thus the link subsequently fails since
there's a reference to an undefined id1234.

I can't really claim that this is wrong code generation; "extern struct
_ddebug id1234;" certainly allows gcc to refer to that object. But this
tells me that the above only really works because newer gccs don't emit
unreachable code such as the foo.part.55 function, and makes me worry
that it might break even with newer compilers in some more complicated case.

So, the questions are: Is there some way I can force gcc to emit the asm
volatile() at least once if id1234 ends up being referenced? (I've tried
passing &id1234 as an input to the asm(), that didn't help). Or is there
some other, reliable, way of defining an object in inline asm and
referencing it from C code?

Thanks,
Rasmus



[Index of Archives]     [Linux C Programming]     [Linux Kernel]     [eCos]     [Fedora Development]     [Fedora Announce]     [Autoconf]     [The DWARVES Debugging Tools]     [Yosemite Campsites]     [Yosemite News]     [Linux GCC]

  Powered by Linux