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

postgresql: fix cross for FreeBSD #387727

Open
wants to merge 1 commit into
base: staging
Choose a base branch
from

Conversation

rhelmot
Copy link
Contributor

@rhelmot rhelmot commented Mar 6, 2025

  • Fix dependencies
  • Fix build of plugins in a cross context by providing pg_config_native copied from an equivalent native build and wrapped to provide the correct paths. The postgres source code indicates that there have been plans to do this upstream for a while, but that they are too complex to do "correctly".
  • Fix the --export-dynamic flag - it is applicable for all lld linkers, and it should be spelled differently.

Pull requests using pg_config_native forthcoming.

Things done

  • Built on platform(s)
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin
    • x86_64-freebsd (cross)
  • For non-Linux: Is sandboxing enabled in nix.conf? (See Nix manual)
    • sandbox = relaxed
    • sandbox = true
  • Tested, as applicable:
  • Tested compilation of all packages that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD". Note: all changes have to be committed, also see nixpkgs-review usage
  • Tested basic functionality of all binary files (usually in ./result/bin/)
  • 25.05 Release Notes (or backporting 24.11 and 25.05 Release notes)
    • (Package updates) Added a release notes entry if the change is major or breaking
    • (Module updates) Added a release notes entry if the change is significant
    • (Module addition) Added a release notes entry if adding a new NixOS module
  • Fits CONTRIBUTING.md.

Add a 👍 reaction to pull requests you find important.

@drupol
Copy link
Contributor

drupol commented Mar 6, 2025

It should target staging too.

@rhelmot
Copy link
Contributor Author

rhelmot commented Mar 6, 2025

Hm... darwin mass rebuild makes sense but I was really expecting rebuild-linux: 0.

Comment on lines +392 to +411
cp ${nativeBuild.dev}/bin/.pg_config-wrapped $dev/bin/.pg_config_native-wrapped
# wrapProgram will use the wrong shell. do it ourselves
cat >"$dev/bin/pg_config_native" <<EOF
#!${lib.getExe buildPackages.bash}
exec > >(sed ${
lib.concatMapStringsSep " " (
output: "-e s@${nativeBuild."${output}"}@${builtins.placeholder output}@g"
) finalAttrs.outputs
})
exec -a "${builtins.placeholder "out"}/bin/pg_config" "${builtins.placeholder "dev"}/bin/.pg_config_native-wrapped" "\$@"
EOF
chmod +x "$dev/bin/pg_config_native"
Copy link
Contributor

Choose a reason for hiding this comment

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

So the idea here is, that you're replacing the native paths with the cross paths, right?

But this is not the only thing that pg_config is returning. It is also returning CFLAGS etc. - and those could be different between native and cross builds, too.

This seems very fragile.

With all the pg_config hackery that we already do elsewhere... we should seriously consider to just write our own shell-script based pg_config implementation doing exactly the right things, which could be run for both native and cross cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree, it is potentially very fragile. I don't really know how to approach the "correct" solution though, and this does seem to work while at the same time being simple enough that its intent (I can document this inline) is pretty basic and can be easily understood as a hack to be replaced as soon as it gives anyone trouble.

Do you think it's a bad enough hack that it shouldn't be in nixpkgs in order to build what it can?

Copy link
Contributor

Choose a reason for hiding this comment

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

Do you think it's a bad enough hack that it shouldn't be in nixpkgs in order to build what it can?

It is indeed a very bad hack. I'd like to take a shot at rewriting pg_config as a script first, before judging whether this hack is too bad for right now or not.

I will give it a shot and come back with some results.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'd like to take a shot at rewriting pg_config as a script first, before judging whether this hack is too bad for right now or not.

Draft in #388807. I like this approach a lot more, it's much cleaner not only for cross, but also for all the other oddities that we have to fight upstream's pg_config on.

@rhelmot rhelmot force-pushed the freebsd-postgres branch from 01a4faa to b504050 Compare March 6, 2025 22:18
@rhelmot
Copy link
Contributor Author

rhelmot commented Mar 6, 2025

Fixed all the issues that don't require rewriting pg_config.

- Fix dependencies
- Fix build of plugins in a cross context by providing pg_config_native
  copied from an equivalent native build and wrapped to provide the
  correct paths. The postgres source code indicates that there have been
  plans to do this upstream for a while, but that they are too complex
  to do "correctly".
- Add the --export-dynamic flag - it is applicable for all non-macos
  cross configurations.
@rhelmot rhelmot force-pushed the freebsd-postgres branch from b504050 to db88aa1 Compare March 6, 2025 22:22
@github-actions github-actions bot added 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin 10.rebuild-linux: 0 This PR does not cause any packages to rebuild on Linux and removed 10.rebuild-darwin: 101-500 10.rebuild-linux: 101-500 labels Mar 6, 2025
@rhelmot rhelmot mentioned this pull request Mar 7, 2025
13 tasks
Comment on lines +133 to +134
nativeBuild =
buildPackages."postgresql_${lib.versions.major version}${lib.optionalString jitSupport "_jit"}";
Copy link
Contributor

Choose a reason for hiding this comment

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

This approach will probably also break with overrides, right?

So when I do pkgsCross.[freebsd...].postgresql.override { [... toggle some flags ...] }, then the native pg_config will still be taken from a derivation without those flags toggled.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed. This approach is predicated on the assumption that the paths are the only thing that are essential. So far this is correct, but critically, I have been only compiling pgrx (rust) plugins.

Comment on lines +285 to +292
# some version of this flag is required in all cross configurations
# since it cannot be automatically detected
++
lib.optionals
((!stdenv'.hostPlatform.isDarwin) && (!(stdenv'.buildPlatform.canExecute stdenv.hostPlatform)))
[
"LDFLAGS_EX_BE=-Wl,--export-dynamic"
]
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be good to understand why this flag can't be detected automatically. Is it inherent to cross-compilation? Or is just a limitation of the build system?

In general, PostgreSQL's autoconf-based build system is not built for cross-compilation at all. But since v16, they also have a meson based build system. I have a branch locally which builds with meson for 16+. Didn't need that in the end, when enabling the build for pkgsStatic... but maybe it would be time to pick this up again?

Cross-compiling should work much better with meson.

Would support for v16+ be enough for your use-case? I assume that there is no need to build older versions for FreeBSD, because nobody is running those with nix there anyway, yet?

Copy link
Contributor

Choose a reason for hiding this comment

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

It would be good to understand why this flag can't be detected automatically. Is it inherent to cross-compilation? Or is just a limitation of the build system?

I tried building... and I don't need to add this flag explicitly at all. The build succeeds. So either the flag is not required - or it needs more explanation (run-time? build time for dependencies?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wow! I saw this question come in on my phone and I immediately got out of bed in order to type up the actual answer.

The answer is related to plugins, which are shared objects loaded by the postgres binary. These objects try to link against symbols from the postgres binary, which fails without this flag because the linker garbage collection settings are so intense that they completely delete some functions that are only required by these plugins. The flag places the symbols in the dynamic symbol table .dynsym (as opposed to the normal one .symtab), which is a) sufficient to not garbage collect them and b) required (haven't empirically verified this) for dynamic linking against them to work. So I believe the actual test for "is this build good" is nm postgres | grep '\<lowerstr\>', for example.

With respect to the first question in the thread, the postgres m4 files claim that it is inherent to cross compilation. See c-compiler.m4 and configure.ac have some entirely unhelpful comments on the matter and I don't have access to a platform where this flag is unsupported so I'm not super sure how to verify that claim.

Copy link
Contributor

Choose a reason for hiding this comment

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

The answer is related to plugins, which are shared objects loaded by the postgres binary. These objects try to link against symbols from the postgres binary, which fails without this flag because the linker garbage collection settings are so intense that they completely delete some functions that are only required by these plugins. The flag places the symbols in the dynamic symbol table .dynsym (as opposed to the normal one .symtab), which is a) sufficient to not garbage collect them and b) required (haven't empirically verified this) for dynamic linking against them to work.

Yes, I understand and agree with all of that. That's what I worked out, too, when I fixed the darwin case back then.

So I believe the actual test for "is this build good" is nm postgres | grep '<lowerstr>', for example.

Ah, right. So for the darwin case it was a native build, so it was running the test-suite - which then failed at run-time. But that was still "build time" in terms of "building the nix derivation". Now, with cross, the tests are ofc not run, so I'll not hit that.

Native linux:

 % nm /nix/store/m3951xl7r08y7ja0x79b8hy4gkbw07hd-postgresql-17.4/bin/postgres | grep lowerstr
00000000004f3320 T lowerstr
00000000004f31c0 T lowerstr_with_len
0000000000153d4b t lowerstr_with_len.cold

cross freebsd:

% nm /nix/store/y3c1cdannia6wy0ym4k5jg1v9xc06wzb-postgresql-x86_64-unknown-freebsd-13.20/bin/postgres | grep lowerstr
0000000000916950 T lowerstr
0000000000916970 T lowerstr_with_len

Not sure what that means. Probably need another symbol to test against.

With respect to the first question in the thread, the postgres m4 files claim that it is inherent to cross compilation. See c-compiler.m4 and configure.ac have some entirely unhelpful comments on the matter and I don't have access to a platform where this flag is unsupported so I'm not super sure how to verify that claim.

Ah, thanks for digging this out, specifically this part:

In fact, we must actually check that the resulting program runs :-(

I see. So that's essentially exactly the thing that I mentioned about the autoconf-based build system. It depends on this configure-time "build and run" check. The meson build system, does not. It depends on knowledge about the different linkers, see https://github.com/mesonbuild/meson/blob/master/mesonbuild/linkers/linkers.py and grep for export-dynamic.

So I think with meson, this should not be a problem and the flag should be added regardless.

Copy link
Contributor

Choose a reason for hiding this comment

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

So I think with meson, this should not be a problem and the flag should be added regardless.

Once I'm done with the pg_config stuff, I will dig out my old meson branch again and rebase that. Then we can have a look at whether that would make cross-compiling easier to do.

Comment on lines +392 to +411
cp ${nativeBuild.dev}/bin/.pg_config-wrapped $dev/bin/.pg_config_native-wrapped
# wrapProgram will use the wrong shell. do it ourselves
cat >"$dev/bin/pg_config_native" <<EOF
#!${lib.getExe buildPackages.bash}
exec > >(sed ${
lib.concatMapStringsSep " " (
output: "-e s@${nativeBuild."${output}"}@${builtins.placeholder output}@g"
) finalAttrs.outputs
})
exec -a "${builtins.placeholder "out"}/bin/pg_config" "${builtins.placeholder "dev"}/bin/.pg_config_native-wrapped" "\$@"
EOF
chmod +x "$dev/bin/pg_config_native"
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you think it's a bad enough hack that it shouldn't be in nixpkgs in order to build what it can?

It is indeed a very bad hack. I'd like to take a shot at rewriting pg_config as a script first, before judging whether this hack is too bad for right now or not.

I will give it a shot and come back with some results.

@wolfgangwalther
Copy link
Contributor

Fixed all the issues that don't require rewriting pg_config.

One other things that comes to mind: The separate libpq package probably deals with the same problem. Did you try building and using in downstream dependencies, yet?

Ofc, if those dependencies behave well, they will use pkg-config and don't need pg_config. But not all do...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
6.topic: cross-compilation Building packages on a different platform than they will be used on 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin 10.rebuild-linux: 0 This PR does not cause any packages to rebuild on Linux
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants