public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 00/14] implement RTLD_NORELOCATE api [BZ #30007]
@ 2023-05-08 15:00 Stas Sergeev
  0 siblings, 0 replies; 2+ messages in thread
From: Stas Sergeev @ 2023-05-08 15:00 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

Note: not posting all patches to the list to avoid spamming people.
The patches can be downloaded from here:
https://sourceware.org/bugzilla/show_bug.cgi?id=30007


RTLD_NORELOCATE api is a proposal that adds a fine-grained control
over the solib dynamic-load process. It allows the user to load the
solib to the particular address he needs, using the mapping type he
needs. The basic idea is that after loading the solib with RTLD_NORELOCATE
flag, the user can move an unrelocated object before relocating it.

The API consist of the following elements:

`RTLD_NORELOCATE' - new dlopen() flag.
It defers the relocation of an object, allowing to perform the
relocation later. Ctors are delayed, and are called immediately
after the relocation is done.
Relocation is performed upon the first dlsym() or dlrelocate()
call with the obtained handle. This flag doesn't delay the
load of an object deps, but their relocation and ctors are
delayed. This flag doesn't delay the LA_ACT_CONSISTENT audit event.


`int dlrelocate(void *handle)' - new function to perform the
object relocation if the RTLD_NORELOCATE flag was used. The object
itself and all of its dependencies are relocated.
Returns EINVAL if already relocated. This function may be omitted
even if RTLD_NORELOCATE was used, in which case the relocation will
be performed upon the first dlsym() call with the obtained handle,
but using dlrelocate() function allows to handle relocation errors
and run ctors before using the object's handle. If the function
returned success then ctors of an object and all of its deps were
called by it.
If it returned error other than EINVAL (EINVAL means object
already relocated), then relocation error happened and the
handle should be closed with dlclose().


`RTLD_DI_MAPINFO' - new dlinfo() request that fills in this structure:
typedef struct
{
  void *map_start;		/* Beginning of mapping containing address.  */
  size_t map_length;		/* Length of mapping.  */
  size_t map_align;		/* Alignment of mapping.  */
  int relocated;		/* Indicates whether an object was relocated. */
} Dl_mapinfo;

The user have to check the `relocated` member, and if it is 0
then the object can be moved to the new location. The new location
must be aligned according to the `map_aligned' member, which is
usually equal to a page size. One way to move a solib image is to
use mmap() for allocating a new memory mapping, then use memcpy()
to copy an image, and finally use munmap() to unmap the memory space
at an old location.
This request may fail if the used handle was not obtained from dlopen().


`int dlset_object_base(void *handle, void *addr)' - new function to
set the new base address of an unrelocated object, after it was moved.
Returns error if the object is already relocated. The base address
set by this function, will be used when relocation is performed.


`RTLD_DI_DEPLIST' is a new dlinfo() request that fills in this structure:
typedef struct
{
  void **deps;			/* Array of handles for the deps.  */
  unsigned int ndeps;		/* Number of entries in the list.  */
} Dl_deplist;

It is needed if the user wants to move also the dependencies of the
loaded solib. In this case he needs to traverse the `deps' array,
make RTLD_DI_MAPINFO dlinfo() request per each handle from an array,
find the object he needs by inspecting the filled-in Dl_mapinfo structure,
make sure this object is not relocated yet, and move it, calling
dlset_object_base() at the end.


Use-case.

Suppose you have a VM that runs a 32bit code. Suppose you wrote a
compatibility layer that allows to compile the old 32bit non-unix code
under linux, into the native 64bit shared libraries. But compiling is
not enough and some calls should still go to a VM. VM's memory is available
in a 4Gb window somewhere in a 64bit space. In order for the code under
VM to handle the calls from a 64bit solib, you need to make sure all
pointers, that may be passed as a call arguments, are within 32 bits.
Heap and stack are dealt with by a custom libc, but in order to use
pointers to .bss objects, we need to relocate the solib to the low 32bit
address. But that's not enough, because in order for that lib to be
visible to the code under VM, it must also be mirrored to the VM window
under the map_address = reloc_address+VM_window_start.

RTLD_NORELOCATE solves that problem by allowing the user to mmap the
shared memory into the low 32bit address space and move an object there.
He may want to do so for all the library deps as well (using RTLD_DI_DEPLIST),
or only with the ones he is interested in. Then he maps the shared memory
into the VM window and either calls dlrelocate() or just starts using the
solib, in which case it will be relocated on the first symbol lookup.

Stas Sergeev (14):
  elf: switch _dl_map_segment() to anonymous mapping
  use initial mmap also for ET_EXEC
  rework maphole
  split do_reloc_1() from dl_open_worker_begin()
  split do_reloc_2() out of do_open_worker()
  move relocation into _dl_object_reloc() func
  split out _dl_finalize_segments()
  finalize elf segments on a relocation step
  implement RTLD_NORELOCATE flag
  add test-case for RTLD_NORELOCATE
  implement dlrelocate()
  implement RTLD_DI_MAPINFO
  implement dlset_object_base()
  implement RTLD_DI_DEPLIST

 bits/dlfcn.h                                  |   3 +
 dlfcn/Makefile                                |  11 +-
 dlfcn/Versions                                |   4 +
 dlfcn/ctorlib1.c                              |  39 ++
 dlfcn/dlfcn.h                                 |  34 +-
 dlfcn/dlinfo.c                                |  28 ++
 dlfcn/dlopen.c                                |   2 +-
 dlfcn/dlrelocate.c                            |  68 +++
 dlfcn/dlset_object_base.c                     | 124 ++++++
 dlfcn/tst-noreloc.c                           | 157 +++++++
 elf/dl-close.c                                |   3 +
 elf/dl-load.c                                 |  35 +-
 elf/dl-load.h                                 |   8 +-
 elf/dl-lookup.c                               |   6 +-
 elf/dl-main.h                                 |   2 +
 elf/dl-map-segments.h                         | 169 +++++---
 elf/dl-open.c                                 | 386 +++++++++++-------
 elf/rtld.c                                    |   1 +
 include/dlfcn.h                               |  11 +
 include/link.h                                |   6 +
 sysdeps/generic/ldsodefs.h                    |   1 +
 sysdeps/mach/hurd/i386/libc.abilist           |   2 +
 sysdeps/unix/sysv/linux/aarch64/libc.abilist  |   2 +
 sysdeps/unix/sysv/linux/alpha/libc.abilist    |   2 +
 sysdeps/unix/sysv/linux/arc/libc.abilist      |   2 +
 sysdeps/unix/sysv/linux/arm/be/libc.abilist   |   2 +
 sysdeps/unix/sysv/linux/arm/le/libc.abilist   |   2 +
 sysdeps/unix/sysv/linux/csky/libc.abilist     |   2 +
 sysdeps/unix/sysv/linux/hppa/libc.abilist     |   2 +
 sysdeps/unix/sysv/linux/i386/libc.abilist     |   2 +
 sysdeps/unix/sysv/linux/ia64/libc.abilist     |   2 +
 .../sysv/linux/loongarch/lp64/libc.abilist    |   2 +
 .../sysv/linux/m68k/coldfire/libc.abilist     |   2 +
 .../unix/sysv/linux/m68k/m680x0/libc.abilist  |   2 +
 .../sysv/linux/microblaze/be/libc.abilist     |   2 +
 .../sysv/linux/microblaze/le/libc.abilist     |   2 +
 .../sysv/linux/mips/mips32/fpu/libc.abilist   |   2 +
 .../sysv/linux/mips/mips32/nofpu/libc.abilist |   2 +
 .../sysv/linux/mips/mips64/n32/libc.abilist   |   2 +
 .../sysv/linux/mips/mips64/n64/libc.abilist   |   2 +
 sysdeps/unix/sysv/linux/nios2/libc.abilist    |   2 +
 sysdeps/unix/sysv/linux/or1k/libc.abilist     |   2 +
 .../linux/powerpc/powerpc32/fpu/libc.abilist  |   2 +
 .../powerpc/powerpc32/nofpu/libc.abilist      |   2 +
 .../linux/powerpc/powerpc64/be/libc.abilist   |   2 +
 .../linux/powerpc/powerpc64/le/libc.abilist   |   2 +
 .../unix/sysv/linux/riscv/rv32/libc.abilist   |   2 +
 .../unix/sysv/linux/riscv/rv64/libc.abilist   |   2 +
 .../unix/sysv/linux/s390/s390-32/libc.abilist |   2 +
 .../unix/sysv/linux/s390/s390-64/libc.abilist |   2 +
 sysdeps/unix/sysv/linux/sh/be/libc.abilist    |   2 +
 sysdeps/unix/sysv/linux/sh/le/libc.abilist    |   2 +
 .../sysv/linux/sparc/sparc32/libc.abilist     |   2 +
 .../sysv/linux/sparc/sparc64/libc.abilist     |   2 +
 .../unix/sysv/linux/x86_64/64/libc.abilist    |   2 +
 .../unix/sysv/linux/x86_64/x32/libc.abilist   |   2 +
 56 files changed, 941 insertions(+), 227 deletions(-)
 create mode 100644 dlfcn/ctorlib1.c
 create mode 100644 dlfcn/dlrelocate.c
 create mode 100644 dlfcn/dlset_object_base.c
 create mode 100644 dlfcn/tst-noreloc.c

-- 
2.39.2


^ permalink raw reply	[flat|nested] 2+ messages in thread
* [PATCH 00/14] implement RTLD_NORELOCATE api [BZ #30007]
@ 2023-05-18  8:28 Stas Sergeev
  0 siblings, 0 replies; 2+ messages in thread
From: Stas Sergeev @ 2023-05-18  8:28 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

RTLD_NORELOCATE api is a proposal that adds a fine-grained control
over the solib dynamic-load process. It allows the user to load the
solib to the particular address he needs, using the mapping type he
needs. The basic idea is that after loading the solib with RTLD_NORELOCATE
flag, the user can move an unrelocated object before relocating it.

The API consist of the following elements:

`RTLD_NORELOCATE' - new dlopen() flag.
It defers the relocation of an object, allowing to perform the
relocation later. Ctors are delayed, and are called immediately
after the relocation is done.
Relocation is performed upon the first dlsym() or dlrelocate()
call with the obtained handle. This flag doesn't delay the
load of an object deps, but their relocation and ctors are
delayed. This flag doesn't delay the LA_ACT_CONSISTENT audit event.


`int dlrelocate(void *handle)' - new function to perform the
object relocation if the RTLD_NORELOCATE flag was used. The object
itself and all of its dependencies are relocated.
Returns EINVAL if already relocated. This function may be omitted
even if RTLD_NORELOCATE was used, in which case the relocation will
be performed upon the first dlsym() call with the obtained handle,
but using dlrelocate() function allows to handle relocation errors
and run ctors before using the object's handle. If the function
returned success then ctors of an object and all of its deps were
called by it.
If it returned error other than EINVAL (EINVAL means object
already relocated), then relocation error happened and the
handle should be closed with dlclose().


`RTLD_DI_MAPINFO' - new dlinfo() request that fills in this structure:
typedef struct
{
  void *map_start;		/* Beginning of mapping containing address.  */
  size_t map_length;		/* Length of mapping.  */
  size_t map_align;		/* Alignment of mapping.  */
  int relocated;		/* Indicates whether an object was relocated. */
} Dl_mapinfo;

The user have to check the `relocated` member, and if it is 0
then the object can be moved to the new location. The new location
must be aligned according to the `map_aligned' member, which is
usually equal to a page size. One way to move a solib image is to
use mmap() for allocating a new memory mapping, then use memcpy()
to copy an image, and finally use munmap() to unmap the memory space
at an old location.
This request may fail if the used handle was not obtained from dlopen().


`int dlset_object_base(void *handle, void *addr)' - new function to
set the new base address of an unrelocated object, after it was moved.
Returns error if the object is already relocated. The base address
set by this function, will be used when relocation is performed.


`RTLD_DI_DEPLIST' is a new dlinfo() request that fills in this structure:
typedef struct
{
  void **deps;			/* Array of handles for the deps.  */
  unsigned int ndeps;		/* Number of entries in the list.  */
} Dl_deplist;

It is needed if the user wants to move also the dependencies of the
loaded solib. In this case he needs to traverse the `deps' array,
make RTLD_DI_MAPINFO dlinfo() request per each handle from an array,
find the object he needs by inspecting the filled-in Dl_mapinfo structure,
make sure this object is not relocated yet, and move it, calling
dlset_object_base() at the end.


Use-case.

Suppose you have a VM that runs a 32bit code. Suppose you wrote a
compatibility layer that allows to compile the old 32bit non-unix code
under linux, into the native 64bit shared libraries. But compiling is
not enough and some calls should still go to a VM. VM's memory is available
in a 4Gb window somewhere in a 64bit space. In order for the code under
VM to handle the calls from a 64bit solib, you need to make sure all
pointers, that may be passed as a call arguments, are within 32 bits.
Heap and stack are dealt with by a custom libc, but in order to use
pointers to .bss objects, we need to relocate the solib to the low 32bit
address. But that's not enough, because in order for that lib to be
visible to the code under VM, it must also be mirrored to the VM window
under the map_address = reloc_address+VM_window_start.

RTLD_NORELOCATE solves that problem by allowing the user to mmap the
shared memory into the low 32bit address space and move an object there.
He may want to do so for all the library deps as well (using RTLD_DI_DEPLIST),
or only with the ones he is interested in. Then he maps the shared memory
into the VM window and either calls dlrelocate() or just starts using the
solib, in which case it will be relocated on the first symbol lookup.

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2023-05-18  8:29 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-05-08 15:00 [PATCH 00/14] implement RTLD_NORELOCATE api [BZ #30007] Stas Sergeev
2023-05-18  8:28 Stas Sergeev

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).