From fc4f90f0c8eca75fb90c736476360584f68d7ef9 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Wed, 28 Aug 2019 18:27:30 +0000 Subject: [PATCH] compiler, runtime: provide index information on bounds check failure This implements https://golang.org/cl/161477 in the gofrontend. Updates golang/go#30116 Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/191881 From-SVN: r274998 --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/expressions.cc | 475 +++++++++++++++++++------------ gcc/go/gofrontend/expressions.h | 17 +- gcc/go/gofrontend/gogo.cc | 1 + gcc/go/gofrontend/runtime.cc | 7 + gcc/go/gofrontend/runtime.def | 69 +++++ libgo/go/runtime/error.go | 108 +++++++ libgo/go/runtime/panic.go | 170 +++++++++-- libgo/go/runtime/panic32.go | 108 +++++++ libgo/mkruntimeinc.sh | 5 +- 10 files changed, 739 insertions(+), 223 deletions(-) create mode 100644 libgo/go/runtime/panic32.go diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 18127de31ac..f596a698840 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -a6ddd0e1208a7d229c10be630c1110b3914038f5 +189ff44b2c26f29f41f0eb159e0d8f3fa508ecae The first line of this file holds the git revision number of the last merge done from the gofrontend repository. diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index 90a39a222b0..939a5f7f374 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -594,67 +594,110 @@ Expression::backend_numeric_constant_expression(Translate_context* context, return ret; } -// Return an expression which evaluates to true if VAL, of arbitrary integer -// type, is negative or is more than the maximum value of the Go type "int". +// Insert bounds checks for an index expression. Check that that VAL +// >= 0 and that it fits in an int. Then check that VAL OP BOUND is +// true. If any condition is false, call one of the CODE runtime +// functions, which will panic. -Expression* -Expression::check_bounds(Expression* val, Location loc) +void +Expression::check_bounds(Expression* val, Operator op, Expression* bound, + Runtime::Function code, + Runtime::Function code_u, + Runtime::Function code_extend, + Runtime::Function code_extend_u, + Statement_inserter* inserter, + Location loc) { - Type* val_type = val->type(); - Type* bound_type = Type::lookup_integer_type("int"); + go_assert(val->is_variable() || val->is_constant()); + go_assert(bound->is_variable() || bound->is_constant()); - int val_type_size; - bool val_is_unsigned = false; - if (val_type->integer_type() != NULL) - { - val_type_size = val_type->integer_type()->bits(); - val_is_unsigned = val_type->integer_type()->is_unsigned(); - } - else - { - if (!val_type->is_numeric_type() - || !Type::are_convertible(bound_type, val_type, NULL)) - { - go_assert(saw_errors()); - return Expression::make_boolean(true, loc); - } + Type* int_type = Type::lookup_integer_type("int"); + int int_type_size = int_type->integer_type()->bits(); - if (val_type->complex_type() != NULL) - val_type_size = val_type->complex_type()->bits(); - else - val_type_size = val_type->float_type()->bits(); + Type* val_type = val->type(); + if (val_type->integer_type() == NULL) + { + go_assert(saw_errors()); + return; } + int val_type_size = val_type->integer_type()->bits(); + bool val_is_unsigned = val_type->integer_type()->is_unsigned(); - Expression* negative_index = Expression::make_boolean(false, loc); - Expression* index_overflows = Expression::make_boolean(false, loc); + // Check that VAL >= 0. + Expression* check = NULL; if (!val_is_unsigned) { Expression* zero = Expression::make_integer_ul(0, val_type, loc); - negative_index = Expression::make_binary(OPERATOR_LT, val, zero, loc); + check = Expression::make_binary(OPERATOR_GE, val->copy(), zero, loc); } - int bound_type_size = bound_type->integer_type()->bits(); - if (val_type_size > bound_type_size - || (val_type_size == bound_type_size + // If VAL's type is larger than int, check that VAL fits in an int. + if (val_type_size > int_type_size + || (val_type_size == int_type_size && val_is_unsigned)) { mpz_t one; mpz_init_set_ui(one, 1UL); - // maxval = 2^(bound_type_size - 1) - 1 + // maxval = 2^(int_type_size - 1) - 1 mpz_t maxval; mpz_init(maxval); - mpz_mul_2exp(maxval, one, bound_type_size - 1); + mpz_mul_2exp(maxval, one, int_type_size - 1); mpz_sub_ui(maxval, maxval, 1); Expression* max = Expression::make_integer_z(&maxval, val_type, loc); mpz_clear(one); mpz_clear(maxval); - index_overflows = Expression::make_binary(OPERATOR_GT, val, max, loc); + Expression* cmp = Expression::make_binary(OPERATOR_LE, val->copy(), + max, loc); + if (check == NULL) + check = cmp; + else + check = Expression::make_binary(OPERATOR_ANDAND, check, cmp, loc); + } + + // For the final check we can assume that VAL fits in an int. + Expression* ival; + if (val_type == int_type) + ival = val->copy(); + else + ival = Expression::make_cast(int_type, val->copy(), loc); + + // BOUND is assumed to fit in an int. Either it comes from len or + // cap, or it was checked by an earlier call. + Expression* ibound; + if (bound->type() == int_type) + ibound = bound->copy(); + else + ibound = Expression::make_cast(int_type, bound->copy(), loc); + + Expression* cmp = Expression::make_binary(op, ival, ibound, loc); + if (check == NULL) + check = cmp; + else + check = Expression::make_binary(OPERATOR_ANDAND, check, cmp, loc); + + Runtime::Function c; + if (val_type_size > int_type_size) + { + if (val_is_unsigned) + c = code_extend_u; + else + c = code_extend; + } + else + { + if (val_is_unsigned) + c = code_u; + else + c = code; } - return Expression::make_binary(OPERATOR_OROR, negative_index, index_overflows, - loc); + Expression* ignore = Expression::make_boolean(true, loc); + Expression* crash = Runtime::make_call(c, loc, 2, + val->copy(), bound->copy()); + Expression* cond = Expression::make_conditional(check, ignore, crash, loc); + inserter->insert(Statement::make_statement(cond, true)); } void @@ -12666,7 +12709,8 @@ Array_index_expression::do_check_types(Gogo*) unsigned long v; if (this->start_->type()->integer_type() == NULL && !this->start_->type()->is_error() - && (!this->start_->numeric_constant_value(&nc) + && (!this->start_->type()->is_abstract() + || !this->start_->numeric_constant_value(&nc) || nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT)) this->report_error(_("index must be integer")); if (this->end_ != NULL @@ -12674,7 +12718,8 @@ Array_index_expression::do_check_types(Gogo*) && !this->end_->type()->is_error() && !this->end_->is_nil_expression() && !this->end_->is_error_expression() - && (!this->end_->numeric_constant_value(&nc) + && (!this->end_->type()->is_abstract() + || !this->end_->numeric_constant_value(&nc) || nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT)) this->report_error(_("slice end must be integer")); if (this->cap_ != NULL @@ -12682,7 +12727,8 @@ Array_index_expression::do_check_types(Gogo*) && !this->cap_->type()->is_error() && !this->cap_->is_nil_expression() && !this->cap_->is_error_expression() - && (!this->cap_->numeric_constant_value(&nc) + && (!this->cap_->type()->is_abstract() + || !this->cap_->numeric_constant_value(&nc) || nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT)) this->report_error(_("slice capacity must be integer")); @@ -12799,13 +12845,21 @@ Array_index_expression::do_must_eval_subexpressions_in_order( return true; } -// Flatten array indexing by using temporary variables for slices and indexes. +// Flatten array indexing: add temporary variables and bounds checks. Expression* -Array_index_expression::do_flatten(Gogo*, Named_object*, +Array_index_expression::do_flatten(Gogo* gogo, Named_object*, Statement_inserter* inserter) { + if (this->is_flattened_) + return this; + this->is_flattened_ = true; + Location loc = this->location(); + + if (this->is_error_expression()) + return Expression::make_error(loc); + Expression* array = this->array_; Expression* start = this->start_; Expression* end = this->end_; @@ -12823,34 +12877,157 @@ Array_index_expression::do_flatten(Gogo*, Named_object*, return Expression::make_error(loc); } + Array_type* array_type = this->array_->type()->array_type(); + if (array_type == NULL) + { + go_assert(saw_errors()); + return Expression::make_error(loc); + } + Temporary_statement* temp; - if (array->type()->is_slice_type() && !array->is_variable()) + if (array_type->is_slice_type() && !array->is_variable()) { temp = Statement::make_temporary(NULL, array, loc); inserter->insert(temp); this->array_ = Expression::make_temporary_reference(temp, loc); + array = this->array_; } - if (!start->is_variable()) + if (!start->is_variable() && !start->is_constant()) { temp = Statement::make_temporary(NULL, start, loc); inserter->insert(temp); this->start_ = Expression::make_temporary_reference(temp, loc); + start = this->start_; } if (end != NULL && !end->is_nil_expression() - && !end->is_variable()) + && !end->is_variable() + && !end->is_constant()) { temp = Statement::make_temporary(NULL, end, loc); inserter->insert(temp); this->end_ = Expression::make_temporary_reference(temp, loc); + end = this->end_; } - if (cap != NULL && !cap->is_variable()) + if (cap != NULL && !cap->is_variable() && !cap->is_constant()) { temp = Statement::make_temporary(NULL, cap, loc); inserter->insert(temp); this->cap_ = Expression::make_temporary_reference(temp, loc); + cap = this->cap_; + } + + if (!this->needs_bounds_check_) + return this; + + Expression* len; + if (!array_type->is_slice_type()) + { + len = array_type->get_length(gogo, this->array_); + go_assert(len->is_constant()); + } + else + { + len = array_type->get_length(gogo, this->array_->copy()); + temp = Statement::make_temporary(NULL, len, loc); + inserter->insert(temp); + len = Expression::make_temporary_reference(temp, loc); + } + + Expression* scap = NULL; + if (array_type->is_slice_type()) + { + scap = array_type->get_capacity(gogo, this->array_->copy()); + temp = Statement::make_temporary(NULL, scap, loc); + inserter->insert(temp); + scap = Expression::make_temporary_reference(temp, loc); } + // The order of bounds checks here matches the order used by the gc + // compiler, as tested by issue30116[u].go. + + if (cap != NULL) + { + if (array_type->is_slice_type()) + Expression::check_bounds(cap, OPERATOR_LE, scap, + Runtime::PANIC_SLICE3_ACAP, + Runtime::PANIC_SLICE3_ACAP_U, + Runtime::PANIC_EXTEND_SLICE3_ACAP, + Runtime::PANIC_EXTEND_SLICE3_ACAP_U, + inserter, loc); + else + Expression::check_bounds(cap, OPERATOR_LE, len, + Runtime::PANIC_SLICE3_ALEN, + Runtime::PANIC_SLICE3_ALEN_U, + Runtime::PANIC_EXTEND_SLICE3_ALEN, + Runtime::PANIC_EXTEND_SLICE3_ALEN_U, + inserter, loc); + + Expression* start_bound = cap; + if (end != NULL && !end->is_nil_expression()) + { + Expression::check_bounds(end, OPERATOR_LE, cap, + Runtime::PANIC_SLICE3_B, + Runtime::PANIC_SLICE3_B_U, + Runtime::PANIC_EXTEND_SLICE3_B, + Runtime::PANIC_EXTEND_SLICE3_B_U, + inserter, loc); + start_bound = end; + } + + Expression::check_bounds(start, OPERATOR_LE, start_bound, + Runtime::PANIC_SLICE3_C, + Runtime::PANIC_SLICE3_C_U, + Runtime::PANIC_EXTEND_SLICE3_C, + Runtime::PANIC_EXTEND_SLICE3_C_U, + inserter, loc); + } + else if (end != NULL && !end->is_nil_expression()) + { + if (array_type->is_slice_type()) + Expression::check_bounds(end, OPERATOR_LE, scap, + Runtime::PANIC_SLICE_ACAP, + Runtime::PANIC_SLICE_ACAP_U, + Runtime::PANIC_EXTEND_SLICE_ACAP, + Runtime::PANIC_EXTEND_SLICE_ACAP_U, + inserter, loc); + else + Expression::check_bounds(end, OPERATOR_LE, len, + Runtime::PANIC_SLICE_ALEN, + Runtime::PANIC_SLICE_ALEN_U, + Runtime::PANIC_EXTEND_SLICE_ALEN, + Runtime::PANIC_EXTEND_SLICE_ALEN_U, + inserter, loc); + + Expression::check_bounds(start, OPERATOR_LE, end, + Runtime::PANIC_SLICE_B, + Runtime::PANIC_SLICE_B_U, + Runtime::PANIC_EXTEND_SLICE_B, + Runtime::PANIC_EXTEND_SLICE_B_U, + inserter, loc); + } + else if (end != NULL) + { + Expression* start_bound; + if (array_type->is_slice_type()) + start_bound = scap; + else + start_bound = len; + Expression::check_bounds(start, OPERATOR_LE, start_bound, + Runtime::PANIC_SLICE_B, + Runtime::PANIC_SLICE_B_U, + Runtime::PANIC_EXTEND_SLICE_B, + Runtime::PANIC_EXTEND_SLICE_B_U, + inserter, loc); + } + else + Expression::check_bounds(start, OPERATOR_LT, len, + Runtime::PANIC_INDEX, + Runtime::PANIC_INDEX_U, + Runtime::PANIC_EXTEND_INDEX, + Runtime::PANIC_EXTEND_INDEX_U, + inserter, loc); + return this; } @@ -12899,10 +13076,8 @@ Array_index_expression::do_get_backend(Translate_context* context) Type* int_type = Type::lookup_integer_type("int"); Btype* int_btype = int_type->get_backend(gogo); - // We need to convert the length and capacity to the Go "int" type here - // because the length of a fixed-length array could be of type "uintptr" - // and gimple disallows binary operations between "uintptr" and other - // integer types. FIXME. + // Convert the length and capacity to "int". FIXME: Do we need to + // do this? Bexpression* length = NULL; if (this->end_ == NULL || this->end_->is_nil_expression()) { @@ -12939,53 +13114,18 @@ Array_index_expression::do_get_backend(Translate_context* context) Bexpression* start = this->start_->get_backend(context); start = gogo->backend()->convert_expression(int_btype, start, loc); - Bexpression* crash = NULL; - Bexpression* bad_index = NULL; - if (this->needs_bounds_check_) - { - int code = (array_type->length() != NULL - ? (this->end_ == NULL - ? RUNTIME_ERROR_ARRAY_INDEX_OUT_OF_BOUNDS - : RUNTIME_ERROR_ARRAY_SLICE_OUT_OF_BOUNDS) - : (this->end_ == NULL - ? RUNTIME_ERROR_SLICE_INDEX_OUT_OF_BOUNDS - : RUNTIME_ERROR_SLICE_SLICE_OUT_OF_BOUNDS)); - crash = gogo->runtime_error(code, loc)->get_backend(context); - bad_index = Expression::check_bounds(this->start_, loc)->get_backend(context); - Bexpression* start_too_large = - gogo->backend()->binary_expression((this->end_ == NULL - ? OPERATOR_GE - : OPERATOR_GT), - start, - (this->end_ == NULL - ? length - : capacity), - loc); - bad_index = gogo->backend()->binary_expression(OPERATOR_OROR, - start_too_large, - bad_index, loc); - } - - Bfunction* bfn = context->function()->func_value()->get_decl(); if (this->end_ == NULL) { - // Simple array indexing. This has to return an l-value, so - // wrap the index check into START. - if (this->needs_bounds_check_) - start = - gogo->backend()->conditional_expression(bfn, int_btype, bad_index, - crash, start, loc); - + // Simple array indexing. Bexpression* ret; - if (array_type->length() != NULL) + if (!array_type->is_slice_type()) { Bexpression* array = this->array_->get_backend(context); ret = gogo->backend()->array_index_expression(array, start, loc); } else { - // Slice. Expression* valptr = array_type->get_value_pointer(gogo, this->array_, this->is_lvalue_); @@ -12999,31 +13139,7 @@ Array_index_expression::do_get_backend(Translate_context* context) return ret; } - // Array slice. - - if (this->cap_ != NULL) - { - cap_arg = gogo->backend()->convert_expression(int_btype, cap_arg, loc); - - if (this->needs_bounds_check_) - { - Bexpression* bounds_bcheck = - Expression::check_bounds(this->cap_, loc)->get_backend(context); - bad_index = - gogo->backend()->binary_expression(OPERATOR_OROR, bounds_bcheck, - bad_index, loc); - - Bexpression* cap_too_small = - gogo->backend()->binary_expression(OPERATOR_LT, cap_arg, start, loc); - Bexpression* cap_too_large = - gogo->backend()->binary_expression(OPERATOR_GT, cap_arg, capacity, loc); - Bexpression* bad_cap = - gogo->backend()->binary_expression(OPERATOR_OROR, cap_too_small, - cap_too_large, loc); - bad_index = gogo->backend()->binary_expression(OPERATOR_OROR, bad_cap, - bad_index, loc); - } - } + // Slice expression. Bexpression* end; if (this->end_->is_nil_expression()) @@ -13032,24 +13148,6 @@ Array_index_expression::do_get_backend(Translate_context* context) { end = this->end_->get_backend(context); end = gogo->backend()->convert_expression(int_btype, end, loc); - if (this->needs_bounds_check_) - { - Bexpression* bounds_bcheck = - Expression::check_bounds(this->end_, loc)->get_backend(context); - bad_index = - gogo->backend()->binary_expression(OPERATOR_OROR, bounds_bcheck, - bad_index, loc); - - Bexpression* end_too_small = - gogo->backend()->binary_expression(OPERATOR_LT, end, start, loc); - Bexpression* end_too_large = - gogo->backend()->binary_expression(OPERATOR_GT, end, cap_arg, loc); - Bexpression* bad_end = - gogo->backend()->binary_expression(OPERATOR_OROR, end_too_small, - end_too_large, loc); - bad_index = gogo->backend()->binary_expression(OPERATOR_OROR, bad_end, - bad_index, loc); - } } Bexpression* result_length = @@ -13081,12 +13179,7 @@ Array_index_expression::do_get_backend(Translate_context* context) init.push_back(result_length); init.push_back(result_capacity); - Bexpression* ret = - gogo->backend()->constructor_expression(struct_btype, init, loc); - if (this->needs_bounds_check_) - ret = gogo->backend()->conditional_expression(bfn, struct_btype, bad_index, - crash, ret, loc); - return ret; + return gogo->backend()->constructor_expression(struct_btype, init, loc); } // Export an array index expression. @@ -13164,7 +13257,15 @@ Expression* String_index_expression::do_flatten(Gogo*, Named_object*, Statement_inserter* inserter) { + if (this->is_flattened_) + return this; + this->is_flattened_ = true; + Location loc = this->location(); + + if (this->is_error_expression()) + return Expression::make_error(loc); + Expression* string = this->string_; Expression* start = this->start_; Expression* end = this->end_; @@ -13180,27 +13281,69 @@ String_index_expression::do_flatten(Gogo*, Named_object*, } Temporary_statement* temp; - if (!this->string_->is_variable()) + if (!string->is_variable()) { - temp = Statement::make_temporary(NULL, this->string_, loc); + temp = Statement::make_temporary(NULL, string, loc); inserter->insert(temp); this->string_ = Expression::make_temporary_reference(temp, loc); + string = this->string_; } - if (!this->start_->is_variable()) + if (!start->is_variable()) { - temp = Statement::make_temporary(NULL, this->start_, loc); + temp = Statement::make_temporary(NULL, start, loc); inserter->insert(temp); this->start_ = Expression::make_temporary_reference(temp, loc); + start = this->start_; } - if (this->end_ != NULL - && !this->end_->is_nil_expression() - && !this->end_->is_variable()) + if (end != NULL + && !end->is_nil_expression() + && !end->is_variable()) { - temp = Statement::make_temporary(NULL, this->end_, loc); + temp = Statement::make_temporary(NULL, end, loc); inserter->insert(temp); this->end_ = Expression::make_temporary_reference(temp, loc); + end = this->end_; } + Expression* len = Expression::make_string_info(string->copy(), + STRING_INFO_LENGTH, loc); + temp = Statement::make_temporary(NULL, len, loc); + inserter->insert(temp); + len = Expression::make_temporary_reference(temp, loc); + + // The order of bounds checks here matches the order used by the gc + // compiler, as tested by issue30116[u].go. + + if (end != NULL && !end->is_nil_expression()) + { + Expression::check_bounds(end, OPERATOR_LE, len, + Runtime::PANIC_SLICE_ALEN, + Runtime::PANIC_SLICE_ALEN_U, + Runtime::PANIC_EXTEND_SLICE_ALEN, + Runtime::PANIC_EXTEND_SLICE_ALEN_U, + inserter, loc); + Expression::check_bounds(start, OPERATOR_LE, end, + Runtime::PANIC_SLICE_B, + Runtime::PANIC_SLICE_B_U, + Runtime::PANIC_EXTEND_SLICE_B, + Runtime::PANIC_EXTEND_SLICE_B_U, + inserter, loc); + } + else if (end != NULL) + Expression::check_bounds(start, OPERATOR_LE, len, + Runtime::PANIC_SLICE_B, + Runtime::PANIC_SLICE_B_U, + Runtime::PANIC_EXTEND_SLICE_B, + Runtime::PANIC_EXTEND_SLICE_B_U, + inserter, loc); + else + Expression::check_bounds(start, OPERATOR_LT, len, + Runtime::PANIC_INDEX, + Runtime::PANIC_INDEX_U, + Runtime::PANIC_EXTEND_INDEX, + Runtime::PANIC_EXTEND_INDEX_U, + inserter, loc); + return this; } @@ -13245,7 +13388,8 @@ String_index_expression::do_check_types(Gogo*) unsigned long v; if (this->start_->type()->integer_type() == NULL && !this->start_->type()->is_error() - && (!this->start_->numeric_constant_value(&nc) + && (!this->start_->type()->is_abstract() + || !this->start_->numeric_constant_value(&nc) || nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT)) this->report_error(_("index must be integer")); if (this->end_ != NULL @@ -13253,7 +13397,8 @@ String_index_expression::do_check_types(Gogo*) && !this->end_->type()->is_error() && !this->end_->is_nil_expression() && !this->end_->is_error_expression() - && (!this->end_->numeric_constant_value(&nc) + && (!this->end_->type()->is_abstract() + || !this->end_->numeric_constant_value(&nc) || nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT)) this->report_error(_("slice end must be integer")); @@ -13303,14 +13448,7 @@ Bexpression* String_index_expression::do_get_backend(Translate_context* context) { Location loc = this->location(); - Expression* bad_index = Expression::check_bounds(this->start_, loc); - - int code = (this->end_ == NULL - ? RUNTIME_ERROR_STRING_INDEX_OUT_OF_BOUNDS - : RUNTIME_ERROR_STRING_SLICE_OUT_OF_BOUNDS); - Gogo* gogo = context->gogo(); - Bexpression* crash = gogo->runtime_error(code, loc)->get_backend(context); Type* int_type = Type::lookup_integer_type("int"); @@ -13342,21 +13480,9 @@ String_index_expression::do_get_backend(Translate_context* context) if (this->end_ == NULL) { - Expression* start_too_large = - Expression::make_binary(OPERATOR_GE, start, length, loc); - bad_index = Expression::make_binary(OPERATOR_OROR, start_too_large, - bad_index, loc); - ptr = gogo->backend()->pointer_offset_expression(ptr, bstart, loc); Btype* ubtype = Type::lookup_integer_type("uint8")->get_backend(gogo); - Bexpression* index = - gogo->backend()->indirect_expression(ubtype, ptr, true, loc); - - Btype* byte_btype = bytes->type()->points_to()->get_backend(gogo); - Bexpression* index_error = bad_index->get_backend(context); - return gogo->backend()->conditional_expression(bfn, byte_btype, - index_error, crash, - index, loc); + return gogo->backend()->indirect_expression(ubtype, ptr, true, loc); } Expression* end = NULL; @@ -13365,20 +13491,8 @@ String_index_expression::do_get_backend(Translate_context* context) else { go_assert(this->end_->is_variable()); - Expression* bounds_check = Expression::check_bounds(this->end_, loc); - bad_index = - Expression::make_binary(OPERATOR_OROR, bounds_check, bad_index, loc); end = Expression::make_cast(int_type, this->end_, loc); - - Expression* end_too_large = - Expression::make_binary(OPERATOR_GT, end, length, loc); - bad_index = Expression::make_binary(OPERATOR_OROR, end_too_large, - bad_index, loc); } - Expression* start_too_large = - Expression::make_binary(OPERATOR_GT, start->copy(), end->copy(), loc); - bad_index = Expression::make_binary(OPERATOR_OROR, start_too_large, - bad_index, loc); end = end->copy(); Bexpression* bend = end->get_backend(context); @@ -13405,12 +13519,7 @@ String_index_expression::do_get_backend(Translate_context* context) std::vector init; init.push_back(ptr); init.push_back(new_length); - Bexpression* bstrslice = - gogo->backend()->constructor_expression(str_btype, init, loc); - - Bexpression* index_error = bad_index->get_backend(context); - return gogo->backend()->conditional_expression(bfn, str_btype, index_error, - crash, bstrslice, loc); + return gogo->backend()->constructor_expression(str_btype, init, loc); } // Export a string index expression. diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h index 3b65e7a5f0c..4c743daa847 100644 --- a/gcc/go/gofrontend/expressions.h +++ b/gcc/go/gofrontend/expressions.h @@ -1059,10 +1059,11 @@ class Expression static Expression* import_expression(Import_expression*, Location); - // Return an expression which checks that VAL, of arbitrary integer type, - // is non-negative and is not more than the maximum integer value. - static Expression* - check_bounds(Expression* val, Location); + // Insert bounds checks for an index expression. + static void + check_bounds(Expression* val, Operator, Expression* bound, Runtime::Function, + Runtime::Function, Runtime::Function, Runtime::Function, + Statement_inserter*, Location); // Return an expression for constructing a direct interface type from a // pointer. @@ -2998,7 +2999,7 @@ class Array_index_expression : public Expression Expression* end, Expression* cap, Location location) : Expression(EXPRESSION_ARRAY_INDEX, location), array_(array), start_(start), end_(end), cap_(cap), type_(NULL), - is_lvalue_(false), needs_bounds_check_(true) + is_lvalue_(false), needs_bounds_check_(true), is_flattened_(false) { } // Return the array. @@ -3121,6 +3122,8 @@ class Array_index_expression : public Expression bool is_lvalue_; // Whether bounds check is needed. bool needs_bounds_check_; + // Whether this has already been flattened. + bool is_flattened_; }; // A string index. This is used for both indexing and slicing. @@ -3131,7 +3134,7 @@ class String_index_expression : public Expression String_index_expression(Expression* string, Expression* start, Expression* end, Location location) : Expression(EXPRESSION_STRING_INDEX, location), - string_(string), start_(start), end_(end) + string_(string), start_(start), end_(end), is_flattened_(false) { } // Return the string being indexed. @@ -3203,6 +3206,8 @@ class String_index_expression : public Expression // The end index of a slice. This may be NULL for a single index, // or it may be a nil expression for the length of the string. Expression* end_; + // Whether this has already been flattened. + bool is_flattened_; }; // An index into a map. diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index 8a240708602..7aec0cf1be5 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -6300,6 +6300,7 @@ Function_declaration::get_or_make_decl(Gogo* gogo, Named_object* no) } if (this->asm_name_ == "runtime.gopanic" + || this->asm_name_.compare(0, 15, "runtime.goPanic") == 0 || this->asm_name_ == "__go_runtime_error" || this->asm_name_ == "runtime.panicdottype" || this->asm_name_ == "runtime.block") diff --git a/gcc/go/gofrontend/runtime.cc b/gcc/go/gofrontend/runtime.cc index e35658b8c5f..3cc5ded3617 100644 --- a/gcc/go/gofrontend/runtime.cc +++ b/gcc/go/gofrontend/runtime.cc @@ -30,6 +30,8 @@ enum Runtime_function_type RFT_BOOLPTR, // Go type int, C type intgo. RFT_INT, + // Go type uint, C type uintgo. + RFT_UINT, // Go type uint8, C type uint8_t. RFT_UINT8, // Go type uint16, C type uint16_t. @@ -113,6 +115,10 @@ runtime_function_type(Runtime_function_type bft) t = Type::lookup_integer_type("int"); break; + case RFT_UINT: + t = Type::lookup_integer_type("uint"); + break; + case RFT_UINT8: t = Type::lookup_integer_type("uint8"); break; @@ -262,6 +268,7 @@ convert_to_runtime_function_type(Runtime_function_type bft, Expression* e, case RFT_BOOL: case RFT_BOOLPTR: case RFT_INT: + case RFT_UINT: case RFT_UINT8: case RFT_UINT16: case RFT_INT32: diff --git a/gcc/go/gofrontend/runtime.def b/gcc/go/gofrontend/runtime.def index 7eac880af6a..d7f5ee2140c 100644 --- a/gcc/go/gofrontend/runtime.def +++ b/gcc/go/gofrontend/runtime.def @@ -499,6 +499,75 @@ DEF_GO_RUNTIME(ATOMIC_OR_FETCH_1, "__atomic_or_fetch_1", P3(POINTER, UINT8, INT32), R1(UINT8)) +// Panics reporting an index or slice out of bounds error. +DEF_GO_RUNTIME(PANIC_INDEX, "runtime.goPanicIndex", + P2(INT, INT), R0()) +DEF_GO_RUNTIME(PANIC_INDEX_U, "runtime.goPanicIndexU", + P2(UINT, INT), R0()) +DEF_GO_RUNTIME(PANIC_SLICE_ALEN, "runtime.goPanicSliceAlen", + P2(INT, INT), R0()) +DEF_GO_RUNTIME(PANIC_SLICE_ALEN_U, "runtime.goPanicSliceAlenU", + P2(UINT, INT), R0()) +DEF_GO_RUNTIME(PANIC_SLICE_ACAP, "runtime.goPanicSliceAcap", + P2(INT, INT), R0()) +DEF_GO_RUNTIME(PANIC_SLICE_ACAP_U, "runtime.goPanicSliceAcapU", + P2(UINT, INT), R0()) +DEF_GO_RUNTIME(PANIC_SLICE_B, "runtime.goPanicSliceB", + P2(INT, INT), R0()) +DEF_GO_RUNTIME(PANIC_SLICE_B_U, "runtime.goPanicSliceBU", + P2(UINT, INT), R0()) +DEF_GO_RUNTIME(PANIC_SLICE3_ALEN, "runtime.goPanicSlice3Alen", + P2(INT, INT), R0()) +DEF_GO_RUNTIME(PANIC_SLICE3_ALEN_U, "runtime.goPanicSlice3AlenU", + P2(UINT, INT), R0()) +DEF_GO_RUNTIME(PANIC_SLICE3_ACAP, "runtime.goPanicSlice3Acap", + P2(INT, INT), R0()) +DEF_GO_RUNTIME(PANIC_SLICE3_ACAP_U, "runtime.goPanicSlice3AcapU", + P2(UINT, INT), R0()) +DEF_GO_RUNTIME(PANIC_SLICE3_B, "runtime.goPanicSlice3B", + P2(INT, INT), R0()) +DEF_GO_RUNTIME(PANIC_SLICE3_B_U, "runtime.goPanicSlice3BU", + P2(UINT, INT), R0()) +DEF_GO_RUNTIME(PANIC_SLICE3_C, "runtime.goPanicSlice3C", + P2(INT, INT), R0()) +DEF_GO_RUNTIME(PANIC_SLICE3_C_U, "runtime.goPanicSlice3CU", + P2(UINT, INT), R0()) + +// Panics reporting an index or slice out of bounds error with a +// 64-bit index type. These are only used by 32-bit targets. +DEF_GO_RUNTIME(PANIC_EXTEND_INDEX, "runtime.goPanicExtendIndex", + P2(INT64, INT), R0()) +DEF_GO_RUNTIME(PANIC_EXTEND_INDEX_U, "runtime.goPanicExtendIndexU", + P2(UINT64, INT), R0()) +DEF_GO_RUNTIME(PANIC_EXTEND_SLICE_ALEN, "runtime.goPanicExtendSliceAlen", + P2(INT64, INT), R0()) +DEF_GO_RUNTIME(PANIC_EXTEND_SLICE_ALEN_U, "runtime.goPanicExtendSliceAlenU", + P2(UINT64, INT), R0()) +DEF_GO_RUNTIME(PANIC_EXTEND_SLICE_ACAP, "runtime.goPanicExtendSliceAcap", + P2(INT64, INT), R0()) +DEF_GO_RUNTIME(PANIC_EXTEND_SLICE_ACAP_U, "runtime.goPanicExtendSliceAcapU", + P2(UINT64, INT), R0()) +DEF_GO_RUNTIME(PANIC_EXTEND_SLICE_B, "runtime.goPanicExtendSliceB", + P2(INT64, INT), R0()) +DEF_GO_RUNTIME(PANIC_EXTEND_SLICE_B_U, "runtime.goPanicExtendSliceBU", + P2(UINT64, INT), R0()) +DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_ALEN, "runtime.goPanicExtendSlice3Alen", + P2(INT64, INT), R0()) +DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_ALEN_U, "runtime.goPanicExtendSlice3AlenU", + P2(UINT64, INT), R0()) +DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_ACAP, "runtime.goPanicExtendSlice3Acap", + P2(INT64, INT), R0()) +DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_ACAP_U, "runtime.goPanicExtendSlice3AcapU", + P2(UINT64, INT), R0()) +DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_B, "runtime.goPanicExtendSlice3B", + P2(INT64, INT), R0()) +DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_B_U, "runtime.goPanicExtendSlice3BU", + P2(UINT64, INT), R0()) +DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_C, "runtime.goPanicExtendSlice3C", + P2(INT64, INT), R0()) +DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_C_U, "runtime.goPanicExtendSlice3CU", + P2(UINT64, INT), R0()) + // Remove helper macros. #undef ABFT6 #undef ABFT2 diff --git a/libgo/go/runtime/error.go b/libgo/go/runtime/error.go index b1a3f68a066..0c7f6319884 100644 --- a/libgo/go/runtime/error.go +++ b/libgo/go/runtime/error.go @@ -79,6 +79,21 @@ func unquote(s string) string { return string(r[:j]) } +//go:nosplit +// itoa converts val to a decimal representation. The result is +// written somewhere within buf and the location of the result is returned. +// buf must be at least 20 bytes. +func itoa(buf []byte, val uint64) []byte { + i := len(buf) - 1 + for val >= 10 { + buf[i] = byte(val%10 + '0') + i-- + val /= 10 + } + buf[i] = byte(val + '0') + return buf[i:] +} + // An errorString represents a runtime error described by a single string. type errorString string @@ -114,6 +129,99 @@ func (e plainError) Error() string { return string(e) } +// An boundsError represents a an indexing or slicing operation gone wrong. +type boundsError struct { + x int64 + y int + // Values in an index or slice expression can be signed or unsigned. + // That means we'd need 65 bits to encode all possible indexes, from -2^63 to 2^64-1. + // Instead, we keep track of whether x should be interpreted as signed or unsigned. + // y is known to be nonnegative and to fit in an int. + signed bool + code boundsErrorCode +} + +type boundsErrorCode uint8 + +const ( + boundsIndex boundsErrorCode = iota // s[x], 0 <= x < len(s) failed + + boundsSliceAlen // s[?:x], 0 <= x <= len(s) failed + boundsSliceAcap // s[?:x], 0 <= x <= cap(s) failed + boundsSliceB // s[x:y], 0 <= x <= y failed (but boundsSliceA didn't happen) + + boundsSlice3Alen // s[?:?:x], 0 <= x <= len(s) failed + boundsSlice3Acap // s[?:?:x], 0 <= x <= cap(s) failed + boundsSlice3B // s[?:x:y], 0 <= x <= y failed (but boundsSlice3A didn't happen) + boundsSlice3C // s[x:y:?], 0 <= x <= y failed (but boundsSlice3A/B didn't happen) + + // Note: in the above, len(s) and cap(s) are stored in y +) + +// boundsErrorFmts provide error text for various out-of-bounds panics. +// Note: if you change these strings, you should adjust the size of the buffer +// in boundsError.Error below as well. +var boundsErrorFmts = [...]string{ + boundsIndex: "index out of range [%x] with length %y", + boundsSliceAlen: "slice bounds out of range [:%x] with length %y", + boundsSliceAcap: "slice bounds out of range [:%x] with capacity %y", + boundsSliceB: "slice bounds out of range [%x:%y]", + boundsSlice3Alen: "slice bounds out of range [::%x] with length %y", + boundsSlice3Acap: "slice bounds out of range [::%x] with capacity %y", + boundsSlice3B: "slice bounds out of range [:%x:%y]", + boundsSlice3C: "slice bounds out of range [%x:%y:]", +} + +// boundsNegErrorFmts are overriding formats if x is negative. In this case there's no need to report y. +var boundsNegErrorFmts = [...]string{ + boundsIndex: "index out of range [%x]", + boundsSliceAlen: "slice bounds out of range [:%x]", + boundsSliceAcap: "slice bounds out of range [:%x]", + boundsSliceB: "slice bounds out of range [%x:]", + boundsSlice3Alen: "slice bounds out of range [::%x]", + boundsSlice3Acap: "slice bounds out of range [::%x]", + boundsSlice3B: "slice bounds out of range [:%x:]", + boundsSlice3C: "slice bounds out of range [%x::]", +} + +func (e boundsError) RuntimeError() {} + +func appendIntStr(b []byte, v int64, signed bool) []byte { + if signed && v < 0 { + b = append(b, '-') + v = -v + } + var buf [20]byte + b = append(b, itoa(buf[:], uint64(v))...) + return b +} + +func (e boundsError) Error() string { + fmt := boundsErrorFmts[e.code] + if e.signed && e.x < 0 { + fmt = boundsNegErrorFmts[e.code] + } + // max message length is 99: "runtime error: slice bounds out of range [::%x] with capacity %y" + // x can be at most 20 characters. y can be at most 19. + b := make([]byte, 0, 100) + b = append(b, "runtime error: "...) + for i := 0; i < len(fmt); i++ { + c := fmt[i] + if c != '%' { + b = append(b, c) + continue + } + i++ + switch fmt[i] { + case 'x': + b = appendIntStr(b, e.x, e.signed) + case 'y': + b = appendIntStr(b, int64(e.y), true) + } + } + return string(b) +} + type stringer interface { String() string } diff --git a/libgo/go/runtime/panic.go b/libgo/go/runtime/panic.go index 88c0a4d8fd2..58684305ac0 100644 --- a/libgo/go/runtime/panic.go +++ b/libgo/go/runtime/panic.go @@ -23,81 +23,189 @@ import ( //go:linkname makefuncreturning runtime.makefuncreturning //go:linkname gorecover runtime.gorecover //go:linkname deferredrecover runtime.deferredrecover +//go:linkname goPanicIndex runtime.goPanicIndex +//go:linkname goPanicIndexU runtime.goPanicIndexU +//go:linkname goPanicSliceAlen runtime.goPanicSliceAlen +//go:linkname goPanicSliceAlenU runtime.goPanicSliceAlenU +//go:linkname goPanicSliceAcap runtime.goPanicSliceAcap +//go:linkname goPanicSliceAcapU runtime.goPanicSliceAcapU +//go:linkname goPanicSliceB runtime.goPanicSliceB +//go:linkname goPanicSliceBU runtime.goPanicSliceBU +//go:linkname goPanicSlice3Alen runtime.goPanicSlice3Alen +//go:linkname goPanicSlice3AlenU runtime.goPanicSlice3AlenU +//go:linkname goPanicSlice3Acap runtime.goPanicSlice3Acap +//go:linkname goPanicSlice3AcapU runtime.goPanicSlice3AcapU +//go:linkname goPanicSlice3B runtime.goPanicSlice3B +//go:linkname goPanicSlice3BU runtime.goPanicSlice3BU +//go:linkname goPanicSlice3C runtime.goPanicSlice3C +//go:linkname goPanicSlice3CU runtime.goPanicSlice3CU //go:linkname panicmem runtime.panicmem // Temporary for C code to call: //go:linkname throw runtime.throw -// Calling panic with one of the errors below will call errorString.Error -// which will call mallocgc to concatenate strings. That will fail if -// malloc is locked, causing a confusing error message. Throw a better -// error message instead. -func panicCheckMalloc(err error) { +// Check to make sure we can really generate a panic. If the panic +// was generated from the runtime, or from inside malloc, then convert +// to a throw of msg. +// pc should be the program counter of the compiler-generated code that +// triggered this panic. +func panicCheck1(pc uintptr, msg string) { + name, _, _, _ := funcfileline(pc-1, -1) + if hasPrefix(name, "runtime.") { + throw(msg) + } + // TODO: is this redundant? How could we be in malloc + // but not in the runtime? runtime/internal/*, maybe? gp := getg() if gp != nil && gp.m != nil && gp.m.mallocing != 0 { - throw(string(err.(errorString))) + throw(msg) } } -var indexError = error(errorString("index out of range")) +// Same as above, but calling from the runtime is allowed. +// +// Using this function is necessary for any panic that may be +// generated by runtime.sigpanic, since those are always called by the +// runtime. +func panicCheck2(err string) { + // panic allocates, so to avoid recursive malloc, turn panics + // during malloc into throws. + gp := getg() + if gp != nil && gp.m != nil && gp.m.mallocing != 0 { + throw(err) + } +} -// The panicindex, panicslice, and panicdivide functions are called by +// Many of the following panic entry-points turn into throws when they +// happen in various runtime contexts. These should never happen in +// the runtime, and if they do, they indicate a serious issue and +// should not be caught by user code. +// +// The panic{Index,Slice,divide,shift} functions are called by // code generated by the compiler for out of bounds index expressions, -// out of bounds slice expressions, and division by zero. The -// panicdivide (again), panicoverflow, panicfloat, and panicmem +// out of bounds slice expressions, division by zero, and shift by negative. +// The panicdivide (again), panicoverflow, panicfloat, and panicmem // functions are called by the signal handler when a signal occurs // indicating the respective problem. // -// Since panicindex and panicslice are never called directly, and +// Since panic{Index,Slice,shift} are never called directly, and // since the runtime package should never have an out of bounds slice -// or array reference, if we see those functions called from the +// or array reference or negative shift, if we see those functions called from the // runtime package we turn the panic into a throw. That will dump the // entire runtime stack for easier debugging. +// +// The entry points called by the signal handler will be called from +// runtime.sigpanic, so we can't disallow calls from the runtime to +// these (they always look like they're called from the runtime). +// Hence, for these, we just check for clearly bad runtime conditions. + +// failures in the comparisons for s[x], 0 <= x < y (y == len(s)) +func goPanicIndex(x int, y int) { + panicCheck1(getcallerpc(), "index out of range") + panic(boundsError{x: int64(x), signed: true, y: y, code: boundsIndex}) +} +func goPanicIndexU(x uint, y int) { + panicCheck1(getcallerpc(), "index out of range") + panic(boundsError{x: int64(x), signed: false, y: y, code: boundsIndex}) +} -func panicindex() { - name, _, _, _ := funcfileline(getcallerpc()-1, -1) - if hasPrefix(name, "runtime.") { - throw(string(indexError.(errorString))) - } - panicCheckMalloc(indexError) - panic(indexError) +// failures in the comparisons for s[:x], 0 <= x <= y (y == len(s) or cap(s)) +func goPanicSliceAlen(x int, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSliceAlen}) +} +func goPanicSliceAlenU(x uint, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceAlen}) +} +func goPanicSliceAcap(x int, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSliceAcap}) +} +func goPanicSliceAcapU(x uint, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceAcap}) } -var sliceError = error(errorString("slice bounds out of range")) +// failures in the comparisons for s[x:y], 0 <= x <= y +func goPanicSliceB(x int, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSliceB}) +} +func goPanicSliceBU(x uint, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceB}) +} -func panicslice() { - name, _, _, _ := funcfileline(getcallerpc()-1, -1) - if hasPrefix(name, "runtime.") { - throw(string(sliceError.(errorString))) - } - panicCheckMalloc(sliceError) - panic(sliceError) +// failures in the comparisons for s[::x], 0 <= x <= y (y == len(s) or cap(s)) +func goPanicSlice3Alen(x int, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSlice3Alen}) +} +func goPanicSlice3AlenU(x uint, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3Alen}) +} +func goPanicSlice3Acap(x int, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSlice3Acap}) +} +func goPanicSlice3AcapU(x uint, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3Acap}) +} + +// failures in the comparisons for s[:x:y], 0 <= x <= y +func goPanicSlice3B(x int, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSlice3B}) +} +func goPanicSlice3BU(x uint, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3B}) +} + +// failures in the comparisons for s[x:y:], 0 <= x <= y +func goPanicSlice3C(x int, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSlice3C}) +} +func goPanicSlice3CU(x uint, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3C}) +} + +var shiftError = error(errorString("negative shift amount")) + +func panicshift() { + panicCheck1(getcallerpc(), "negative shift amount") + panic(shiftError) } var divideError = error(errorString("integer divide by zero")) func panicdivide() { - panicCheckMalloc(divideError) + panicCheck2("integer divide by zero") panic(divideError) } var overflowError = error(errorString("integer overflow")) func panicoverflow() { - panicCheckMalloc(overflowError) + panicCheck2("integer overflow") panic(overflowError) } var floatError = error(errorString("floating point error")) func panicfloat() { - panicCheckMalloc(floatError) + panicCheck2("floating point error") panic(floatError) } var memoryError = error(errorString("invalid memory address or nil pointer dereference")) func panicmem() { - panicCheckMalloc(memoryError) + panicCheck2("invalid memory address or nil pointer dereference") panic(memoryError) } diff --git a/libgo/go/runtime/panic32.go b/libgo/go/runtime/panic32.go new file mode 100644 index 00000000000..f68d4c71b0b --- /dev/null +++ b/libgo/go/runtime/panic32.go @@ -0,0 +1,108 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build 386 amd64p32 arm mips mipsle m68k nios2 sh shbe + +package runtime + +import _ "unsafe" // for go:linkname + +// For gccgo, use go:linkname to rename compiler-called functions to +// themselves, so that the compiler will export them. +// +//go:linkname goPanicExtendIndex runtime.goPanicExtendIndex +//go:linkname goPanicExtendIndexU runtime.goPanicExtendIndexU +//go:linkname goPanicExtendSliceAlen runtime.goPanicExtendSliceAlen +//go:linkname goPanicExtendSliceAlenU runtime.goPanicExtendSliceAlenU +//go:linkname goPanicExtendSliceAcap runtime.goPanicExtendSliceAcap +//go:linkname goPanicExtendSliceAcapU runtime.goPanicExtendSliceAcapU +//go:linkname goPanicExtendSliceB runtime.goPanicExtendSliceB +//go:linkname goPanicExtendSliceBU runtime.goPanicExtendSliceBU +//go:linkname goPanicExtendSlice3Alen runtime.goPanicExtendSlice3Alen +//go:linkname goPanicExtendSlice3AlenU runtime.goPanicExtendSlice3AlenU +//go:linkname goPanicExtendSlice3Acap runtime.goPanicExtendSlice3Acap +//go:linkname goPanicExtendSlice3AcapU runtime.goPanicExtendSlice3AcapU +//go:linkname goPanicExtendSlice3B runtime.goPanicExtendSlice3B +//go:linkname goPanicExtendSlice3BU runtime.goPanicExtendSlice3BU +//go:linkname goPanicExtendSlice3C runtime.goPanicExtendSlice3C +//go:linkname goPanicExtendSlice3CU runtime.goPanicExtendSlice3CU + +// Additional index/slice error paths for 32-bit platforms. +// Used when the high word of a 64-bit index is not zero. + +// failures in the comparisons for s[x], 0 <= x < y (y == len(s)) +func goPanicExtendIndex(x int64, y int) { + panicCheck1(getcallerpc(), "index out of range") + panic(boundsError{x: x, signed: true, y: y, code: boundsIndex}) +} +func goPanicExtendIndexU(x uint64, y int) { + panicCheck1(getcallerpc(), "index out of range") + panic(boundsError{x: int64(x), signed: false, y: y, code: boundsIndex}) +} + +// failures in the comparisons for s[:x], 0 <= x <= y (y == len(s) or cap(s)) +func goPanicExtendSliceAlen(x int64, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: x, signed: true, y: y, code: boundsSliceAlen}) +} +func goPanicExtendSliceAlenU(x uint64, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceAlen}) +} +func goPanicExtendSliceAcap(x int64, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: x, signed: true, y: y, code: boundsSliceAcap}) +} +func goPanicExtendSliceAcapU(x uint64, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceAcap}) +} + +// failures in the comparisons for s[x:y], 0 <= x <= y +func goPanicExtendSliceB(x int64, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: x, signed: true, y: y, code: boundsSliceB}) +} +func goPanicExtendSliceBU(x uint64, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceB}) +} + +// failures in the comparisons for s[::x], 0 <= x <= y (y == len(s) or cap(s)) +func goPanicExtendSlice3Alen(x int64, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: x, signed: true, y: y, code: boundsSlice3Alen}) +} +func goPanicExtendSlice3AlenU(x uint64, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3Alen}) +} +func goPanicExtendSlice3Acap(x int64, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: x, signed: true, y: y, code: boundsSlice3Acap}) +} +func goPanicExtendSlice3AcapU(x uint64, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3Acap}) +} + +// failures in the comparisons for s[:x:y], 0 <= x <= y +func goPanicExtendSlice3B(x int64, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: x, signed: true, y: y, code: boundsSlice3B}) +} +func goPanicExtendSlice3BU(x uint64, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3B}) +} + +// failures in the comparisons for s[x:y:], 0 <= x <= y +func goPanicExtendSlice3C(x int64, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: x, signed: true, y: y, code: boundsSlice3C}) +} +func goPanicExtendSlice3CU(x uint64, y int) { + panicCheck1(getcallerpc(), "slice bounds out of range") + panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3C}) +} diff --git a/libgo/mkruntimeinc.sh b/libgo/mkruntimeinc.sh index cd95595c731..d29da9b6846 100755 --- a/libgo/mkruntimeinc.sh +++ b/libgo/mkruntimeinc.sh @@ -15,14 +15,15 @@ rm -f runtime.inc.tmp2 runtime.inc.tmp3 # types and should not be exported back to C # semt is a Go translation of the C type sem_t; it fails to convert on # some systems and need not be exported back to C. -# sigset conflicts with system type sigset on AIX, so we need to rename it +# sigset conflicts with system type sigset on AIX, so we need to rename it. +# boundsError has a field name that is a C keyword, and we don't need it. grep -v "#define _" ${IN} | grep -v "#define [cm][01234] " | grep -v "#define empty " | grep -v "#define \\$" > runtime.inc.tmp2 for pattern in '_[GP][a-z]' _Max _Lock _Sig _Trace _MHeap _Num do grep "#define $pattern" ${IN} >> runtime.inc.tmp2 done -TYPES="_Complex_lock _Reader_lock semt" +TYPES="_Complex_lock _Reader_lock semt boundsError" for TYPE in $TYPES do sed -e '/struct '${TYPE}' {/,/^}/s/^.*$//' runtime.inc.tmp2 > runtime.inc.tmp3; -- 2.30.2