Only link res_init() on GNU/*nix
To workaround a bug in glibc <= 2.26 lookup_host() calls res_init() based on the glibc version detected at runtime. While this avoids calling res_init() on platforms where it's not required we will still end up linking against the symbol. This causes an issue on macOS where res_init() is implemented in a separate library (libresolv.9.dylib) from the main libc. While this is harmless for standalone programs it becomes a problem if Rust code is statically linked against another program. If the linked program doesn't already specify -lresolv it will cause the link to fail. This is captured in issue #46797 Fix this by hooking in to the glibc workaround in `cvt_gai` and only activating it for the "gnu" environment on Unix This should include all glibc platforms while excluding musl, windows-gnu, macOS, FreeBSD, etc. This has the side benefit of removing the #[cfg] in sys_common; only unix.rs has code related to the workaround now.
This commit is contained in:
parent
8ff449d505
commit
090a968fe7
3 changed files with 16 additions and 32 deletions
|
@ -51,6 +51,10 @@ pub fn cvt_gai(err: c_int) -> io::Result<()> {
|
|||
if err == 0 {
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
// We may need to trigger a glibc workaround. See on_resolver_failure() for details.
|
||||
on_resolver_failure();
|
||||
|
||||
if err == EAI_SYSTEM {
|
||||
return Err(io::Error::last_os_error())
|
||||
}
|
||||
|
@ -377,21 +381,22 @@ impl IntoInner<c_int> for Socket {
|
|||
// res_init unconditionally, we call it only when we detect we're linking
|
||||
// against glibc version < 2.26. (That is, when we both know its needed and
|
||||
// believe it's thread-safe).
|
||||
pub fn res_init_if_glibc_before_2_26() -> io::Result<()> {
|
||||
#[cfg(target_env = "gnu")]
|
||||
fn on_resolver_failure() {
|
||||
// If the version fails to parse, we treat it the same as "not glibc".
|
||||
if let Some(Ok(version_str)) = glibc_version_cstr().map(CStr::to_str) {
|
||||
if let Some(version) = parse_glibc_version(version_str) {
|
||||
if version < (2, 26) {
|
||||
let ret = unsafe { libc::res_init() };
|
||||
if ret != 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
unsafe { libc::res_init() };
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(target_env = "gnu"))]
|
||||
fn on_resolver_failure() {}
|
||||
|
||||
#[cfg(target_env = "gnu")]
|
||||
fn glibc_version_cstr() -> Option<&'static CStr> {
|
||||
weak! {
|
||||
fn gnu_get_libc_version() -> *const libc::c_char
|
||||
|
@ -405,6 +410,7 @@ fn glibc_version_cstr() -> Option<&'static CStr> {
|
|||
|
||||
// Returns Some((major, minor)) if the string is a valid "x.y" version,
|
||||
// ignoring any extra dot-separated parts. Otherwise return None.
|
||||
#[cfg(target_env = "gnu")]
|
||||
fn parse_glibc_version(version: &str) -> Option<(usize, usize)> {
|
||||
let mut parsed_ints = version.split(".").map(str::parse::<usize>).fuse();
|
||||
match (parsed_ints.next(), parsed_ints.next()) {
|
||||
|
@ -413,7 +419,7 @@ fn parse_glibc_version(version: &str) -> Option<(usize, usize)> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(all(test, taget_env = "gnu"))]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue