Skip to content

Commit e88af44

Browse files
committed
fix: format linker plugin messages via C shim and prints full log messages
1 parent 4602cd0 commit e88af44

6 files changed

Lines changed: 83 additions & 19 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

libwild/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ zstd = { workspace = true }
5656
[target.'cfg(all(target_os = "linux", any(target_arch = "x86_64", target_arch = "aarch64")))'.dependencies]
5757
perf-event = { workspace = true }
5858

59+
[build-dependencies]
60+
cc = "1.1.12"
61+
5962
[dev-dependencies]
6063
ar = { workspace = true }
6164
tempfile = { workspace = true }

libwild/build.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
fn main() {
2+
if std::env::var("CARGO_FEATURE_PLUGINS").is_ok() {
3+
cc::Build::new()
4+
.file("src/plugin_message_shim.c")
5+
.compile("plugin_message_shim");
6+
}
7+
}

libwild/src/linker_plugins.rs

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,12 @@ impl LoadedPlugin {
361361
// Linker plugins handle entries of this vector serially, which means the message callback
362362
// should be registered first. Otherwise, they won't be able to indicate the problem with
363363
// entries preceding the callback and, for example, silently skip invalid arguments.
364-
transfer_vector.push(LdPluginTv::fn_ptr2(Tag::Message, message));
364+
// The message callback is variadic (printf-style), so we register the C trampoline
365+
// directly as a raw pointer value rather than going through fn_ptr2.
366+
transfer_vector.push(LdPluginTv {
367+
tag: Tag::Message as u32,
368+
value: wild_plugin_message_callback as *const () as usize,
369+
});
365370

366371
for arg in &args.plugin_args {
367372
transfer_vector.push(LdPluginTv::c_str(Tag::Option, arg));
@@ -992,26 +997,28 @@ extern "C" fn add_input_library(lib_name: *const libc::c_char) -> Status {
992997
Status::Ok
993998
}
994999

995-
/// This function is called when the plugin wants to emit a message. It's supposed to accept varargs
996-
/// similar to printf. Unfortunately that's not exactly easy for us to do, so we just report the
997-
/// format string.
998-
extern "C" fn message(level: libc::c_int, format: *const libc::c_char) -> Status {
999-
catch_panics(|| {
1000-
let Some(level) = MessageLevel::from_raw(level) else {
1001-
return Status::Err;
1002-
};
1000+
unsafe extern "C" {
1001+
/// C trampoline that accepts the plugin's printf-style varargs, formats them via vsnprintf,
1002+
/// then calls `wild_handle_plugin_message` with the resulting string. Defined in
1003+
/// `plugin_message_shim.c`.
1004+
fn wild_plugin_message_callback(level: libc::c_int, fmt: *const libc::c_char, ...);
1005+
}
1006+
1007+
/// Called by the C shim `wild_plugin_message_callback` with the already-formatted message string.
1008+
/// The `no_mangle` is required so the C shim can link against it by name.
1009+
#[unsafe(no_mangle)]
1010+
extern "C" fn wild_handle_plugin_message(level: libc::c_int, message: *const libc::c_char) {
1011+
let Some(level) = MessageLevel::from_raw(level) else {
1012+
return;
1013+
};
10031014

1004-
let format = unsafe { CStr::from_ptr(format) };
1015+
let text = unsafe { CStr::from_ptr(message) }.to_string_lossy();
10051016

1006-
if level == MessageLevel::Error || level == MessageLevel::Fatal {
1007-
println!("Linker plugin {level}: {}", format.to_string_lossy());
1008-
ERROR_MESSAGE.replace(Some(format.to_string_lossy().to_string()));
1009-
} else {
1010-
println!("Linker plugin {level}: {}", format.to_string_lossy());
1011-
}
1017+
println!("Linker plugin {level}: {text}");
10121018

1013-
Status::Ok
1014-
})
1019+
if level == MessageLevel::Error || level == MessageLevel::Fatal {
1020+
ERROR_MESSAGE.replace(Some(text.into_owned()));
1021+
}
10151022
}
10161023

10171024
/// Runs `body`, catching any panics. In the case of a panic, the return status is changed to an

libwild/src/plugin_message_shim.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Trampoline for the linker plugin message callback.
3+
*
4+
* The Gold plugin API defines the message callback as:
5+
* ld_plugin_status (*ld_plugin_message)(int level, const char *format, ...)
6+
*
7+
* Rust cannot implement C variadic functions on stable, so we implement the
8+
* callback here in C. It formats the printf-style message and forwards the
9+
* result to wild_handle_plugin_message (defined in linker_plugins.rs).
10+
*/
11+
12+
#include <stdarg.h>
13+
#include <stdio.h>
14+
#include <stdlib.h>
15+
16+
/* Rust function we call with the fully-formatted message string. */
17+
extern void wild_handle_plugin_message(int level, const char *message);
18+
19+
void wild_plugin_message_callback(int level, const char *fmt, ...) {
20+
va_list args1, args2;
21+
va_start(args1, fmt);
22+
va_copy(args2, args1);
23+
24+
int len = vsnprintf(NULL, 0, fmt, args1);
25+
va_end(args1);
26+
27+
if (len < 0) {
28+
va_end(args2);
29+
/* Fall back to the raw format string if sizing failed. */
30+
wild_handle_plugin_message(level, fmt);
31+
return;
32+
}
33+
34+
char *buf = malloc((size_t)len + 1);
35+
if (buf == NULL) {
36+
va_end(args2);
37+
wild_handle_plugin_message(level, fmt);
38+
return;
39+
}
40+
41+
vsnprintf(buf, (size_t)len + 1, fmt, args2);
42+
va_end(args2);
43+
44+
wild_handle_plugin_message(level, buf);
45+
free(buf);
46+
}

wild/tests/sources/elf/linker-plugin-lto/linker-plugin-lto.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@
8282
//#SkipLinker:ld
8383
//#LinkArgs:-Wl,-znow -flto -nostdlib -Wl,-plugin-opt=jobs=foo
8484
//#Archive:empty.c:-flto
85-
//#ExpectError:(Error from linker plugin: Invalid parallelism level: \%s|Wild was compiled without linker-plugin support)
85+
//#ExpectError:(Error from linker plugin: Invalid parallelism level: (foo|\%s)|Wild was compiled without linker-plugin support)
8686

8787
#include "runtime.h"
8888

0 commit comments

Comments
 (0)