Do not allow parameters in naked functions.

This commit is contained in:
Christoffer Lerno
2025-07-30 01:01:56 +02:00
parent 8151305701
commit 44f4efa5aa
6 changed files with 55 additions and 28 deletions

View File

@@ -87,7 +87,7 @@
- Lambdas now properly follow its attributes #2346.
- Not setting android-ndk resulted in a "set ndk-path" error.
- Lambda deduplication would be incorrect when generated at the global scope.
- Allow accessing parameters in a naked function, just disallow `return`, this fixes #1955.
- Disallow accessing parameters in a naked function, as well as `return`, this fixes #1955.
### Stdlib changes
- Improve contract for readline. #2280

View File

@@ -497,6 +497,7 @@ void llvm_emit_body(GenContext *c, LLVMValueRef function, FunctionPrototype *pro
// Generate LLVMValueRef's for all parameters, so we can use them as local vars in code
FOREACH_IDX(i, Decl *, param, signature->params)
{
if (!param->name && is_naked) continue;
llvm_emit_func_parameter(c, param, prototype->abi_args[i], &arg, i);
}

View File

@@ -59,6 +59,29 @@ static inline AsmArgGroup sema_ireg_for_type(Type *type)
}
}
*/
static inline Decl *sema_resolve_external_symbol(SemaContext *context, Expr *expr, const char *name)
{
Decl *decl = sema_resolve_symbol(context, name, NULL, expr->span);
if (!decl) return NULL;
if (decl->decl_kind != DECL_VAR)
{
SEMA_ERROR(expr, "Expected a global or local variable.");
return NULL;
}
if (IS_OPTIONAL(decl))
{
SEMA_ERROR(expr, "Optional variables are not allowed in asm.");
return NULL;
}
if (decl->var.kind == VARDECL_PARAM && context->call_env.is_naked_fn)
{
SEMA_ERROR(expr, "Function parameters in '@naked' functions may not be directly referenced.");
return NULL;
}
return decl;
}
static inline bool sema_reg_int_suported_type(AsmArgType arg, Type *type)
{
ASSERT(type_flatten(type) == type);
@@ -347,21 +370,11 @@ static inline bool sema_check_asm_var(SemaContext *context, AsmInlineBlock *bloc
{
ExprAsmArg *arg = &expr->expr_asm_arg;
const char *name = arg->ident.name;
Decl *decl = sema_resolve_symbol(context, name, NULL, expr->span);
Decl *decl = sema_resolve_external_symbol(context, expr, name);
if (!decl) return false;
ASSERT(arg->kind == ASM_ARG_REGVAR);
arg->ident.ident_decl = decl;
if (decl->decl_kind != DECL_VAR)
{
SEMA_ERROR(expr, "Expected a global or local variable.");
return false;
}
if (IS_OPTIONAL(decl))
{
SEMA_ERROR(expr, "Optional variables are not allowed in asm.");
return false;
}
bool is_write = arg_type.is_write;
bool is_read = !arg_type.is_write || arg_type.is_readwrite;
arg->ident.is_input = !is_write;
@@ -441,18 +454,10 @@ static inline bool sema_check_asm_memvar(SemaContext *context, AsmInlineBlock *b
{
ExprAsmArg *arg = &expr->expr_asm_arg;
const char *name = arg->ident.name;
Decl *decl = sema_resolve_symbol(context, name, NULL, expr->span);
Decl *decl = sema_resolve_external_symbol(context, expr, name);
if (!decl) return false;
ASSERT(arg->kind == ASM_ARG_MEMVAR);
arg->ident.ident_decl = decl;
if (decl->decl_kind != DECL_VAR)
{
RETURN_SEMA_ERROR(expr, "Expected a global or local variable.");
}
if (IS_OPTIONAL(decl))
{
RETURN_SEMA_ERROR(expr, "Optional variables are not allowed in asm.");
}
bool is_write = arg_type.is_write;
bool is_read = !arg_type.is_write || arg_type.is_readwrite;
arg->ident.is_input = !is_write;

View File

@@ -1262,12 +1262,18 @@ static inline bool sema_expr_analyse_identifier(SemaContext *context, Type *to,
return true;
}
break;
case VARDECL_PARAM:
if (context->call_env.is_naked_fn)
{
RETURN_SEMA_ERROR(expr, "Parameters may not be directly accessed in '@naked' functions.");
}
break;
case VARDECL_GLOBAL:
if (context->call_env.pure)
{
SEMA_ERROR(expr, "'@pure' functions may not access globals.");
return false;
RETURN_SEMA_ERROR(expr, "'@pure' functions may not access globals.");
}
break;
default:
break;
}

View File

@@ -1,28 +1,37 @@
// #target: macos-x64
module test;
import std;
fn void main()
{
char[8] foo = {255, 0, 0, 0, 0, 0, 0, 0};
io::printn(to_ulong(&foo));
ulong a = to_ulong2(&foo);
ulong b = to_ulong(&foo);
}
fn ulong to_ulong(char[8]* buf) @naked @noinline
fn ulong to_ulong(char[8]*) @naked @noinline
{
asm
{
movq $rax, [buf];
movq [$rsp - 8], $rdi;
movq $rax, [$rsp - 8];
movq $rax, [$rax];
ret;
}
unreachable();
}
fn ulong to_ulong2(char[8]* buf)
{
return *(ulong*)buf;
}
/* #expect: test.ll
define i64 @test.to_ulong(ptr %0) #1 {
entry:
call void asm sideeffect alignstack "movq ($0), %rax\0Aret \0A", "r,~{rax},~{flags},~{dirflag},~{fspr}"(ptr %0)
call void asm sideeffect alignstack "movq %rdi, -8(%rsp)\0Amovq -8(%rsp), %rax\0Amovq (%rax), %rax\0Aret \0A", "~{rax},~{flags},~{dirflag},~{fspr}"()
unreachable
}
attributes #1 = { naked noinline nounwind uwtable "no-trapping-math"="true" "stack-protector-buffer-size"="8" }

View File

@@ -10,4 +10,10 @@ fn ulong? test() @naked @noinline
int? z;
z!; // #error: Rethrow is not allowed in a '@naked' function
unreachable();
}
fn ulong test2(int a) @naked @noinline
{
int x = a; // #error: Parameters may not be directly accessed in '@naked' functions
unreachable();
}