Skip to content

Commit 40b6d37

Browse files
author
Pascal Beyer
committed
First working version of SEH. It only works for exe's and only simple filters with __except, but by god we did it.
1 parent e24a539 commit 40b6d37

File tree

8 files changed

+332
-19
lines changed

8 files changed

+332
-19
lines changed

implicit/include/excpt.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,9 @@ enum{
1313
EXCEPTION_EXECUTE_HANDLER = 1,
1414
EXCEPTION_CONTINUE_EXECUTION = -1,
1515
};
16+
17+
// https://learn.microsoft.com/en-us/cpp/cpp/try-except-statement?view=msvc-170#structured-exception-handling-intrinsic-functions
18+
#define GetExceptionCode() _exception_code()
19+
#define GetExceptionInformation() ((struct _EXCEPTION_POINTERS *)_exception_info())
20+
#define AbnormalTermination() _abnormal_termination()
21+

src/ast.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,10 @@ enum token_type{
169169
TOKEN_generic, // C11 _Generic
170170
TOKEN_thread_local, // C11 _Thread_local
171171

172+
TOKEN_seh_try,
173+
TOKEN_seh_except,
174+
TOKEN_seh_finally,
175+
172176
TOKEN_count,
173177
TOKEN_one_past_last_keyword = TOKEN_count,
174178
};
@@ -251,6 +255,11 @@ static struct{
251255

252256
{const_string("_Generic"), TOKEN_generic},
253257
{const_string("_Thread_local"), TOKEN_thread_local},
258+
259+
{const_string("__try"), TOKEN_seh_try},
260+
{const_string("__except"), TOKEN_seh_except},
261+
{const_string("__finally"), TOKEN_seh_finally},
262+
254263
};
255264

