This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Dealing with target variance


Some time ago, I posted a patch to introduce a new target properties
header:

  <https://sourceware.org/ml/libc-alpha/2018-07/msg00051.html>

It contains this:

  #ifndef _PLATFORM_PARAMS_H
  #define _PLATFORM_PARAMS_H
  
  /* Most platforms use _init/_fini symbols to call constructors and
     destructors.  If defined to 0, the dynamic loader will ignore
     DT_INIT and DT_FINI tags, and static binaries will not call the
     _init or _fini functions.  */
  #define ELF_INITFINI 1
  
  #endif /* _PLATFORM_PARAMS_H */

At the time, only a riscv override was needed, and it looked like this:

  #ifndef _PLATFORM_PARAMS_H
  
  #define _PLATFORM_PARAMS_H
  /* RISC-V does not have .init/.fini.  */
  #define ELF_INITFINI 0
  
  #endif /* _PLATFORM_PARAMS_H */

I need a second property, to indicate whether the glibc port supports
more than one page size, and probably more after that.  So the default
could look like this (for modern targets, preferring true-by-default so
that #if conditionals have the common code first):

  #ifndef _PLATFORM_PARAMS_H

  /* Do not perform _init/_fini calls.  */
  #define GLIBC_TARGET_NO_INITFINI 1

  /* EXEC_PAGESIZE is defined and always correct.  */
  #define GLIBC_TARGET_SINGLE_PAGE_SIZE 1

  /* Target is new enough so that it does not need to worry about vtable
     compatibility in libio.  */
  #define GLIBC_TARGET_NO_LIBIO_VTABLES 1
  
  #endif /* _PLATFORM_PARAMS_H */

(These are just some random examples, there are probably others
scattered throughout the code, some of them Linux-specific.)

As mentioned in the commit message posted back then, <sysdep.h> and
<dl-machine.h> are not suitable fits because of assembler
incompatibilities.

My main question is now how we should deal with target variance.
Historically, we have used sysdeps overrides for that.  For example, we
would have a file sysdeps/unix/sysv/linux/x86_64/platform-params.h
containing this:

  #ifndef _PLATFORM_PARAMS_H

  #include <sysdeps/generic/platform-params.h>

  #undef GLIBC_TARGET_NO_INITFINI
  #define  GLIBC_TARGET_NO_INITFINI 0

  #endif /* _PLATFORM_PARAMS_H */

(Or perhaps using #include_next.)  Or a copy of the whole file:

  #ifndef _PLATFORM_PARAMS_H
  #define GLIBC_TARGET_NO_INITFINI 1
  #define GLIBC_TARGET_SINGLE_PAGE_SIZE 1
  #define GLIBC_TARGET_NO_LIBIO_VTABLES 1
  #endif /* _PLATFORM_PARAMS_H */

I like the simplicity of the second style, but it means that we need to
touch all architectures if we add a new property.

But neither of these styles address the core problem I have from a
global maintainer perspective: it's not obvious what the outlier ports
are and how many are of them.

That's why I'm wondering if something like this would be preferable:

  #ifndef _PLATFORM_PARAMS_H

  /* Modern platforms do not use _init/_fini symbols.  If
     GLIBC_TARGET_NO_INITFINI is defined as 1, the dynamic loader will
     ignore DT_INIT and DT_FINI tags, and static binaries will not call
     the _init or _fini functions.  */
  #if (GLIBC_TOPLEVEL_ARCH_AARCH64 || GLIBC_TOPLEVEL_ARCH_ALPHA \
       || GLIBC_TOPLEVEL_ARCH_ARM || GLIBC_TOPLEVEL_ARCH_HPPA  \
       || GLIBC_TOPLEVEL_ARCH_I386 || GLIBC_TOPLEVEL_ARCH_IA64 \
       || GLIBC_TOPLEVEL_ARCH_M68K || GLIBC_TOPLEVEL_ARCH_MICROBLAZE \
       || GLIBC_TOPLEVEL_ARCH_MIPS || GLIBC_TOPLEVEL_ARCH_NIOS2 \
       || GLIBC_TOPLEVEL_ARCH_POWERPC || GLIBC_TOPLEVEL_ARCH_S390 \
       || GLIBC_TOPLEVEL_ARCH_SH || GLIBC_TOPLEVEL_ARCH_SPARC \
       || GLIBC_TOPLEVEL_ARCH_X86_64)
  # define GLIBC_TARGET_NO_INITFINI 0
  #else
  # define GLIBC_TARGET_NO_INITFINI 1
  #endif

  /* GLIBC_TARGET_SINGLE_PAGE_SIZE is defined as 1 if EXEC_PAGESIZE is
     defined and always correct.  With 0, a glibc build supports
     different page sizes at run time.  */
  #if (GLIBC_TOPLEVEL_ARCH_AARCH64 || GLIBC_TOPLEVEL_ARCH_IA64 \
       || GLIBC_TOPLEVEL_ARCH_M68K || GLIBC_TOPLEVEL_ARCH_MIPS \
       || GLIBC_TOPLEVEL_ARCH_POWERPC)
  # define GLIBC_TARGET_SINGLE_PAGE_SIZE 0
  #else
  # define GLIBC_TARGET_SINGLE_PAGE_SIZE 1
  #endif

  /* GLIBC_TARGET_NO_LIBIO_VTABLES defined as 1 indicates that the
     target new enough so that it does not need to worry about vtable
     compatibility in libio.  */
  #if (GLIBC_TOPLEVEL_ARCH_ALPHA || GLIBC_TOPLEVEL_ARCH_I386 \
       || GLIBC_ARCH_M68K || GLIBC_TOPLEVEL_ARCH_MIPS  \
       || GLIBC_ARCH_POWERPC_POWERPC32 || GLIBC_ARCH_S390_32 \
       || GLIBC_ARCH_SPARC_SPARC32)
  # define GLIBC_TARGET_NO_LIBIO_VTABLES 0
  #else
  # define GLIBC_TARGET_NO_LIBIO_VTABLES 1
  #endif

  #endif /* _PLATFORM_PARAMS_H */

The difference between GLIBC_TOPLEVEL_ARCH_xxx and GLIBC_ARCH_xxx is
that GLIBC_TOPLEVEL_ARCH_xxx is true for any subarchitecture (so
GLIBC_TOPLEVEL_ARCH_M68K includes coldfire, but GLIBC_ARCH_M68K does
not).

The #if/define/#else/#endif sequence is desirable for assembler support.

The conditionals are quite messy, admittedly, but they list the
architectures explicitly, which I think it is an important benefit.  For
example, based on the header file quoted above, it is very clear which
targets support different page sizes with a single glibc build.

Of course, this is a very bad idea if we still need to support private
ports, but I don't think that's the case.  There doesn't seem ot be that
much parallel, port-specific development these days, and most port
maintainers also work on generic changes and would likely benefit from
this change as well.

What do you think?

Thanks,
Florian


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]