- Package:
- src:gcc-16
- Source:
- src:gcc-16
- Submitter:
- Michael Tokarev
- Date:
- 2026-08-12 21:55:01 UTC
- Severity:
- normal
Building firmware with hppa and hppa64 gcc, with option
-ffreestanding. Starting with gcc-16, the following code
struct tpm_log_entry le = {
.hdr.pcrindex = pcpes->pcrindex,
.hdr.eventtype = pcpes->eventtype,
};
causes the following error to me emitted at link time:
hppa64-linux-gnu-ld.bfd: o64/ccode32flat.o: in function `hash_log_extend':
ccode32flat.o.tmp.c:(.text+0x5400): undefined reference to `memset'
hppa64-linux-gnu-ld.bfd: o64/ccode32flat.o(.text+0x5400): cannot reach memset
ccode32flat.o.tmp.c:(.text+0x5400): relocation truncated to fit: R_PARISC_PCREL22F against undefined symbol `memset'
The struct in question is a bit large, so it really needs
some memory zeroing in this case. But the compiler is given
-ffreestanding.
Just in case, the code in question links in libgcc.a, but
it doesn't help either.
Apparently this is only a problem for hppa (both 32 and 64 bits)
compilers, other compilers does the right thing here.
This code worked fine when built with gcc-15 and before.
Thanks,
/mjt
Hi Michael, I suggest you file a gcc bug report. You would need to provide the .i file and compile options. Code simplification is helpful. memset is typically provided by libc. One option might be for you to provide a simple implementation of it with your code. I don't believe this issue is hppa specific. The -ffreestanding option is handled by common code and the pa backend does not do anything with this option. The pa backend also does not issue calls to memset. It doesn't deal with the initialization or layout of structs. But hppa is big endian. This and some other factors affect struct initialization and layout. Dave
Hi Michael, I suggest you file a gcc bug report. You would need to provide the .i file and compile options. Code simplification is helpful. memset is typically provided by libc. One option might be for you to provide a simple implementation of it with your code. I don't believe this issue is hppa specific. The -ffreestanding option is handled by common code and the pa backend does not do anything with this option. The pa backend also does not issue calls to memset. It doesn't deal with the initialization or layout of structs. But hppa is big endian. This and some other factors affect struct initialization and layout. Dave
Control: found -1 16.2.0-1 Hi! Thank you for your reply. Yeah, I'm at it, for a few days already :) The prob here is that memset *is* provided by the code already. When I naively added another one, it conflicted with the already-provided by the code. But the one provided is not found by the linker. And there are a few more observations here. First, -ffreestanding should ensure there's no calls to memset generated. Second, there are no calls to memset found in the generated disassembly of the functions in question (where the linker complains about missing memset) - though I don't know hppa assembly. 3rd, in objdump -t output, I do see *two* variants of memset symbol - more on this below. And 4th, this seems to be happening only when using -fwhole-program option - which makes the code simplification for the gcc bug report quite a bit more difficult. Now, two variants of memset. Here they are: 00003eec l F .text 00000020 memset.constprop.0 00000000 *UND* 00000000 memset The first is the actual memset provided by the code in question. And the second is the undefined reference. It feels like the prob is .constprop.0 suffix, added with -fwhole-program? Because all other symbols defined by the code have the same .constprop.0 suffix. If my teory is right, we have TWO bugs here. First is -fwhole-program renaming all symbols, so that internal references to them can't be resolved. And second is -ffreestanding which actually does not generate free-standing code. Well, whatever it is, so far I only see it on hppa (and hhpa64) - or actually with hppa[64]-linux-gnu-gcc. I'm building qemu, and it uses a lot of other compilers. However, only hppa and x86 builds with -fwhole-program. Without -fwhole-program it seems to work. But I dunno if the resulting binary works. Thanks, /mjt
If you add -save-temps to the compile options for your program, the .i, .s and .o files
will be generated. I suspect you will only see memset.constprop.0 in the .s and .o
intermediate files. However, there are likely some undefined symbols in the .o that
need to be resolved by linking against libgcc. There are millicode routines in it for
multiplication, etc, and routines for unwind EH support (e.g., uw_frame_state_for) that
use memset.
At the moment, I would say hppa is not compatible with -fwhole-program. The
simplest fix might be to add "_attribute__((used, externally_visible))" to the memcpy
declaration in your code.
This what google said about this:
That context completely changes the problem. In the HPPA (PA-RISC) architecture, this is a classic bootstrap/runtime boundary issue.When a user
provides their own runtime override for a standard library function like memset, and the compiler optimization engine applies -fwhole-program,
it localizes and renames the user's memset (e.g., to memset.isra.0).However, HPPA's heavy reliance on out-of-line millicode routines (like
$$divI, $$mulI) and compiler-generated calls to standard builtins like memset (which the middle-end emits implicitly for structural
initializations during exception handling and unwind setups in libgcc) creates an immediate breakage. The generated code still expects a
hardcoded symbol literal memset, but the actual code has been completely hidden or transformed by the compiler.To prevent GCC from renaming or
localizing a built-in symbol block at the target level, you need to use specific backend configuration hooks.1. The Right Hook:
TARGET_ASM_OUTPUT_ANCHOR / TARGET_C_MODE_FOR_FLOATING_TYPE (The Symbol Table Level)Because -fwhole-program intercepts declarations at the symbol
table (symtab) stage during Interprocedural Analysis (IPA), modifying the assembly generation phase is too late.To override this layout
programmatically within gcc/config/pa/pa.cc, you must enforce visibility during compilation. The idiomatic way to handle this in a GCC target
backend is to explicitly register target-specific builtins using the TARGET_COMMON_TARGET_ATTRIBUTE_TABLE or by patching the backend
initialization function where target attributes are registered.You can modify pa.cc (or pa-pinsn.cc depending on layout) to explicitly find
these declarations during compilation and assign them the properties of externally_visible.c/* In gcc/config/pa/pa.cc */
void
pa_preserve_runtime_builtins (tree decl)
{
if (DECL_NAME (decl) && IDENTIFIER_POINTER (DECL_NAME (decl)))
{
const char *name = IDENTIFIER_POINTER (DECL_NAME (decl));
/* Protect memset, millicode names, and other vital symbols */
if (strcmp (name, "memset") == 0 || strncmp (name, "$$", 2) == 0)
{
DECL_PRESERVED_P (decl) = 1;
if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (decl)) == NULL_TREE)
{
DECL_ATTRIBUTES (decl) = tree_cons (
get_identifier ("externally_visible"),
NULL_TREE,
DECL_ATTRIBUTES (decl)
);
}
}
}
}
Use code with caution.2. Utilizing SET_DECL_ASSEMBLER_NAMEIf the middle-end attempts to clone or mangle memset into an internal static reference
during clone generation passes, the HPPA backend can trap the symbol layout using the macro SET_DECL_ASSEMBLER_NAME.By overriding how the
assembler name is assigned to standard runtime blocks, you can ensure that the string literal targeted by the backend's millicode emitting logic
(like output_millicode_call) matches what the compiler maps.3. The Cleanest Intermediate Fix for the debian/patches ArrayIf modifying the
middle-end compiler logic introduces structural regression to standard IPA optimization passes, you can enforce protection via the target_clones
or architectural default definitions.By modifying the header target layout files for HPPA (gcc/config/pa/pa.h), you can force the compilation
environment to append specific compiler properties to standard libgcc symbols via UNSET_FRONTEND_OPTIMIZATION:c/* Force compiler builtins to
retain globally exposed symbols for millicode compatibility */
#define SUBTARGET_ATTRIBUTE_TABLE \
{ "interrupt", 0, 0, false, true, true, false, NULL, NULL },
Use code with caution.Direct Verification PointBecause this issue impacts code compiled with -fwhole-program when stepping through the landing
pads for DWARF2 unwind routines (which heavily rely on clean block allocation via memset in libgcc/unwind-dw2.c), you can confirm your hook fix
is working by compiling a simple test case using -fwhole-program and checking the output of:bashhppa-linux-gnu-gcc -fwhole-program -O2 test.c -S
-o - | grep -E "(memset|\$\$)"
Use code with caution.If the output maps direct calls to un-mangled memset strings instead of memset.isra.X, the backend mutation pass has
successfully preserved the ABI boundaries.Are you building this compiler directly as a native cross-toolchain pass for the Debian unstable (sid)
ports architecture, or are you building an upstream tracking branch?
Dave
The binary is actually linked against libgcc too, that's not the problem. ... And this one helps to finish the build, and it finishes successfully. I see. It's a fun stuff. So, for the time being, I'm including this workaround. For the future, I guess something has to be done with the build system to avoid -fwhole-program (Cc'ing Helge Deller). BTW, this is the complete compiler command line: hppa64-linux-gnu-gcc -I. -Io64/ -Isrc -Ivgasrc -Os -MD -g \ -Wall -Wno-strict-aliasing -Wold-style-definition -Wno-address-of-packed-member \ -Wno-stringop-overflow -Wno-array-bounds -Wtype-limits \ -fomit-frame-pointer -freg-struct-return -ffreestanding -fno-delete-null-pointer-checks \ -fdata-sections -fno-common -fno-merge-constants -mdisable-fpregs -fno-builtin-printf \ -fno-ipa-sra -fno-pie -fno-stack-protector -fstack-check=no -Wno-pointer-to-int-cast \ -Wno-int-to-pointer-cast -DMODE16=0 -DMODESEGMENT=0 -fwhole-program -DWHOLE_PROGRAM \ -c o64/ccode32flat.o.tmp.c -o o64/ccode32flat.o Without -fwhole-program there are numerous error messages in there, such as "impossible constraint in 'asm'" - many of them. Different optimization levels - such as omitting -Os - gives the same errors too. Thank you very much for the help! /mjt
Excellent! When I get a chance, I will look at making memcpy and millicode calls externally visible. It would be useful to know what code results in the call to memcpy. This seems a bug in gcc's -fwhole-program support. Avoiding millicode calls is likely tricky as they are used for integer multiplication and division. I'm not sure why -mdisable-fpregs option is used. This will make integer multiplication slower. All machines of interest have floating-point hardware. Dave
They are ok, even in SeaBIOS. We are not saving the FP regs when entering the SeaBIOS firmware. That's why I want to avoid FP regs. Helge
I wonder why they aren't munged by -fwhole-program. Dave
Maybe it's only the declarations in the code that are munged.
The following routines in libgcc on hppa are provided by glibc:
dave@mx3210:~/gnu/gcc/objdir/hppa-linux-gnu/libgcc$ nm libgcc_s.so.4|grep " U "
U abort@GLIBC_2.2
U calloc@GLIBC_2.2
U _dl_find_object@GLIBC_2.35
U free@GLIBC_2.2
U malloc@GLIBC_2.2
U memcpy@GLIBC_2.2
U memmove@GLIBC_2.2
U memset@GLIBC_2.2
U pthread_cond_broadcast@GLIBC_2.3.2
U pthread_cond_wait@GLIBC_2.3.2
U pthread_getspecific@GLIBC_2.34
U pthread_key_create@GLIBC_2.34
U pthread_mutex_lock@GLIBC_2.2
U pthread_mutex_unlock@GLIBC_2.2
U pthread_once@GLIBC_2.34
U pthread_setspecific@GLIBC_2.34
U realloc@GLIBC_2.2
U strlen@GLIBC_2.2
If any of these are implemented in qemu, they also may need the attribute fix.
Dave