Skip to content
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

meson: add support for building static libsystemd and libudev #8689

Merged
merged 4 commits into from May 9, 2018

Conversation

davide125
Copy link
Contributor

This PR adds optional (and disabled by default) support for building and installing static library versions of libsystemd and libudev, in PIC and non-PIC versions. While static build for systemd as whole obviously doesn't make much sense, I believe offering static versions of these two libraries is reasonable, as they're what expose the external interface provided by systemd and what other programs will link to. We're using static builds of these at Facebook to integrate libsystemd and libudev with a number of internally-built tools, as it's been working fine; I'd love to get this upstream in some form so we don't have to continue carrying a patch for it.

@yuwata yuwata added the meson label Apr 10, 2018
Copy link
Member

@keszybz keszybz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit niche, but the patch is small and self-contained, so I think it is OK to add it. I expect that this will also find use for embedded.

@@ -26,6 +26,12 @@ option('rootprefix', type : 'string',
description : '''override the root prefix''')
option('link-udev-shared', type : 'boolean',
description : 'link systemd-udev and its helpers to libsystemd-shared.so')
option('static-libsystemd', type : 'combo',
choices : ['true', 'false', 'pic', 'no-pic'], value : 'false',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We generally just put the default first in the choices list, and do not specify it explicitly (mostly to make this all a bit shorter).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: Having "true" and "false" together with other options sounds a lot like using a boolean as a tri-state

Can we have a name this option differently (not really sure what to suggest) so that the choices could be "shared", "static", "pic" and "no-pic" instead? I think that would be slightly cleaner...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not a choice between "shared" and "static", this option only governs the static library. We could split this in two options: -Dstatic-libsystemd=true|false, -Dstatic-libsystemd-pic=true|false, to avoid, but I don't think it would be any better. I think it's totally OK to have multiple "true" values.

pic : pic,
dependencies : [threads,
librt],
c_args : c_args)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, does this mean that the meson pic option does not work on its own and requires flags to be twiddled manually? You don't do this in the other library, can you comment on this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, what I've observed (using meson 0.44.1) is that if c_args is being set explicitly, I have to make sure it includes -fno-PIC for the non-PIC build to be done properly. For libudev we aren't setting c_args, so this isn't needed.

'udev.h',
include_directories : includes,
link_with : udev_link_with,
link_whole : libudev_basic)

static = get_option('static-libudev')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make the name of the variable match the name of the option, i.e. static_libudev. Also the same for the other variable.

install : true,
install_dir : rootlibdir,
pic : pic)
endif
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like this simplified by removing the conditional (here and for the other one too):

static_libudev = get_option('static-libudev')
install_libudev_static = static_library(
        'udev',
        'udev.h',
        include_directories : includes,
        link_with : udev_link_with,
        link_whole : libudev_basic,
        build_by_default : static_libudev != 'false',
        install : static_libudev != 'false',
        install_dir : rootlibdir,
        pic : static_libudev == 'true' or static == 'pic')

This has the advantage that the static library can be build by explicit call

$ ninja -C build src/udev/libudev.a
ninja: Entering directory `build'
[1/1] Linking static target src/udev/libudev.a.

We do that with man pages, which can be built even if -Dman=false, and its useful for testing and debugging and such.

@keszybz keszybz added the reviewed/needs-rework 🔨 PR has been reviewed and needs another round of reworks label Apr 11, 2018
@davide125
Copy link
Contributor Author

I've pushed an update addressing the review comments, but found a potentially significant issue. It looks like meson will only include objects that have been explicitly listed in sources in the static_library. This is why I replaced udev.h with libudev_sources for libudev, as otherwise meson would produce an empty library archive. For libsystemd.a, the current build is missing all the journal-related objects, and probably more:

$ ar tvf usr/lib64/libsystemd.a 
rw-r--r-- 0/0  10576 Dec 31 16:00 1969 sd-bus_bus-common-errors.c.o
rw-r--r-- 0/0  29608 Dec 31 16:00 1969 sd-bus_bus-container.c.o
rw-r--r-- 0/0  79976 Dec 31 16:00 1969 sd-bus_bus-control.c.o
rw-r--r-- 0/0  80352 Dec 31 16:00 1969 sd-bus_bus-convenience.c.o
rw-r--r-- 0/0  82432 Dec 31 16:00 1969 sd-bus_bus-creds.c.o
rw-r--r-- 0/0  71704 Dec 31 16:00 1969 sd-bus_bus-dump.c.o
rw-r--r-- 0/0  34928 Dec 31 16:00 1969 sd-bus_bus-error.c.o
rw-r--r-- 0/0  21208 Dec 31 16:00 1969 sd-bus_bus-gvariant.c.o
rw-r--r-- 0/0  45032 Dec 31 16:00 1969 sd-bus_bus-internal.c.o
rw-r--r-- 0/0  40928 Dec 31 16:00 1969 sd-bus_bus-introspect.c.o
rw-r--r-- 0/0  31352 Dec 31 16:00 1969 sd-bus_bus-kernel.c.o
rw-r--r-- 0/0  77136 Dec 31 16:00 1969 sd-bus_bus-match.c.o
rw-r--r-- 0/0 275976 Dec 31 16:00 1969 sd-bus_bus-message.c.o
rw-r--r-- 0/0 163008 Dec 31 16:00 1969 sd-bus_bus-objects.c.o
rw-r--r-- 0/0   8816 Dec 31 16:00 1969 sd-bus_bus-signature.c.o
rw-r--r-- 0/0  42040 Dec 31 16:00 1969 sd-bus_bus-slot.c.o
rw-r--r-- 0/0  92648 Dec 31 16:00 1969 sd-bus_bus-socket.c.o
rw-r--r-- 0/0  53112 Dec 31 16:00 1969 sd-bus_bus-track.c.o
rw-r--r-- 0/0  10832 Dec 31 16:00 1969 sd-bus_bus-type.c.o
rw-r--r-- 0/0 259152 Dec 31 16:00 1969 sd-bus_sd-bus.c.o
rw-r--r-- 0/0  61880 Dec 31 16:00 1969 sd-device_device-enumerator.c.o
rw-r--r-- 0/0  85392 Dec 31 16:00 1969 sd-device_device-private.c.o
rw-r--r-- 0/0 125144 Dec 31 16:00 1969 sd-device_sd-device.c.o
rw-r--r-- 0/0  37680 Dec 31 16:00 1969 sd-hwdb_sd-hwdb.c.o
rw-r--r-- 0/0  15336 Dec 31 16:00 1969 sd-netlink_generic-netlink.c.o
rw-r--r-- 0/0  19984 Dec 31 16:00 1969 sd-netlink_local-addresses.c.o
rw-r--r-- 0/0  69368 Dec 31 16:00 1969 sd-netlink_netlink-message.c.o
rw-r--r-- 0/0  35088 Dec 31 16:00 1969 sd-netlink_netlink-socket.c.o
rw-r--r-- 0/0  72576 Dec 31 16:00 1969 sd-netlink_netlink-types.c.o
rw-r--r-- 0/0  19480 Dec 31 16:00 1969 sd-netlink_netlink-util.c.o
rw-r--r-- 0/0  89120 Dec 31 16:00 1969 sd-netlink_rtnl-message.c.o
rw-r--r-- 0/0  63992 Dec 31 16:00 1969 sd-netlink_sd-netlink.c.o
rw-r--r-- 0/0   4496 Dec 31 16:00 1969 sd-network_network-util.c.o
rw-r--r-- 0/0  28288 Dec 31 16:00 1969 sd-network_sd-network.c.o
rw-r--r-- 0/0  31144 Dec 31 16:00 1969 sd-path_sd-path.c.o
rw-r--r-- 0/0  72640 Dec 31 16:00 1969 sd-resolve_sd-resolve.c.o
rw-r--r-- 0/0   5088 Dec 31 16:00 1969 sd-utf8_sd-utf8.c.o
rw-r--r-- 0/0  13776 Dec 31 16:00 1969 sd-id128_id128-util.c.o
rw-r--r-- 0/0  20336 Dec 31 16:00 1969 sd-id128_sd-id128.c.o
rw-r--r-- 0/0  40320 Dec 31 16:00 1969 sd-daemon_sd-daemon.c.o
rw-r--r-- 0/0 157848 Dec 31 16:00 1969 sd-event_sd-event.c.o
rw-r--r-- 0/0  67056 Dec 31 16:00 1969 sd-login_sd-login.c.o

@davide125
Copy link
Contributor Author

On a related note, it looks like meson upstream is doing some work to better support mixed libraries (i.e. libraries built as both static and shared at the same time) in mesonbuild/meson#2711 and mesonbuild/meson#2816. I gave these a quick test, and while they do make things clearer, the sources issue still remains.

@keszybz
Copy link
Member

keszybz commented Apr 19, 2018

The journal client code was missing because it is added separately. I force-pushed a version which should avoid this issue. PTAL.

@keszybz keszybz removed the reviewed/needs-rework 🔨 PR has been reviewed and needs another round of reworks label Apr 19, 2018
@davide125
Copy link
Contributor Author

Thanks @keszybz! This seems to work from a quick check, though I had to add the c_args override for libudev too to make the non-PIC build work correctly. I'll run more tests and try to rebuild some of our internal stuff against it to confirm and report back.

@davide125
Copy link
Contributor Author

This is working fine, and all our stuff builds and runs correctly against it. We might be able to improve it in the future, but for now I think it's good to go.

@davide125
Copy link
Contributor Author

Looks like the same issue we had with journal_client happens with basic as well. I force pushed a new version and validated that libsystemd is built correctly and works as expected here, but I'm having trouble linking against the resulting libudev (it looks like symbols from libbasic aren't picked up, despite being included in the .a.

@keszybz
Copy link
Member

keszybz commented Apr 23, 2018

Can you attach the test case you're using?

@keszybz
Copy link
Member

keszybz commented Apr 23, 2018

Please see the fixup commit: I think some bits were missing.
With my fixup patch, I can compile the test cases with -Dstatic-lib{systemd,udev}=pic , but not with no-pic. I suspect that doing that would require having static versions of all the other libraries (xz, lz4, etc.), which I don't have. In that case, the resultant libraries themselves should be OK. Maybe the test cases should be skipped in the no-pic case.

@davide125
Copy link
Contributor Author

Even with the static versions of the libraries available, for me the non-pic testcase build fails with

ninja: Entering directory `build'
[1251/1615] Linking target test-libudev-static-sym.
FAILED: test-libudev-static-sym 
cc  -o test-libudev-static-sym 'test-libudev-static-sym@exe/meson-generated_.._src_test_test-libudev-sym.c.o' -Wl,--no-undefined -Wl,--as-needed -Wl,-z,relro -Wl,-z,now -pie -Wl,--start-group src/udev/libudev.a src/shared/libsystemd-shared-238.a src/libsystemd/libsystemd_static.a src/basic/libbasic.a src/udev/libudev-basic.a -lrt -lcap -lacl -lcryptsetup -lgcrypt -lip4tc -lip6tc -lseccomp -lselinux -Wl,--end-group -lidn -llzma -llz4 -lblkid -lrt -lcap -lblkid -lmount -lselinux  
/bin/ld: test-libudev-static-sym@exe/meson-generated_.._src_test_test-libudev-sym.c.o: relocation R_X86_64_32S against `functions' can not be used when making a shared object; recompile with -fPIC
test-libudev-static-sym@exe/meson-generated_.._src_test_test-libudev-sym.c.o: error adding symbols: Bad value
collect2: error: ld returned 1 exit status
[1259/1615] Linking target test-libsystemd-static-sym.
FAILED: test-libsystemd-static-sym 
cc  -o test-libsystemd-static-sym 'test-libsystemd-static-sym@exe/meson-generated_.._src_test_test-libsystemd-sym.c.o' -Wl,--no-undefined -Wl,--as-needed -Wl,-z,relro -Wl,-z,now -pie -Wl,--start-group libsystemd.a src/basic/libbasic.a src/basic/libbasic-gcrypt.a -pthread -fno-PIC -lrt -llzma -llz4 -lgcrypt -Wl,--end-group -lcap -lblkid -lmount -lselinux -lgcrypt  
/bin/ld: test-libsystemd-static-sym@exe/meson-generated_.._src_test_test-libsystemd-sym.c.o: relocation R_X86_64_32S against `functions' can not be used when making a shared object; recompile with -fPIC
test-libsystemd-static-sym@exe/meson-generated_.._src_test_test-libsystemd-sym.c.o: error adding symbols: Bad value
collect2: error: ld returned 1 exit status
[1300/1615] Generating systemd-fsck@.s...ce with a meson_exe.py custom command.
ninja: build stopped: subcommand failed.

so I think you're right, we should probably just disable the tests in the non-pic case.

@davide125
Copy link
Contributor Author

As a dumb testcase (unfortunately teasing a real one out of our internal build system is hard), try something like:

# doing this to ensure we pick up only static libraries
$ mkdir -p /tmp/systemd-static
$ cp -P libsystemd.a libudev.a /tmp/systemd-static
# build something
$ wget http://www.signal11.us/oss/udev/udev_example.c
$ gcc udev_example.c -L/tmp/systemd-static -ludev

This is for libudev, but you can do the same for libsystemd e.g. trying to build a simple source using sd_journal or whatever. For the udev_example case here, for me it fails with a ton of undefined references, mostly for symbols like safe_atollu that come from basic.

@keszybz
Copy link
Member

keszybz commented Apr 25, 2018

I get the same failure with udev_example.c. And it's expected, because meson does

rm -f src/udev/libudev.a && gcc-ar csrD src/udev/libudev.a 'src/udev/udev@sta/.._libudev_libudev.c.o' 'src/udev/udev@sta/.._libudev_libudev-list.c.o' 'src/udev/udev@sta/.._libudev_libudev-util.c.o' 'src/udev/udev@sta/.._libudev_libudev-device.c.o' 'src/udev/udev@sta/.._libudev_libudev-device-private.c.o' 'src/udev/udev@sta/.._libudev_libudev-enumerate.c.o' 'src/udev/udev@sta/.._libudev_libudev-monitor.c.o' 'src/udev/udev@sta/.._libudev_libudev-queue.c.o' 'src/udev/udev@sta/.._libudev_libudev-hwdb.c.o'

so there's no attempt to add the objects from src/basic/ and other necessary places.

Based on https://stackoverflow.com/questions/2157629/linking-static-libraries-to-other-static-libraries and https://sourceware.org/binutils/docs-2.30/binutils/ar-scripts.html, I tried the following:

$ cd build
$ ln -s udev@sta  src/udev/udev_sta        # ar doesn't like @ in filenames
$ gcc-ar -M <<__EOF
CREATE src/udev/libudev.a
ADDLIB src/basic/libbasic.a
ADDLIB src/basic/libbasic-gcrypt.a
ADDLIB src/udev/libudev-basic.a
ADDLIB src/libsystemd/libsystemd.a
ADDMOD src/udev/udev_sta/.._libudev_libudev.c.o
ADDMOD src/udev/udev_sta/.._libudev_libudev-list.c.o
ADDMOD src/udev/udev_sta/.._libudev_libudev-util.c.o
ADDMOD src/udev/udev_sta/.._libudev_libudev-device.c.o
ADDMOD src/udev/udev_sta/.._libudev_libudev-device-private.c.o
ADDMOD src/udev/udev_sta/.._libudev_libudev-enumerate.c.o
ADDMOD src/udev/udev_sta/.._libudev_libudev-monitor.c.o
ADDMOD src/udev/udev_sta/.._libudev_libudev-queue.c.o
ADDMOD src/udev/udev_sta/.._libudev_libudev-hwdb.c.o
SAVE
END
__EOF
$ cp src/udev/libudev.a /tmp/systemd-static
$ gcc udev_example.c -L/tmp/systemd-static -ludev -lmount

This works and the link succeeds. This is nice and efficient, but doing this manually is too ugly. Maybe if meson was smart enough to do this automatically.

Another option would be to just compile all the sources twice.

@keszybz
Copy link
Member

keszybz commented Apr 25, 2018

I came up with the following POC. Dunno, this works, and the same could be made to work for libsystemd.a, but I don't know if it's worth the trouble.

diff --git a/src/shared/meson.build b/src/shared/meson.build
index d0cb38650b..e3b076122b 100644
--- a/src/shared/meson.build
+++ b/src/shared/meson.build
@@ -2,7 +2,7 @@
 #
 # Copyright 2017 Zbigniew Jędrzejewski-Szmek
 
-shared_sources = '''
+shared_sources = files('''
         acl-util.h
         acpi-fpdt.c
         acpi-fpdt.h
@@ -104,25 +104,25 @@ shared_sources = '''
         watchdog.c
         watchdog.h
         wireguard-netlink.h
-'''.split()
+'''.split())
 
 test_tables_h = files('test-tables.h')
 shared_sources += [test_tables_h]
 
 if conf.get('HAVE_ACL') == 1
-        shared_sources += ['acl-util.c']
+        shared_sources += files('acl-util.c')
 endif
 
 if conf.get('ENABLE_UTMP') == 1
-        shared_sources += ['utmp-wtmp.c']
+        shared_sources += files('utmp-wtmp.c')
 endif
 
 if conf.get('HAVE_SECCOMP') == 1
-        shared_sources += ['seccomp-util.c']
+        shared_sources += files('seccomp-util.c')
 endif
 
 if conf.get('HAVE_LIBIPTC') == 1
-        shared_sources += ['firewall-util.c']
+        shared_sources += files('firewall-util.c')
 endif
 
 libshared_name = 'systemd-shared-@0@'.format(meson.project_version())
diff --git a/src/udev/meson.build b/src/udev/meson.build
index 613a2e24cd..8fa0627d00 100644
--- a/src/udev/meson.build
+++ b/src/udev/meson.build
@@ -121,16 +121,16 @@ static_libudev = get_option('static-libudev')
 static_libudev_pic = static_libudev == 'true' or static_libudev == 'pic'
 install_libudev_static = static_library(
         'udev',
+        basic_sources,
+        shared_sources,
+        libsystemd_sources,
         libudev_sources,
         include_directories : includes,
-        link_with : [libshared_static,
-                     libsystemd_static],
-        link_whole : libudev_basic,
         build_by_default : static_libudev != 'false',
         install : static_libudev != 'false',
         install_dir : rootlibdir,
         link_depends : libudev_sym,
-        dependencies : [threads],
+        dependencies : libshared_deps + [libmount],
         c_args : static_libudev_pic ? [] : ['-fno-PIC'],
         pic : static_libudev_pic)

@davide125
Copy link
Contributor Author

Thanks @keszybz! Using your POC I was able to get libsystemd and libudev to build correctly -- as far as I can tell the resulting libraries work fine now.

davide125 and others added 4 commits May 8, 2018 17:33
This is the same as test-lib{systemd,udev}-sym, but linked to the static
variants of those libraries.
…ibsystemd_static

This means that when those targets are built, all the sources are built again,
instead of reusing the work done to create libbasic.a and other convenience static
libraries. It would be nice to not do this, but there seems to be no support in
our toolchain for joining multiple static libraries into one. When linking
a static library, any -l arguments are simply ignored by ar/gcc-ar, and .a
libraries given as positional arguments are copied verbatim into the archive
so they objects in them cannot be accessed.

https://stackoverflow.com/questions/2157629/linking-static-libraries-to-other-static-libraries
suggests either unzipping all the archives and putting them back togather,
or using a linker script. Unzipping and zipping back together seems ugly.
The other option is not very nice. The linker script language does not
allow "+" to appear in the filenames, and filenames that meson generates
use that, so files would have to be renamed before a linker script was used.
And we would have to generate the linker script on the fly. Either way, this
doesn't seem attractive. Since those static libraries are a niche use case,
it seems reasonable to just go with the easiest and safest solution and
recompile all the source files. Thanks to ccache, this is probably almost as
cheap as actually reusing the convenience .a libraries.

test-libsystemd-sym.c and test-libudev-sym.c compile fine with the generated
static libs, so it seems that they indeed provide all the symbols they should.
@keszybz
Copy link
Member

keszybz commented May 8, 2018

I force-pushed to your branch with my POC patch, now with support for both static libs. PTAL.

@davide125
Copy link
Contributor Author

lgtm, we've been using a 238 backport of this for a few weeks now and it's working well, so it should be good to go. Thanks again for all your help with this!

@keszybz
Copy link
Member

keszybz commented May 9, 2018

OK, let's merge then. This shouldn't have any impact for anyone not using the static libs.

@keszybz keszybz merged commit f5ce2e7 into systemd:master May 9, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

Successfully merging this pull request may close these issues.

None yet

4 participants