diff --git a/c2rust-transpile/src/translator/variadic.rs b/c2rust-transpile/src/translator/variadic.rs index 9a5700bf8e..3e8bb09008 100644 --- a/c2rust-transpile/src/translator/variadic.rs +++ b/c2rust-transpile/src/translator/variadic.rs @@ -58,6 +58,23 @@ impl<'c> Translation<'c> { Some(va_id) } + // struct-based va_list (e.g. x86_64) where va_list is accessed as a member of a struct pointer + // supporting this pattern is necessary to transpile [graphviz](https://gitlab.com/graphviz/graphviz/-/blob/5.0.0/lib/sfio/sftable.c#L321) + fn match_vastart_struct_pointer_member( + ast_context: &TypedAstContext, + expr: CExprId, + ) -> Option { + match_or! { [ast_context[expr].kind] + CExprKind::ImplicitCast(_, me, _, _, _) => me } + match_or! { [ast_context[me].kind] + CExprKind::Member(_, ie, _, _, _) => ie } + match_or! { [ast_context[ie].kind] + CExprKind::ImplicitCast(_, e, _, _, _) => e } + match_or! { [ast_context[e].kind] + CExprKind::DeclRef(_, va_id, _) => va_id } + Some(va_id) + } + // char pointer-based va_list (e.g. x86) fn match_vastart_pointer(ast_context: &TypedAstContext, expr: CExprId) -> Option { match_or! { [ast_context[expr].kind] @@ -68,6 +85,7 @@ impl<'c> Translation<'c> { match_vastart_struct(&self.ast_context, expr) .or_else(|| match_vastart_pointer(&self.ast_context, expr)) .or_else(|| match_vastart_struct_member(&self.ast_context, expr)) + .or_else(|| match_vastart_struct_pointer_member(&self.ast_context, expr)) } pub fn match_vaend(&self, expr: CExprId) -> Option { diff --git a/tests/items/src/test_varargs.rs b/tests/items/src/test_varargs.rs index fb697dfcbb..5ccb21db49 100644 --- a/tests/items/src/test_varargs.rs +++ b/tests/items/src/test_varargs.rs @@ -2,7 +2,7 @@ use crate::varargs::{ rust_call_printf, rust_call_vprintf, rust_my_printf, rust_restart_valist, rust_sample_stddev, - rust_simple_vacopy, rust_valist_struct_member, + rust_simple_vacopy, rust_valist_struct_member, rust_valist_struct_pointer_member, }; use libc::c_char; @@ -20,6 +20,8 @@ extern "C" { fn valist_struct_member(_: *const c_char, ...); + fn valist_struct_pointer_member(_: *const c_char, ...); + fn restart_valist(_: *const c_char, ...); fn sample_stddev(count: i32, ...) -> f64; @@ -69,6 +71,14 @@ pub fn test_valist_struct_member() { } } +pub fn test_valist_struct_pointer_member() { + let fmt_str = CString::new("%d, %f\n").unwrap(); + unsafe { + valist_struct_pointer_member(fmt_str.as_ptr(), 10, 1.5); + rust_valist_struct_pointer_member(fmt_str.as_ptr(), 10, 1.5); + } +} + pub fn test_restart_valist() { let fmt_str = CString::new("%d, %f\n").unwrap(); unsafe { diff --git a/tests/items/src/varargs.c b/tests/items/src/varargs.c index 1546ec7543..3dc3745325 100644 --- a/tests/items/src/varargs.c +++ b/tests/items/src/varargs.c @@ -73,11 +73,27 @@ struct vastruct { // pattern first seen in apache (util_script.c) void valist_struct_member(const char *fmt, ...) { - struct vastruct thestruct; + struct vastruct a, b; + + va_start(a.args, fmt); + va_copy(b.args, a.args); + vprintf(fmt, a.args); + vprintf(fmt, b.args); + va_end(a.args); + va_end(b.args); +} - va_start(thestruct.args, fmt); - vprintf(fmt, thestruct.args); - va_end(thestruct.args); +// pattern first seen in graphviz (sftable.c) +void valist_struct_pointer_member(const char *fmt, ...) { + struct vastruct a, b; + struct vastruct *p = &a, *q = &b; + + va_start(p->args, fmt); + va_copy(q->args, p->args); + vprintf(fmt, p->args); + vprintf(fmt, q->args); + va_end(p->args); + va_end(q->args); } // mirrors pattern from json-c's sprintbuf