Commit 7f854858 authored by Willy Tarreau's avatar Willy Tarreau Committed by Paul E. McKenney
Browse files

tools/nolibc: make compiler and assembler agree on the section around _start



The out-of-block asm() statement carrying _start does not allow the
compiler to know what section the assembly code is being emitted to,
and there's no easy way to push/pop the current section and restore
it. It sometimes causes issues depending on the include files ordering
and compiler optimizations. For example if a variable is declared
immediately before the asm() block and another one after, the compiler
assumes that the current section is still .bss and doesn't re-emit it,
making the second variable appear inside the .text section instead.
Forcing .bss at the end of the _start block doesn't work either because
at certain optimizations the compiler may reorder blocks and will make
some real code appear just after this block.

A significant number of solutions were attempted, but many of them were
still sensitive to section reordering. In the end, the best way to make
sure the compiler and assembler agree on the current section is to place
this code inside a function. Here the function is directly called _start
and configured not to emit a frame-pointer, hence to have no prologue.
If some future architectures would still emit some prologue, another
working approach consists in naming the function differently and placing
the _start label inside the asm statement. But the current solution is
simpler.

It was tested with nolibc-test at -O,-O0,-O2,-O3,-Os for arm,arm64,i386,
mips,riscv,s390 and x86_64.

Signed-off-by: default avatarWilly Tarreau <w@1wt.eu>
Signed-off-by: default avatarPaul E. McKenney <paulmck@kernel.org>
parent 28ef4c37
Loading
Loading
Loading
Loading
+15 −14
Original line number Diff line number Diff line
@@ -182,9 +182,9 @@ struct sys_stat_struct {
})

/* startup code */
__asm__ (".section .text\n"
    ".weak _start\n"
    "_start:\n"
void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) _start(void)
{
	__asm__ volatile (
		"ldr x0, [sp]\n"     // argc (x0) was in the stack
		"add x1, sp, 8\n"    // argv (x1) = sp
		"lsl x2, x0, 3\n"    // envp (x2) = 8*argc ...
@@ -194,6 +194,7 @@ __asm__ (".section .text\n"
		"bl main\n"          // main() returns the status code, we'll exit with it.
		"mov x8, 93\n"       // NR_exit == 93
		"svc #0\n"
    "");

	);
	__builtin_unreachable();
}
#endif // _NOLIBC_ARCH_AARCH64_H
+15 −25
Original line number Diff line number Diff line
@@ -175,21 +175,9 @@ struct sys_stat_struct {
})

/* startup code */
__asm__ (".section .text\n"
    ".weak _start\n"
    "_start:\n"
#if defined(__THUMBEB__) || defined(__THUMBEL__)
    /* We enter here in 32-bit mode but if some previous functions were in
     * 16-bit mode, the assembler cannot know, so we need to tell it we're in
     * 32-bit now, then switch to 16-bit (is there a better way to do it than
     * adding 1 by hand ?) and tell the asm we're now in 16-bit mode so that
     * it generates correct instructions. Note that we do not support thumb1.
     */
    ".code 32\n"
    "add     r0, pc, #1\n"
    "bx      r0\n"
    ".code 16\n"
#endif
void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) _start(void)
{
	__asm__ volatile (
		"pop {%r0}\n"                 // argc was in the stack
		"mov %r1, %sp\n"              // argv = sp
		"add %r2, %r1, %r0, lsl #2\n" // envp = argv + 4*argc ...
@@ -199,6 +187,8 @@ __asm__ (".section .text\n"
		"bl main\n"                   // main() returns the status code, we'll exit with it.
		"movs r7, $1\n"               // NR_exit == 1
		"svc $0x00\n"
    "");
	  );
	__builtin_unreachable();
}

#endif // _NOLIBC_ARCH_ARM_H
+20 −18
Original line number Diff line number Diff line
@@ -197,9 +197,9 @@ struct sys_stat_struct {
 * 2) The deepest stack frame should be set to zero
 *
 */
__asm__ (".section .text\n"
    ".weak _start\n"
    "_start:\n"
void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) _start(void)
{
	__asm__ volatile (
		"pop %eax\n"                // argc   (first arg, %eax)
		"mov %esp, %ebx\n"          // argv[] (second arg, %ebx)
		"lea 4(%ebx,%eax,4),%ecx\n" // then a NULL then envp (third arg, %ecx)
@@ -214,6 +214,8 @@ __asm__ (".section .text\n"
		"movl $1, %eax\n"           // NR_exit == 1
		"int $0x80\n"               // exit now
		"hlt\n"                     // ensure it does not
    "");
	);
	__builtin_unreachable();
}

#endif // _NOLIBC_ARCH_I386_H
+27 −24
Original line number Diff line number Diff line
@@ -189,14 +189,15 @@ struct sys_stat_struct {
})

/* startup code, note that it's called __start on MIPS */
__asm__ (".section .text\n"
    ".weak __start\n"
    ".set nomips16\n"
void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) __start(void)
{
	__asm__ volatile (
		//".set nomips16\n"
		".set push\n"
		".set    noreorder\n"
		".option pic0\n"
    ".ent __start\n"
    "__start:\n"
		//".ent __start\n"
		//"__start:\n"
		"lw $a0,($sp)\n"        // argc was in the stack
		"addiu  $a1, $sp, 4\n"  // argv = sp + 4
		"sll $a2, $a0, 2\n"     // a2 = argc * 4
@@ -210,8 +211,10 @@ __asm__ (".section .text\n"
		"move $a0, $v0\n"       // retrieve 32-bit exit code from v0
		"li $v0, 4001\n"        // NR_exit == 4001
		"syscall\n"
    ".end __start\n"
		//".end __start\n"
		".set pop\n"
    "");
	);
	__builtin_unreachable();
}

#endif // _NOLIBC_ARCH_MIPS_H
+19 −17
Original line number Diff line number Diff line
@@ -183,9 +183,9 @@ struct sys_stat_struct {
})

/* startup code */
__asm__ (".section .text\n"
    ".weak _start\n"
    "_start:\n"
void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) _start(void)
{
	__asm__ volatile (
		".option push\n"
		".option norelax\n"
		"lla   gp, __global_pointer$\n"
@@ -199,6 +199,8 @@ __asm__ (".section .text\n"
		"call  main\n"               // main() returns the status code, we'll exit with it.
		"li a7, 93\n"                // NR_exit == 93
		"ecall\n"
    "");
	);
	__builtin_unreachable();
}

#endif // _NOLIBC_ARCH_RISCV_H
Loading