Bug 41045 - Extended asm with C operands doesn’t work at top level
Summary: Extended asm with C operands doesn’t work at top level
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: c (show other bugs)
Version: unknown
: P5 enhancement
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: inline-asm
Depends on:
Blocks:
 
Reported: 2009-08-12 16:51 UTC by Anders Kaseorg
Modified: 2023-10-25 08:44 UTC (History)
7 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2009-11-22 20:00:11


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Anders Kaseorg 2009-08-12 16:51:29 UTC
I’d like to be able to write toplevel inline assembly with C operands that are compile-time constants, e.g.

static const char foo[] = "Hello, world!";
enum { bar = 17 };
asm(".pushsection baz; .long %c0, %c1, %c2; .popsection"
    : : "i" (foo), "i" (sizeof(foo)), "i" (bar));

However, this currently fails with “error: expected ‘)’ before ‘:’ token” at the top level, even though it works fine inside a function.
Comment 1 Andrew Pinski 2009-08-12 16:56:59 UTC
For you example why can you do something like:
static void *bazsection1[] __attribute__((section("baz"), used)) = { (void*)foo, (void*)(sizeof(foo)), (void*)(bar) };

This seems like a better option than using inline-asm and cleaner and more portable.
Comment 2 Nelson Elhage 2009-10-08 22:20:41 UTC
For an example of real code that wanted to use toplevel inline ASM, see the following recent Linux commit: http://git.kernel.org/linus/796216a57fe45c04adc35bda1f0782efec78a713

Lack of this feature necessitated a fairly hideous (IMO) workaround, and a multi-paragraph documentation of it in the commit messages.

Note that while (as stated in the commit message) an unrelated GCC issue originally caused the desire to use inline assembly, a later commit (0b1c723d0bd199300a1a2de57a46000d17577498) takes advantage of the fact that this block uses inline assembly, and so they really do want a toplevel assembly block here.
Comment 3 Andrew Pinski 2009-10-08 22:28:14 UTC
So they want a way to turn off progbits instead?
That seems better than doing extended inline-asm at the top level.
Comment 4 Andrew Pinski 2009-10-08 22:32:34 UTC
(In reply to comment #3)
> So they want a way to turn off progbits instead?
> That seems better than doing extended inline-asm at the top level.

And is less messy really since then the syntax would be something like:
#define RESERVE_BRK(name,sz)   \
static char __brk_reservation_#name[sz] __attribute__((section("brk_reservation", nobits), used);

Which gets rid of the messy inline-asm and makes things look like a real variable, etc.
Comment 5 Anders Kaseorg 2009-10-09 16:12:14 UTC
As Nelson pointed out, the subsequent commit to that macro <http://git.kernel.org/linus/0b1c723d0bd199300a1a2de57a46000d17577498> cannot be done with C.  In any event, while it may also be useful to add extensions like nobits to C, adding enough extensions to subsume all conceivable uses of inline assembly clearly isn’t a sustainable solution.

A more fundamental source of ugliness in the Linux kernel is that many functions which are written entirely in assembly (and must be, because they have special calling conventions dictated by the hardware, or are involved in setting up the appropriate environment for executing C code, or must be hand-optimized for performance, etc.) need to access members of a C struct.  Since there is no way to get the offset of a C struct member from assembly, the kernel’s current solution is to compile (but not assemble) a special file named asm-offsets.c:

#define DEFINE(sym, val) \
        asm volatile("\n->" #sym " %0 " #val : : "i" (val))
int main(void)
{
#define ENTRY(entry) DEFINE(tsk_ ## entry, offsetof(struct task_struct, entry))
        ENTRY(state);
        ENTRY(flags); 
        ENTRY(pid);
…	
}

resulting in asm-offsets.s:

main:
        pushq   %rbp    #
        movq    %rsp, %rbp      #,
#APP
->tsk_state $0 offsetof(struct task_struct, state)      #
->tsk_flags $20 offsetof(struct task_struct, flags)     #
->tsk_pid $680 offsetof(struct task_struct, pid)        #
…

which is post-processed with sed into a header asm-offsets.h that consists of a long list of definitions for assembly code:

#define tsk_state 0 /* offsetof(struct task_struct, state)      # */
#define tsk_flags 20 /* offsetof(struct task_struct, flags)     # */
#define tsk_pid 680 /* offsetof(struct task_struct, pid)        # */
…

If toplevel extended asm was available, this entire mechanism could have been unnecessary, because one could write this directly:

asm("kernel_thread:\n"
    …
    "movl %c[tsk_pid](%%rax),%%eax\n"
    : : [tsk_pid] "n" (offsetof(struct task_struct, pid)));
Comment 6 Andrew Pinski 2009-10-09 17:19:16 UTC
Really if the kernel should have compile time asserts that the offsets don't match up with the inline-asm.

There are ways of doing that it is not hard:
typedef static_assert_1[offsetof(a, b) != OFFSETAB ? -1 : 1];
Comment 7 Anders Kaseorg 2009-10-09 17:45:40 UTC
> Really if the kernel should have compile time asserts that the offsets don't
> match up with the inline-asm.

Yes, it’s entirely possible that the kernel’s kludge could be made more robust by adding complexity.  This doesn’t change the point that a solution based on extended toplevel asm would be easier and simpler.
Comment 8 Joseph S. Myers 2009-11-22 20:00:11 UTC
I think this would be a sensible feature to add.
Comment 9 Steven Fuerst 2012-06-10 19:32:05 UTC
For those interested, it appears that you can use a @nobits segment for variables using the gcc attribute syntax:

__attribute__ ((section(".sbss,\"awT\",@nobits #"))) int variable;

will emit:

.section	.sbss,"awT",@nobits #,"awT",@progbits

in assembly.  The '#' comment character will get rid of the pesky @progbits setting, leaving the @nobits option set.

Since the section name is included verbatim in the output, you can do even bigger hacks by using embedded carriage returns in the section name string.  This allows arbitrary asm to be inserted at top level.  You don't have access to anything but compile-time string constants though, which makes usage a little annoying.

static __attribute__((used)) __attribute__ ((section(".text\n\t"
		".globl a_function\n"
		"a_function:\n\t"
		"mov %al, %ah\n\t"
		"ret\n\t"
		"# "))) char variable;

Of course... since this is not exactly the intent of the (section()) attribute, tricks like this may break at any time.
Comment 10 Eric Gallager 2017-07-28 14:35:01 UTC
*** Bug 54450 has been marked as a duplicate of this bug. ***
Comment 11 Eric Gallager 2019-01-20 05:38:37 UTC
(In reply to Steven Fuerst from comment #9)
> 
> Of course... since this is not exactly the intent of the (section())
> attribute, tricks like this may break at any time.

"not exactly the intent" is a bit of an understatement!