diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 5f4ce08e..9860648c 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1553,6 +1553,11 @@ bool Converter::VisitCallExpr(clang::CallExpr *expr) { } if (Mapper::Contains(expr->getCallee())) { + if (Mapper::IsLibcPassthrough(GetCalleeOrExpr(expr))) { + ConvertGenericCallExpr(expr); + return false; + } + auto **args = expr->getArgs(); auto num_args = expr->getNumArgs(); auto ctx = CollectRefBindingTempArgs(expr); @@ -1627,20 +1632,20 @@ Converter::CallInfo Converter::CollectCallInfo(clang::CallExpr *expr) { using Kind = CallArg::Kind; CallInfo info; - info.callee = expr->getCallee(); + info.expr = expr; + auto callee = GetCallee(expr); unsigned arg_begin = 0; if (auto op_call = llvm::dyn_cast(expr)) { if (op_call->getOperator() == clang::OO_Call) { - info.callee = op_call->getArg(0); arg_begin = 1; } } - const auto *function = - expr->getCalleeDecl() ? expr->getCalleeDecl()->getAsFunction() : nullptr; + auto decl = expr->getCalleeDecl(); + const auto *function = decl ? decl->getAsFunction() : nullptr; const clang::FunctionProtoType *proto = nullptr; if (!function) { - auto callee_ty = info.callee->getType().getDesugaredType(ctx_); + auto callee_ty = callee->getType().getDesugaredType(ctx_); if (auto ptr_ty = callee_ty->getAs()) { proto = ptr_ty->getPointeeType()->getAs(); } @@ -1653,6 +1658,7 @@ Converter::CallInfo Converter::CollectCallInfo(clang::CallExpr *expr) { function ? function->getNumParams() : proto->getNumParams(); info.is_variadic = function ? function->isVariadic() : proto->isVariadic(); info.is_fn_ptr_call = !function; + info.is_libc_passthrough = Mapper::IsLibcPassthrough(GetCalleeOrExpr(expr)); for (unsigned i = 0; i < num_named_params && i < num_args; ++i) { auto *arg = expr->getArg(i + arg_begin); @@ -1664,7 +1670,8 @@ Converter::CallInfo Converter::CollectCallInfo(clang::CallExpr *expr) { : proto->getParamType(i), .expr = arg, .has_default = function && function->getParamDecl(i)->hasDefaultArg(), - .kind = IsLiteral(arg) ? Kind::Inline : Kind::Hoisted, + .kind = (IsLiteral(arg) || info.is_libc_passthrough) ? Kind::Inline + : Kind::Hoisted, }; bool is_materialize = clang::isa(arg); if (is_materialize && ca.param_type->isLValueReferenceType()) { @@ -1720,7 +1727,9 @@ void Converter::EmitArgList(const CallInfo &info) { using Kind = CallArg::Kind; PushParen call_args(*this); - for (const auto &ca : info.args) { + for (unsigned i = 0; i < info.args.size(); i++) { + const auto &ca = info.args[i]; + if (ca.has_default) { StrCat("Some"); } @@ -1736,6 +1745,10 @@ void Converter::EmitArgList(const CallInfo &info) { break; case Kind::Inline: ConvertParamTy(ca.param_type, ca.expr); + if (info.is_libc_passthrough) { + StrCat(std::format( + "as {}", Mapper::GetParamType(GetCalleeOrExpr(info.expr), i))); + } break; } } @@ -1744,14 +1757,19 @@ void Converter::EmitArgList(const CallInfo &info) { } if (info.is_variadic) { - StrCat(token::kRef); - PushBracket push(*this); + if (!info.is_libc_passthrough) { + StrCat(token::kRef); + } + PushBracket push(*this, !info.is_libc_passthrough); for (auto *arg : info.variadic_args) { { PushParen p(*this); ConvertVariadicArg(arg); } - StrCat(".into()", token::kComma); + if (!info.is_libc_passthrough) { + StrCat(".into()"); + } + StrCat(token::kComma); } } } @@ -1760,10 +1778,14 @@ void Converter::EmitCall(CallInfo &&info) { EmitHoistedArgs(info); if (info.is_fn_ptr_call) { - EmitFnPtrCall(info.callee); + EmitFnPtrCall(GetCallee(info.expr)); + } else if (info.is_libc_passthrough) { + auto *direct_callee = info.expr->getDirectCallee(); + assert(direct_callee); + StrCat("libc::", direct_callee->getName()); } else { PushExprKind push(*this, ExprKind::Callee); - Convert(info.callee); + Convert(GetCallee(info.expr)); } EmitArgList(info); @@ -4193,6 +4215,8 @@ std::string Converter::ConvertIRFragment( .is_index_base = ph->is_index_base, }; result += ConvertPlaceholder(expr, arg, ph_ctx); + } else if (std::get_if(&frag)) { + result += ConvertVariadicTail(expr, all_args); } else if (auto *mc = std::get_if>(&frag)) { result += ConvertMappedMethodCall(expr, **mc, args, num_args, ctx); @@ -4202,6 +4226,25 @@ std::string Converter::ConvertIRFragment( return result; } +std::string +Converter::ConvertVariadicTail(clang::Expr *expr, + const std::vector &all_args) { + const auto *tgt_ir = Mapper::GetExprRule(GetCalleeOrExpr(expr)); + unsigned fixed = tgt_ir ? tgt_ir->params.size() : 0; + + Buffer buf(*this); + StrCat("&["); + for (unsigned i = fixed; i < all_args.size(); ++i) { + { + PushParen p(*this); + ConvertVariadicArg(all_args[i]); + } + StrCat(".into()", token::kComma); + } + StrCat("]"); + return std::move(buf).str(); +} + std::string Converter::AccessLValueObject(clang::MemberExpr *member) { auto *object = member->getBase(); auto type = object->getType(); diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 63de20e4..5a8ebaf8 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -245,9 +245,10 @@ class Converter : public clang::RecursiveASTVisitor { struct CallInfo { std::vector args; std::vector variadic_args; - clang::Expr *callee; + clang::CallExpr *expr; bool is_variadic; bool is_fn_ptr_call; + bool is_libc_passthrough; }; CallInfo CollectCallInfo(clang::CallExpr *expr); @@ -573,6 +574,9 @@ class Converter : public clang::RecursiveASTVisitor { std::string ConvertPlaceholder(clang::Expr *expr, clang::Expr *arg, const PlaceholderCtx &ph_ctx); + std::string ConvertVariadicTail(clang::Expr *expr, + const std::vector &all_args); + virtual std::string ConvertMappedMethodCall( clang::Expr *expr, const TranslationRule::MethodCallFragment &mc, clang::Expr **args, unsigned num_args, TempMaterializationCtx *ctx); diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index 12c0bf69..018e647c 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -657,6 +657,15 @@ BuildUnifiedArgs(clang::Expr *expr, clang::Expr **args, unsigned num_args) { return all_args; } +clang::Expr *GetCallee(clang::CallExpr *expr) { + if (auto op_call = clang::dyn_cast(expr)) { + if (op_call->getOperator() == clang::OO_Call) { + return op_call->getArg(0); + } + } + return expr->getCallee(); +} + clang::Expr *GetCalleeOrExpr(clang::Expr *expr) { if (auto *call = clang::dyn_cast(expr)) { return call->getCallee(); diff --git a/cpp2rust/converter/converter_lib.h b/cpp2rust/converter/converter_lib.h index a09e0e5d..8e0ba4e6 100644 --- a/cpp2rust/converter/converter_lib.h +++ b/cpp2rust/converter/converter_lib.h @@ -139,6 +139,8 @@ void ForEachTemplateArgument( clang::Expr *GetCallObject(clang::CallExpr *expr); +clang::Expr *GetCallee(clang::CallExpr *expr); + clang::Expr *GetCalleeOrExpr(clang::Expr *expr); bool HasReceiver(clang::Expr *expr); diff --git a/cpp2rust/converter/mapper.cpp b/cpp2rust/converter/mapper.cpp index cc39e781..7087ed33 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -641,6 +641,19 @@ const TranslationRule::ExprRule *GetExprRule(const clang::Expr *expr) { return search(expr); } +bool IsLibcPassthrough(const clang::Expr *expr) { + const auto *tgt_ir = GetExprRule(expr); + if (tgt_ir == nullptr || !tgt_ir->body.empty() || !tgt_ir->is_extern) { + return false; + } + const auto *ref = + clang::dyn_cast(expr->IgnoreParenImpCasts()); + const auto *decl = ref != nullptr ? ref->getDecl() : nullptr; + return decl != nullptr && + decl->getASTContext().getSourceManager().isInSystemHeader( + decl->getLocation()); +} + std::string MapFunctionName(const clang::FunctionDecl *decl) { assert(decl); if (!IsUserDefinedDecl(decl) && @@ -920,6 +933,12 @@ std::string ToString(const clang::NamedDecl *decl) { } os << ToString(func_decl->getParamDecl(i)->getType()); } + if (func_decl->isVariadic()) { + if (func_decl->getNumParams()) { + os << ", "; + } + os << "..."; + } os << ')'; if (const auto *method_decl = diff --git a/cpp2rust/converter/mapper.h b/cpp2rust/converter/mapper.h index b49ba2a9..92f6f7f6 100644 --- a/cpp2rust/converter/mapper.h +++ b/cpp2rust/converter/mapper.h @@ -30,6 +30,7 @@ bool Contains(const clang::Expr *expr); std::string Map(clang::QualType qual_type); std::string MapInitializer(clang::QualType qual_type); const TranslationRule::ExprRule *GetExprRule(const clang::Expr *expr); +bool IsLibcPassthrough(const clang::Expr *expr); std::string MapFunctionName(const clang::FunctionDecl *decl); std::string InstantiateTemplate(const clang::Expr *expr, unsigned n); bool ReturnsPointer(const clang::Expr *expr); diff --git a/cpp2rust/converter/translation_rule.cpp b/cpp2rust/converter/translation_rule.cpp index 65fcaa3f..4536dee2 100644 --- a/cpp2rust/converter/translation_rule.cpp +++ b/cpp2rust/converter/translation_rule.cpp @@ -86,6 +86,8 @@ std::vector ParseBodyFragmentsJSON(const llvm::json::Array &arr) { } else if (auto *mc = frag_obj->getObject("method_call")) { result.push_back(std::make_unique( ParseMethodCallFragmentJSON(*mc))); + } else if (frag_obj->get("va_args")) { + result.push_back(VaArgsFragment{}); } } return result; @@ -104,11 +106,17 @@ ExprRule ParseExprRuleJSON(const llvm::json::Object &obj) { } } - if (auto *rt = obj.getObject("return_type")) + if (auto *rt = obj.getObject("return_type")) { ir.return_type = ParseTypeInfoJSON(*rt); + } - if (auto ms = obj.getBoolean("multi_statement")) + if (auto ms = obj.getBoolean("multi_statement")) { ir.multi_statement = *ms; + } + + if (auto v = obj.getBoolean("is_extern")) { + ir.is_extern = *v; + } if (auto *generics = obj.getObject("generics")) { for (auto &[key, val] : *generics) { @@ -222,6 +230,8 @@ void BodyFragmentDump(const BodyFragment &frag) { p->dump(); } else if (auto *g = std::get_if(&frag)) { g->dump(); + } else if (auto *v = std::get_if(&frag)) { + v->dump(); } else if (auto *mc = std::get_if>(&frag)) { (*mc)->dump(); @@ -232,6 +242,8 @@ void BodyFragmentDump(const BodyFragment &frag) { void TextFragment::dump() const { log() << " text: \"" << text << "\"\n"; } +void VaArgsFragment::dump() const { log() << " va_args\n"; } + void PlaceholderFragment::dump() const { log() << " placeholder: " << n; switch (access) { diff --git a/cpp2rust/converter/translation_rule.h b/cpp2rust/converter/translation_rule.h index 906cc7f3..dc07c4ef 100644 --- a/cpp2rust/converter/translation_rule.h +++ b/cpp2rust/converter/translation_rule.h @@ -39,11 +39,15 @@ struct GenericFragment { void dump() const; }; +struct VaArgsFragment { + void dump() const; +}; + struct MethodCallFragment; // forward declaration using BodyFragment = std::variant>; + VaArgsFragment, std::unique_ptr>; struct MethodCallFragment { std::vector receiver; @@ -71,6 +75,7 @@ struct ExprRule { std::vector> generics; // "T1" -> ["Ord", "Clone"] std::vector body; bool multi_statement = false; + bool is_extern = false; void dump() const; void validate(const std::string &name) const; diff --git a/cpp2rust/cpp_rule_preprocessor.cpp b/cpp2rust/cpp_rule_preprocessor.cpp index 51e10c48..f39c6733 100644 --- a/cpp2rust/cpp_rule_preprocessor.cpp +++ b/cpp2rust/cpp_rule_preprocessor.cpp @@ -354,7 +354,9 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback { createTemplateArguments(clang::TemplateDecl *decl, llvm::SmallVectorImpl &out) { for (clang::NamedDecl *param : *decl->getTemplateParameters()) { - if (llvm::isa(param)) { + if (param->isTemplateParameterPack()) { + out.emplace_back(clang::TemplateArgument::getEmptyPack()); + } else if (llvm::isa(param)) { clang::RecordDecl *rdecl = createRecordDecl(param->getName()); clang::QualType type = sema_->Context.getTagType(clang::ElaboratedTypeKeyword::None, @@ -414,7 +416,14 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback { clang::OverloadCandidateSet &candidates) { clang::LookupResult decls(*sema_, name, loc_, clang::Sema::LookupOrdinaryName); - sema_->LookupQualifiedName(decls, sema_->getStdNamespace()); + if (clang::NamespaceDecl *std_ns = sema_->getStdNamespace()) { + sema_->LookupQualifiedName(decls, std_ns); + } + if (decls.empty()) { + decls.clear(); + sema_->LookupQualifiedName(decls, + sema_->Context.getTranslationUnitDecl()); + } for (auto *ndecl : decls) { if (auto *candidate = createCandidate(ndecl, callArgs, explicitTArgs)) { sema_->AddOverloadCandidate( @@ -578,6 +587,7 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback { cxxConstructorNameLookup(rule->getReturnType(), callArgs, candidates); break; case LookupKind::ADL: + regularNameLookup(callArgs, &explicitTArgs, name, candidates); adlLookup(callArgs, name, candidates); break; } diff --git a/rule-preprocessor/src/ir.rs b/rule-preprocessor/src/ir.rs index 97a4ce0c..e9b353c3 100644 --- a/rule-preprocessor/src/ir.rs +++ b/rule-preprocessor/src/ir.rs @@ -59,6 +59,8 @@ pub struct FnIr { pub params: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub return_type: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub is_extern: Option, } impl FnIr { @@ -106,7 +108,10 @@ impl FnIr { 1, &format!("Rule {name} generics"), ); - assert!(!self.body.is_empty(), "Rule {name}: body must not be empty"); + assert!( + self.is_extern == Some(true) || !self.body.is_empty(), + "Rule {name}: body must not be empty" + ); } } @@ -120,10 +125,21 @@ pub struct TypeIr { #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] pub enum BodyFragment { - Text { text: String }, - Placeholder { placeholder: PlaceholderInner }, - Generic { generic: i32 }, - MethodCall { method_call: MethodCallInner }, + Text { + text: String, + }, + Placeholder { + placeholder: PlaceholderInner, + }, + Generic { + generic: i32, + }, + MethodCall { + method_call: MethodCallInner, + }, + VaArgs { + va_args: std::marker::PhantomData<()>, + }, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] diff --git a/rule-preprocessor/src/syntactic.rs b/rule-preprocessor/src/syntactic.rs index aa3d8871..da74cc12 100644 --- a/rule-preprocessor/src/syntactic.rs +++ b/rule-preprocessor/src/syntactic.rs @@ -40,6 +40,20 @@ fn pointer_flags(ty: &ast::Type) -> (bool, bool) { flags } +fn is_va_args_type(ty: &ast::Type) -> bool { + if let ast::Type::RefType(r) = ty + && let Some(ast::Type::SliceType(slice)) = r.ty() + { + return matches!(slice.ty(), Some(ast::Type::PathType(path)) + if path + .path() + .and_then(|p| p.segment()) + .and_then(|s| s.name_ref()) + .is_some_and(|name| name.text() == "VaArg")); + } + false +} + fn cfg_matches_host(fn_item: &ast::Fn) -> bool { for attr in fn_item.attrs() { let Some(meta) = attr.meta() else { continue }; @@ -205,6 +219,13 @@ impl<'a> FragmentCtx<'a> { } if token.kind() == SyntaxKind::IDENT { if let Some(param) = self.params.iter().find(|p| p.name == token.text()) { + if param.is_va_args { + self.flush_text(); + self.fragments.push(BodyFragment::VaArgs { + va_args: std::marker::PhantomData, + }); + return; + } let mut access = self.builder.classify_access(token); if param.is_mut_ref && self.text_buf.ends_with('*') { self.text_buf.pop(); @@ -272,6 +293,7 @@ struct ParamInfo { is_refcount_pointer: bool, is_unsafe_pointer: bool, is_mut_ref: bool, + is_va_args: bool, } struct FnIrBuilder<'a> { @@ -297,15 +319,22 @@ impl<'a> FnIrBuilder<'a> { }; let (is_refcount_pointer, is_unsafe_pointer) = pointer_flags(&ty); + let name = ident + .name() + .map(|n| n.text().to_string()) + .unwrap_or_default(); + let is_va_args = is_va_args_type(&ty); + assert!( + !is_va_args || name == "va", + "variadic argument parameter must be named `va`, found `{name}`" + ); params.push(ParamInfo { - name: ident - .name() - .map(|n| n.text().to_string()) - .unwrap_or_default(), + name, ty: ty.syntax().text().to_string(), is_refcount_pointer, is_unsafe_pointer, is_mut_ref: matches!(&ty, ast::Type::RefType(r) if r.mut_token().is_some()), + is_va_args, }); } params @@ -417,6 +446,13 @@ impl<'a> FnIrBuilder<'a> { .unwrap_or(Access::Read) } + fn is_extern(&self) -> bool { + self.fn_item + .syntax() + .ancestors() + .any(|a| a.kind() == SyntaxKind::EXTERN_BLOCK) + } + fn returns_mut_ref(&self) -> bool { self.fn_item .ret_type() @@ -476,6 +512,7 @@ impl<'a> FnIrBuilder<'a> { let params_map: BTreeMap = params .iter() + .filter(|p| !p.is_va_args) .map(|p| { ( p.name.clone(), @@ -519,6 +556,7 @@ impl<'a> FnIrBuilder<'a> { }, multi_statement, body, + is_extern: self.is_extern().then_some(true), }; ir.validate(&format!("{}:{}", path.display(), fn_name)); ir diff --git a/rules/builtin/src.c b/rules/builtin/src.c new file mode 100644 index 00000000..805fbc1c --- /dev/null +++ b/rules/builtin/src.c @@ -0,0 +1,2 @@ +int f12(long a, long b, long *r) { return __builtin_mul_overflow(a, b, r); } +int f13(long long a, long long b, long long *r) { return __builtin_mul_overflow(a, b, r); } diff --git a/rules/builtin/tgt_refcount.rs b/rules/builtin/tgt_refcount.rs index 0f076e1c..4e1afbbc 100644 --- a/rules/builtin/tgt_refcount.rs +++ b/rules/builtin/tgt_refcount.rs @@ -13,3 +13,13 @@ fn f10(a0: i64, a1: i64, a2: Ptr) -> bool { a2.write(val); ovf } +fn f12(a0: i64, a1: i64, a2: Ptr) -> bool { + let (val, ovf) = a0.overflowing_mul(a1); + a2.write(val); + ovf +} +fn f13(a0: i64, a1: i64, a2: Ptr) -> bool { + let (val, ovf) = a0.overflowing_mul(a1); + a2.write(val); + ovf +} diff --git a/rules/builtin/tgt_unsafe.rs b/rules/builtin/tgt_unsafe.rs index 16638e3f..1d0e7cb8 100644 --- a/rules/builtin/tgt_unsafe.rs +++ b/rules/builtin/tgt_unsafe.rs @@ -39,3 +39,14 @@ unsafe fn f10(a0: i64, a1: i64, a2: *mut i64) -> bool { unsafe fn f11() { std::hint::spin_loop(); } + +unsafe fn f12(a0: i64, a1: i64, a2: *mut i64) -> bool { + let (val, ovf) = a0.overflowing_mul(a1); + *a2 = val; + ovf +} +unsafe fn f13(a0: i64, a1: i64, a2: *mut i64) -> bool { + let (val, ovf) = a0.overflowing_mul(a1); + *a2 = val; + ovf +} diff --git a/rules/fcntl/src.cpp b/rules/fcntl/src.cpp new file mode 100644 index 00000000..657b35cc --- /dev/null +++ b/rules/fcntl/src.cpp @@ -0,0 +1,14 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include + +template +int f1(int a0, int a1, Args... args) { + return fcntl(a0, a1, args...); +} + +template +int f2(const char *a0, int a1, Args... args) { + return open(a0, a1, args...); +} diff --git a/rules/fcntl/tgt_refcount.rs b/rules/fcntl/tgt_refcount.rs new file mode 100644 index 00000000..00cc04dd --- /dev/null +++ b/rules/fcntl/tgt_refcount.rs @@ -0,0 +1,22 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +use libcc2rs::*; + +fn f1(a0: i32, a1: i32, va: &[VaArg]) -> i32 { + panic!( + "fcntl is not supported in the refcount model (fd={}, cmd={}, varargs={})", + a0, + a1, + va.len() + ) +} + +fn f2(a0: Ptr, a1: i32, va: &[VaArg]) -> i32 { + panic!( + "open is not supported in the refcount model (path={:?}, flags={}, varargs={})", + a0.to_rust_string(), + a1, + va.len() + ) +} diff --git a/rules/fcntl/tgt_unsafe.rs b/rules/fcntl/tgt_unsafe.rs new file mode 100644 index 00000000..be3a31ab --- /dev/null +++ b/rules/fcntl/tgt_unsafe.rs @@ -0,0 +1,7 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +unsafe extern "C" { + fn f1(a0: i32, a1: i32, ...) -> i32; + fn f2(a0: *const i8, a1: i32, ...) -> i32; +} diff --git a/rules/ioctl/src.cpp b/rules/ioctl/src.cpp new file mode 100644 index 00000000..02587ce4 --- /dev/null +++ b/rules/ioctl/src.cpp @@ -0,0 +1,9 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include + +template +int f1(int a0, unsigned long a1, Args... args) { + return ioctl(a0, a1, args...); +} diff --git a/rules/ioctl/tgt_refcount.rs b/rules/ioctl/tgt_refcount.rs new file mode 100644 index 00000000..03c17893 --- /dev/null +++ b/rules/ioctl/tgt_refcount.rs @@ -0,0 +1,13 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +use libcc2rs::*; + +fn f1(a0: i32, a1: u64, va: &[VaArg]) -> i32 { + panic!( + "ioctl is not supported in the refcount model (fd={}, request={}, varargs={})", + a0, + a1, + va.len() + ) +} diff --git a/rules/ioctl/tgt_unsafe.rs b/rules/ioctl/tgt_unsafe.rs new file mode 100644 index 00000000..00d6cbea --- /dev/null +++ b/rules/ioctl/tgt_unsafe.rs @@ -0,0 +1,6 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +unsafe extern "C" { + fn f1(a0: i32, a1: u64, ...) -> i32; +} diff --git a/rules/src/modules.rs b/rules/src/modules.rs index 9a7971c5..e8f03ba0 100644 --- a/rules/src/modules.rs +++ b/rules/src/modules.rs @@ -40,6 +40,10 @@ pub mod deque_tgt_unsafe; pub mod dirent_tgt_unsafe; #[path = r#"../errno/tgt_unsafe.rs"#] pub mod errno_tgt_unsafe; +#[path = r#"../fcntl/tgt_refcount.rs"#] +pub mod fcntl_tgt_refcount; +#[path = r#"../fcntl/tgt_unsafe.rs"#] +pub mod fcntl_tgt_unsafe; #[path = r#"../fnmatch/tgt_unsafe.rs"#] pub mod fnmatch_tgt_unsafe; #[path = r#"../fstream/tgt_refcount.rs"#] @@ -50,6 +54,10 @@ pub mod fstream_tgt_unsafe; pub mod ifaddrs_tgt_unsafe; #[path = r#"../initializer_list/tgt_unsafe.rs"#] pub mod initializer_list_tgt_unsafe; +#[path = r#"../ioctl/tgt_refcount.rs"#] +pub mod ioctl_tgt_refcount; +#[path = r#"../ioctl/tgt_unsafe.rs"#] +pub mod ioctl_tgt_unsafe; #[path = r#"../iomanip/tgt_unsafe.rs"#] pub mod iomanip_tgt_unsafe; #[path = r#"../iostream/tgt_refcount.rs"#] diff --git a/rules/stdio/src.cpp b/rules/stdio/src.cpp index 7ef4882a..21822c9a 100644 --- a/rules/stdio/src.cpp +++ b/rules/stdio/src.cpp @@ -57,6 +57,11 @@ int f19(FILE *stream, off_t offset, int whence) { FILE *f20(int fd, const char *mode) { return fdopen(fd, mode); } +template +int f21(char *a0, size_t a1, const char *a2, Args... args) { + return snprintf(a0, a1, a2, args...); +} + int f22(const char *a0, const char *a1) { return rename(a0, a1); } diff --git a/rules/stdio/tgt_refcount.rs b/rules/stdio/tgt_refcount.rs index 24026de2..3b914c0c 100644 --- a/rules/stdio/tgt_refcount.rs +++ b/rules/stdio/tgt_refcount.rs @@ -94,3 +94,13 @@ fn f9() -> Ptr<::std::fs::File> { fn f10() -> Ptr<::std::fs::File> { libcc2rs::cin() } + +fn f21(a0: Ptr, a1: usize, a2: Ptr, va: &[VaArg]) -> i32 { + panic!( + "snprintf is not supported in the refcount model (buf_is_null={}, size={}, fmt={:?}, varargs={})", + a0.is_null(), + a1, + a2.to_rust_string(), + va.len() + ) +} diff --git a/rules/stdio/tgt_unsafe.rs b/rules/stdio/tgt_unsafe.rs index 6351056a..b164d9a7 100644 --- a/rules/stdio/tgt_unsafe.rs +++ b/rules/stdio/tgt_unsafe.rs @@ -85,6 +85,10 @@ unsafe fn f20(a0: i32, a1: *const u8) -> *mut ::libc::FILE { libc::fdopen(a0, a1 as *const i8) } +unsafe extern "C" { + fn f21(a0: *mut i8, a1: usize, a2: *const i8, ...) -> i32; +} + unsafe fn f22(a0: *const i8, a1: *const i8) -> i32 { libc::rename(a0 as *const i8, a1 as *const i8) } diff --git a/tests/unit/out/unsafe/unistd.rs b/tests/unit/out/unsafe/unistd.rs index 90fac9bc..0511676a 100644 --- a/tests/unit/out/unsafe/unistd.rs +++ b/tests/unit/out/unsafe/unistd.rs @@ -219,19 +219,58 @@ pub unsafe fn test_ftruncate_5() { assert!(((((libc::fclose(fp)) == (0)) as i32) != 0)); libc::unlink(path as *const i8); } -pub unsafe fn test_isatty_6() { +pub unsafe fn test_open_6() { + let mut fd: i32 = (unsafe { + libc::open( + (b"/dev/null\0".as_ptr().cast_mut()).cast_const() as *const i8, + 0 as i32, + (420), + ) + }); + assert!(((((fd) >= (-1_i32)) as i32) != 0)); + if ((((fd) >= (0)) as i32) != 0) { + libc::close(fd); + } + fd = (unsafe { + libc::open( + (b"/dev/null\0".as_ptr().cast_mut()).cast_const() as *const i8, + 0 as i32, + ) + }); + assert!(((((fd) >= (-1_i32)) as i32) != 0)); + if ((((fd) >= (0)) as i32) != 0) { + libc::close(fd); + } +} +pub unsafe fn test_fcntl_7() { + assert!(((((unsafe { libc::fcntl(0 as i32, 1 as i32,) }) >= (-1_i32)) as i32) != 0)); + let mut duped: i32 = (unsafe { libc::fcntl(0 as i32, 0 as i32, (100)) }); + assert!(((((duped) >= (-1_i32)) as i32) != 0)); + if ((((duped) >= (0)) as i32) != 0) { + libc::close(duped); + } +} +pub unsafe fn test_ioctl_8() { + let mut arg: i32 = 0; + assert!( + ((((unsafe { libc::ioctl(0 as i32, 0_u64 as u64, (&mut arg as *mut i32),) }) >= (-1_i32)) + as i32) + != 0) + ); +} +pub unsafe fn test_isatty_9() { printf( (b"%d\n\0".as_ptr().cast_mut()).cast_const() as *const i8, libc::isatty(0), ); } -pub unsafe fn test_geteuid_7() { +pub unsafe fn test_geteuid_10() { printf( (b"%u\n\0".as_ptr().cast_mut()).cast_const() as *const i8, libc::geteuid(), ); } -pub unsafe fn test_gethostname_8() { +pub unsafe fn test_gethostname_11() { let mut name: [u8; 256] = [0_u8; 256]; assert!( ((((libc::gethostname( @@ -257,8 +296,11 @@ unsafe fn main_0() -> i32 { (unsafe { test_unlink_3() }); (unsafe { test_pipe_4() }); (unsafe { test_ftruncate_5() }); - (unsafe { test_isatty_6() }); - (unsafe { test_geteuid_7() }); - (unsafe { test_gethostname_8() }); + (unsafe { test_open_6() }); + (unsafe { test_fcntl_7() }); + (unsafe { test_ioctl_8() }); + (unsafe { test_isatty_9() }); + (unsafe { test_geteuid_10() }); + (unsafe { test_gethostname_11() }); return 0; } diff --git a/tests/unit/unistd.c b/tests/unit/unistd.c index 2ed49f68..2cd047c1 100644 --- a/tests/unit/unistd.c +++ b/tests/unit/unistd.c @@ -1,7 +1,9 @@ // no-compile: refcount #include +#include #include #include +#include #include #include @@ -87,6 +89,33 @@ static void test_ftruncate(void) { unlink(path); } +static void test_open(void) { + int fd = open("/dev/null", 0, 0644); + assert(fd >= -1); + if (fd >= 0) { + close(fd); + } + fd = open("/dev/null", 0); + assert(fd >= -1); + if (fd >= 0) { + close(fd); + } +} + +static void test_fcntl(void) { + assert(fcntl(0, 1) >= -1); + int duped = fcntl(0, 0, 100); + assert(duped >= -1); + if (duped >= 0) { + close(duped); + } +} + +static void test_ioctl(void) { + int arg = 0; + assert(ioctl(0, 0, &arg) >= -1); +} + static void test_isatty(void) { printf("%d\n", isatty(0)); } static void test_geteuid(void) { printf("%u\n", geteuid()); } @@ -104,6 +133,9 @@ int main(void) { test_unlink(); test_pipe(); test_ftruncate(); + test_open(); + test_fcntl(); + test_ioctl(); test_isatty(); test_geteuid(); test_gethostname();