256265
enum preprocessor_directive{
@@ -722,6 +731,8 @@ struct ast_compound_type{
722731
#define FUNCTION_TYPE_FLAGS_is_printlike 0x4
723732
#define FUNCTION_TYPE_FLAGS_is_inline_asm 0x8 // @cleanup: This should not be on the type.
724733
#define FUNCTION_TYPE_FLAGS_is_noreturn 0x10
734+
#define FUNCTION_TYPE_FLAGS_is_seh_intrinsic 0x20
735+
#define FUNCTION_TYPE_FLAGS_is_seh_filter 0x40
725736
struct ast_function_type{
726737
struct ast_type base;
727738
struct ast_type *return_type;
@@ -732,6 +743,13 @@ struct ast_function_type{
732743
struct ast_list argument_list;
733744
};
734745

746+
struct seh_exception_handler{
747+
struct seh_exception_handler *next;
748+
u32 start_offset_in_function;
749+
u32 end_offset_in_function;
750+
struct ast_function *filter_function;
751+
};
752+
735753
struct ast_function{
736754
union{
737755
struct{
@@ -800,6 +818,7 @@ struct ast_function{
800818
u32 pushed_register_mask; // (1 << REGISTER_XXX) is set if we pushed that register in the prolog
801819

802820
struct dll_import_node *dll_import_node;
821+
struct seh_exception_handler *seh_exception_handler;
803822
};
804823

805824
struct ast_label{

src/emit_x64.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4647,6 +4647,20 @@ void emit_code_for_function__internal(struct context *context, struct ast_functi
46474647
ir_arena_at += sizeof(struct ir);
46484648
}break;
46494649

4650+
case IR_start_exception_block:{
4651+
struct ir_exception_marker *marker = (struct ir_exception_marker *)ir;
4652+
ir_arena_at += sizeof(*marker);
4653+
4654+
marker->handler->start_offset_in_function = (u32)get_bytes_emitted(context);
4655+
}break;
4656+
4657+
case IR_end_exception_block:{
4658+
struct ir_exception_marker *marker = (struct ir_exception_marker *)ir;
4659+
ir_arena_at += sizeof(*marker);
4660+
4661+
marker->handler->end_offset_in_function = (u32)get_bytes_emitted(context);
4662+
}break;
4663+
46504664
default:{
46514665

46524666
#ifdef FUZZING

src/explain.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ func void report_errors_for_unresolved_sleepers(struct context *context){
6565
struct sleeper_node *sleeper_node = sleeper_table->nodes + sleeper_table_index;
6666
struct work_queue_entry *first_sleeper = (struct work_queue_entry *)(sleeper_node->first_sleeper_and_sleep_purpose & ~SLEEP_PURPOSE_mask);
6767
enum sleep_purpose sleep_purpose = sleeper_node->first_sleeper_and_sleep_purpose & SLEEP_PURPOSE_mask;
68+
(void)sleep_purpose; // @cleanup: Probably use this for better errors?
6869
if(!first_sleeper) continue;
6970

7071
struct token *sleeping_on = sleeper_node->token;

src/ir.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ enum ir_kind{
7070
IR_jump_if_true,
7171
IR_jump_if_false,
7272

73+
IR_start_exception_block,
74+
IR_end_exception_block,
75+
7376
IR_switch,
7477
IR_case,
7578

@@ -685,4 +688,10 @@ struct ir_string_literal{
685688
u32 symbol_table_index; // needed for .obj (this should maybe be named unique_string_index as that is what it realy is)
686689
};
687690

691+
692+
struct ir_exception_marker{
693+
struct ir base;
694+
struct seh_exception_handler *handler;
695+
};
696+
688697
#pragma pack(pop)

src/main.c

Lines changed: 136 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,7 @@ static struct{
525525
struct token *token;
526526
} *globally_referenced_declarations;
527527

528+
528529
//
529530
// Basic types:
530531
// @WARNING: The non-atomic version need to stay in the same order as the atomic versions,
@@ -571,6 +572,8 @@ static struct{
571572
struct ast_type *typedef_u8_pointer;
572573
struct ast_type *typedef_s8_pointer;
573574

575+
struct ast_function_type *seh_filter_funtion_type;
576+
574577
//
575578
// Invalid/Default values
576579
//
@@ -583,6 +586,8 @@ static struct{
583586

584587
struct ast_declaration *tls_index_declaration;
585588

589+
struct token *seh_used;
590+
struct ast_function *C_specific_handler_declaration;
586591
} globals;
587592

588593
//_____________________________________________________________________________________________________________________
@@ -759,6 +764,10 @@ struct context{
759764
smm in_conditional_expression;
760765
int current_statement_returns_a_value;
761766

767+
smm in_exception_filter;
768+
smm in_finally_block;
769+
smm filter_function_index;
770+
762771
struct ast_stack_entry{
763772
enum ast_kind ast_kind;
764773
struct token *token;
@@ -956,7 +965,7 @@ func void emit_patch(struct context *context, enum patch_kind kind, struct ir *s
956965
// Reference graph.
957966

958967
func void add_global_reference_for_declaration(struct memory_arena *arena, struct ast_declaration *declaration){
959-
assert(globals.compile_stage == COMPILE_STAGE_parse_global_scope_entries);
968+
// assert(globals.compile_stage == COMPILE_STAGE_parse_global_scope_entries); This is not true anymore because of __C_specific_handler.
960969

961970
struct declaration_reference_node *node = push_struct(arena, struct declaration_reference_node);
962971
node->declaration = declaration;
@@ -1493,6 +1502,18 @@ func void wake_up_sleepers(struct sleeper_table *sleeper_table, struct token *sl
14931502
#include "preprocess.c"
14941503
//_____________________________________________________________________________________________________________________
14951504

1505+
func struct token *push_dummy_token(struct memory_arena *arena, struct atom token_atom, enum token_type token_type){
1506+
1507+
struct token *token = push_struct(arena, struct token);
1508+
token->type = token_type;
1509+
token->atom = token_atom;
1510+
token->file_index = -1; // invalid file index.
1511+
token->line = 1;
1512+
token->column = 1;
1513+
1514+
return token;
1515+
}
1516+
14961517
func struct parse_work *push_parse_work(struct context *context, struct token_array tokens, struct compilation_unit *compilation_unit, struct ast_function *function){
14971518
struct parse_work *parse_work = push_uninitialized_struct(context->arena, struct parse_work); // @note: No need to zero, 'arena' never has any non-zero bytes.
14981519
parse_work->tokens = tokens;
@@ -2640,6 +2661,8 @@ func void reset_context(struct context *context){
26402661
context->goto_list.first = context->goto_list.last = 0;
26412662
context->label_list.first = context->label_list.last = 0;
26422663

2664+
context->filter_function_index = 0;
2665+
26432666
context->pragma_pack_stack.first = context->pragma_pack_stack.last = null;
26442667
}
26452668

@@ -3477,19 +3500,7 @@ func u32 work_thread_proc(void *param){
34773500
//_____________________________________________________________________________________________________________________
34783501
// Setup code needed by main
34793502

3480-
func struct token *push_dummy_token(struct memory_arena *arena, struct atom token_atom, enum token_type token_type){
3481-
3482-
struct token *token = push_struct(arena, struct token);
3483-
token->type = token_type;
3484-
token->atom = token_atom;
3485-
token->file_index = -1; // invalid file index.
3486-
token->line = 1;
3487-
token->column = 1;
3488-
3489-
return token;
3490-
}
3491-
3492-
func void register_intrinsic_function_declaration(struct context *context, struct token *token, struct ast_function_type *type){
3503+
func struct ast_declaration *register_intrinsic_function_declaration(struct context *context, struct token *token, struct ast_function_type *type){
34933504

34943505
struct ast_function *declaration = push_uninitialized_struct(context->arena, struct ast_function); // @note: No need to zero, 'arena' never has any non-zero bytes.
34953506
declaration->kind = IR_function;
@@ -3499,7 +3510,10 @@ func void register_intrinsic_function_declaration(struct context *context, struc
34993510
declaration->compilation_unit = &globals.hacky_global_compilation_unit;
35003511
declaration->as_decl.flags |= DECLARATION_FLAGS_is_intrinsic | DECLARATION_FLAGS_is_global;
35013512

3502-
ast_table_add_or_return_previous_entry(&globals.global_declarations, &declaration->kind, token);
3513+
struct ast_declaration *redecl = (struct ast_declaration *)ast_table_add_or_return_previous_entry(&globals.global_declarations, &declaration->kind, token);
3514+
if(redecl) return redecl;
3515+
3516+
return &declaration->as_decl;
35033517
}
35043518

35053519

@@ -4464,6 +4478,11 @@ globals.typedef_##postfix = (struct ast_type){
44644478
void_pointer->base.alignment = 8;
44654479
globals.typedef_void_pointer = &void_pointer->base;
44664480

4481+
struct ast_function_type *seh_filter_function_type = parser_type_push(context, function_type);
4482+
seh_filter_function_type->return_type = &globals.typedef_s32;
4483+
seh_filter_function_type->flags |= FUNCTION_TYPE_FLAGS_is_seh_filter;
4484+
globals.seh_filter_funtion_type = seh_filter_function_type;
4485+
44674486
globals.wake_event = CreateEventA(0, true, 0, 0);
44684487

44694488
globals.declaration_sleeper_table = sleeper_table_create(1 << 8);
@@ -4537,6 +4556,44 @@ globals.typedef_##postfix = (struct ast_type){
45374556
register_intrinsic_function_declaration(context, token, type);
45384557
}
45394558

4559+
{ //
4560+
// void *_exception_info(void)
4561+
//
4562+
4563+
struct token *token = push_dummy_token(arena, atom_for_string(string("_exception_info")), TOKEN_identifier);
4564+
4565+
struct ast_function_type *type = parser_type_push(context, function_type);
4566+
type->return_type = &void_pointer->base;
4567+
type->flags |= FUNCTION_TYPE_FLAGS_is_seh_intrinsic;
4568+
4569+
register_intrinsic_function_declaration(context, token, type);
4570+
}
4571+
4572+
{ //
4573+
// unsigned __int32 _exception_code(void)
4574+
//
4575+
4576+
struct token *token = push_dummy_token(arena, atom_for_string(string("_exception_code")), TOKEN_identifier);
4577+
4578+
struct ast_function_type *type = parser_type_push(context, function_type);
4579+
type->return_type = &globals.typedef_u32;
4580+
type->flags |= FUNCTION_TYPE_FLAGS_is_seh_intrinsic;
4581+
4582+
register_intrinsic_function_declaration(context, token, type);
4583+
}
4584+
4585+
{ //
4586+
// int _abnormal_termination(void)
4587+
//
4588+
4589+
struct token *token = push_dummy_token(arena, atom_for_string(string("_abnormal_termination")), TOKEN_identifier);
4590+
4591+
struct ast_function_type *type = parser_type_push(context, function_type);
4592+
type->return_type = &globals.typedef_s32;
4593+
type->flags |= FUNCTION_TYPE_FLAGS_is_seh_intrinsic;
4594+
4595+
register_intrinsic_function_declaration(context, token, type);
4596+
}
45404597

45414598
globals.invalid_identifier_token = push_dummy_token(arena, globals.invalid_identifier, TOKEN_identifier);
45424599

@@ -5065,7 +5122,6 @@ globals.typedef_##postfix = (struct ast_type){
50655122
}
50665123
}
50675124

5068-
50695125
//
50705126
// At this point no more sleepers will get filled in.
50715127
// So _if_ there are any, report them.
@@ -5100,8 +5156,72 @@ globals.typedef_##postfix = (struct ast_type){
51005156
assert(globals.work_queue_parse_functions.work_entries_in_flight == 0);
51015157
if(globals.an_error_has_occurred) goto end;
51025158

5159+
51035160
// @cleanup: is there a good way here to assert that nothing is sleeping?
51045161

5162+
5163+
if(globals.seh_used){
5164+
5165+
struct atom atom = atom_for_string(string("__C_specific_handler"));
5166+
5167+
struct ast_declaration *decl = (struct ast_declaration *)ast_table_get(&globals.global_declarations, atom);
5168+
5169+
if(decl && decl->assign_expr){
5170+
// We found a user-defined version, add a global reference to it!
5171+
add_global_reference_for_declaration(arena, decl);
5172+
}else{
5173+
5174+
if(decl){
5175+
// It is not defined, but we already have it.
5176+
}else{
5177+
struct token *token = push_dummy_token(arena, atom, TOKEN_identifier);
5178+
5179+
struct ast_function_type *type = parser_type_push(context, function_type);
5180+
type->return_type = &globals.typedef_s32;
5181+
5182+
decl = register_intrinsic_function_declaration(context, token, type);
5183+
decl->flags &= ~DECLARATION_FLAGS_is_intrinsic;
5184+
}
5185+
5186+
decl->flags |= DECLARATION_FLAGS_is_dllimport;
5187+
decl->flags |= DECLARATION_FLAGS_need_dllimport_stub_function;
5188+
decl->flags |= DECLARATION_FLAGS_is_reachable_from_entry;
5189+
5190+
struct dll_node *vcruntime = null;
5191+
5192+
for(struct dll_node *dll = globals.dlls.first; dll; dll = dll->next){
5193+
if(string_match_case_insensitive(dll->name, string("ntdll.dll"))){
5194+
vcruntime = dll;
5195+
break;
5196+
}
5197+
}
5198+
5199+
if(!vcruntime){
5200+
vcruntime = push_struct(arena, struct dll_node);
5201+
vcruntime->name = string("ntdll.dll");
5202+
sll_push_back(globals.dlls, vcruntime);
5203+
globals.dlls.amount += 1;
5204+
}
5205+
5206+
struct dll_import_node *import_node = push_struct(arena, struct dll_import_node);
5207+
import_node->import_name = atom.string;
5208+
sll_push_back(vcruntime->import_list, import_node);
5209+
vcruntime->import_list.count += 1;
5210+
5211+
if(decl->kind == AST_function){
5212+
((struct ast_function *)decl)->dll_import_node = import_node;
5213+
}
5214+
}
5215+
5216+
if(decl->kind != AST_function){
5217+
// :Error
5218+
report_error(context, decl->identifier, "__C_specific_handler defined as non-function. It is needed for Structured Exception Handling (SEH) (__try/__except).");
5219+
}else{
5220+
globals.C_specific_handler_declaration = (struct ast_function *)decl;
5221+
}
5222+
}
5223+
5224+
51055225
stage_two_parsing_time = os_get_time_in_seconds() - stage_two_parsing_time;
51065226

51075227
//
@@ -5471,8 +5591,6 @@ globals.typedef_##postfix = (struct ast_type){
54715591
#endif
54725592
}
54735593

5474-
end_counter(context, report_error_for_undefined_functions_and_types);
5475-
54765594
end:;
54775595

54785596
print_warning_or_error_reports(context);

0 commit comments

Comments
 (0)