Skip to content

Link executable files using non PIC object files. #4678

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 6, 2019

Conversation

dstogov
Copy link
Member

@dstogov dstogov commented Sep 3, 2019

This reduces PIC overhead and improves performance.

@dstogov
Copy link
Member Author

dstogov commented Sep 4, 2019

@nikic do you see any problems?
I don't understand, why we used PIC object for binaries at all.

@dstogov
Copy link
Member Author

dstogov commented Sep 4, 2019

The patch makes x86_64 Linux PHP build ~100K smaller and ~1% faster on WP.

@nikic
Copy link
Member

nikic commented Sep 4, 2019

Looks conceptually right to me, but full implications not totally clear. For example, may people be building PIE for use with ASLR?

@dstogov
Copy link
Member Author

dstogov commented Sep 4, 2019

PHP build system and libtool don't provide any special support for PIE.

However, I see Fedora provides PIE binaries.
They use "configure --with-pic", that produces only PIC objects, and "_hardened_build 1", that implies -pie linker flag.
So, the patch shouldn't affect them. @remicollet Can you confirm?

To properly support PIE without full PIC overhead, libtool should build non-PIC object with -fPIE.

@php-pulls php-pulls merged commit 56e880a into php:master Sep 6, 2019
@dstogov
Copy link
Member Author

dstogov commented Sep 6, 2019

I've merged this into master only, but I would like to merge this into PHP-7.4 as well.
The changes are minor, and the effect on Linux x86_64 performance is really visible.

@derickr @petk your decision?

@petk
Copy link
Member

petk commented Sep 6, 2019

Sounds ok with me.

@derickr
Copy link
Member

derickr commented Sep 6, 2019

I'm OK with this as well. I'm curious as to how this works though :-)

@smalyshev
Copy link
Contributor

@dstogov
Copy link
Member Author

dstogov commented Sep 9, 2019

This breaks oss-fuzz build: https://oss-fuzz-build-logs.storage.googleapis.com/log-66ab74a7-4ece-4e14-b21b-f60527dd7244.txt

Adding "--with-pic" to PHP ./configure and full rebuild, should negate the changes of this patch.

@remicollet
Copy link
Member

Looks like this change break out of sources tree build

/bin/sh /builddir/build/BUILD/php-7.4.0RC2/build-apache/libtool --silent --preserve-dup-deps --mode=link cc -export-dynamic -O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -fno-strict-aliasing -Wno-pointer-sign -fvisibility=hidden -Wall -Wno-strict-aliasing -DZEND_SIGNALS   -Wl,-zcommon-page-size=2097152 -Wl,-zmax-page-size=2097152   Zend/zend_dtrace.d.o ext/date/php_date.o ext/date/lib/astro.o ext/date/lib/dow.o ext/date/lib/parse_date.o ext/date/lib/parse_tz.o ext/date/lib/timelib.o ext/date/lib/tm2unixtime.o ext/date/lib/unixtime2tm.o ext/date/lib/parse_iso_intervals.o ext/date/lib/interval.o ext/libxml/libxml.o ext/openssl/openssl.o ext/openssl/xp_ssl.o ext/pcre/php_pcre.o ext/zlib/zlib.o ext/zlib/zlib_fopen_wrapper.o ext/zlib/zlib_filter.o ext/filter/filter.o ext/filter/sanitizing_filters.o ext/filter/logical_filters.o ext/filter/callback_filter.o ext/hash/hash.o ext/hash/hash_md.o ext/hash/hash_sha.o ext/hash/hash_ripemd.o ext/hash/hash_haval.o ext/hash/hash_tiger.o ext/hash/hash_gost.o ext/hash/hash_snefru.o ext/hash/hash_whirlpool.o ext/hash/hash_adler32.o ext/hash/hash_crc32.o ext/hash/hash_fnv.o ext/hash/hash_joaat.o ext/hash/sha3/generic64lc/KeccakP-1600-opt64.o ext/hash/sha3/generic64lc/KeccakHash.o ext/hash/sha3/generic64lc/KeccakSponge.o ext/hash/hash_sha3.o ext/reflection/php_reflection.o ext/session/mod_user_class.o ext/session/session.o ext/session/mod_files.o ext/session/mod_mm.o ext/session/mod_user.o ext/spl/php_spl.o ext/spl/spl_functions.o ext/spl/spl_engine.o ext/spl/spl_iterators.o ext/spl/spl_array.o ext/spl/spl_directory.o ext/spl/spl_exceptions.o ext/spl/spl_observer.o ext/spl/spl_dllist.o ext/spl/spl_heap.o ext/spl/spl_fixedarray.o ext/standard/crypt_freesec.o ext/standard/crypt_blowfish.o ext/standard/crypt_sha512.o ext/standard/crypt_sha256.o ext/standard/php_crypt_r.o ext/standard/array.o ext/standard/base64.o ext/standard/basic_functions.o ext/standard/browscap.o ext/standard/crc32.o ext/standard/crypt.o ext/standard/cyr_convert.o ext/standard/datetime.o ext/standard/dir.o ext/standard/dl.o ext/standard/dns.o ext/standard/exec.o ext/standard/file.o ext/standard/filestat.o ext/standard/flock_compat.o ext/standard/formatted_print.o ext/standard/fsock.o ext/standard/head.o ext/standard/html.o ext/standard/image.o ext/standard/info.o ext/standard/iptc.o ext/standard/lcg.o ext/standard/link.o ext/standard/mail.o ext/standard/math.o ext/standard/md5.o ext/standard/metaphone.o ext/standard/microtime.o ext/standard/pack.o ext/standard/pageinfo.o ext/standard/quot_print.o ext/standard/rand.o ext/standard/mt_rand.o ext/standard/soundex.o ext/standard/string.o ext/standard/scanf.o ext/standard/syslog.o ext/standard/type.o ext/standard/uniqid.o ext/standard/url.o ext/standard/var.o ext/standard/versioning.o ext/standard/assert.o ext/standard/strnatcmp.o ext/standard/levenshtein.o ext/standard/incomplete_class.o ext/standard/url_scanner_ex.o ext/standard/ftp_fopen_wrapper.o ext/standard/http_fopen_wrapper.o ext/standard/php_fopen_wrapper.o ext/standard/credits.o ext/standard/css.o ext/standard/var_unserializer.o ext/standard/ftok.o ext/standard/sha1.o ext/standard/user_filters.o ext/standard/uuencode.o ext/standard/filters.o ext/standard/proc_open.o ext/standard/streamsfuncs.o ext/standard/http.o ext/standard/password.o ext/standard/random.o ext/standard/net.o ext/standard/hrtime.o TSRM/TSRM.o main/main.o main/snprintf.o main/spprintf.o main/fopen_wrappers.o main/alloca.o main/php_scandir.o main/php_ini.o main/SAPI.o main/rfc1867.o main/php_content_types.o main/strlcpy.o main/strlcat.o main/explicit_bzero.o main/mergesort.o main/reentrancy.o main/php_variables.o main/php_ticks.o main/network.o main/php_open_temporary_file.o main/output.o main/getopt.o main/php_syslog.o main/streams/streams.o main/streams/cast.o main/streams/memory.o main/streams/filter.o main/streams/plain_wrapper.o main/streams/userspace.o main/streams/transports.o main/streams/xp_socket.o main/streams/mmap.o main/streams/glob_wrapper.o Zend/zend_language_parser.o Zend/zend_language_scanner.o Zend/zend_ini_parser.o Zend/zend_ini_scanner.o Zend/zend_alloc.o Zend/zend_compile.o Zend/zend_constants.o Zend/zend_dtrace.o Zend/zend_execute_API.o Zend/zend_highlight.o Zend/zend_llist.o Zend/zend_vm_opcodes.o Zend/zend_opcode.o Zend/zend_operators.o Zend/zend_ptr_stack.o Zend/zend_stack.o Zend/zend_variables.o Zend/zend.o Zend/zend_API.o Zend/zend_extensions.o Zend/zend_hash.o Zend/zend_list.o Zend/zend_builtin_functions.o Zend/zend_ini.o Zend/zend_sort.o Zend/zend_multibyte.o Zend/zend_ts_hash.o Zend/zend_stream.o Zend/zend_iterators.o Zend/zend_interfaces.o Zend/zend_exceptions.o Zend/zend_strtod.o Zend/zend_gc.o Zend/zend_closures.o Zend/zend_weakrefs.o Zend/zend_float.o Zend/zend_string.o Zend/zend_signal.o Zend/zend_generators.o Zend/zend_virtual_cwd.o Zend/zend_ast.o Zend/zend_objects.o Zend/zend_object_handlers.o Zend/zend_objects_API.o Zend/zend_default_classes.o Zend/zend_inheritance.o Zend/zend_smart_str.o Zend/zend_cpuinfo.o Zend/zend_execute.o main/internal_functions_cli.o sapi/cli/php_cli.o sapi/cli/php_http_parser.o sapi/cli/php_cli_server.o sapi/cli/ps_title.o sapi/cli/php_cli_process_title.o -lcrypt -lresolv -lcrypt -lrt -lm -ldl -lxml2 -lgssapi_krb5 -lkrb5 -lk5crypto -lcom_err -lssl -lcrypto -lpcre2-8 -lz -lcrypt -lcrypt  -o sapi/cli/php
cc: error: ext/date/php_date.o: No such file or directory
cc: error: ext/date/lib/astro.o: No such file or directory
cc: error: ext/date/lib/dow.o: No such file or directory
cc: error: ext/date/lib/parse_date.o: No such file or directory
cc: error: ext/date/lib/parse_tz.o: No such file or directory
cc: error: ext/date/lib/timelib.o: No such file or directory
cc: error: ext/date/lib/tm2unixtime.o: No such file or directory
cc: error: ext/date/lib/unixtime2tm.o: No such file or directory
cc: error: ext/date/lib/parse_iso_intervals.o: No such file or directory
cc: error: ext/date/lib/interval.o: No such file or directory
cc: error: ext/libxml/libxml.o: No such file or directory
cc: error: ext/openssl/openssl.o: No such file or directory
cc: error: ext/openssl/xp_ssl.o: No such file or directory
cc: error: ext/pcre/php_pcre.o: No such file or directory
cc: error: ext/zlib/zlib.o: No such file or directory
cc: error: ext/zlib/zlib_fopen_wrapper.o: No such file or directory
cc: error: ext/zlib/zlib_filter.o: No such file or directory
cc: error: ext/filter/filter.o: No such file or directory
cc: error: ext/filter/sanitizing_filters.o: No such file or directory
cc: error: ext/filter/logical_filters.o: No such file or directory
cc: error: ext/filter/callback_filter.o: No such file or directory
cc: error: ext/hash/hash.o: No such file or directory
cc: error: ext/hash/hash_md.o: No such file or directory
cc: error: ext/hash/hash_sha.o: No such file or directory
cc: error: ext/hash/hash_ripemd.o: No such file or directory
cc: error: ext/hash/hash_haval.o: No such file or directory
cc: error: ext/hash/hash_tiger.o: No such file or directory
cc: error: ext/hash/hash_gost.o: No such file or directory
cc: error: ext/hash/hash_snefru.o: No such file or directory
cc: error: ext/hash/hash_whirlpool.o: No such file or directory
cc: error: ext/hash/hash_adler32.o: No such file or directory
cc: error: ext/hash/hash_crc32.o: No such file or directory
cc: error: ext/hash/hash_fnv.o: No such file or directory
cc: error: ext/hash/hash_joaat.o: No such file or directory
cc: error: ext/hash/sha3/generic64lc/KeccakP-1600-opt64.o: No such file or directory
cc: error: ext/hash/sha3/generic64lc/KeccakHash.o: No such file or directory
cc: error: ext/hash/sha3/generic64lc/KeccakSponge.o: No such file or directory
cc: error: ext/hash/hash_sha3.o: No such file or directory
cc: error: ext/reflection/php_reflection.o: No such file or directory
cc: error: ext/session/mod_user_class.o: No such file or directory
cc: error: ext/session/session.o: No such file or directory
cc: error: ext/session/mod_files.o: No such file or directory
cc: error: ext/session/mod_mm.o: No such file or directory
cc: error: ext/session/mod_user.o: No such file or directory
cc: error: ext/spl/php_spl.o: No such file or directory
cc: error: ext/spl/spl_functions.o: No such file or directory
cc: error: ext/spl/spl_engine.o: No such file or directory
cc: error: ext/spl/spl_iterators.o: No such file or directory
cc: error: ext/spl/spl_array.o: No such file or directory
cc: error: ext/spl/spl_directory.o: No such file or directory
cc: error: ext/spl/spl_exceptions.o: No such file or directory
cc: error: ext/spl/spl_observer.o: No such file or directory
cc: error: ext/spl/spl_dllist.o: No such file or directory
cc: error: ext/spl/spl_heap.o: No such file or directory
cc: error: ext/spl/spl_fixedarray.o: No such file or directory
cc: error: ext/standard/crypt_freesec.o: No such file or directory
cc: error: ext/standard/crypt_blowfish.o: No such file or directory
cc: error: ext/standard/crypt_sha512.o: No such file or directory
cc: error: ext/standard/crypt_sha256.o: No such file or directory
cc: error: ext/standard/php_crypt_r.o: No such file or directory
cc: error: ext/standard/array.o: No such file or directory
cc: error: ext/standard/base64.o: No such file or directory
cc: error: ext/standard/basic_functions.o: No such file or directory
cc: error: ext/standard/browscap.o: No such file or directory
cc: error: ext/standard/crc32.o: No such file or directory
cc: error: ext/standard/crypt.o: No such file or directory
cc: error: ext/standard/cyr_convert.o: No such file or directory
cc: error: ext/standard/datetime.o: No such file or directory
cc: error: ext/standard/dir.o: No such file or directory
cc: error: ext/standard/dl.o: No such file or directory
cc: error: ext/standard/dns.o: No such file or directory
cc: error: ext/standard/exec.o: No such file or directory
cc: error: ext/standard/file.o: No such file or directory
cc: error: ext/standard/filestat.o: No such file or directory
cc: error: ext/standard/flock_compat.o: No such file or directory
cc: error: ext/standard/formatted_print.o: No such file or directory
cc: error: ext/standard/fsock.o: No such file or directory
cc: error: ext/standard/head.o: No such file or directory
cc: error: ext/standard/html.o: No such file or directory
cc: error: ext/standard/image.o: No such file or directory
cc: error: ext/standard/info.o: No such file or directory
cc: error: ext/standard/iptc.o: No such file or directory
cc: error: ext/standard/lcg.o: No such file or directory
cc: error: ext/standard/link.o: No such file or directory
cc: error: ext/standard/mail.o: No such file or directory
cc: error: ext/standard/math.o: No such file or directory
cc: error: ext/standard/md5.o: No such file or directory
cc: error: ext/standard/metaphone.o: No such file or directory
cc: error: ext/standard/microtime.o: No such file or directory
cc: error: ext/standard/pack.o: No such file or directory
cc: error: ext/standard/pageinfo.o: No such file or directory
cc: error: ext/standard/quot_print.o: No such file or directory
cc: error: ext/standard/rand.o: No such file or directory
cc: error: ext/standard/mt_rand.o: No such file or directory
cc: error: ext/standard/soundex.o: No such file or directory
cc: error: ext/standard/string.o: No such file or directory
cc: error: ext/standard/scanf.o: No such file or directory
cc: error: ext/standard/syslog.o: No such file or directory
cc: error: ext/standard/type.o: No such file or directory
cc: error: ext/standard/uniqid.o: No such file or directory
cc: error: ext/standard/url.o: No such file or directory
cc: error: ext/standard/var.o: No such file or directory
cc: error: ext/standard/versioning.o: No such file or directory
cc: error: ext/standard/assert.o: No such file or directory
cc: error: ext/standard/strnatcmp.o: No such file or directory
cc: error: ext/standard/levenshtein.o: No such file or directory
cc: error: ext/standard/incomplete_class.o: No such file or directory
cc: error: ext/standard/url_scanner_ex.o: No such file or directory
cc: error: ext/standard/ftp_fopen_wrapper.o: No such file or directory
cc: error: ext/standard/http_fopen_wrapper.o: No such file or directory
cc: error: ext/standard/php_fopen_wrapper.o: No such file or directory
cc: error: ext/standard/credits.o: No such file or directory
cc: error: ext/standard/css.o: No such file or directory
cc: error: ext/standard/var_unserializer.o: No such file or directory
cc: error: ext/standard/ftok.o: No such file or directory
cc: error: ext/standard/sha1.o: No such file or directory
cc: error: ext/standard/user_filters.o: No such file or directory
cc: error: ext/standard/uuencode.o: No such file or directory
cc: error: ext/standard/filters.o: No such file or directory
cc: error: ext/standard/proc_open.o: No such file or directory
cc: error: ext/standard/streamsfuncs.o: No such file or directory
cc: error: ext/standard/http.o: No such file or directory
cc: error: ext/standard/password.o: No such file or directory
cc: error: ext/standard/random.o: No such file or directory
cc: error: ext/standard/net.o: No such file or directory
cc: error: ext/standard/hrtime.o: No such file or directory
cc: error: TSRM/TSRM.o: No such file or directory
cc: error: main/main.o: No such file or directory
cc: error: main/snprintf.o: No such file or directory
cc: error: main/spprintf.o: No such file or directory
cc: error: main/fopen_wrappers.o: No such file or directory
cc: error: main/alloca.o: No such file or directory
cc: error: main/php_scandir.o: No such file or directory
cc: error: main/php_ini.o: No such file or directory
cc: error: main/SAPI.o: No such file or directory
cc: error: main/rfc1867.o: No such file or directory
cc: error: main/php_content_types.o: No such file or directory
cc: error: main/strlcpy.o: No such file or directory
cc: error: main/strlcat.o: No such file or directory
cc: error: main/explicit_bzero.o: No such file or directory
cc: error: main/mergesort.o: No such file or directory
cc: error: main/reentrancy.o: No such file or directory
cc: error: main/php_variables.o: No such file or directory
cc: error: main/php_ticks.o: No such file or directory
cc: error: main/network.o: No such file or directory
cc: error: main/php_open_temporary_file.o: No such file or directory
cc: error: main/output.o: No such file or directory
cc: error: main/getopt.o: No such file or directory
cc: error: main/php_syslog.o: No such file or directory
cc: error: main/streams/streams.o: No such file or directory
cc: error: main/streams/cast.o: No such file or directory
cc: error: main/streams/memory.o: No such file or directory
cc: error: main/streams/filter.o: No such file or directory
cc: error: main/streams/plain_wrapper.o: No such file or directory
cc: error: main/streams/userspace.o: No such file or directory
cc: error: main/streams/transports.o: No such file or directory
cc: error: main/streams/xp_socket.o: No such file or directory
cc: error: main/streams/mmap.o: No such file or directory
cc: error: main/streams/glob_wrapper.o: No such file or directory
cc: error: Zend/zend_language_parser.o: No such file or directory
cc: error: Zend/zend_language_scanner.o: No such file or directory
cc: error: Zend/zend_ini_parser.o: No such file or directory
cc: error: Zend/zend_ini_scanner.o: No such file or directory
cc: error: Zend/zend_alloc.o: No such file or directory
cc: error: Zend/zend_compile.o: No such file or directory
cc: error: Zend/zend_constants.o: No such file or directory
cc: error: Zend/zend_dtrace.o: No such file or directory
cc: error: Zend/zend_execute_API.o: No such file or directory
cc: error: Zend/zend_highlight.o: No such file or directory
cc: error: Zend/zend_llist.o: No such file or directory
cc: error: Zend/zend_vm_opcodes.o: No such file or directory
cc: error: Zend/zend_opcode.o: No such file or directory
cc: error: Zend/zend_operators.o: No such file or directory
cc: error: Zend/zend_ptr_stack.o: No such file or directory
cc: error: Zend/zend_stack.o: No such file or directory
cc: error: Zend/zend_variables.o: No such file or directory
cc: error: Zend/zend.o: No such file or directory
cc: error: Zend/zend_API.o: No such file or directory
cc: error: Zend/zend_extensions.o: No such file or directory
cc: error: Zend/zend_hash.o: No such file or directory
cc: error: Zend/zend_list.o: No such file or directory
cc: error: Zend/zend_builtin_functions.o: No such file or directory
cc: error: Zend/zend_ini.o: No such file or directory
cc: error: Zend/zend_sort.o: No such file or directory
cc: error: Zend/zend_multibyte.o: No such file or directory
cc: error: Zend/zend_ts_hash.o: No such file or directory
cc: error: Zend/zend_stream.o: No such file or directory
cc: error: Zend/zend_iterators.o: No such file or directory
cc: error: Zend/zend_interfaces.o: No such file or directory
cc: error: Zend/zend_exceptions.o: No such file or directory
cc: error: Zend/zend_strtod.o: No such file or directory
cc: error: Zend/zend_gc.o: No such file or directory
cc: error: Zend/zend_closures.o: No such file or directory
cc: error: Zend/zend_weakrefs.o: No such file or directory
cc: error: Zend/zend_float.o: No such file or directory
cc: error: Zend/zend_string.o: No such file or directory
cc: error: Zend/zend_signal.o: No such file or directory
cc: error: Zend/zend_generators.o: No such file or directory
cc: error: Zend/zend_virtual_cwd.o: No such file or directory
cc: error: Zend/zend_ast.o: No such file or directory
cc: error: Zend/zend_objects.o: No such file or directory
cc: error: Zend/zend_object_handlers.o: No such file or directory
cc: error: Zend/zend_objects_API.o: No such file or directory
cc: error: Zend/zend_default_classes.o: No such file or directory
cc: error: Zend/zend_inheritance.o: No such file or directory
cc: error: Zend/zend_smart_str.o: No such file or directory
cc: error: Zend/zend_cpuinfo.o: No such file or directory
cc: error: Zend/zend_execute.o: No such file or directory
cc: error: main/internal_functions_cli.o: No such file or directory
cc: error: sapi/cli/php_cli.o: No such file or directory
cc: error: sapi/cli/php_http_parser.o: No such file or directory
cc: error: sapi/cli/php_cli_server.o: No such file or directory
cc: error: sapi/cli/ps_title.o: No such file or directory
cc: error: sapi/cli/php_cli_process_title.o: No such file or directory
make: *** [Makefile:260: sapi/cli/php] Error 1

