Am 06.05.26 um 09:51 schrieb Richard Biener:
On Tue, May 5, 2026 at 8:16 PM Georg-Johann Lay <[email protected]> wrote:
Am 05.05.26 um 19:05 schrieb Richard Biener:
On Tue, May 5, 2026 at 5:41 PM Georg-Johann Lay via Gcc <[email protected]> wrote:

In the avr backend there is a static variable avr_no_call_main_p that
is set in TARGET_INSERT_ATTRIBUTES and used in TARGET_ASM_FILE_END.

This works as expected in non-LTO compilations, with LTO however,
TARGET_INSERT_ATTRIBUTES runs in cc1[plus] but TARGET_ASM_FILE_END
is run by lto1, hence the variable is not set as expected.

What's the recommended way to handle such a situation?

Without looking too closely I assume there's an actual attribute somewhere
(on the main function decl?).  Instead of using a global variable you'd
check for the presence of the attribute.  Now - the question is whether
the TARGET_ASM_FILE_END behavior is only required in the TU with
the main() definition?  In that case this should work.

What works is to attach a new attribute to main when the conditions are
right, then check for that attribute in, say,
TARGET_DECLARE_FUNCTION_NAME and set a variable accordingly, and then
check for that var in TARGET_ASM_FILE_END.

It's not technically required to be handled from TARGET_ASM_FILE_END,
correct?

Exactly.  Background: main is called by __call_main which is provided
by some default lib*.a.  It is referenced by crt*.o to pull it in.

With -mno-call-main, main is effectively a part of the startup-code.
The application emits
   .global __call_main
   __call_main = 0
so __call_main it is no more pulled in since it is no more needed.

> It should work to emit the symbol from ASM_OUTPUT_FUNCTION_LABEL?> (not sure if there's sth more appropriate for 'before function' or
'after function')  You'd at least have access to the functions decl there.

Using a new attribute and emit .global __call_main + __call_main=0 in
ASM_OUTPUT_FUNCTION_LABEL (or TARGET_ASM_FILE END) generates code as
expected, however I am getting an error from the linker because
__call_main is defined in lib*.a and also in some app module.

What works fine is adding  %{mno-call-main: --defsym __call_main=0}
to the link specs, and skip messing with __call_main in avr.cc
altogether.  See attached delta.

When the user sets -mcall-main as well as -mno-call-main in the same
app, then it's fine for me it that doesn't work.

Johann

What's a bit ugly is that it requires a new target attribute that's only
for internal usage and should not to be set by the user.  Maybe not
document it is okay then.

As Sam says, an attribute with a space cannot be used by users.  Like
"avr no_call_main".

But also see the PR iff the only way for the user to get the behavior
with -mno-call-main
diff --git a/gcc/config/avr/avr.cc b/gcc/config/avr/avr.cc
index b0e9d9000e..25cb518b0f 100644
--- a/gcc/config/avr/avr.cc
+++ b/gcc/config/avr/avr.cc
@@ -249,9 +249,6 @@ bool avr_need_clear_bss_p = false;
 bool avr_need_copy_data_p = false;
 bool avr_has_rodata_p = false;
 
-/* To track if we satisfy __call_main from AVR-LibC.  */
-bool avr_no_call_main_p = false;
-
 /* Counts how often pass avr-fuse-add has been executed.  Is is kept in
    sync with cfun->machine->n_avr_fuse_add_executed and serves as an
    insn condition for shift insn splitters.  */
@@ -11906,7 +11903,9 @@ avr_insert_attributes (tree node, tree *attributes)
 	      *attributes = tree_cons (get_identifier ("section"),
 				       arg, *attributes);
 	    }
-	  avr_no_call_main_p = true;
+	  if (!lookup_attribute ("used", *attributes))
+	    *attributes = tree_cons (get_identifier ("used"),
+				     NULL_TREE, *attributes);
 	}
     } // -mno-call-main
 #endif // AVR-LibC
@@ -12467,15 +12466,6 @@ avr_file_end (void)
 
   if (avr_need_clear_bss_p)
     fputs (".global __do_clear_bss\n", asm_out_file);
-
-  /* Don't let __call_main call main() and exit().
-     Defining this symbol will keep the code from being pulled
-     in from lib<mcu>.a as requested by AVR-LibC's gcrt1.S.
-     We invoke main() by other means: putting it in .init9.  */
-
-  if (avr_no_call_main_p)
-    fputs (".global __call_main\n"
-	   "__call_main = 0\n", asm_out_file);
 }
 
 
diff --git a/gcc/config/avr/avr.opt b/gcc/config/avr/avr.opt
index 8bc2f5f173..56264ee6d1 100644
--- a/gcc/config/avr/avr.opt
+++ b/gcc/config/avr/avr.opt
@@ -39,7 +39,7 @@ Target RejectNegative Joined Var(avropt_n_flash) UInteger Init(-1)
 This option is used internally. Set the number of 64 KiB flash segments.
 
 mcall-main
-Target Var(avropt_call_main) UInteger Init(1)
+Target Var(avropt_call_main) UInteger Init(1) Save
 Call main and exit (default).
 
 mskip-bug
diff --git a/gcc/config/avr/gen-avr-mmcu-specs.cc b/gcc/config/avr/gen-avr-mmcu-specs.cc
index 42623d7e87..470d40538e 100644
--- a/gcc/config/avr/gen-avr-mmcu-specs.cc
+++ b/gcc/config/avr/gen-avr-mmcu-specs.cc
@@ -355,6 +355,8 @@ print_mcu (const avr_mcu_t *mcu, const McuInfo &mi)
 
   fprintf (f, "*link_relax:\n\t%s\n\n", LINK_RELAX_SPEC);
 
+  fprintf (f, "*link_no_call_main:\n\t%s\n\n", "%{mno-call-main: --defsym __call_main=0}");
+
   fprintf (f, "*link_arch:\n\t%s", link_arch_spec);
   if (mi.is_device
       && rodata_pm_offset)
diff --git a/gcc/config/avr/specs.h b/gcc/config/avr/specs.h
index 4f71f8a657..1fca4a1d6e 100644
--- a/gcc/config/avr/specs.h
+++ b/gcc/config/avr/specs.h
@@ -67,6 +67,7 @@ along with GCC; see the file COPYING3.  If not see
   "%(link_relax) "                              \
   "%(link_pmem_wrap) "                          \
   "%(link_rodata_in_ram) "                      \
+  "%(link_no_call_main) "                       \
   "%{shared:%eshared is not supported} "
 
 #undef  LIB_SPEC

Reply via email to