Skip to content

Commit 74c5ba4

Browse files
committed
Add a tool for reducing glibc versions
1 parent cf1cab9 commit 74c5ba4

File tree

11 files changed

+515
-0
lines changed

11 files changed

+515
-0
lines changed

cpython-unix/antiquator/.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
lib*_placeholder.c
2+
lib*_placeholder.versions
3+
lib*_placeholder.so
4+
*.o
5+
*.a
6+
glibc-*/
7+
glibc_*.debian.tar.xz
8+
glibc_*.dsc
9+
glibc_*.orig.tar.xz
10+
glibc_*.orig.tar.xz.asc

cpython-unix/antiquator/Makefile

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
GLIBC ?= glibc-2.39/
2+
3+
LIBS := libanl libc libdl libm libpthread libresolv librt libutil
4+
HELPERS := elf-init.o libc_start_main.o
5+
6+
all: ${LIBS:=_placeholder.so} libantiquator_helpers.a(${HELPERS})
7+
8+
%.so: %.c %.versions
9+
${CC} -fPIC -shared -o $@ $< -Wl,--version-script,$*.versions
10+
11+
${LIBS:=_placeholder.c} ${LIBS:=_placeholder.versions} &: make_shadow_libraries.py ${GLIBC}
12+
./make_shadow_libraries.py ${GLIBC} .
13+
14+
clean:
15+
-${RM} ${LIBS:=_placeholder.so} ${LIBS:=_placeholder.c} ${LIBS:=._placeholder.versions} libc_start_main.o
16+
17+
elf-init.o: CFLAGS += -I.
18+
19+
glibc-%/:
20+
apt-get source glibc
21+
22+
.PHONY: all clean
23+
.SECONDARY:

cpython-unix/antiquator/README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
Antiquator - Use a newer glibc to build for older glibc versions
2+
===
3+
4+
This is a set of utilities to enable building binaries using a newer
5+
glibc that can run on older glibc versions: if a symbol exists in an
6+
older version, prefer that implementation, and if a symbol does not
7+
exist in an older version, enable weak linking against it (runtime value
8+
is NULL if not found).
9+
10+
First, run `make` to build shadow libraries from glibc sources. If you
11+
already have a glibc checkout, you can use `make GLIBC=/path/to/glibc`.
12+
This should be as new as your compile-time glibc; newer will probably
13+
work fine but is probably not helpful, since you won't pick up any
14+
symbols from it.
15+
16+
Then set the `antiquator` environment variable to this directory. (This
17+
needs to be an exported environment variable.)
18+
19+
Finally, use `gcc -specs ${antiquator}/gcc.spec` or `clang --config
20+
${antiquator}/clang.config` for compiling and linking (e.g. add those options
21+
to `CFLAGS` and `LDFLAGS`).
22+
23+
The clang implementation uses `-fuse-ld` internally; to pick the actual
24+
linker, you can use `-Wl,-fuse-ld=...`.
25+
26+
Requirements
27+
---
28+
29+
You need a relatively recent toolchain (at least binutils 2.35+) and
30+
patchelf.
31+
32+
lld seems to not work as a linker. This might be an lld bug. (bfd ld and
33+
gold both seem to work.)
34+
35+
Credits
36+
---
37+
38+
elf-init.c is taken from glibc 2.33, the last version that had it. It is
39+
unmodified from the version that was in glibc and used under the license
40+
exception stated in the file.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#include_next <bits/mman-shared.h>
2+
3+
#define weaken(sym) extern __typeof(sym) sym __attribute__((weak))
4+
5+
#ifdef _GNU_SOURCE
6+
weaken(memfd_create);
7+
#endif
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-isystem <CFGDIR>
2+
-fuse-ld=<CFGDIR>/wrap-linker

cpython-unix/antiquator/elf-init.c

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/* Startup support for ELF initializers/finalizers in the main executable.
2+
Copyright (C) 2002-2021 Free Software Foundation, Inc.
3+
This file is part of the GNU C Library.
4+
5+
The GNU C Library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
In addition to the permissions in the GNU Lesser General Public
11+
License, the Free Software Foundation gives you unlimited
12+
permission to link the compiled version of this file with other
13+
programs, and to distribute those programs without any restriction
14+
coming from the use of this file. (The GNU Lesser General Public
15+
License restrictions do apply in other respects; for example, they
16+
cover modification of the file, and distribution when not linked
17+
into another program.)
18+
19+
Note that people who make modified versions of this file are not
20+
obligated to grant this special exception for their modified
21+
versions; it is their choice whether to do so. The GNU Lesser
22+
General Public License gives permission to release a modified
23+
version without this exception; this exception also makes it
24+
possible to release a modified version which carries forward this
25+
exception.
26+
27+
The GNU C Library is distributed in the hope that it will be useful,
28+
but WITHOUT ANY WARRANTY; without even the implied warranty of
29+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
30+
Lesser General Public License for more details.
31+
32+
You should have received a copy of the GNU Lesser General Public
33+
License along with the GNU C Library; if not, see
34+
<https://www.gnu.org/licenses/>. */
35+
36+
#include <stddef.h>
37+
#include <elf-initfini.h>
38+
39+
40+
/* These magic symbols are provided by the linker. */
41+
extern void (*__preinit_array_start []) (int, char **, char **)
42+
attribute_hidden;
43+
extern void (*__preinit_array_end []) (int, char **, char **)
44+
attribute_hidden;
45+
extern void (*__init_array_start []) (int, char **, char **)
46+
attribute_hidden;
47+
extern void (*__init_array_end []) (int, char **, char **)
48+
attribute_hidden;
49+
extern void (*__fini_array_start []) (void) attribute_hidden;
50+
extern void (*__fini_array_end []) (void) attribute_hidden;
51+
52+
53+
#if ELF_INITFINI
54+
/* These function symbols are provided for the .init/.fini section entry
55+
points automagically by the linker. */
56+
extern void _init (void);
57+
extern void _fini (void);
58+
#endif
59+
60+
61+
/* These functions are passed to __libc_start_main by the startup code.
62+
These get statically linked into each program. For dynamically linked
63+
programs, this module will come from libc_nonshared.a and differs from
64+
the libc.a module in that it doesn't call the preinit array. */
65+
66+
67+
void
68+
__libc_csu_init (int argc, char **argv, char **envp)
69+
{
70+
/* For dynamically linked executables the preinit array is executed by
71+
the dynamic linker (before initializing any shared object). */
72+
73+
#ifndef LIBC_NONSHARED
74+
/* For static executables, preinit happens right before init. */
75+
{
76+
const size_t size = __preinit_array_end - __preinit_array_start;
77+
size_t i;
78+
for (i = 0; i < size; i++)
79+
(*__preinit_array_start [i]) (argc, argv, envp);
80+
}
81+
#endif
82+
83+
#if ELF_INITFINI
84+
_init ();
85+
#endif
86+
87+
const size_t size = __init_array_end - __init_array_start;
88+
for (size_t i = 0; i < size; i++)
89+
(*__init_array_start [i]) (argc, argv, envp);
90+
}
91+
92+
/* This function should not be used anymore. We run the executable's
93+
destructor now just like any other. We cannot remove the function,
94+
though. */
95+
void
96+
__libc_csu_fini (void)
97+
{
98+
#ifndef LIBC_NONSHARED
99+
size_t i = __fini_array_end - __fini_array_start;
100+
while (i-- > 0)
101+
(*__fini_array_start [i]) ();
102+
103+
# if ELF_INITFINI
104+
_fini ();
105+
# endif
106+
#endif
107+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Somewhat confusing name - this means "this code is going into
2+
// libc_nonshared.a", the library of static code that is linked when
3+
// you're using libc.so. So effectively it means you _are_ targeting a
4+
// shared link and not a static link.
5+
#define LIBC_NONSHARED 1
6+
7+
#define ELF_INITFINI 1
8+
9+
#define attribute_hidden __attribute__((visibility("hidden")))

cpython-unix/antiquator/gcc.specs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Spec file for GCC. `gcc -specs antiquator.spec`
2+
#
3+
# There are two ways to add to a spec intead of replacing it. One is to
4+
# use +, which appends. But we need -lc_placeholder to come before -lc.
5+
# The usual trick for that (documented in the GCC manual) is
6+
# %rename lib old_lib
7+
# *lib: -lc_placeholder %(old_lib)
8+
# but GCC gets mad if old_lib already exists, and contrary to the
9+
# documentation you cannot delete a spec, and if you set -specs in both
10+
# CFLAGS and LDFLAGS, then this spec file runs twice if you build a
11+
# program in one command with $(CC) $(CFLAGS) $(LDFLAGS), which autoconf
12+
# does for test programs, causing it to fail. So, instead take advantage
13+
# of %(mflib), an unused variable in the built-in specs in a spot we
14+
# like (from the old "Mudflap" precursor to ASan, which was removed from
15+
# GCC over a decade ago but they left this variable in the specs). Just
16+
# in case anyone else had the same clever idea, we append to it, but we
17+
# expect it to be empty. (Everything we add is idempotent, just wasteful
18+
# to run twice.)
19+
*cpp:
20+
+ -isystem %:getenv(antiquator /)
21+
22+
*mflib:
23+
+ -L %:getenv(antiquator /) \
24+
--push-state --as-needed \
25+
-lantiquator_helpers \
26+
-lanl_placeholder \
27+
-lc_placeholder \
28+
-ldl_placeholder \
29+
-lm_placeholder \
30+
-lpthread_placeholder \
31+
-lresolv_placeholder \
32+
-lrt_placeholder \
33+
-lutil_placeholder \
34+
--pop-state
35+
36+
*post_link: +
37+
%:getenv(antiquator /wrap-linker) -fuse-ld=true %{o*}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#include <stdio.h>
2+
3+
extern int __libc_csu_init(int argc, char **argv, char **envp);
4+
5+
#define LIBC_START_MAIN_ARGS \
6+
int (*main) (int, char **, char **), \
7+
int argc, char **argv, \
8+
__typeof (main) init, \
9+
void (*fini) (void), \
10+
void (*rtld_fini) (void), void *stack_end
11+
12+
extern int real_libc_start_main(LIBC_START_MAIN_ARGS);
13+
14+
// The static linker needs to find this under the name
15+
// __libc_start_main, so that crt1.o calls this one instead of the real
16+
// one in libc. But after we rename real_libc_start_main with patchelf
17+
// to __libc_start_main, the dynamic linker needs to _not_ find this one
18+
// and instead find the real one. To accomplish this, we give it a
19+
// non-default symbol version that does not match the symbol version
20+
// that we actually want.
21+
__attribute__((symver("__libc_start_main@ANTIQUATOR_SHIM")))
22+
int __libc_start_main(LIBC_START_MAIN_ARGS) {
23+
return real_libc_start_main(main, argc, argv, __libc_csu_init, fini, rtld_fini, stack_end);
24+
}
25+

0 commit comments

Comments
 (0)