@remicollet
Copy link
Member

@dstogov can you please have a look ?

@dstogov
Copy link
Member Author

dstogov commented Sep 17, 2019

@dstogov can you please have a look ?

Do you really miss Zend/zend.o?
Can you show the contents of Zend/zend.lo?

@dstogov
Copy link
Member Author

dstogov commented Sep 17, 2019

The problem occurs only if CLI and apache SAPIs are built at once (using single configure script).
When apache SAPI is involved "configure" set $php_sapi_module" = "shared", independently of CLI and this prevents generation of non-PIC object files.

@remicollet
Copy link
Member

Indeed, in build-cgi (no apache SAPI), build is ok

$ find ext/date/ -name php_date.o
ext/date/php_date.o
ext/date/.libs/php_date.o

$ cat ext/date/php_date.lo 
# ext/date/php_date.lo - a libtool object file
# Generated by libtool (GNU libtool) 2.4.6
#
# Please DO NOT delete this file!
# It is necessary for linking the library.

# Name of the PIC object.
pic_object='.libs/php_date.o'

# Name of the non-PIC object
non_pic_object='php_date.o'

And in build-apache which is broken

$ find ext/date/ -name php_date.o
ext/date/.libs/php_date.o

$ cat ext/date/php_date.lo 
# ext/date/php_date.lo - a libtool object file
# Generated by libtool (GNU libtool) 2.4.6
#
# Please DO NOT delete this file!
# It is necessary for linking the library.

# Name of the PIC object.
pic_object='.libs/php_date.o'

# Name of the non-PIC object
non_pic_object=none

@dstogov
Copy link
Member Author

dstogov commented Sep 17, 2019

Fixed by db094b4

@remicollet
Copy link
Member

remicollet commented Sep 17, 2019

@dstogov thanks
Indeed, but is now OK

Side effects, embed build now start producing libphp7.a

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants