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

Update cargo dependencies, fix key engine LTO #499

Merged
merged 4 commits into from
Jan 19, 2023
Merged

Conversation

gordonwang0
Copy link
Contributor

@gordonwang0 gordonwang0 commented Jan 17, 2023

LTO is enabled by default for .deb packages starting in Ubuntu
21.041, causing build failures on Ubuntu 22.04. Previously, we
hacked around this by compiling outside of the dpkg-buildpackage
environment2. However, this hack stopped working when the cc crate
began emitting rerun-if-env-changed in version 1.0.743. Hence,
would need to set emit_rerun_if_env_changed(false) for all cc::Build
instances in aziot-key-openssl-engine-shared and its dependency graph
to restore our hack for cc > 1.0.73. This is not remotely practical.

LTO is incompatible with __asm__(".symver ..") because it uses the
linker-script-provided exported symbols to determine what is safe to
optimize out, and the linker script is naturally unaware of
manually-exported symbols. We can work around this by adding
__attribute__((used)) to the functions we want to keep in the final
object.

Another option would be to use GCC's
__attribute__((symver("..@")))4 directive, but this relies on too
new of a toolchain version (GCC 10).

Addendum 1: The fundamental reason for why LTO is a problem for
aziot-key-openssl-engine-shared in the first place is that this crate
uses what is, in effect, a "symbol stub" to hook Rust code into
OpenSSL's engine macros. First, build/engine.c declares the engine
function signature and uses OpenSSL's macros to expand the dynamic
engine binding. This file is then compiled (but not linked) into an
object that will become the dynamic library's public interface. The way
this is accomplished is by linking the whole object into the cdylib
(as opposed to only linking referenced functions). LTO requires us to
go one step further by preventing the linker from optimizing out symbols
not declared as globally-exported in rustc's linker script, which does
not know of the symbol declaration in the stub object. There is an open
RFC request for allowing re-export of C symbols from cdylib crates:
rust-lang/rfcs#2771.

Addendum 2: When our supported platforms start shipping with GNU as >=
2.35 and/or Clang 13, we may want to add ,remove to the .symver
directive arguments to lift the restriction that
aziot-key-openssl-engine-shared cannot be included in tests5.

Footnotes

  1. https://wiki.ubuntu.com/ToolChain/LTO

  2. https://github.com/Azure/iot-identity-service/commit/f66c155113cce5089d398376d16b206f7973d6f6

  3. https://github.com/rust-lang/cc-rs/releases/tag/1.0.74

  4. ..@ instead of ..@@ since we do not define a version
    node name, meaning ..@@ leads to symbol duplication.

  5. https://maskray.me/blog/2020-11-26-all-about-symbol-versioning

@onalante-msft onalante-msft force-pushed the main branch 5 times, most recently from 4aba0a6 to 589091a Compare January 19, 2023 21:01
LTO is enabled by default for .deb packages starting in Ubuntu
21.04[^1], causing build failures on Ubuntu 22.04.  Previously, we
hacked around this by compiling outside of the `dpkg-buildpackage`
environment[^2].  However, this hack stopped working when the `cc` crate
began emitting `rerun-if-env-changed` in version `1.0.74`[^3].  Hence,
would need to set `emit_rerun_if_env_changed(false)` for all `cc::Build`
instances in `aziot-key-openssl-engine-shared` and its dependency graph
to restore our hack for `cc > 1.0.73`.  This is not remotely practical.

LTO is incompatible with `__asm__(".symver ..")` because it uses the
linker-script-provided exported symbols to determine what is safe to
optimize out, and the linker script is naturally unaware of
manually-exported symbols.  We can work around this by adding
`__attribute__((used))` to the functions we want to keep in the final
object.

Another option would be to use GCC's
`__attribute__((symver("..@")))`[^4] directive, but this relies on too
new of a toolchain version (GCC 10).

Addendum 1: The fundamental reason for why LTO is a problem for
`aziot-key-openssl-engine-shared` in the first place is that this crate
uses what is, in effect, a "symbol stub" to hook Rust code into
OpenSSL's engine macros.  First, `build/engine.c` declares the engine
function signature and uses OpenSSL's macros to expand the dynamic
engine binding.  This file is then compiled (but not linked) into an
object that will become the dynamic library's public interface.  The way
this is accomplished is by linking the whole object into the `cdylib`
(as opposed to only linking referenced functions).  LTO requires us to
go one step further by preventing the linker from optimizing out symbols
not declared as globally-exported in `rustc`'s linker script, which does
not know of the symbol declaration in the stub object.  There is an open
RFC request for allowing re-export of C symbols from `cdylib` crates:
rust-lang/rfcs#2771.

Addendum 2: When our supported platforms start shipping with GNU as >=
2.35 and/or Clang 13, we may want to add `,remove` to the `.symver`
directive arguments to lift the restriction that
`aziot-key-openssl-engine-shared` cannot be included in tests[^5].

[^1]: https://wiki.ubuntu.com/ToolChain/LTO
[^2]: Azure@f66c155
[^3]: https://github.com/rust-lang/cc-rs/releases/tag/1.0.74
[^4]: `..@` instead of `..@@` since we do not define a version
    node name, meaning `..@@` leads to symbol duplication.
[^5]: https://maskray.me/blog/2020-11-26-all-about-symbol-versioning
@onalante-msft onalante-msft changed the title Update cargo dependencies Update cargo dependencies, fix key engine LTO Jan 19, 2023
@kodiakhq kodiakhq bot merged commit 0c244f9 into Azure:main Jan 19, 2023
damonbarry pushed a commit to damonbarry/iot-identity-service that referenced this pull request Feb 9, 2023
LTO is enabled by default for .deb packages starting in Ubuntu
21.04[^1], causing build failures on Ubuntu 22.04.  Previously, we
hacked around this by compiling outside of the `dpkg-buildpackage`
environment[^2].  However, this hack stopped working when the `cc` crate
began emitting `rerun-if-env-changed` in version `1.0.74`[^3].  Hence,
would need to set `emit_rerun_if_env_changed(false)` for all `cc::Build`
instances in `aziot-key-openssl-engine-shared` and its dependency graph
to restore our hack for `cc > 1.0.73`.  This is not remotely practical.

LTO is incompatible with `__asm__(".symver ..")` because it uses the
linker-script-provided exported symbols to determine what is safe to
optimize out, and the linker script is naturally unaware of
manually-exported symbols.  We can work around this by adding
`__attribute__((used))` to the functions we want to keep in the final
object.

Another option would be to use GCC's
`__attribute__((symver("..@")))`[^4] directive, but this relies on too
new of a toolchain version (GCC 10).

Addendum 1: The fundamental reason for why LTO is a problem for
`aziot-key-openssl-engine-shared` in the first place is that this crate
uses what is, in effect, a "symbol stub" to hook Rust code into
OpenSSL's engine macros.  First, `build/engine.c` declares the engine
function signature and uses OpenSSL's macros to expand the dynamic
engine binding.  This file is then compiled (but not linked) into an
object that will become the dynamic library's public interface.  The way
this is accomplished is by linking the whole object into the `cdylib`
(as opposed to only linking referenced functions).  LTO requires us to
go one step further by preventing the linker from optimizing out symbols
not declared as globally-exported in `rustc`'s linker script, which does
not know of the symbol declaration in the stub object.  There is an open
RFC request for allowing re-export of C symbols from `cdylib` crates:
rust-lang/rfcs#2771.

Addendum 2: When our supported platforms start shipping with GNU as >=
2.35 and/or Clang 13, we may want to add `,remove` to the `.symver`
directive arguments to lift the restriction that
`aziot-key-openssl-engine-shared` cannot be included in tests[^5].

[^1]: https://wiki.ubuntu.com/ToolChain/LTO
[^2]: Azure@f66c155
[^3]: https://github.com/rust-lang/cc-rs/releases/tag/1.0.74
[^4]: `..@` instead of `..@@` since we do not define a version
    node name, meaning `..@@` leads to symbol duplication.
[^5]: https://maskray.me/blog/2020-11-26-all-about-symbol-versioning
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants